@neta-art/cohub 5.2.0 → 5.3.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/chunks/http.d.ts +45 -2
- package/dist/chunks/http.js +240 -13
- package/dist/chunks/transport.js +38 -5
- package/dist/chunks/websocket.d.ts +94 -4
- package/dist/chunks/websocket.js +1 -1
- package/dist/http.d.ts +2 -2
- package/dist/index.d.ts +100 -3
- package/dist/index.js +400 -40
- package/dist/protocol/dist/provenance.js +1 -0
- package/docs/work-runtime-guide.md +32 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { _ as
|
|
1
|
+
import { A as parseAssistantMessageCommit, B as ModelsApi, C as SpaceClient, D as buildSpacePath, E as buildSpaceInvitePath, F as ReferencesApi, H as CronJobsApi, I as SearchApi, L as PublicAssetsApi, M as createSessionPatchReducer, N as ensureRealtimeConnected, O as SessionGenerationStreamClient, P as SessionAccessApi, R as SkillsApi, S as BoardTransactionError, T as PublicInviteApi, U as ChannelsApi, V as GenerationsApi, _ as isUiSurfaceMethod, a as ReferralsApi, b as TasksApi, c as UiCommandsApi, d as UI_COMMAND_PAYLOAD_MAX_BYTES, f as UI_COMMAND_PENDING_TTL_SECONDS, g as isTerminalUiCommandStatus, h as UI_COMMAND_VERSION, i as WorksApi, j as SessionPatchReducer, k as createSessionGenerationStreamClient, l as UI_COMMAND_DEFAULT_TIMEOUT_MS, m as UI_COMMAND_TERMINAL_TTL_SECONDS, n as createHttpClient, o as UsersApi, p as UI_COMMAND_SETTLEMENT_GRACE_SECONDS, r as WorkCommerceApi, s as UserApi, t as CohubHttpClient, u as UI_COMMAND_MAX_TIMEOUT_MS, v as parseUiCommand, w as SpacesApi, x as BoardClient, y as parseUiCommandId, z as PromptsApi } from "./chunks/http.js";
|
|
2
|
+
import { _ as requestSourceToHeaders, a as sanitizeAccessToken, 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 REALTIME_ROOM_EVENT_NAME_PATTERN } 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";
|
|
5
5
|
import { VoiceApi, VoiceInputClient, createVoiceInputClient } from "./voice-input.js";
|
|
@@ -389,7 +389,8 @@ z.object({
|
|
|
389
389
|
"session",
|
|
390
390
|
"space",
|
|
391
391
|
"label",
|
|
392
|
-
"room"
|
|
392
|
+
"room",
|
|
393
|
+
"ui"
|
|
393
394
|
]),
|
|
394
395
|
type: z.string(),
|
|
395
396
|
requestId: z.string().nullable().optional(),
|
|
@@ -843,6 +844,257 @@ var WorkRealtimeApi = class {
|
|
|
843
844
|
}
|
|
844
845
|
};
|
|
845
846
|
//#endregion
|
|
847
|
+
//#region ../protocol/dist/work-surface.js
|
|
848
|
+
const WORK_SURFACE_PROTOCOL = "cohub.surface";
|
|
849
|
+
const WORK_SURFACE_READY_TIMEOUT_MS = 1e4;
|
|
850
|
+
const WORK_SURFACE_REQUEST_TIMEOUT_MS = 15e3;
|
|
851
|
+
const WORK_COMPOSER_CHIP_KEY_MAX_LENGTH = 80;
|
|
852
|
+
const WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH = 120;
|
|
853
|
+
const WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES = 32 * 1024;
|
|
854
|
+
const isRecord$1 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
855
|
+
const isSurfaceEnvelope = (value) => isRecord$1(value) && value.protocol === "cohub.surface" && value.version === 1;
|
|
856
|
+
const parseWorkSurfaceReady = (value) => {
|
|
857
|
+
if (!isSurfaceEnvelope(value) || value.type !== "ready") return null;
|
|
858
|
+
const methods = Array.isArray(value.methods) ? value.methods.filter((method) => typeof method === "string" && Boolean(method)) : [];
|
|
859
|
+
return {
|
|
860
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
861
|
+
version: 1,
|
|
862
|
+
type: "ready",
|
|
863
|
+
methods
|
|
864
|
+
};
|
|
865
|
+
};
|
|
866
|
+
const parseWorkSurfaceResponse = (value) => {
|
|
867
|
+
if (!isSurfaceEnvelope(value) || value.type !== "response") return null;
|
|
868
|
+
if (typeof value.requestId !== "string" || !value.requestId) return null;
|
|
869
|
+
const error = isRecord$1(value.error) ? {
|
|
870
|
+
code: typeof value.error.code === "string" && value.error.code ? value.error.code : "surface_error",
|
|
871
|
+
message: typeof value.error.message === "string" ? value.error.message : "Work surface call failed"
|
|
872
|
+
} : void 0;
|
|
873
|
+
return {
|
|
874
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
875
|
+
version: 1,
|
|
876
|
+
type: "response",
|
|
877
|
+
requestId: value.requestId,
|
|
878
|
+
ok: value.ok === true,
|
|
879
|
+
...error ? { error } : {}
|
|
880
|
+
};
|
|
881
|
+
};
|
|
882
|
+
const parseComposerChipKey = (value) => {
|
|
883
|
+
if (typeof value !== "string") return null;
|
|
884
|
+
const key = value.trim();
|
|
885
|
+
if (!key || key.length > 80) return null;
|
|
886
|
+
return key;
|
|
887
|
+
};
|
|
888
|
+
const parseWorkComposerChipSet = (value) => {
|
|
889
|
+
if (!isSurfaceEnvelope(value) || value.type !== "composer.chip.set" || !isRecord$1(value.chip)) return null;
|
|
890
|
+
const key = parseComposerChipKey(value.chip.key);
|
|
891
|
+
if (!key || typeof value.chip.label !== "string" || typeof value.chip.content !== "string") return null;
|
|
892
|
+
const label = value.chip.label.trim();
|
|
893
|
+
if (!label || label.length > 120) return null;
|
|
894
|
+
if (!value.chip.content.trim()) return null;
|
|
895
|
+
if (new TextEncoder().encode(value.chip.content).length > 32768) return null;
|
|
896
|
+
return {
|
|
897
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
898
|
+
version: 1,
|
|
899
|
+
type: "composer.chip.set",
|
|
900
|
+
chip: {
|
|
901
|
+
key,
|
|
902
|
+
label,
|
|
903
|
+
content: value.chip.content
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
};
|
|
907
|
+
const parseWorkComposerChipClear = (value) => {
|
|
908
|
+
if (!isSurfaceEnvelope(value) || value.type !== "composer.chip.clear") return null;
|
|
909
|
+
const key = parseComposerChipKey(value.key);
|
|
910
|
+
return key ? {
|
|
911
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
912
|
+
version: 1,
|
|
913
|
+
type: "composer.chip.clear",
|
|
914
|
+
key
|
|
915
|
+
} : null;
|
|
916
|
+
};
|
|
917
|
+
const parseWorkSurfaceRequest = (value) => {
|
|
918
|
+
if (!isSurfaceEnvelope(value) || value.type !== "request") return null;
|
|
919
|
+
if (typeof value.requestId !== "string" || !value.requestId) return null;
|
|
920
|
+
if (typeof value.method !== "string" || !value.method) return null;
|
|
921
|
+
const commandId = parseUiCommandId(value.commandId);
|
|
922
|
+
if (!commandId) return null;
|
|
923
|
+
return {
|
|
924
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
925
|
+
version: 1,
|
|
926
|
+
type: "request",
|
|
927
|
+
requestId: value.requestId,
|
|
928
|
+
method: value.method,
|
|
929
|
+
...value.input === void 0 ? {} : { input: value.input },
|
|
930
|
+
commandId
|
|
931
|
+
};
|
|
932
|
+
};
|
|
933
|
+
const buildWorkSurfaceReady = (methods) => ({
|
|
934
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
935
|
+
version: 1,
|
|
936
|
+
type: "ready",
|
|
937
|
+
methods: [...methods]
|
|
938
|
+
});
|
|
939
|
+
const buildWorkSurfaceRequest = (input) => ({
|
|
940
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
941
|
+
version: 1,
|
|
942
|
+
type: "request",
|
|
943
|
+
...input
|
|
944
|
+
});
|
|
945
|
+
const buildWorkSurfaceResponse = (input) => ({
|
|
946
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
947
|
+
version: 1,
|
|
948
|
+
type: "response",
|
|
949
|
+
...input
|
|
950
|
+
});
|
|
951
|
+
const buildWorkComposerChipSet = (chip) => ({
|
|
952
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
953
|
+
version: 1,
|
|
954
|
+
type: "composer.chip.set",
|
|
955
|
+
chip
|
|
956
|
+
});
|
|
957
|
+
const buildWorkComposerChipClear = (key) => ({
|
|
958
|
+
protocol: WORK_SURFACE_PROTOCOL,
|
|
959
|
+
version: 1,
|
|
960
|
+
type: "composer.chip.clear",
|
|
961
|
+
key
|
|
962
|
+
});
|
|
963
|
+
//#endregion
|
|
964
|
+
//#region src/work-surface.ts
|
|
965
|
+
/**
|
|
966
|
+
* Explicit app origins, not a `*.cohub.run` suffix match: Works themselves are
|
|
967
|
+
* served from Cohub subdomains, so a suffix match would let one Work call into
|
|
968
|
+
* another and turn a subdomain takeover into surface access.
|
|
969
|
+
*/
|
|
970
|
+
const COHUB_APP_ORIGINS = [
|
|
971
|
+
"https://cohub.run",
|
|
972
|
+
"https://www.cohub.run",
|
|
973
|
+
"https://dev.cohub.run"
|
|
974
|
+
];
|
|
975
|
+
const isCohubHostOrigin = (origin) => COHUB_APP_ORIGINS.includes(origin);
|
|
976
|
+
const resolveEmbedderOrigin = () => {
|
|
977
|
+
if (typeof window === "undefined" || window.parent === window) return null;
|
|
978
|
+
const ancestor = window.location?.ancestorOrigins?.[0];
|
|
979
|
+
if (typeof ancestor === "string" && ancestor) return ancestor;
|
|
980
|
+
try {
|
|
981
|
+
const referrer = typeof document === "undefined" ? "" : document.referrer;
|
|
982
|
+
return referrer ? new URL(referrer).origin : null;
|
|
983
|
+
} catch {
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
986
|
+
};
|
|
987
|
+
var WorkSurfaceApi = class {
|
|
988
|
+
handlers = /* @__PURE__ */ new Map();
|
|
989
|
+
listening = false;
|
|
990
|
+
allowedOrigins = null;
|
|
991
|
+
trustedOrigin;
|
|
992
|
+
allowHostOrigins(origins) {
|
|
993
|
+
this.allowedOrigins = origins.map((origin) => origin.trim()).filter(Boolean).map((origin) => {
|
|
994
|
+
try {
|
|
995
|
+
return new URL(origin).origin;
|
|
996
|
+
} catch {
|
|
997
|
+
return "";
|
|
998
|
+
}
|
|
999
|
+
}).filter(Boolean);
|
|
1000
|
+
this.trustedOrigin = void 0;
|
|
1001
|
+
this.announce();
|
|
1002
|
+
}
|
|
1003
|
+
handle(method, handler) {
|
|
1004
|
+
const name = method.trim();
|
|
1005
|
+
if (!name) throw new Error("Work surface method name is required");
|
|
1006
|
+
this.handlers.set(name, handler);
|
|
1007
|
+
this.start();
|
|
1008
|
+
this.announce();
|
|
1009
|
+
return () => {
|
|
1010
|
+
if (this.handlers.get(name) === handler) {
|
|
1011
|
+
this.handlers.delete(name);
|
|
1012
|
+
this.announce();
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
get methods() {
|
|
1017
|
+
return [...this.handlers.keys()];
|
|
1018
|
+
}
|
|
1019
|
+
setComposerChip(chip) {
|
|
1020
|
+
const message = parseWorkComposerChipSet(buildWorkComposerChipSet(chip));
|
|
1021
|
+
if (!message) throw new Error("Invalid Work composer chip");
|
|
1022
|
+
this.post(message);
|
|
1023
|
+
}
|
|
1024
|
+
clearComposerChip(key) {
|
|
1025
|
+
const message = parseWorkComposerChipClear(buildWorkComposerChipClear(key));
|
|
1026
|
+
if (!message) throw new Error("Invalid Work composer chip key");
|
|
1027
|
+
this.post(message);
|
|
1028
|
+
}
|
|
1029
|
+
announce() {
|
|
1030
|
+
this.post(buildWorkSurfaceReady(this.methods));
|
|
1031
|
+
}
|
|
1032
|
+
isTrusted(origin) {
|
|
1033
|
+
if (!origin || origin === "null") return false;
|
|
1034
|
+
if (typeof window !== "undefined" && origin === window.location?.origin) return true;
|
|
1035
|
+
return this.allowedOrigins ? this.allowedOrigins.includes(origin) : isCohubHostOrigin(origin);
|
|
1036
|
+
}
|
|
1037
|
+
resolveTrustedOrigin() {
|
|
1038
|
+
if (this.trustedOrigin !== void 0) return this.trustedOrigin;
|
|
1039
|
+
const embedder = resolveEmbedderOrigin();
|
|
1040
|
+
this.trustedOrigin = embedder && this.isTrusted(embedder) ? embedder : null;
|
|
1041
|
+
return this.trustedOrigin;
|
|
1042
|
+
}
|
|
1043
|
+
start() {
|
|
1044
|
+
if (this.listening || typeof window === "undefined") return;
|
|
1045
|
+
this.listening = true;
|
|
1046
|
+
window.addEventListener("message", this.onMessage);
|
|
1047
|
+
}
|
|
1048
|
+
onMessage = (event) => {
|
|
1049
|
+
if (typeof window === "undefined" || event.source !== window.parent) return;
|
|
1050
|
+
if (!this.isTrusted(event.origin)) return;
|
|
1051
|
+
const request = parseWorkSurfaceRequest(event.data);
|
|
1052
|
+
if (!request) return;
|
|
1053
|
+
const commandId = request.commandId;
|
|
1054
|
+
if (!commandId) return;
|
|
1055
|
+
this.trustedOrigin = event.origin;
|
|
1056
|
+
this.dispatch(request.requestId, request.method, request.input, commandId);
|
|
1057
|
+
};
|
|
1058
|
+
async dispatch(requestId, method, input, commandId) {
|
|
1059
|
+
const handler = this.handlers.get(method);
|
|
1060
|
+
if (!handler) {
|
|
1061
|
+
this.post(buildWorkSurfaceResponse({
|
|
1062
|
+
requestId,
|
|
1063
|
+
ok: false,
|
|
1064
|
+
error: {
|
|
1065
|
+
code: "method_not_found",
|
|
1066
|
+
message: `This Work does not expose "${method}".`
|
|
1067
|
+
}
|
|
1068
|
+
}));
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
try {
|
|
1072
|
+
await handler(input, { commandId });
|
|
1073
|
+
this.post(buildWorkSurfaceResponse({
|
|
1074
|
+
requestId,
|
|
1075
|
+
ok: true
|
|
1076
|
+
}));
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
this.post(buildWorkSurfaceResponse({
|
|
1079
|
+
requestId,
|
|
1080
|
+
ok: false,
|
|
1081
|
+
error: {
|
|
1082
|
+
code: "handler_failed",
|
|
1083
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1084
|
+
}
|
|
1085
|
+
}));
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
post(message) {
|
|
1089
|
+
if (typeof window === "undefined" || window.parent === window) return;
|
|
1090
|
+
const origin = this.resolveTrustedOrigin();
|
|
1091
|
+
if (!origin) return;
|
|
1092
|
+
try {
|
|
1093
|
+
window.parent.postMessage(message, origin);
|
|
1094
|
+
} catch {}
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
//#endregion
|
|
846
1098
|
//#region src/work-runtime.ts
|
|
847
1099
|
const isBrowser$1 = () => typeof window !== "undefined" && typeof window.parent !== "undefined";
|
|
848
1100
|
const hasParent = () => isBrowser$1() && window.parent !== window;
|
|
@@ -1240,6 +1492,7 @@ var CohubClient = class {
|
|
|
1240
1492
|
references;
|
|
1241
1493
|
tasks;
|
|
1242
1494
|
cronJobs;
|
|
1495
|
+
ui;
|
|
1243
1496
|
invite;
|
|
1244
1497
|
referrals;
|
|
1245
1498
|
voice;
|
|
@@ -1296,6 +1549,7 @@ var CohubClient = class {
|
|
|
1296
1549
|
this.references = new ReferencesApi(this.transport);
|
|
1297
1550
|
this.tasks = new TasksApi(this.transport);
|
|
1298
1551
|
this.cronJobs = new CronJobsApi(this.transport);
|
|
1552
|
+
this.ui = new UiCommandsApi(this.transport);
|
|
1299
1553
|
this.invite = new PublicInviteApi(this.transport);
|
|
1300
1554
|
this.referrals = new ReferralsApi(this.transport);
|
|
1301
1555
|
this.works = new WorksApi(this.transport);
|
|
@@ -1308,6 +1562,14 @@ var CohubClient = class {
|
|
|
1308
1562
|
auth = { request: (input) => this.workRuntime.requestAuthorization(input) };
|
|
1309
1563
|
work = {
|
|
1310
1564
|
realtime: null,
|
|
1565
|
+
/** Expose callable methods from inside a published Work. */
|
|
1566
|
+
surface: new WorkSurfaceApi(),
|
|
1567
|
+
composer: {
|
|
1568
|
+
/** Attach or update context from this Work in the Cohub composer. */
|
|
1569
|
+
setChip: (chip) => this.work.surface.setComposerChip(chip),
|
|
1570
|
+
/** Remove context previously attached by this Work. */
|
|
1571
|
+
clearChip: (key) => this.work.surface.clearComposerChip(key)
|
|
1572
|
+
},
|
|
1311
1573
|
commerce: {
|
|
1312
1574
|
resolveProducts: async (input) => {
|
|
1313
1575
|
const context = await this.workRuntime.context();
|
|
@@ -1393,6 +1655,140 @@ var CohubClient = class {
|
|
|
1393
1655
|
};
|
|
1394
1656
|
const createCohubClient = (options) => new CohubClient(options);
|
|
1395
1657
|
//#endregion
|
|
1658
|
+
//#region ../protocol/dist/public-identifiers.js
|
|
1659
|
+
const USERNAME_PATTERN = /^(?!-)(?!.*--)[a-z0-9-]{1,39}(?<!-)$/;
|
|
1660
|
+
const SPACE_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9_-]{0,78}[a-z0-9])?$/;
|
|
1661
|
+
/**
|
|
1662
|
+
* Platform-owned path segments that must not be newly assigned to public
|
|
1663
|
+
* identities. Existing stored values remain readable through parse helpers.
|
|
1664
|
+
*/
|
|
1665
|
+
const RESERVED_PLATFORM_PATH_SEGMENTS = Object.freeze([
|
|
1666
|
+
"admin",
|
|
1667
|
+
"api",
|
|
1668
|
+
"assets",
|
|
1669
|
+
"auth",
|
|
1670
|
+
"callback",
|
|
1671
|
+
"changelog",
|
|
1672
|
+
"docs",
|
|
1673
|
+
"explore",
|
|
1674
|
+
"invite",
|
|
1675
|
+
"login",
|
|
1676
|
+
"logout",
|
|
1677
|
+
"new",
|
|
1678
|
+
"org",
|
|
1679
|
+
"pricing",
|
|
1680
|
+
"pwa",
|
|
1681
|
+
"referrals",
|
|
1682
|
+
"sessions",
|
|
1683
|
+
"settings",
|
|
1684
|
+
"spaces",
|
|
1685
|
+
"static",
|
|
1686
|
+
"teams",
|
|
1687
|
+
"trending",
|
|
1688
|
+
"u",
|
|
1689
|
+
"user",
|
|
1690
|
+
"users",
|
|
1691
|
+
"work-auth"
|
|
1692
|
+
]);
|
|
1693
|
+
new Set(RESERVED_PLATFORM_PATH_SEGMENTS);
|
|
1694
|
+
[...RESERVED_PLATFORM_PATH_SEGMENTS];
|
|
1695
|
+
function parseUsername(value) {
|
|
1696
|
+
if (value === null || value === void 0) return null;
|
|
1697
|
+
const normalized = value.trim().toLowerCase();
|
|
1698
|
+
return USERNAME_PATTERN.test(normalized) ? normalized : null;
|
|
1699
|
+
}
|
|
1700
|
+
function parseSpaceSlug(value) {
|
|
1701
|
+
if (value === null || value === void 0) return null;
|
|
1702
|
+
const normalized = value.trim();
|
|
1703
|
+
return SPACE_SLUG_PATTERN.test(normalized) ? normalized : null;
|
|
1704
|
+
}
|
|
1705
|
+
//#endregion
|
|
1706
|
+
//#region src/work-ref.ts
|
|
1707
|
+
/**
|
|
1708
|
+
* Accepts every way a Work is named across Cohub — id, management URL, public
|
|
1709
|
+
* URL, `cohub://works` URI, or `username/space/work` — and normalizes it.
|
|
1710
|
+
*
|
|
1711
|
+
* Public and mention forms may carry launch state (`?query#hash`); it is kept
|
|
1712
|
+
* separately so it can be forwarded to the Work while the stable identity stays
|
|
1713
|
+
* clean.
|
|
1714
|
+
*/
|
|
1715
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1716
|
+
const isWorkId = (value) => UUID_PATTERN.test(value.trim());
|
|
1717
|
+
function decodePart(value) {
|
|
1718
|
+
try {
|
|
1719
|
+
return decodeURIComponent(value).trim();
|
|
1720
|
+
} catch {
|
|
1721
|
+
return "";
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
function publicRef(parts) {
|
|
1725
|
+
if (parts.length !== 3) return null;
|
|
1726
|
+
const [usernameRaw = "", spaceSlugRaw = "", workSlugRaw = ""] = parts.map(decodePart);
|
|
1727
|
+
const username = parseUsername(usernameRaw);
|
|
1728
|
+
const spaceSlug = parseSpaceSlug(spaceSlugRaw);
|
|
1729
|
+
const workSlug = parseSpaceSlug(workSlugRaw);
|
|
1730
|
+
return username && spaceSlug && workSlug ? {
|
|
1731
|
+
username,
|
|
1732
|
+
spaceSlug,
|
|
1733
|
+
workSlug
|
|
1734
|
+
} : null;
|
|
1735
|
+
}
|
|
1736
|
+
function launchState(url) {
|
|
1737
|
+
return {
|
|
1738
|
+
...url.search ? { search: url.search } : {},
|
|
1739
|
+
...url.hash ? { hash: url.hash } : {}
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
function parseUrlRef(value) {
|
|
1743
|
+
let url;
|
|
1744
|
+
try {
|
|
1745
|
+
url = new URL(value);
|
|
1746
|
+
} catch {
|
|
1747
|
+
return null;
|
|
1748
|
+
}
|
|
1749
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
1750
|
+
if (url.protocol === "cohub:" && url.hostname === "works") {
|
|
1751
|
+
const ref = publicRef(parts);
|
|
1752
|
+
return ref ? {
|
|
1753
|
+
...ref,
|
|
1754
|
+
...launchState(url)
|
|
1755
|
+
} : null;
|
|
1756
|
+
}
|
|
1757
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
1758
|
+
if (parts.length === 4 && parts[0] === "spaces" && UUID_PATTERN.test(parts[1] ?? "") && parts[2] === "works" && UUID_PATTERN.test(parts[3] ?? "")) return { id: parts[3] };
|
|
1759
|
+
if (parts.length === 4 && parts[2] === "w") {
|
|
1760
|
+
const ref = publicRef([
|
|
1761
|
+
parts[0],
|
|
1762
|
+
parts[1],
|
|
1763
|
+
parts[3]
|
|
1764
|
+
]);
|
|
1765
|
+
return ref ? {
|
|
1766
|
+
...ref,
|
|
1767
|
+
...launchState(url)
|
|
1768
|
+
} : null;
|
|
1769
|
+
}
|
|
1770
|
+
return null;
|
|
1771
|
+
}
|
|
1772
|
+
var WorkRefParseError = class extends Error {
|
|
1773
|
+
constructor() {
|
|
1774
|
+
super("Work must be an id, public URL, cohub://works URI, or username/space/work reference");
|
|
1775
|
+
this.name = "WorkRefParseError";
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
function parseWorkRef(input) {
|
|
1779
|
+
const value = input.trim();
|
|
1780
|
+
if (UUID_PATTERN.test(value)) return { id: value };
|
|
1781
|
+
const parsedUrl = parseUrlRef(value.includes("://") ? value : value.startsWith("/") ? `https://cohub.invalid${value}` : value);
|
|
1782
|
+
if (parsedUrl) return parsedUrl;
|
|
1783
|
+
const parts = value.split("/").filter(Boolean);
|
|
1784
|
+
const parsedPublic = parts.length === 3 ? publicRef(parts) : null;
|
|
1785
|
+
if (parsedPublic) return parsedPublic;
|
|
1786
|
+
throw new WorkRefParseError();
|
|
1787
|
+
}
|
|
1788
|
+
function formatWorkRef(ref) {
|
|
1789
|
+
return "id" in ref ? ref.id : `${ref.username}/${ref.spaceSlug}/${ref.workSlug}`;
|
|
1790
|
+
}
|
|
1791
|
+
//#endregion
|
|
1396
1792
|
//#region src/work-grant-cache.ts
|
|
1397
1793
|
const STORAGE_PREFIX = "cohub:work-grants";
|
|
1398
1794
|
const CACHE_VERSION = 1;
|
|
@@ -2246,42 +2642,6 @@ z.discriminatedUnion("type", [
|
|
|
2246
2642
|
})
|
|
2247
2643
|
]);
|
|
2248
2644
|
//#endregion
|
|
2249
|
-
//#region ../protocol/dist/public-identifiers.js
|
|
2250
|
-
/**
|
|
2251
|
-
* Platform-owned path segments that must not be newly assigned to public
|
|
2252
|
-
* identities. Existing stored values remain readable through parse helpers.
|
|
2253
|
-
*/
|
|
2254
|
-
const RESERVED_PLATFORM_PATH_SEGMENTS = Object.freeze([
|
|
2255
|
-
"admin",
|
|
2256
|
-
"api",
|
|
2257
|
-
"assets",
|
|
2258
|
-
"auth",
|
|
2259
|
-
"callback",
|
|
2260
|
-
"changelog",
|
|
2261
|
-
"docs",
|
|
2262
|
-
"explore",
|
|
2263
|
-
"invite",
|
|
2264
|
-
"login",
|
|
2265
|
-
"logout",
|
|
2266
|
-
"new",
|
|
2267
|
-
"org",
|
|
2268
|
-
"pricing",
|
|
2269
|
-
"pwa",
|
|
2270
|
-
"referrals",
|
|
2271
|
-
"sessions",
|
|
2272
|
-
"settings",
|
|
2273
|
-
"spaces",
|
|
2274
|
-
"static",
|
|
2275
|
-
"teams",
|
|
2276
|
-
"trending",
|
|
2277
|
-
"u",
|
|
2278
|
-
"user",
|
|
2279
|
-
"users",
|
|
2280
|
-
"work-auth"
|
|
2281
|
-
]);
|
|
2282
|
-
new Set(RESERVED_PLATFORM_PATH_SEGMENTS);
|
|
2283
|
-
[...RESERVED_PLATFORM_PATH_SEGMENTS];
|
|
2284
|
-
//#endregion
|
|
2285
2645
|
//#region src/board/animation.ts
|
|
2286
2646
|
const ZERO_COST = {
|
|
2287
2647
|
particles: 0,
|
|
@@ -2633,4 +2993,4 @@ function createBoardExtensionRegistry(input = {}) {
|
|
|
2633
2993
|
return registry;
|
|
2634
2994
|
}
|
|
2635
2995
|
//#endregion
|
|
2636
|
-
export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BillingApi, BoardClient, BoardExtensionRegistry, BoardPlaybackPolicySchema, BoardTransactionError, 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, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRoom, WorkRuntimeApi, WorksApi, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, clearGrantedWorkScopes, clip, compileSequence, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceEmpty, isRequestSourceUuid, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sanitizeAccessToken, setGrantedWorkScopes, timeline };
|
|
2996
|
+
export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BillingApi, BoardClient, BoardExtensionRegistry, BoardPlaybackPolicySchema, BoardTransactionError, 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, 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, clip, compileSequence, 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, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseUiCommand, parseWorkRef, parseWorkSurfaceReady, parseWorkSurfaceResponse, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sanitizeAccessToken, setGrantedWorkScopes, timeline };
|
|
@@ -771,6 +771,38 @@ new one. The server keeps the participant ID, updates `room.participantId`, and
|
|
|
771
771
|
closes the superseded connection without emitting a leave event. Without this
|
|
772
772
|
mode, an unclean disconnect can retain its seat lease for up to one minute.
|
|
773
773
|
|
|
774
|
+
#### UI command calls that complete later
|
|
775
|
+
|
|
776
|
+
A `preview.show` command with a Surface request stays pending after the Work
|
|
777
|
+
acknowledges it. The host waits for the Work to be mounted and ready, then the
|
|
778
|
+
Work receives the originating `commandId` in the handler context and returns an
|
|
779
|
+
acknowledgement immediately:
|
|
780
|
+
|
|
781
|
+
```js
|
|
782
|
+
let activeCommandId = null;
|
|
783
|
+
|
|
784
|
+
client.work.surface.handle("image.open", async (input, { commandId }) => {
|
|
785
|
+
if (!commandId) throw new Error("image.open must be called by a UI command");
|
|
786
|
+
activeCommandId = commandId;
|
|
787
|
+
openImageStudio(input);
|
|
788
|
+
return { accepted: true };
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
async function useImage(result) {
|
|
792
|
+
if (!activeCommandId) return;
|
|
793
|
+
await client.ui.reportResult(activeCommandId, {
|
|
794
|
+
status: "applied",
|
|
795
|
+
result,
|
|
796
|
+
error: null,
|
|
797
|
+
});
|
|
798
|
+
activeCommandId = null;
|
|
799
|
+
}
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
The Work should persist the command id alongside its local/server-backed draft
|
|
803
|
+
so a reload can restore the pending interaction. A Work session may only report a command that targets that same Work. Existing
|
|
804
|
+
`client.work.surface.handle(method, handler)` usage remains unchanged.
|
|
805
|
+
|
|
774
806
|
---
|
|
775
807
|
|
|
776
808
|
## 6. Complete working example
|