@neta-art/cohub 7.0.0 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as SessionAccessApi, A as isTerminalUiCommandStatus, B as BOARD_ANIMATION_CHANNELS, C as UI_COMMAND_DEFAULT_TIMEOUT_MS, D as UI_COMMAND_SETTLEMENT_GRACE_SECONDS, E as UI_COMMAND_PENDING_TTL_SECONDS, F as BoardItemPatchSchema, G as BoardTrackSchema, H as BoardCompositionInputSchema, I as BoardSemanticMutationSchema, J as BOARD_ARROW_STROKE_SIZE, K as parseBoardCompositionInput, L as BoardEffectInputSchema, M as parseUiCommand, N as parseUiCommandId, O as UI_COMMAND_TERMINAL_TTL_SECONDS, P as BoardAuthoringItemSchema, Q as DEFAULT_BOARD_RENDER_LIMITS, R as BoardEffectSchema, S as ensureRealtimeConnected, T as UI_COMMAND_PAYLOAD_MAX_BYTES, U as BoardCompositionSchema, V as BOARD_ANIMATION_CHANNEL_CAPABILITIES, W as BoardProceduralClipSchema, X as BOARD_BUILTIN_CLIP_KINDS, Y as BOARD_BUILTIN_CAPABILITIES, Z as BOARD_BUILTIN_EFFECT_KINDS, _ as SessionGenerationStreamClient, a as ReferralsApi, at as ModelsApi, b as SessionPatchReducer, c as UiCommandsApi, ct as ChannelsApi, d as SpaceClient, et as ReferencesApi, f as SpacePublicFilesApi, g as buildSpacePath, h as buildSpaceInvitePath, i as WorksApi, it as PromptsApi, j as isUiSurfaceMethod, k as UI_COMMAND_VERSION, l as TasksApi, m as PublicInviteApi, n as createHttpClient, nt as PublicAssetsApi, o as UsersApi, ot as GenerationsApi, p as SpacesApi, q as BoardConnectionSchema, r as WorkCommerceApi, rt as SkillsApi, s as UserApi, st as CronJobsApi, t as CohubHttpClient, tt as SearchApi, u as BoardClient, v as createSessionGenerationStreamClient, w as UI_COMMAND_MAX_TIMEOUT_MS, x as createSessionPatchReducer, y as parseAssistantMessageCommit, z as parseBoardEffectInput } from "./chunks/http.js";
1
+ import { $ as BOARD_BUILTIN_EFFECT_KINDS, A as DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, B as BoardEffectSchema, C as createSessionPatchReducer, D as DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, E as DESKTOP_COMMAND_MAX_TIMEOUT_MS, F as parseDesktopCommandId, G as BoardCompositionSchema, H as BOARD_ANIMATION_CHANNELS, I as BoardAuthoringItemSchema, J as parseBoardCompositionInput, K as BoardProceduralClipSchema, L as BoardItemPatchSchema, M as isDesktopCallMethod, N as isTerminalDesktopCommandStatus, O as DESKTOP_COMMAND_PENDING_TTL_SECONDS, P as parseDesktopCommand, Q as BOARD_BUILTIN_CLIP_KINDS, R as BoardSemanticMutationSchema, S as SessionPatchReducer, T as DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, U as BOARD_ANIMATION_CHANNEL_CAPABILITIES, V as parseBoardEffectInput, W as BoardCompositionInputSchema, X as BOARD_ARROW_STROKE_SIZE, Y as BoardConnectionSchema, Z as BOARD_BUILTIN_CAPABILITIES, _ as buildSpaceInvitePath, a as DesktopCommandsApi, at as SkillsApi, b as createSessionGenerationStreamClient, c as ReferralsApi, ct as GenerationsApi, d as TasksApi, et as DEFAULT_BOARD_RENDER_LIMITS, f as BoardClient, g as PublicInviteApi, h as SpacesApi, i as WorkCommerceApi, it as PublicAssetsApi, j as DESKTOP_COMMAND_VERSION, k as DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, l as UsersApi, lt as CronJobsApi, m as SpacePublicFilesApi, n as createHttpClient, nt as ReferencesApi, o as UiCommandsApi, ot as PromptsApi, p as SpaceClient, q as BoardTrackSchema, r as AppCommerceApi, rt as SearchApi, s as AppsApi, st as ModelsApi, t as CohubHttpClient, tt as SessionAccessApi, u as UserApi, ut as ChannelsApi, v as buildSpacePath, w as ensureRealtimeConnected, x as parseAssistantMessageCommit, y as SessionGenerationStreamClient, z as BoardEffectInputSchema } from "./chunks/http.js";
2
2
  import { _ as requestSourceToHeaders, a as sanitizeAccessToken, b as REALTIME_ROOM_EVENT_NAME_PATTERN, c as REQUEST_SOURCE_VIA_MAX_LENGTH, d as isRequestSourceEmpty, f as isRequestSourceUuid, g as readRequestSourceFromEnv, h as parseRequestSourceFromHeaders, i as matchesUnauthorizedErrorToken, l as hasRequestSourceIdentity, m as normalizeRequestSource, n as HttpTransport, o as COHUB_SOURCE_HEADER, p as mergeRequestSourceIntoMeta, r as joinApiUrl, s as COHUB_SOURCE_HEADER_NAMES, t as HttpError, u as isRequestSourceClientId, v as resolveRequestSourceChannel, y as isUuid } from "./chunks/transport.js";
3
3
  import { a as resolveApiBaseUrl, c as resolveWebsocketUrl, i as normalizeWebsocketUrl, n as normalizeBaseUrl, o as resolveCohubEnvironment, r as normalizeVoiceInputWebsocketUrl, s as resolveVoiceInputWebsocketUrl, t as COHUB_ENVIRONMENTS } from "./chunks/environment.js";
4
4
  import { a as extractBillingPayload, c as isFeatureNotEntitledError, i as FEATURE_NOT_ENTITLED_ERROR_CODE, l as isHttpErrorCode, n as createWebsocketClient, o as isBillingAccessBlockedCode, r as BILLING_ACCESS_BLOCKED_ERROR_CODE, s as isBillingAccessBlockedError, t as WebsocketClient } from "./chunks/websocket.js";
@@ -1149,7 +1149,8 @@ const RESERVED_PLATFORM_PATH_SEGMENTS = Object.freeze([
1149
1149
  "u",
1150
1150
  "user",
1151
1151
  "users",
1152
- "work-auth"
1152
+ "work-auth",
1153
+ "app-auth"
1153
1154
  ]);
1154
1155
  new Set(RESERVED_PLATFORM_PATH_SEGMENTS);
1155
1156
  [...RESERVED_PLATFORM_PATH_SEGMENTS];
@@ -1164,34 +1165,34 @@ function parseSpaceSlug(value) {
1164
1165
  return SPACE_SLUG_PATTERN.test(normalized) ? normalized : null;
1165
1166
  }
1166
1167
  //#endregion
1167
- //#region ../protocol/dist/work-surface.js
1168
- const WORK_SURFACE_PROTOCOL = "cohub.surface";
1169
- const WORK_SURFACE_READY_TIMEOUT_MS = 1e4;
1170
- const WORK_SURFACE_REQUEST_TIMEOUT_MS = 15e3;
1171
- const WORK_COMPOSER_CHIP_KEY_MAX_LENGTH = 80;
1172
- const WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH = 120;
1173
- const WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES = 32 * 1024;
1168
+ //#region ../protocol/dist/app-surface.js
1169
+ const APP_SURFACE_PROTOCOL = "cohub.app.surface";
1170
+ const APP_SURFACE_READY_TIMEOUT_MS = 1e4;
1171
+ const APP_SURFACE_REQUEST_TIMEOUT_MS = 15e3;
1172
+ const APP_COMPOSER_CHIP_KEY_MAX_LENGTH = 80;
1173
+ const APP_COMPOSER_CHIP_LABEL_MAX_LENGTH = 120;
1174
+ const APP_COMPOSER_CHIP_CONTENT_MAX_BYTES = 32 * 1024;
1174
1175
  const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
1175
- const isSurfaceEnvelope = (value) => isRecord(value) && value.protocol === "cohub.surface" && value.version === 1;
1176
- const parseWorkSurfaceReady = (value) => {
1176
+ const isSurfaceEnvelope = (value) => isRecord(value) && value.protocol === "cohub.app.surface" && value.version === 1;
1177
+ const parseAppSurfaceReady = (value) => {
1177
1178
  if (!isSurfaceEnvelope(value) || value.type !== "ready") return null;
1178
1179
  const methods = Array.isArray(value.methods) ? value.methods.filter((method) => typeof method === "string" && Boolean(method)) : [];
1179
1180
  return {
1180
- protocol: WORK_SURFACE_PROTOCOL,
1181
+ protocol: APP_SURFACE_PROTOCOL,
1181
1182
  version: 1,
1182
1183
  type: "ready",
1183
1184
  methods
1184
1185
  };
1185
1186
  };
1186
- const parseWorkSurfaceResponse = (value) => {
1187
+ const parseAppSurfaceResponse = (value) => {
1187
1188
  if (!isSurfaceEnvelope(value) || value.type !== "response") return null;
1188
1189
  if (typeof value.requestId !== "string" || !value.requestId) return null;
1189
1190
  const error = isRecord(value.error) ? {
1190
1191
  code: typeof value.error.code === "string" && value.error.code ? value.error.code : "surface_error",
1191
- message: typeof value.error.message === "string" ? value.error.message : "Work surface call failed"
1192
+ message: typeof value.error.message === "string" ? value.error.message : "App surface call failed"
1192
1193
  } : void 0;
1193
1194
  return {
1194
- protocol: WORK_SURFACE_PROTOCOL,
1195
+ protocol: APP_SURFACE_PROTOCOL,
1195
1196
  version: 1,
1196
1197
  type: "response",
1197
1198
  requestId: value.requestId,
@@ -1205,7 +1206,7 @@ const parseComposerChipKey = (value) => {
1205
1206
  if (!key || key.length > 80) return null;
1206
1207
  return key;
1207
1208
  };
1208
- const parseWorkComposerChipSet = (value) => {
1209
+ const parseAppComposerChipSet = (value) => {
1209
1210
  if (!isSurfaceEnvelope(value) || value.type !== "composer.chip.set" || !isRecord(value.chip)) return null;
1210
1211
  const key = parseComposerChipKey(value.chip.key);
1211
1212
  if (!key || typeof value.chip.label !== "string" || typeof value.chip.content !== "string") return null;
@@ -1214,7 +1215,7 @@ const parseWorkComposerChipSet = (value) => {
1214
1215
  if (!value.chip.content.trim()) return null;
1215
1216
  if (new TextEncoder().encode(value.chip.content).length > 32768) return null;
1216
1217
  return {
1217
- protocol: WORK_SURFACE_PROTOCOL,
1218
+ protocol: APP_SURFACE_PROTOCOL,
1218
1219
  version: 1,
1219
1220
  type: "composer.chip.set",
1220
1221
  chip: {
@@ -1224,24 +1225,24 @@ const parseWorkComposerChipSet = (value) => {
1224
1225
  }
1225
1226
  };
1226
1227
  };
1227
- const parseWorkComposerChipClear = (value) => {
1228
+ const parseAppComposerChipClear = (value) => {
1228
1229
  if (!isSurfaceEnvelope(value) || value.type !== "composer.chip.clear") return null;
1229
1230
  const key = parseComposerChipKey(value.key);
1230
1231
  return key ? {
1231
- protocol: WORK_SURFACE_PROTOCOL,
1232
+ protocol: APP_SURFACE_PROTOCOL,
1232
1233
  version: 1,
1233
1234
  type: "composer.chip.clear",
1234
1235
  key
1235
1236
  } : null;
1236
1237
  };
1237
- const parseWorkSurfaceRequest = (value) => {
1238
+ const parseAppSurfaceRequest = (value) => {
1238
1239
  if (!isSurfaceEnvelope(value) || value.type !== "request") return null;
1239
1240
  if (typeof value.requestId !== "string" || !value.requestId) return null;
1240
1241
  if (typeof value.method !== "string" || !value.method) return null;
1241
- const commandId = parseUiCommandId(value.commandId);
1242
+ const commandId = parseDesktopCommandId(value.commandId);
1242
1243
  if (!commandId) return null;
1243
1244
  return {
1244
- protocol: WORK_SURFACE_PROTOCOL,
1245
+ protocol: APP_SURFACE_PROTOCOL,
1245
1246
  version: 1,
1246
1247
  type: "request",
1247
1248
  requestId: value.requestId,
@@ -1250,37 +1251,45 @@ const parseWorkSurfaceRequest = (value) => {
1250
1251
  commandId
1251
1252
  };
1252
1253
  };
1253
- const buildWorkSurfaceReady = (methods) => ({
1254
- protocol: WORK_SURFACE_PROTOCOL,
1254
+ const buildAppSurfaceReady = (methods) => ({
1255
+ protocol: APP_SURFACE_PROTOCOL,
1255
1256
  version: 1,
1256
1257
  type: "ready",
1257
1258
  methods: [...methods]
1258
1259
  });
1259
- const buildWorkSurfaceRequest = (input) => ({
1260
- protocol: WORK_SURFACE_PROTOCOL,
1260
+ const buildAppSurfaceRequest = (input) => ({
1261
+ protocol: APP_SURFACE_PROTOCOL,
1261
1262
  version: 1,
1262
1263
  type: "request",
1263
1264
  ...input
1264
1265
  });
1265
- const buildWorkSurfaceResponse = (input) => ({
1266
- protocol: WORK_SURFACE_PROTOCOL,
1266
+ const buildAppSurfaceResponse = (input) => ({
1267
+ protocol: APP_SURFACE_PROTOCOL,
1267
1268
  version: 1,
1268
1269
  type: "response",
1269
1270
  ...input
1270
1271
  });
1271
- const buildWorkComposerChipSet = (chip) => ({
1272
- protocol: WORK_SURFACE_PROTOCOL,
1272
+ const buildAppComposerChipSet = (chip) => ({
1273
+ protocol: APP_SURFACE_PROTOCOL,
1273
1274
  version: 1,
1274
1275
  type: "composer.chip.set",
1275
1276
  chip
1276
1277
  });
1277
- const buildWorkComposerChipClear = (key) => ({
1278
- protocol: WORK_SURFACE_PROTOCOL,
1278
+ const buildAppComposerChipClear = (key) => ({
1279
+ protocol: APP_SURFACE_PROTOCOL,
1279
1280
  version: 1,
1280
1281
  type: "composer.chip.clear",
1281
1282
  key
1282
1283
  });
1283
1284
  //#endregion
1285
+ //#region ../protocol/dist/app-runtime.js
1286
+ const APP_RUNTIME_PROTOCOL = "cohub.app.runtime";
1287
+ const buildAppRuntimeReady = () => ({
1288
+ protocol: APP_RUNTIME_PROTOCOL,
1289
+ version: 1,
1290
+ type: "ready"
1291
+ });
1292
+ //#endregion
1284
1293
  //#region src/apis/billing.ts
1285
1294
  var BillingApi = class {
1286
1295
  transport;
@@ -1353,7 +1362,7 @@ var BillingApi = class {
1353
1362
  }
1354
1363
  };
1355
1364
  //#endregion
1356
- //#region src/apis/work-realtime.ts
1365
+ //#region src/apis/app-realtime.ts
1357
1366
  const randomId = () => globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`;
1358
1367
  const textEncoder = new TextEncoder();
1359
1368
  const byteLength = (text) => textEncoder.encode(text).byteLength;
@@ -1377,7 +1386,7 @@ const isRoomEvent = (event, roomId) => {
1377
1386
  if (event.domain !== "room") return false;
1378
1387
  return event.payload.roomId === roomId || event.rooms?.includes(`room:${roomId}`);
1379
1388
  };
1380
- var WorkRoom = class {
1389
+ var AppRoom = class {
1381
1390
  websocket;
1382
1391
  admission;
1383
1392
  id;
@@ -1569,7 +1578,7 @@ var WorkRoom = class {
1569
1578
  if (this.state === "expired") return;
1570
1579
  this.setState("closed");
1571
1580
  this.dispose();
1572
- console.warn("[Cohub WorkRoom] failed to rejoin room", error);
1581
+ console.warn("[Cohub AppRoom] failed to rejoin room", error);
1573
1582
  }
1574
1583
  }
1575
1584
  async joinOverWebsocket(isReconnect) {
@@ -1734,7 +1743,7 @@ var WorkRoom = class {
1734
1743
  this.expiryTimer = null;
1735
1744
  }
1736
1745
  };
1737
- var WorkRealtimeApi = class {
1746
+ var AppRealtimeApi = class {
1738
1747
  transport;
1739
1748
  websocket;
1740
1749
  getContext;
@@ -1744,7 +1753,7 @@ var WorkRealtimeApi = class {
1744
1753
  this.getContext = getContext;
1745
1754
  }
1746
1755
  async createRoom(input = {}) {
1747
- const admission = await this.transport.request(`/api/works/${encodeURIComponent(await this.requireWorkId())}/realtime/rooms`, {
1756
+ const admission = await this.transport.request(`/api/apps/${encodeURIComponent(await this.requireAppId())}/realtime/rooms`, {
1748
1757
  method: "POST",
1749
1758
  headers: { "Content-Type": "application/json" },
1750
1759
  body: JSON.stringify(input)
@@ -1752,20 +1761,20 @@ var WorkRealtimeApi = class {
1752
1761
  return this.openRoom(admission);
1753
1762
  }
1754
1763
  async joinRoom(input) {
1755
- const admission = await this.transport.request(`/api/works/${encodeURIComponent(await this.requireWorkId())}/realtime/rooms/join`, {
1764
+ const admission = await this.transport.request(`/api/apps/${encodeURIComponent(await this.requireAppId())}/realtime/rooms/join`, {
1756
1765
  method: "POST",
1757
1766
  headers: { "Content-Type": "application/json" },
1758
1767
  body: JSON.stringify(input)
1759
1768
  });
1760
1769
  return this.openRoom(admission);
1761
1770
  }
1762
- async requireWorkId() {
1771
+ async requireAppId() {
1763
1772
  const context = await this.getContext();
1764
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
1765
- return context.work.id;
1773
+ if (!context?.app?.id) throw new Error("App context is unavailable — not running inside a published app runtime.");
1774
+ return context.app.id;
1766
1775
  }
1767
1776
  async openRoom(admission) {
1768
- const room = new WorkRoom(this.websocket, admission);
1777
+ const room = new AppRoom(this.websocket, admission);
1769
1778
  try {
1770
1779
  await room.connect();
1771
1780
  return room;
@@ -1775,11 +1784,15 @@ var WorkRealtimeApi = class {
1775
1784
  }
1776
1785
  }
1777
1786
  };
1787
+ /** @deprecated Use `AppRealtimeApi`. */
1788
+ var WorkRealtimeApi = class extends AppRealtimeApi {};
1789
+ /** @deprecated Use `AppRoom`. */
1790
+ var WorkRoom = class extends AppRoom {};
1778
1791
  //#endregion
1779
- //#region src/work-surface.ts
1792
+ //#region src/app-surface.ts
1780
1793
  /**
1781
- * Explicit app origins, not a Cohub suffix match: Works themselves are
1782
- * served from Cohub subdomains, so a suffix match would let one Work call into
1794
+ * Explicit app origins, not a Cohub suffix match: apps themselves are
1795
+ * served from Cohub subdomains, so a suffix match would let one app call into
1783
1796
  * another and turn a subdomain takeover into surface access.
1784
1797
  */
1785
1798
  const COHUB_APP_ORIGINS = [
@@ -1802,7 +1815,7 @@ const resolveEmbedderOrigin = () => {
1802
1815
  return null;
1803
1816
  }
1804
1817
  };
1805
- var WorkSurfaceApi = class {
1818
+ var AppSurfaceApi = class {
1806
1819
  handlers = /* @__PURE__ */ new Map();
1807
1820
  listening = false;
1808
1821
  allowedOrigins = null;
@@ -1820,7 +1833,7 @@ var WorkSurfaceApi = class {
1820
1833
  }
1821
1834
  handle(method, handler) {
1822
1835
  const name = method.trim();
1823
- if (!name) throw new Error("Work surface method name is required");
1836
+ if (!name) throw new Error("App surface method name is required");
1824
1837
  this.handlers.set(name, handler);
1825
1838
  this.start();
1826
1839
  this.announce();
@@ -1835,17 +1848,17 @@ var WorkSurfaceApi = class {
1835
1848
  return [...this.handlers.keys()];
1836
1849
  }
1837
1850
  setComposerChip(chip) {
1838
- const message = parseWorkComposerChipSet(buildWorkComposerChipSet(chip));
1839
- if (!message) throw new Error("Invalid Work composer chip");
1851
+ const message = parseAppComposerChipSet(buildAppComposerChipSet(chip));
1852
+ if (!message) throw new Error("Invalid App composer chip");
1840
1853
  this.post(message);
1841
1854
  }
1842
1855
  clearComposerChip(key) {
1843
- const message = parseWorkComposerChipClear(buildWorkComposerChipClear(key));
1844
- if (!message) throw new Error("Invalid Work composer chip key");
1856
+ const message = parseAppComposerChipClear(buildAppComposerChipClear(key));
1857
+ if (!message) throw new Error("Invalid App composer chip key");
1845
1858
  this.post(message);
1846
1859
  }
1847
1860
  announce() {
1848
- this.post(buildWorkSurfaceReady(this.methods));
1861
+ this.post(buildAppSurfaceReady(this.methods));
1849
1862
  }
1850
1863
  isTrusted(origin) {
1851
1864
  if (!origin || origin === "null") return false;
@@ -1866,7 +1879,7 @@ var WorkSurfaceApi = class {
1866
1879
  onMessage = (event) => {
1867
1880
  if (typeof window === "undefined" || event.source !== window.parent) return;
1868
1881
  if (!this.isTrusted(event.origin)) return;
1869
- const request = parseWorkSurfaceRequest(event.data);
1882
+ const request = parseAppSurfaceRequest(event.data);
1870
1883
  if (!request) return;
1871
1884
  const commandId = request.commandId;
1872
1885
  if (!commandId) return;
@@ -1876,24 +1889,24 @@ var WorkSurfaceApi = class {
1876
1889
  async dispatch(requestId, method, input, commandId) {
1877
1890
  const handler = this.handlers.get(method);
1878
1891
  if (!handler) {
1879
- this.post(buildWorkSurfaceResponse({
1892
+ this.post(buildAppSurfaceResponse({
1880
1893
  requestId,
1881
1894
  ok: false,
1882
1895
  error: {
1883
1896
  code: "method_not_found",
1884
- message: `This Work does not expose "${method}".`
1897
+ message: `This app does not expose "${method}".`
1885
1898
  }
1886
1899
  }));
1887
1900
  return;
1888
1901
  }
1889
1902
  try {
1890
1903
  await handler(input, { commandId });
1891
- this.post(buildWorkSurfaceResponse({
1904
+ this.post(buildAppSurfaceResponse({
1892
1905
  requestId,
1893
1906
  ok: true
1894
1907
  }));
1895
1908
  } catch (error) {
1896
- this.post(buildWorkSurfaceResponse({
1909
+ this.post(buildAppSurfaceResponse({
1897
1910
  requestId,
1898
1911
  ok: false,
1899
1912
  error: {
@@ -1912,8 +1925,10 @@ var WorkSurfaceApi = class {
1912
1925
  } catch {}
1913
1926
  }
1914
1927
  };
1928
+ /** @deprecated Use `AppSurfaceApi`. */
1929
+ var WorkSurfaceApi = class extends AppSurfaceApi {};
1915
1930
  //#endregion
1916
- //#region src/work-runtime.ts
1931
+ //#region src/app-runtime.ts
1917
1932
  const isBrowser$1 = () => typeof window !== "undefined" && typeof window.parent !== "undefined";
1918
1933
  const hasParent = () => isBrowser$1() && window.parent !== window;
1919
1934
  const getParentOrigin = () => {
@@ -1929,11 +1944,39 @@ const getParentOrigin = () => {
1929
1944
  const generateRequestId = () => globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`;
1930
1945
  /**
1931
1946
  * Bridge-mode transport: posts messages to `window.parent` (the Cohub host
1932
- * embedding the work in an iframe) and listens for the matching reply.
1947
+ * embedding the app in an iframe) and listens for the matching reply.
1933
1948
  * Behaviorally identical to the previous module-level `request()` helper.
1934
1949
  */
1935
1950
  var ParentBridgeTransport = class {
1936
1951
  trustedParentOrigin = null;
1952
+ contextListeners = /* @__PURE__ */ new Set();
1953
+ contextListener = null;
1954
+ subscribeContextChanged(listener) {
1955
+ if (!hasParent()) return () => {};
1956
+ this.contextListeners.add(listener);
1957
+ if (!this.contextListener) {
1958
+ this.contextListener = (event) => {
1959
+ if (event.source !== window.parent) return;
1960
+ const parentOrigin = this.trustedParentOrigin ?? getParentOrigin();
1961
+ if (parentOrigin && event.origin !== parentOrigin) return;
1962
+ const data = event.data;
1963
+ if (data?.type !== "cohub.app.context.changed" || !data.context) return;
1964
+ for (const current of this.contextListeners) current(data.context);
1965
+ };
1966
+ window.addEventListener("message", this.contextListener);
1967
+ const parentOrigin = this.trustedParentOrigin ?? getParentOrigin();
1968
+ if (parentOrigin) try {
1969
+ window.parent.postMessage(buildAppRuntimeReady(), parentOrigin);
1970
+ } catch {}
1971
+ }
1972
+ return () => {
1973
+ this.contextListeners.delete(listener);
1974
+ if (this.contextListeners.size === 0 && this.contextListener) {
1975
+ window.removeEventListener("message", this.contextListener);
1976
+ this.contextListener = null;
1977
+ }
1978
+ };
1979
+ }
1937
1980
  request(message, options) {
1938
1981
  const timeoutMs = options?.timeoutMs ?? 1200;
1939
1982
  const retryIntervalMs = options?.retryIntervalMs;
@@ -1968,7 +2011,7 @@ var ParentBridgeTransport = class {
1968
2011
  if (!data || data.requestId !== requestId) return;
1969
2012
  cleanup();
1970
2013
  this.trustedParentOrigin = event.origin;
1971
- if (data.type === "cohub.work.error") {
2014
+ if (data.type === "cohub.app.error") {
1972
2015
  reject(new Error(data.message));
1973
2016
  return;
1974
2017
  }
@@ -1981,64 +2024,64 @@ var ParentBridgeTransport = class {
1981
2024
  }
1982
2025
  };
1983
2026
  /**
1984
- * Broker-mode transport for standalone-deployed works. Opens a popup window to
2027
+ * Broker-mode transport for standalone-deployed apps. Opens a popup window to
1985
2028
  * the Cohub auth broker page, performs a ready-handshake, sends the request via
1986
2029
  * postMessage, and resolves with the broker's response. The popup is closed
1987
2030
  * after a single request is fulfilled (one-shot, per §7.2 of the plan).
1988
2031
  *
1989
2032
  * Non-interactive messages (`context`, `checkout-state`) are answered locally
1990
- * without opening a popup — the work already knows its own workId, and
1991
- * checkout state is not available on the work's own origin in broker mode.
2033
+ * without opening a popup — the app already knows its own appId, and
2034
+ * checkout state is not available on the app's own origin in broker mode.
1992
2035
  */
1993
2036
  var PopupBrokerTransport = class {
1994
2037
  brokerOrigin;
1995
- workId;
1996
- getWorkId;
2038
+ appId;
2039
+ getAppId;
1997
2040
  constructor(config) {
1998
2041
  this.brokerOrigin = config.brokerOrigin;
1999
- this.workId = config.workId;
2000
- this.getWorkId = config.getWorkId;
2001
- if (!this.workId && this.getWorkId) this.getWorkId();
2042
+ this.appId = config.appId;
2043
+ this.getAppId = config.getAppId;
2044
+ if (!this.appId && this.getAppId) this.getAppId();
2002
2045
  }
2003
- resolveWorkId() {
2004
- if (this.workId) return Promise.resolve(this.workId);
2005
- if (this.getWorkId) return this.getWorkId();
2046
+ resolveAppId() {
2047
+ if (this.appId) return Promise.resolve(this.appId);
2048
+ if (this.getAppId) return this.getAppId();
2006
2049
  return Promise.resolve(null);
2007
2050
  }
2008
2051
  async request(message, options) {
2009
- if (message.type === "cohub.work.context") return {
2010
- type: "cohub.work.context.result",
2052
+ if (message.type === "cohub.app.context") return {
2053
+ type: "cohub.app.context.result",
2011
2054
  context: {
2012
- work: {
2013
- id: await this.resolveWorkId() ?? "",
2055
+ app: {
2056
+ id: await this.resolveAppId() ?? "",
2014
2057
  slug: "",
2015
2058
  url: null
2016
2059
  },
2017
2060
  space: { id: "" },
2018
2061
  permissions: {
2019
2062
  scopes: [],
2020
- workScopes: [],
2063
+ appScopes: [],
2021
2064
  viewerScopes: []
2022
2065
  }
2023
2066
  }
2024
2067
  };
2025
- if (message.type === "cohub.work.checkout-state") return {
2026
- type: "cohub.work.checkout-state.result",
2068
+ if (message.type === "cohub.app.checkout-state") return {
2069
+ type: "cohub.app.checkout-state.result",
2027
2070
  status: null,
2028
2071
  orderId: null
2029
2072
  };
2030
2073
  const timeoutMs = options?.timeoutMs ?? 12e4;
2031
2074
  const requestId = generateRequestId();
2032
- const workId = await this.resolveWorkId();
2033
- if (!workId) throw new Error("Unable to resolve the work id for broker mode. Provide `workId` or a valid slug triple (ownerUsername, spaceSlug, workSlug).");
2075
+ const appId = await this.resolveAppId();
2076
+ if (!appId) throw new Error("Unable to resolve the app id for broker mode. Provide `appId` or a valid slug triple (ownerUsername, spaceSlug, appSlug).");
2034
2077
  return new Promise((resolve, reject) => {
2035
2078
  if (typeof window === "undefined" || typeof window.open !== "function") {
2036
2079
  resolve(null);
2037
2080
  return;
2038
2081
  }
2039
- const workOrigin = window.location.origin;
2040
- const brokerUrl = `${this.brokerOrigin}/work-auth?work=${encodeURIComponent(workId)}&origin=${encodeURIComponent(workOrigin)}`;
2041
- const popup = window.open(brokerUrl, "cohub-work-auth", "popup,width=480,height=640");
2082
+ const appOrigin = window.location.origin;
2083
+ const brokerUrl = `${this.brokerOrigin}/app-auth?app=${encodeURIComponent(appId)}&origin=${encodeURIComponent(appOrigin)}`;
2084
+ const popup = window.open(brokerUrl, "cohub-app-auth", "popup,width=480,height=640");
2042
2085
  if (!popup) {
2043
2086
  reject(/* @__PURE__ */ new Error("Failed to open authorization window. Please allow popups for this site."));
2044
2087
  return;
@@ -2069,7 +2112,7 @@ var PopupBrokerTransport = class {
2069
2112
  if (event.origin !== this.brokerOrigin) return;
2070
2113
  const data = event.data;
2071
2114
  if (!data) return;
2072
- if (data.type === "cohub.work.broker.ready" && !ready) {
2115
+ if (data.type === "cohub.app.broker.ready" && !ready) {
2073
2116
  ready = true;
2074
2117
  try {
2075
2118
  popup.postMessage({
@@ -2083,7 +2126,7 @@ var PopupBrokerTransport = class {
2083
2126
  }
2084
2127
  if (data.requestId !== requestId) return;
2085
2128
  finish(() => {
2086
- if (data.type === "cohub.work.error") {
2129
+ if (data.type === "cohub.app.error") {
2087
2130
  reject(new Error(data.message));
2088
2131
  return;
2089
2132
  }
@@ -2100,30 +2143,30 @@ var PopupBrokerTransport = class {
2100
2143
  });
2101
2144
  }
2102
2145
  };
2103
- const TOKEN_STORAGE_PREFIX = "cohub:work-token";
2104
- const AUTHORIZED_SCOPES_STORAGE_PREFIX = "cohub:work-auth-scopes";
2105
- var WorkRuntimeApi = class {
2146
+ const TOKEN_STORAGE_PREFIX = "cohub:app-token";
2147
+ const AUTHORIZED_SCOPES_STORAGE_PREFIX = "cohub:app-auth-scopes";
2148
+ var AppRuntimeApi = class {
2106
2149
  token = null;
2107
2150
  transport;
2108
2151
  tokenStorageKey;
2109
2152
  scopesStorageKey;
2110
- workIdResolver;
2153
+ appIdResolver;
2111
2154
  /** Ensures storage keys are resolved (via slug lookup) at most once. */
2112
2155
  storageKeysReady;
2113
2156
  /** Scopes previously granted via requestAuthorization, retained so token
2114
2157
  * refreshes can re-authorize (preserving viewerScopes) instead of falling
2115
- * back to a base session token that only carries workScopes. */
2158
+ * back to a base session token that only carries appScopes. */
2116
2159
  authorizedScopes = null;
2117
- constructor(transport = new ParentBridgeTransport(), workId, workIdResolver) {
2160
+ constructor(transport = new ParentBridgeTransport(), appId, appIdResolver) {
2118
2161
  this.transport = transport;
2119
- this.workIdResolver = workIdResolver;
2120
- if (workId) {
2121
- this.tokenStorageKey = `${TOKEN_STORAGE_PREFIX}:${workId}`;
2122
- this.scopesStorageKey = `${AUTHORIZED_SCOPES_STORAGE_PREFIX}:${workId}`;
2162
+ this.appIdResolver = appIdResolver;
2163
+ if (appId) {
2164
+ this.tokenStorageKey = `${TOKEN_STORAGE_PREFIX}:${appId}`;
2165
+ this.scopesStorageKey = `${AUTHORIZED_SCOPES_STORAGE_PREFIX}:${appId}`;
2123
2166
  this.storageKeysReady = Promise.resolve();
2124
2167
  this.token = this.readStoredToken();
2125
2168
  this.authorizedScopes = this.readStoredScopes();
2126
- } else if (workIdResolver) {
2169
+ } else if (appIdResolver) {
2127
2170
  this.tokenStorageKey = null;
2128
2171
  this.scopesStorageKey = null;
2129
2172
  this.storageKeysReady = null;
@@ -2134,17 +2177,17 @@ var WorkRuntimeApi = class {
2134
2177
  }
2135
2178
  }
2136
2179
  /**
2137
- * Resolves the localStorage keys once the workId is known. When the workId is
2180
+ * Resolves the localStorage keys once the appId is known. When the appId is
2138
2181
  * only available via slug reverse lookup, this performs the lookup on first
2139
- * use and then restores any cached token/scopes for that workId.
2182
+ * use and then restores any cached token/scopes for that appId.
2140
2183
  */
2141
2184
  ensureStorageKeys() {
2142
2185
  if (this.storageKeysReady) return this.storageKeysReady;
2143
2186
  this.storageKeysReady = (async () => {
2144
- const workId = this.workIdResolver ? await this.workIdResolver() : null;
2145
- if (workId) {
2146
- this.tokenStorageKey = `${TOKEN_STORAGE_PREFIX}:${workId}`;
2147
- this.scopesStorageKey = `${AUTHORIZED_SCOPES_STORAGE_PREFIX}:${workId}`;
2187
+ const appId = this.appIdResolver ? await this.appIdResolver() : null;
2188
+ if (appId) {
2189
+ this.tokenStorageKey = `${TOKEN_STORAGE_PREFIX}:${appId}`;
2190
+ this.scopesStorageKey = `${AUTHORIZED_SCOPES_STORAGE_PREFIX}:${appId}`;
2148
2191
  const stored = this.readStoredToken();
2149
2192
  if (stored && !this.token) this.token = stored;
2150
2193
  const storedScopes = this.readStoredScopes();
@@ -2187,11 +2230,14 @@ var WorkRuntimeApi = class {
2187
2230
  } catch {}
2188
2231
  }
2189
2232
  async context() {
2190
- return (await this.transport.request({ type: "cohub.work.context" }, {
2233
+ return (await this.transport.request({ type: "cohub.app.context" }, {
2191
2234
  timeoutMs: 8e3,
2192
2235
  retryIntervalMs: 250
2193
2236
  }))?.context ?? null;
2194
2237
  }
2238
+ onContextChanged(listener) {
2239
+ return this.transport.subscribeContextChanged?.(listener) ?? (() => {});
2240
+ }
2195
2241
  async getAccessToken(options) {
2196
2242
  await this.ensureStorageKeys();
2197
2243
  if (this.token && !options?.forceRefresh) return this.token;
@@ -2201,7 +2247,7 @@ var WorkRuntimeApi = class {
2201
2247
  }
2202
2248
  if (options?.forceRefresh && this.authorizedScopes && this.authorizedScopes.length > 0) {
2203
2249
  const response = await this.transport.request({
2204
- type: "cohub.work.authorize",
2250
+ type: "cohub.app.authorize",
2205
2251
  scopes: this.authorizedScopes
2206
2252
  }, { timeoutMs: 12e4 });
2207
2253
  this.token = response?.token ?? null;
@@ -2209,7 +2255,7 @@ var WorkRuntimeApi = class {
2209
2255
  return this.token;
2210
2256
  }
2211
2257
  const response = await this.transport.request({
2212
- type: "cohub.work.token",
2258
+ type: "cohub.app.token",
2213
2259
  forceRefresh: Boolean(options?.forceRefresh)
2214
2260
  }, { timeoutMs: 2e4 });
2215
2261
  this.token = response?.token ?? null;
@@ -2219,7 +2265,7 @@ var WorkRuntimeApi = class {
2219
2265
  async requestAuthorization(input) {
2220
2266
  await this.ensureStorageKeys();
2221
2267
  const response = await this.transport.request({
2222
- type: "cohub.work.authorize",
2268
+ type: "cohub.app.authorize",
2223
2269
  scopes: input.scopes,
2224
2270
  reason: input.reason
2225
2271
  }, { timeoutMs: 12e4 });
@@ -2234,32 +2280,34 @@ var WorkRuntimeApi = class {
2234
2280
  async purchase(input) {
2235
2281
  const purchaseAttemptId = input.purchaseAttemptId?.trim() || generateRequestId();
2236
2282
  return (await this.transport.request({
2237
- type: "cohub.work.purchase",
2283
+ type: "cohub.app.purchase",
2238
2284
  productKey: input.productKey,
2239
2285
  purchaseAttemptId
2240
2286
  }, { timeoutMs: 12e4 }))?.checkout ?? null;
2241
2287
  }
2242
2288
  async checkoutState() {
2243
- return await this.transport.request({ type: "cohub.work.checkout-state" }, {
2289
+ return await this.transport.request({ type: "cohub.app.checkout-state" }, {
2244
2290
  timeoutMs: 8e3,
2245
2291
  retryIntervalMs: 250
2246
2292
  }) ?? null;
2247
2293
  }
2248
2294
  };
2249
2295
  /**
2250
- * Builds a memoized workId resolver that reverse-looks-up the workId from the
2251
- * public slug triple via `GET /api/works/by-slug/:username/:spaceSlug/:workSlug`.
2252
- * The endpoint is anonymous (no auth) for public works, so no token is needed.
2253
- * The result is cached; a failed lookup is not cached so it can be retried.
2296
+ * Builds a memoized appId resolver that reverse-looks-up the appId from the
2297
+ * public slug triple via `GET /api/apps/by-slug/:username/:spaceSlug/:appSlug`
2298
+ * (the works REST routes are dual-mounted; `/api/works` keeps serving older
2299
+ * consumers with identical payloads). The endpoint is
2300
+ * anonymous (no auth) for public apps, so no token is needed. The result is
2301
+ * cached; a failed lookup is not cached so it can be retried.
2254
2302
  */
2255
- function createSlugWorkIdResolver(deps) {
2303
+ function createSlugAppIdResolver(deps) {
2256
2304
  let cached = null;
2257
2305
  return () => {
2258
2306
  if (cached) return cached;
2259
2307
  const run = (async () => {
2260
2308
  const doFetch = deps.fetch ?? globalThis.fetch;
2261
2309
  if (typeof doFetch !== "function") return null;
2262
- const response = await doFetch(`${deps.apiBaseUrl}/api/works/by-slug/${encodeURIComponent(deps.ownerUsername)}/${encodeURIComponent(deps.spaceSlug)}/${encodeURIComponent(deps.workSlug)}`);
2310
+ const response = await doFetch(`${deps.apiBaseUrl}/api/apps/by-slug/${encodeURIComponent(deps.ownerUsername)}/${encodeURIComponent(deps.spaceSlug)}/${encodeURIComponent(deps.appSlug)}`);
2263
2311
  if (!response.ok) throw new Error(`getBySlug failed: ${response.status}`);
2264
2312
  return (await response.json())?.work?.id ?? null;
2265
2313
  })().catch(() => {
@@ -2271,27 +2319,27 @@ function createSlugWorkIdResolver(deps) {
2271
2319
  };
2272
2320
  }
2273
2321
  /**
2274
- * Resolves the appropriate transport based on the work mode configuration.
2322
+ * Resolves the appropriate transport based on the app mode configuration.
2275
2323
  * Auto-detection: inside an iframe → bridge; standalone with broker config →
2276
- * broker; otherwise → bridge (returns null for non-work contexts).
2324
+ * broker; otherwise → bridge (returns null for non-app contexts).
2277
2325
  */
2278
- function resolveWorkTransport(config, workIdResolver) {
2326
+ function resolveAppTransport(config, appIdResolver) {
2279
2327
  const explicitMode = config?.mode;
2280
2328
  const brokerOrigin = config?.brokerOrigin;
2281
- const workId = config?.workId;
2282
- const canResolveWorkId = Boolean(workId || workIdResolver);
2283
- const hasBrokerConfig = Boolean(brokerOrigin && canResolveWorkId);
2284
- const createBroker = () => brokerOrigin && canResolveWorkId ? new PopupBrokerTransport({
2329
+ const appId = config?.appId;
2330
+ const canResolveAppId = Boolean(appId || appIdResolver);
2331
+ const hasBrokerConfig = Boolean(brokerOrigin && canResolveAppId);
2332
+ const createBroker = () => brokerOrigin && canResolveAppId ? new PopupBrokerTransport({
2285
2333
  brokerOrigin,
2286
- workId,
2287
- getWorkId: workIdResolver
2334
+ appId,
2335
+ getAppId: appIdResolver
2288
2336
  }) : new ParentBridgeTransport();
2289
2337
  if (explicitMode === "bridge") return new ParentBridgeTransport();
2290
2338
  if (explicitMode === "broker") return createBroker();
2291
2339
  if (typeof window !== "undefined" && window.parent !== window) return new ParentBridgeTransport();
2292
2340
  return hasBrokerConfig ? createBroker() : new ParentBridgeTransport();
2293
2341
  }
2294
- const createWorkRuntime = (transport, workId, workIdResolver) => new WorkRuntimeApi(transport, workId, workIdResolver);
2342
+ const createAppRuntime = (transport, appId, appIdResolver) => new AppRuntimeApi(transport, appId, appIdResolver);
2295
2343
  //#endregion
2296
2344
  //#region src/client.ts
2297
2345
  var CohubClient = class {
@@ -2310,27 +2358,40 @@ var CohubClient = class {
2310
2358
  references;
2311
2359
  tasks;
2312
2360
  cronJobs;
2313
- ui;
2361
+ desktop;
2314
2362
  invite;
2315
2363
  referrals;
2316
2364
  voice;
2317
- works;
2318
- workCommerce;
2365
+ apps;
2366
+ appCommerce;
2367
+ /** @deprecated Use `client.desktop`. */
2368
+ get ui() {
2369
+ return this.desktop;
2370
+ }
2371
+ /** @deprecated Use `client.apps`. */
2372
+ get works() {
2373
+ return this.apps;
2374
+ }
2375
+ /** @deprecated Use `client.appCommerce`. */
2376
+ get workCommerce() {
2377
+ return this.appCommerce;
2378
+ }
2319
2379
  transport;
2320
2380
  websocketClient;
2321
- workRuntime;
2381
+ appRuntime;
2322
2382
  constructor(options = {}) {
2323
2383
  const apiBaseUrl = resolveApiBaseUrl(options);
2324
- const workIdResolver = !options.work?.workId && options.work?.ownerUsername && options.work?.spaceSlug && options.work?.workSlug ? createSlugWorkIdResolver({
2384
+ const appRuntime = options.app ?? options.work;
2385
+ const appIdResolver = !appRuntime?.appId && appRuntime?.ownerUsername && appRuntime?.spaceSlug && appRuntime?.appSlug ? createSlugAppIdResolver({
2325
2386
  apiBaseUrl,
2326
2387
  fetch: options.fetch,
2327
- ownerUsername: options.work.ownerUsername,
2328
- spaceSlug: options.work.spaceSlug,
2329
- workSlug: options.work.workSlug
2388
+ ownerUsername: appRuntime.ownerUsername,
2389
+ spaceSlug: appRuntime.spaceSlug,
2390
+ appSlug: appRuntime.appSlug
2330
2391
  }) : void 0;
2331
- const workTransport = resolveWorkTransport(options.work, workIdResolver);
2332
- this.workRuntime = createWorkRuntime(workTransport, options.work?.workId, workIdResolver);
2333
- const getAccessToken = options.getAccessToken ?? ((tokenOptions) => this.workRuntime.getAccessToken(tokenOptions));
2392
+ const appTransport = resolveAppTransport(appRuntime, appIdResolver);
2393
+ this.appRuntime = createAppRuntime(appTransport, appRuntime?.appId, appIdResolver);
2394
+ const getAccessToken = options.getAccessToken ?? ((tokenOptions) => this.appRuntime.getAccessToken(tokenOptions));
2334
2395
  const resolvedOptions = {
2335
2396
  ...options,
2336
2397
  getAccessToken
@@ -2367,55 +2428,56 @@ var CohubClient = class {
2367
2428
  this.references = new ReferencesApi(this.transport);
2368
2429
  this.tasks = new TasksApi(this.transport);
2369
2430
  this.cronJobs = new CronJobsApi(this.transport);
2370
- this.ui = new UiCommandsApi(this.transport);
2431
+ this.desktop = new DesktopCommandsApi(this.transport);
2371
2432
  this.invite = new PublicInviteApi(this.transport);
2372
2433
  this.referrals = new ReferralsApi(this.transport);
2373
- this.works = new WorksApi(this.transport);
2374
- this.workCommerce = new WorkCommerceApi(this.transport);
2375
- this.work.realtime = new WorkRealtimeApi(this.transport, this.websocketClient, () => this.workRuntime.context());
2434
+ this.apps = new AppsApi(this.transport);
2435
+ this.appCommerce = new AppCommerceApi(this.transport);
2436
+ this.app.realtime = new AppRealtimeApi(this.transport, this.websocketClient, () => this.appRuntime.context());
2376
2437
  }
2377
2438
  context() {
2378
- return this.workRuntime.context();
2439
+ return this.appRuntime.context();
2379
2440
  }
2380
- auth = { request: (input) => this.workRuntime.requestAuthorization(input) };
2381
- work = {
2441
+ auth = { request: (input) => this.appRuntime.requestAuthorization(input) };
2442
+ app = {
2382
2443
  realtime: null,
2383
- /** Expose callable methods from inside a published Work. */
2384
- surface: new WorkSurfaceApi(),
2444
+ /** Expose callable methods from inside a published app. */
2445
+ surface: new AppSurfaceApi(),
2446
+ onContextChanged: (listener) => this.appRuntime.onContextChanged(listener),
2385
2447
  composer: {
2386
- /** Attach or update context from this Work in the Cohub composer. */
2387
- setChip: (chip) => this.work.surface.setComposerChip(chip),
2388
- /** Remove context previously attached by this Work. */
2389
- clearChip: (key) => this.work.surface.clearComposerChip(key)
2448
+ /** Attach or update context from this app in the Cohub composer. */
2449
+ setChip: (chip) => this.app.surface.setComposerChip(chip),
2450
+ /** Remove context previously attached by this app. */
2451
+ clearChip: (key) => this.app.surface.clearComposerChip(key)
2390
2452
  },
2391
2453
  commerce: {
2392
2454
  resolveProducts: async (input) => {
2393
- const context = await this.workRuntime.context();
2394
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
2395
- return this.workCommerce.resolveProducts(context.work.id, input);
2455
+ const context = await this.appRuntime.context();
2456
+ if (!context?.app?.id) throw new Error("App context is unavailable — not running inside a published app runtime.");
2457
+ return this.appCommerce.resolveProducts(context.app.id, input);
2396
2458
  },
2397
2459
  getEntitlements: async () => {
2398
- const context = await this.workRuntime.context();
2399
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
2400
- return this.workCommerce.getEntitlements(context.work.id);
2460
+ const context = await this.appRuntime.context();
2461
+ if (!context?.app?.id) throw new Error("App context is unavailable — not running inside a published app runtime.");
2462
+ return this.appCommerce.getEntitlements(context.app.id);
2401
2463
  },
2402
2464
  consumeCredits: async (input) => {
2403
- const context = await this.workRuntime.context();
2404
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
2405
- return this.workCommerce.consumeCredits(context.work.id, input);
2465
+ const context = await this.appRuntime.context();
2466
+ if (!context?.app?.id) throw new Error("App context is unavailable — not running inside a published app runtime.");
2467
+ return this.appCommerce.consumeCredits(context.app.id, input);
2406
2468
  },
2407
- purchase: async (input) => this.workRuntime.purchase(input),
2469
+ purchase: async (input) => this.appRuntime.purchase(input),
2408
2470
  getCheckoutState: async () => {
2409
- const result = await this.workRuntime.checkoutState();
2471
+ const result = await this.appRuntime.checkoutState();
2410
2472
  return {
2411
2473
  status: result?.status ?? null,
2412
2474
  orderId: result?.orderId ?? null
2413
2475
  };
2414
2476
  },
2415
2477
  getOrder: async (orderId) => {
2416
- const context = await this.workRuntime.context();
2417
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
2418
- return this.workCommerce.getOrder(context.work.id, orderId);
2478
+ const context = await this.appRuntime.context();
2479
+ if (!context?.app?.id) throw new Error("App context is unavailable — not running inside a published app runtime.");
2480
+ return this.appCommerce.getOrder(context.app.id, orderId);
2419
2481
  }
2420
2482
  }
2421
2483
  };
@@ -2473,8 +2535,8 @@ var CohubClient = class {
2473
2535
  };
2474
2536
  const createCohubClient = (options) => new CohubClient(options);
2475
2537
  //#endregion
2476
- //#region src/work-ref.ts
2477
- const isWorkId = (value) => isUuid(value.trim());
2538
+ //#region src/app-ref.ts
2539
+ const isAppId = (value) => isUuid(value.trim());
2478
2540
  function decodePart(value) {
2479
2541
  try {
2480
2542
  return decodeURIComponent(value).trim();
@@ -2484,14 +2546,14 @@ function decodePart(value) {
2484
2546
  }
2485
2547
  function publicRef(parts) {
2486
2548
  if (parts.length !== 3) return null;
2487
- const [usernameRaw = "", spaceSlugRaw = "", workSlugRaw = ""] = parts.map(decodePart);
2549
+ const [usernameRaw = "", spaceSlugRaw = "", appSlugRaw = ""] = parts.map(decodePart);
2488
2550
  const username = parseUsername(usernameRaw);
2489
2551
  const spaceSlug = parseSpaceSlug(spaceSlugRaw);
2490
- const workSlug = parseSpaceSlug(workSlugRaw);
2491
- return username && spaceSlug && workSlug ? {
2552
+ const appSlug = parseSpaceSlug(appSlugRaw);
2553
+ return username && spaceSlug && appSlug ? {
2492
2554
  username,
2493
2555
  spaceSlug,
2494
- workSlug
2556
+ appSlug
2495
2557
  } : null;
2496
2558
  }
2497
2559
  function launchState(url) {
@@ -2508,15 +2570,22 @@ function parseUrlRef(value) {
2508
2570
  return null;
2509
2571
  }
2510
2572
  const parts = url.pathname.split("/").filter(Boolean);
2511
- if (url.protocol === "cohub:" && url.hostname === "works") {
2573
+ if (url.protocol === "cohub:" && (url.hostname === "apps" || url.hostname === "works")) {
2512
2574
  const ref = publicRef(parts);
2513
2575
  return ref ? {
2514
2576
  ...ref,
2515
2577
  ...launchState(url)
2516
2578
  } : null;
2517
2579
  }
2580
+ if (url.protocol === "app:" || url.protocol === "work:") {
2581
+ const ref = publicRef([url.hostname, ...parts]);
2582
+ return ref ? {
2583
+ ...ref,
2584
+ ...launchState(url)
2585
+ } : null;
2586
+ }
2518
2587
  if (url.protocol !== "http:" && url.protocol !== "https:") return null;
2519
- if (parts.length === 4 && parts[0] === "spaces" && isUuid(parts[1] ?? "") && parts[2] === "works" && isUuid(parts[3] ?? "")) return { id: parts[3] };
2588
+ if (parts.length === 4 && parts[0] === "spaces" && isUuid(parts[1] ?? "") && (parts[2] === "apps" || parts[2] === "works") && isUuid(parts[3] ?? "")) return { id: parts[3] };
2520
2589
  if (parts.length === 4 && parts[2] === "w") {
2521
2590
  const ref = publicRef([
2522
2591
  parts[0],
@@ -2530,13 +2599,13 @@ function parseUrlRef(value) {
2530
2599
  }
2531
2600
  return null;
2532
2601
  }
2533
- var WorkRefParseError = class extends Error {
2602
+ var AppRefParseError = class extends Error {
2534
2603
  constructor() {
2535
- super("Work must be an id, public URL, cohub://works URI, or username/space/work reference");
2536
- this.name = "WorkRefParseError";
2604
+ super("App must be an id, public URL, cohub://apps URI, or username/space/app reference");
2605
+ this.name = "AppRefParseError";
2537
2606
  }
2538
2607
  };
2539
- function parseWorkRef(input) {
2608
+ function parseAppRef(input) {
2540
2609
  const value = input.trim();
2541
2610
  if (isUuid(value)) return { id: value };
2542
2611
  const parsedUrl = parseUrlRef(value.includes("://") ? value : value.startsWith("/") ? `https://cohub.invalid${value}` : value);
@@ -2544,21 +2613,29 @@ function parseWorkRef(input) {
2544
2613
  const parts = value.split("/").filter(Boolean);
2545
2614
  const parsedPublic = parts.length === 3 ? publicRef(parts) : null;
2546
2615
  if (parsedPublic) return parsedPublic;
2547
- throw new WorkRefParseError();
2548
- }
2549
- function formatWorkRef(ref) {
2550
- return "id" in ref ? ref.id : `${ref.username}/${ref.spaceSlug}/${ref.workSlug}`;
2551
- }
2616
+ throw new AppRefParseError();
2617
+ }
2618
+ function formatAppRef(ref) {
2619
+ return "id" in ref ? ref.id : `${ref.username}/${ref.spaceSlug}/${ref.appSlug}`;
2620
+ }
2621
+ /** @deprecated Use `isAppId`. */
2622
+ const isWorkId = isAppId;
2623
+ /** @deprecated Use `AppRefParseError`. */
2624
+ const WorkRefParseError = AppRefParseError;
2625
+ /** @deprecated Use `parseAppRef`. */
2626
+ const parseWorkRef = parseAppRef;
2627
+ /** @deprecated Use `formatAppRef`. */
2628
+ const formatWorkRef = formatAppRef;
2552
2629
  //#endregion
2553
- //#region src/work-grant-cache.ts
2630
+ //#region src/app-grant-cache.ts
2554
2631
  const STORAGE_PREFIX = "cohub:work-grants";
2555
2632
  const CACHE_VERSION = 1;
2556
2633
  const MAX_AGE_MS = 336 * 60 * 60 * 1e3;
2557
2634
  function isBrowser() {
2558
2635
  return typeof localStorage !== "undefined";
2559
2636
  }
2560
- function storageKey(userUuid, workId) {
2561
- return `${STORAGE_PREFIX}:${encodeURIComponent(userUuid)}:${encodeURIComponent(workId)}:v${CACHE_VERSION}`;
2637
+ function storageKey(userUuid, appId) {
2638
+ return `${STORAGE_PREFIX}:${encodeURIComponent(userUuid)}:${encodeURIComponent(appId)}:v${CACHE_VERSION}`;
2562
2639
  }
2563
2640
  function userPrefix(userUuid) {
2564
2641
  return `${STORAGE_PREFIX}:${encodeURIComponent(userUuid)}:`;
@@ -2569,16 +2646,16 @@ function isPermissionArray(value) {
2569
2646
  function isCachedWorkGrant(value) {
2570
2647
  if (!value || typeof value !== "object") return false;
2571
2648
  const record = value;
2572
- return record.version === CACHE_VERSION && typeof record.userUuid === "string" && typeof record.workId === "string" && isPermissionArray(record.scopes) && typeof record.updatedAt === "number";
2649
+ return record.version === CACHE_VERSION && typeof record.userUuid === "string" && typeof record.appId === "string" && isPermissionArray(record.scopes) && typeof record.updatedAt === "number";
2573
2650
  }
2574
- function readEntry(userUuid, workId) {
2651
+ function readEntry(userUuid, appId) {
2575
2652
  if (!isBrowser()) return null;
2576
- const key = storageKey(userUuid, workId);
2653
+ const key = storageKey(userUuid, appId);
2577
2654
  try {
2578
2655
  const raw = localStorage.getItem(key);
2579
2656
  if (!raw) return null;
2580
2657
  const parsed = JSON.parse(raw);
2581
- if (!isCachedWorkGrant(parsed) || parsed.userUuid !== userUuid || parsed.workId !== workId) {
2658
+ if (!isCachedWorkGrant(parsed) || parsed.userUuid !== userUuid || parsed.appId !== appId) {
2582
2659
  localStorage.removeItem(key);
2583
2660
  return null;
2584
2661
  }
@@ -2598,9 +2675,9 @@ function readEntry(userUuid, workId) {
2598
2675
  * Returns true when the viewer has previously granted every requested scope
2599
2676
  * for this work, allowing a silent re-authorization.
2600
2677
  */
2601
- function hasGrantedWorkScopes(userUuid, workId, scopes) {
2602
- if (!userUuid || !workId || scopes.length === 0) return false;
2603
- const entry = readEntry(userUuid, workId);
2678
+ function hasGrantedAppScopes(userUuid, appId, scopes) {
2679
+ if (!userUuid || !appId || scopes.length === 0) return false;
2680
+ const entry = readEntry(userUuid, appId);
2604
2681
  if (!entry) return false;
2605
2682
  const granted = new Set(entry.scopes);
2606
2683
  return scopes.every((scope) => granted.has(scope));
@@ -2609,31 +2686,31 @@ function hasGrantedWorkScopes(userUuid, workId, scopes) {
2609
2686
  * Records the granted scopes for a work, merged with any previously granted
2610
2687
  * scopes so a growing permission set stays covered.
2611
2688
  */
2612
- function setGrantedWorkScopes(userUuid, workId, scopes) {
2613
- if (!userUuid || !workId || scopes.length === 0) return;
2614
- const existing = readEntry(userUuid, workId);
2689
+ function setGrantedAppScopes(userUuid, appId, scopes) {
2690
+ if (!userUuid || !appId || scopes.length === 0) return;
2691
+ const existing = readEntry(userUuid, appId);
2615
2692
  const merged = Array.from(/* @__PURE__ */ new Set([...existing?.scopes ?? [], ...scopes]));
2616
2693
  const entry = {
2617
2694
  version: CACHE_VERSION,
2618
2695
  userUuid,
2619
- workId,
2696
+ appId,
2620
2697
  scopes: merged,
2621
2698
  updatedAt: Date.now()
2622
2699
  };
2623
2700
  if (!isBrowser()) return;
2624
2701
  try {
2625
- localStorage.setItem(storageKey(userUuid, workId), JSON.stringify(entry));
2702
+ localStorage.setItem(storageKey(userUuid, appId), JSON.stringify(entry));
2626
2703
  } catch {}
2627
2704
  }
2628
2705
  /**
2629
- * Clears cached grants. Pass a workId to clear a single work, or omit it to
2706
+ * Clears cached grants. Pass a appId to clear a single work, or omit it to
2630
2707
  * clear every cached grant for the user (used on sign-out).
2631
2708
  */
2632
- function clearGrantedWorkScopes(userUuid, workId) {
2709
+ function clearGrantedAppScopes(userUuid, appId) {
2633
2710
  if (!isBrowser() || !userUuid) return;
2634
2711
  try {
2635
- if (workId) {
2636
- localStorage.removeItem(storageKey(userUuid, workId));
2712
+ if (appId) {
2713
+ localStorage.removeItem(storageKey(userUuid, appId));
2637
2714
  return;
2638
2715
  }
2639
2716
  const prefix = userPrefix(userUuid);
@@ -2644,7 +2721,7 @@ function clearGrantedWorkScopes(userUuid, workId) {
2644
2721
  } catch {}
2645
2722
  }
2646
2723
  //#endregion
2647
- //#region src/work-bridge-core.ts
2724
+ //#region src/app-bridge-core.ts
2648
2725
  function readTokenResponse(value) {
2649
2726
  if (!value || typeof value !== "object") return null;
2650
2727
  const token = value.token;
@@ -2654,20 +2731,78 @@ function clonePermissionScopes(scopes) {
2654
2731
  return Array.from(scopes ?? []).filter((scope) => typeof scope === "string");
2655
2732
  }
2656
2733
  /**
2657
- * Framework-agnostic work bridge host core — message handling, work session
2734
+ * Framework-agnostic app bridge host core — message handling, app session
2658
2735
  * token minting, authorization (with silent re-grant cache), and
2659
2736
  * purchase/checkout flow — without any rendering or reactive primitives.
2660
2737
  *
2661
- * Both the Cohub iframe host (WorkSurface, Svelte) and the standalone broker
2738
+ * Both the Cohub iframe host (AppSurface, Svelte) and the standalone broker
2662
2739
  * page compose this with their own transport-specific reply and auth
2663
2740
  * dependencies. External hosts (e.g. Neta-Studio in React) can do the same.
2664
2741
  */
2665
- function createWorkBridgeCore(config) {
2666
- const { work, reply, getCheckoutState, getAccessToken, getViewerUuid } = config;
2742
+ function createAppBridgeCore(config) {
2743
+ const { app, reply, getCheckoutState, getAccessToken, getViewerUuid } = config;
2667
2744
  const apiOrigin = config.apiOrigin;
2668
2745
  const authorizationContext = config.authorizationContext ?? (config.isBackground ? { surface: "background" } : { surface: "page" });
2669
2746
  const onStateChange = config.onStateChange;
2670
- let workToken = null;
2747
+ let appToken = null;
2748
+ let activeInvocation;
2749
+ let contextChangeVersion = 0;
2750
+ const legacyRequestIds = /* @__PURE__ */ new Set();
2751
+ async function getContext() {
2752
+ const invocation = activeInvocation !== void 0 ? activeInvocation : config.getInvocation?.() ?? config.invocation;
2753
+ const appScopes = clonePermissionScopes(app.appScopes);
2754
+ const viewerUuid = await getViewerUuid();
2755
+ return {
2756
+ app: {
2757
+ id: app.id,
2758
+ slug: app.slug,
2759
+ url: typeof location !== "undefined" ? location.href : ""
2760
+ },
2761
+ space: { id: app.spaceId },
2762
+ viewer: viewerUuid ? { userUuid: viewerUuid } : null,
2763
+ ...invocation ? { invocation: { ...invocation } } : {},
2764
+ permissions: {
2765
+ scopes: appScopes,
2766
+ appScopes,
2767
+ viewerScopes: []
2768
+ }
2769
+ };
2770
+ }
2771
+ function replyForRequest(requestId, payload, complete = false) {
2772
+ const namespace = legacyRequestIds.has(requestId) ? "work" : "app";
2773
+ const type = payload.type;
2774
+ reply(requestId, {
2775
+ ...payload,
2776
+ ...typeof type === "string" && type.startsWith("cohub.app.") ? { type: type.replace("cohub.app.", `cohub.${namespace}.`) } : {}
2777
+ });
2778
+ if (complete) legacyRequestIds.delete(requestId);
2779
+ }
2780
+ function toLegacyWorkContext(context) {
2781
+ const permissions = context.permissions ? {
2782
+ scopes: context.permissions.scopes,
2783
+ workScopes: context.permissions.appScopes,
2784
+ appScopes: context.permissions.appScopes,
2785
+ viewerScopes: context.permissions.viewerScopes
2786
+ } : void 0;
2787
+ return {
2788
+ work: context.app,
2789
+ space: context.space,
2790
+ ...context.viewer !== void 0 ? { viewer: context.viewer } : {},
2791
+ ...context.invocation ? { invocation: context.invocation } : {},
2792
+ ...permissions ? { permissions } : {}
2793
+ };
2794
+ }
2795
+ async function notifyContextChanged(invocation) {
2796
+ activeInvocation = invocation;
2797
+ const version = ++contextChangeVersion;
2798
+ if (!config.notify) return;
2799
+ const context = await getContext();
2800
+ if (version !== contextChangeVersion) return;
2801
+ config.notify({
2802
+ type: "cohub.app.context.changed",
2803
+ context
2804
+ });
2805
+ }
2671
2806
  const state = {
2672
2807
  authOpen: false,
2673
2808
  pendingAuth: null,
@@ -2681,30 +2816,30 @@ function createWorkBridgeCore(config) {
2681
2816
  function notify() {
2682
2817
  onStateChange?.({ ...state });
2683
2818
  }
2684
- const pendingPurchaseStorageKey = `cohub-work-purchase:${work.id}`;
2685
- async function isCurrentViewerWorkOwner() {
2819
+ const pendingPurchaseStorageKey = `cohub-app-purchase:${app.id}`;
2820
+ async function isCurrentViewerAppOwner() {
2686
2821
  const viewerUuid = await getViewerUuid();
2687
- return Boolean(viewerUuid && viewerUuid === work.userUuid);
2822
+ return Boolean(viewerUuid && viewerUuid === app.userUuid);
2688
2823
  }
2689
2824
  function allowsOwnerAutoAuthorization() {
2690
- return authorizationContext.surface === "background" || authorizationContext.surface === "preview";
2825
+ return authorizationContext.surface === "background" || authorizationContext.surface === "app";
2691
2826
  }
2692
2827
  async function ensureBaseToken(forceRefresh = false) {
2693
- if (workToken && !forceRefresh) return workToken;
2828
+ if (appToken && !forceRefresh) return appToken;
2694
2829
  const userToken = await getAccessToken({ forceRefresh });
2695
2830
  if (!userToken) {
2696
2831
  await config.requestSignIn(typeof location !== "undefined" ? location.pathname : "/");
2697
2832
  return null;
2698
2833
  }
2699
- const response = await fetch(`${apiOrigin}/api/works/${work.id}/session`, {
2834
+ const response = await fetch(`${apiOrigin}/api/apps/${app.id}/session`, {
2700
2835
  method: "POST",
2701
2836
  headers: { Authorization: `Bearer ${userToken}` }
2702
2837
  });
2703
- if (!response.ok) throw new Error("Failed to create work session.");
2838
+ if (!response.ok) throw new Error("Failed to create app session.");
2704
2839
  const token = readTokenResponse(await response.json());
2705
- if (!token) throw new Error("Invalid work session response.");
2706
- workToken = token;
2707
- return workToken;
2840
+ if (!token) throw new Error("Invalid app session response.");
2841
+ appToken = token;
2842
+ return appToken;
2708
2843
  }
2709
2844
  async function authorize(scopes) {
2710
2845
  const userToken = await getAccessToken();
@@ -2712,7 +2847,7 @@ function createWorkBridgeCore(config) {
2712
2847
  await config.requestSignIn(typeof location !== "undefined" ? location.pathname : "/");
2713
2848
  return null;
2714
2849
  }
2715
- const response = await fetch(`${apiOrigin}/api/works/${work.id}/authorize`, {
2850
+ const response = await fetch(`${apiOrigin}/api/apps/${app.id}/authorize`, {
2716
2851
  method: "POST",
2717
2852
  headers: {
2718
2853
  Authorization: `Bearer ${userToken}`,
@@ -2722,9 +2857,9 @@ function createWorkBridgeCore(config) {
2722
2857
  });
2723
2858
  if (!response.ok) throw new Error((await response.json().catch(() => null))?.message ?? "Authorization failed.");
2724
2859
  const token = readTokenResponse(await response.json());
2725
- if (!token) throw new Error("Invalid work authorization response.");
2726
- workToken = token;
2727
- return workToken;
2860
+ if (!token) throw new Error("Invalid app authorization response.");
2861
+ appToken = token;
2862
+ return appToken;
2728
2863
  }
2729
2864
  function writePendingPurchase(input) {
2730
2865
  if (typeof sessionStorage === "undefined") return;
@@ -2763,7 +2898,7 @@ function createWorkBridgeCore(config) {
2763
2898
  return null;
2764
2899
  }
2765
2900
  const promotionAttribution = config.getPromotionAttribution?.() ?? null;
2766
- const response = await fetch(`${apiOrigin}/api/works/${work.id}/commerce/purchase`, {
2901
+ const response = await fetch(`${apiOrigin}/api/apps/${app.id}/commerce/purchase`, {
2767
2902
  method: "POST",
2768
2903
  headers: {
2769
2904
  Authorization: `Bearer ${userToken}`,
@@ -2781,62 +2916,49 @@ function createWorkBridgeCore(config) {
2781
2916
  async function handleMessage(event) {
2782
2917
  const data = event.data;
2783
2918
  if (!data?.requestId) return;
2919
+ const isLegacyWork = data.type?.startsWith("cohub.work.") === true;
2920
+ if (isLegacyWork) legacyRequestIds.add(data.requestId);
2784
2921
  try {
2785
- if (data.type === "cohub.work.context") {
2786
- const workScopes = clonePermissionScopes(work.workScopes);
2787
- const viewerUuid = await getViewerUuid();
2788
- reply(data.requestId, {
2789
- type: "cohub.work.context.result",
2790
- context: {
2791
- work: {
2792
- id: work.id,
2793
- slug: work.slug,
2794
- url: typeof location !== "undefined" ? location.href : ""
2795
- },
2796
- space: { id: work.spaceId },
2797
- viewer: viewerUuid ? { userUuid: viewerUuid } : null,
2798
- ...config.invocation ? { invocation: { ...config.invocation } } : {},
2799
- permissions: {
2800
- scopes: workScopes,
2801
- workScopes,
2802
- viewerScopes: []
2803
- }
2804
- }
2805
- });
2922
+ if (data.type === "cohub.app.context" || data.type === "cohub.work.context") {
2923
+ const context = await getContext();
2924
+ replyForRequest(data.requestId, {
2925
+ type: "cohub.app.context.result",
2926
+ context: isLegacyWork ? toLegacyWorkContext(context) : context
2927
+ }, true);
2806
2928
  }
2807
- if (data.type === "cohub.work.token") {
2929
+ if (data.type === "cohub.app.token" || data.type === "cohub.work.token") {
2808
2930
  const token = await ensureBaseToken(Boolean(data.forceRefresh));
2809
- reply(data.requestId, {
2810
- type: "cohub.work.token.result",
2931
+ replyForRequest(data.requestId, {
2932
+ type: "cohub.app.token.result",
2811
2933
  token
2812
- });
2934
+ }, true);
2813
2935
  }
2814
- if (data.type === "cohub.work.checkout-state") {
2936
+ if (data.type === "cohub.app.checkout-state" || data.type === "cohub.work.checkout-state") {
2815
2937
  const pending = readPendingPurchase();
2816
2938
  const checkoutState = getCheckoutState();
2817
2939
  const orderId = checkoutState.orderId ?? pending?.orderId ?? null;
2818
2940
  if (checkoutState.status && checkoutState.orderId) clearPendingPurchase();
2819
- reply(data.requestId, {
2820
- type: "cohub.work.checkout-state.result",
2941
+ replyForRequest(data.requestId, {
2942
+ type: "cohub.app.checkout-state.result",
2821
2943
  status: checkoutState.status,
2822
2944
  orderId
2823
- });
2945
+ }, true);
2824
2946
  }
2825
- if (data.type === "cohub.work.purchase") {
2947
+ if (data.type === "cohub.app.purchase" || data.type === "cohub.work.purchase") {
2826
2948
  const productKey = typeof data.productKey === "string" ? data.productKey.trim() : "";
2827
2949
  if (!productKey) {
2828
- reply(data.requestId, {
2829
- type: "cohub.work.error",
2950
+ replyForRequest(data.requestId, {
2951
+ type: "cohub.app.error",
2830
2952
  message: "Product key is required."
2831
- });
2953
+ }, true);
2832
2954
  return;
2833
2955
  }
2834
2956
  const purchaseAttemptId = (typeof data.purchaseAttemptId === "string" ? data.purchaseAttemptId.trim() : "") || data.requestId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
2835
2957
  if (!/^[a-zA-Z0-9_-]{1,128}$/.test(purchaseAttemptId)) {
2836
- reply(data.requestId, {
2837
- type: "cohub.work.error",
2958
+ replyForRequest(data.requestId, {
2959
+ type: "cohub.app.error",
2838
2960
  message: "Purchase attempt id is invalid."
2839
- });
2961
+ }, true);
2840
2962
  return;
2841
2963
  }
2842
2964
  state.pendingPurchase = {
@@ -2849,34 +2971,34 @@ function createWorkBridgeCore(config) {
2849
2971
  notify();
2850
2972
  config.onPurchaseRequested?.({ ...state.pendingPurchase });
2851
2973
  }
2852
- if (data.type === "cohub.work.authorize") {
2853
- const allowedViewerScopes = clonePermissionScopes(work.allowedViewerScopes);
2974
+ if (data.type === "cohub.app.authorize" || data.type === "cohub.work.authorize") {
2975
+ const allowedViewerScopes = clonePermissionScopes(app.allowedViewerScopes);
2854
2976
  const scopes = clonePermissionScopes(data.scopes).filter((scope) => allowedViewerScopes.includes(scope));
2855
2977
  if (scopes.length === 0) {
2856
- reply(data.requestId, {
2857
- type: "cohub.work.error",
2978
+ replyForRequest(data.requestId, {
2979
+ type: "cohub.app.error",
2858
2980
  message: "No allowed scopes requested."
2859
- });
2981
+ }, true);
2860
2982
  return;
2861
2983
  }
2862
- if (allowsOwnerAutoAuthorization() && await isCurrentViewerWorkOwner()) {
2984
+ if (allowsOwnerAutoAuthorization() && await isCurrentViewerAppOwner()) {
2863
2985
  const token = await authorize(scopes);
2864
- reply(data.requestId, {
2865
- type: "cohub.work.authorize.result",
2986
+ replyForRequest(data.requestId, {
2987
+ type: "cohub.app.authorize.result",
2866
2988
  token
2867
- });
2989
+ }, true);
2868
2990
  return;
2869
2991
  }
2870
2992
  const viewerUuid = await getViewerUuid();
2871
- if (viewerUuid && hasGrantedWorkScopes(viewerUuid, work.id, scopes)) try {
2993
+ if (viewerUuid && hasGrantedAppScopes(viewerUuid, app.id, scopes)) try {
2872
2994
  const token = await authorize(scopes);
2873
- reply(data.requestId, {
2874
- type: "cohub.work.authorize.result",
2995
+ replyForRequest(data.requestId, {
2996
+ type: "cohub.app.authorize.result",
2875
2997
  token
2876
- });
2998
+ }, true);
2877
2999
  return;
2878
3000
  } catch {
2879
- clearGrantedWorkScopes(viewerUuid, work.id);
3001
+ clearGrantedAppScopes(viewerUuid, app.id);
2880
3002
  }
2881
3003
  state.pendingAuth = {
2882
3004
  requestId: data.requestId,
@@ -2888,19 +3010,19 @@ function createWorkBridgeCore(config) {
2888
3010
  notify();
2889
3011
  }
2890
3012
  } catch (error) {
2891
- reply(data.requestId, {
2892
- type: "cohub.work.error",
3013
+ replyForRequest(data.requestId, {
3014
+ type: "cohub.app.error",
2893
3015
  message: error instanceof Error ? error.message : "Request failed."
2894
- });
3016
+ }, true);
2895
3017
  }
2896
3018
  }
2897
3019
  function cancelAuth() {
2898
3020
  if (state.authSaving) return;
2899
3021
  if (!state.pendingAuth) return;
2900
- reply(state.pendingAuth.requestId, {
2901
- type: "cohub.work.authorize.result",
3022
+ replyForRequest(state.pendingAuth.requestId, {
3023
+ type: "cohub.app.authorize.result",
2902
3024
  token: null
2903
- });
3025
+ }, true);
2904
3026
  state.authOpen = false;
2905
3027
  state.pendingAuth = null;
2906
3028
  state.authError = null;
@@ -2910,10 +3032,10 @@ function createWorkBridgeCore(config) {
2910
3032
  function cancelPurchase() {
2911
3033
  if (state.purchaseSaving) return;
2912
3034
  if (!state.pendingPurchase) return;
2913
- reply(state.pendingPurchase.requestId, {
2914
- type: "cohub.work.purchase.result",
3035
+ replyForRequest(state.pendingPurchase.requestId, {
3036
+ type: "cohub.app.purchase.result",
2915
3037
  checkout: null
2916
- });
3038
+ }, true);
2917
3039
  state.purchaseOpen = false;
2918
3040
  state.purchaseError = null;
2919
3041
  state.pendingPurchase = null;
@@ -2927,10 +3049,10 @@ function createWorkBridgeCore(config) {
2927
3049
  notify();
2928
3050
  try {
2929
3051
  const checkout = await createPurchase(state.pendingPurchase.productKey, state.pendingPurchase.purchaseAttemptId);
2930
- reply(state.pendingPurchase.requestId, {
2931
- type: "cohub.work.purchase.result",
3052
+ replyForRequest(state.pendingPurchase.requestId, {
3053
+ type: "cohub.app.purchase.result",
2932
3054
  checkout
2933
- });
3055
+ }, true);
2934
3056
  if (checkout && typeof checkout === "object") {
2935
3057
  const next = checkout;
2936
3058
  if (typeof next.orderId === "string" && typeof next.productKey === "string") writePendingPurchase({
@@ -2963,11 +3085,11 @@ function createWorkBridgeCore(config) {
2963
3085
  notify();
2964
3086
  try {
2965
3087
  const token = await authorize(state.pendingAuth.scopes);
2966
- setGrantedWorkScopes(await getViewerUuid(), work.id, state.pendingAuth.scopes);
2967
- reply(state.pendingAuth.requestId, {
2968
- type: "cohub.work.authorize.result",
3088
+ setGrantedAppScopes(await getViewerUuid(), app.id, state.pendingAuth.scopes);
3089
+ replyForRequest(state.pendingAuth.requestId, {
3090
+ type: "cohub.app.authorize.result",
2969
3091
  token
2970
- });
3092
+ }, true);
2971
3093
  state.authOpen = false;
2972
3094
  state.pendingAuth = null;
2973
3095
  } catch (error) {
@@ -2980,6 +3102,7 @@ function createWorkBridgeCore(config) {
2980
3102
  return {
2981
3103
  getState: () => ({ ...state }),
2982
3104
  handleMessage,
3105
+ notifyContextChanged,
2983
3106
  confirmAuth,
2984
3107
  cancelAuth,
2985
3108
  confirmPurchase,
@@ -3220,4 +3343,4 @@ function createBoardExtensionRegistry() {
3220
3343
  }
3221
3344
  const BOARD_CHANNELS = BOARD_ANIMATION_CHANNELS;
3222
3345
  //#endregion
3223
- export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_ANIMATION_CHANNEL_CAPABILITIES, BOARD_CHANNELS, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BillingApi, BoardAuthoringItemSchema, BoardClient, BoardCompositionInputSchema, BoardCompositionSchema, BoardEffectInputSchema, BoardEffectSchema, BoardExtensionRegistry, BoardItemPatchSchema, BoardPlaybackPolicySchema, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, SpacePublicFilesApi, UI_COMMAND_DEFAULT_TIMEOUT_MS, UI_COMMAND_MAX_TIMEOUT_MS, UI_COMMAND_PAYLOAD_MAX_BYTES, UI_COMMAND_PENDING_TTL_SECONDS, UI_COMMAND_SETTLEMENT_GRACE_SECONDS, UI_COMMAND_TERMINAL_TTL_SECONDS, UI_COMMAND_VERSION, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES, WORK_COMPOSER_CHIP_KEY_MAX_LENGTH, WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH, WORK_SURFACE_READY_TIMEOUT_MS, WORK_SURFACE_REQUEST_TIMEOUT_MS, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkRuntimeApi, WorkSurfaceApi, WorksApi, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, buildWorkSurfaceRequest, clearGrantedWorkScopes, compileComposition, composition, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, formatWorkRef, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalUiCommandStatus, isUiSurfaceMethod, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardCompositionInput, parseBoardEffectInput, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseUiCommand, parseWorkRef, parseWorkSurfaceReady, parseWorkSurfaceResponse, proceduralClip, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sampleCompositionTracks, sampleEasing, sampleTrack, sanitizeAccessToken, setGrantedWorkScopes, track };
3346
+ export { APP_COMPOSER_CHIP_CONTENT_MAX_BYTES, APP_COMPOSER_CHIP_KEY_MAX_LENGTH, APP_COMPOSER_CHIP_LABEL_MAX_LENGTH, APP_SURFACE_READY_TIMEOUT_MS, APP_SURFACE_REQUEST_TIMEOUT_MS, AppCommerceApi, AppRealtimeApi, AppRefParseError, AppRoom, AppRuntimeApi, AppRuntimeApi as WorkRuntimeApi, AppSurfaceApi, AppsApi, BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_ANIMATION_CHANNEL_CAPABILITIES, BOARD_CHANNELS, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BillingApi, BoardAuthoringItemSchema, BoardClient, BoardCompositionInputSchema, BoardCompositionSchema, BoardEffectInputSchema, BoardEffectSchema, BoardExtensionRegistry, BoardItemPatchSchema, BoardPlaybackPolicySchema, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, DESKTOP_COMMAND_MAX_TIMEOUT_MS, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, DESKTOP_COMMAND_PENDING_TTL_SECONDS, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, DESKTOP_COMMAND_VERSION, DesktopCommandsApi, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, SpacePublicFilesApi, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkSurfaceApi, assertGenerationRequestAllowedByPolicy, buildAppSurfaceRequest, buildSpaceInvitePath, buildSpacePath, clearGrantedAppScopes, compileComposition, composition, createAppBridgeCore, createAppBridgeCore as createWorkBridgeCore, createAppRuntime, createAppRuntime as createWorkRuntime, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugAppIdResolver, createSlugAppIdResolver as createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, formatAppRef, formatWorkRef, getAllowedGenerationModelIds, hasGrantedAppScopes, hasRequestSourceIdentity, isAppId, isBillingAccessBlockedCode, isBillingAccessBlockedError, isDesktopCallMethod, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalDesktopCommandStatus, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAppRef, parseAppSurfaceReady, parseAppSurfaceResponse, parseAssistantMessageCommit, parseBoardCompositionInput, parseBoardEffectInput, parseBoardPlaybackPolicy, parseDesktopCommand, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseWorkRef, proceduralClip, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveAppTransport, resolveAppTransport as resolveWorkTransport, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, sampleCompositionTracks, sampleEasing, sampleTrack, sanitizeAccessToken, setGrantedAppScopes, track };