@neta-art/cohub 8.18.0 → 8.20.0
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/README.md +17 -9
- package/dist/chunks/environment.d.ts +72 -10
- package/dist/chunks/environment.js +5 -1
- package/dist/chunks/http.d.ts +78 -16
- package/dist/chunks/http.js +27 -11
- package/dist/chunks/websocket.d.ts +1 -1
- package/dist/chunks/websocket.js +1 -1
- package/dist/http.d.ts +2 -2
- package/dist/http.js +1 -1
- package/dist/index.d.ts +80 -3
- package/dist/index.js +826 -71
- package/docs/app-runtime-guide.md +174 -146
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { $ as BoardEffectInputSchema, A as SkillsApi, B as DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, C as SessionPatchReducer, D as ReferencesApi, E as SessionAccessApi, F as ChannelsApi, G as parseDesktopCommand, H as DESKTOP_COMMAND_VERSION, I as DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, J as NAVIGATION_ERROR_MESSAGE_MAX_LENGTH, K as parseDesktopCommandId, L as DESKTOP_COMMAND_MAX_TIMEOUT_MS, M as ModelsApi, N as GenerationsApi, O as SearchApi, P as CronJobsApi, Q as BoardSemanticMutationSchema, R as DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, S as parseAssistantMessageCommit, T as ensureRealtimeConnected, U as isDesktopCallMethod, V as DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, W as isTerminalDesktopCommandStatus, X as BoardItemPatchSchema, Y as BoardAuthoringItemSchema, Z as BoardSemanticCommandSchema, _ as PublicInviteApi, a as DesktopCommandsApi, at as BoardCompositionSchema, b as SessionGenerationStreamClient, c as ReferralsApi, ct as parseBoardCompositionInput, d as TasksApi, dt as BOARD_BUILTIN_CAPABILITIES, et as BoardEffectSchema, f as BoardClient, ft as BOARD_BUILTIN_CLIP_KINDS, g as SpacesApi, h as SpaceWebhooksApi, i as WorkCommerceApi, it as BoardCompositionInputSchema, j as PromptsApi, k as PublicAssetsApi, l as UsersApi, lt as BoardConnectionSchema, m as SpacePublicFilesApi, mt as DEFAULT_BOARD_RENDER_LIMITS, n as createHttpClient, nt as BOARD_ANIMATION_CHANNELS, o as UiCommandsApi, ot as BoardProceduralClipSchema, p as SpaceClient, pt as BOARD_BUILTIN_EFFECT_KINDS, q as resolveOpenSurface, r as AppCommerceApi, rt as BOARD_ANIMATION_CHANNEL_CAPABILITIES, s as AppsApi, st as BoardTrackSchema, t as CohubHttpClient, tt as parseBoardEffectInput, u as UserApi, ut as BOARD_ARROW_STROKE_SIZE, v as buildSpaceInvitePath, w as createSessionPatchReducer, x as createSessionGenerationStreamClient, y as buildSpacePath, z as DESKTOP_COMMAND_PENDING_TTL_SECONDS } 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
|
-
import { a as normalizeWebsocketUrl, c as resolveExecutionAppId, d as
|
|
3
|
+
import { a as normalizeWebsocketUrl, c as resolveExecutionAppId, d as resolveWebBaseUrl, f as resolveWebsocketUrl, g as scopeListHasPermission, h as isUserLevelPermission, i as normalizeVoiceInputWebsocketUrl, l as resolveExecutionToken, m as USER_LEVEL_PERMISSIONS, n as getCohubContext, o as resolveApiBaseUrl, p as PERMISSIONS, r as normalizeBaseUrl, s as resolveCohubEnvironment, t as COHUB_ENVIRONMENTS, u as resolveVoiceInputWebsocketUrl } 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";
|
|
6
6
|
import { filterSpacePickerItems, normalizeSpacePickerQuery, orderSpacePickerItems, selectSpacePickerItems } from "./space-picker.js";
|
|
@@ -14,6 +14,15 @@ const appAuthorizationTargetSchema = z.discriminatedUnion("kind", [
|
|
|
14
14
|
}),
|
|
15
15
|
z.object({ kind: z.literal("pick-space") })
|
|
16
16
|
]);
|
|
17
|
+
const APP_SILENT_SHELL_SCOPE_SET = /* @__PURE__ */ new Set([
|
|
18
|
+
"space.view",
|
|
19
|
+
"file.view",
|
|
20
|
+
"file.view.filtered",
|
|
21
|
+
"session.view",
|
|
22
|
+
"taskrun.view",
|
|
23
|
+
"checkpoint.view"
|
|
24
|
+
]);
|
|
25
|
+
const isAppSilentShellScope = (scope) => APP_SILENT_SHELL_SCOPE_SET.has(scope);
|
|
17
26
|
const appAuthorizationRequestSchema = z.object({
|
|
18
27
|
target: appAuthorizationTargetSchema,
|
|
19
28
|
scopes: z.array(z.string().min(1)).min(1).max(128),
|
|
@@ -1344,6 +1353,457 @@ function validateArchiveBoundary(index, parent) {
|
|
|
1344
1353
|
if (!index.segments.length && parent?.sha256 !== index.sha256) throw new Error("Unchanged archive checksum mismatch");
|
|
1345
1354
|
}
|
|
1346
1355
|
//#endregion
|
|
1356
|
+
//#region ../protocol/dist/runtime/projection.js
|
|
1357
|
+
const DEFAULT_TIMESTAMP = "1970-01-01T00:00:00.000Z";
|
|
1358
|
+
const asMeta = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1359
|
+
const safeId = (value) => value.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
1360
|
+
const entryId = (messageId, ordinal) => `cohub_${safeId(messageId)}_${ordinal}`;
|
|
1361
|
+
const isProjectionCompaction = (message) => message.role === "system" && message.content.some((block) => block.type === "system_note" && block.note_type === "compacted");
|
|
1362
|
+
const isPendingGeneration = (message) => message.role === "assistant" && message.meta?.messageKind === "generation_result" && !["completed", "failed"].includes(String(message.meta.generationStatus));
|
|
1363
|
+
const messageMeta = (message) => asMeta(message.meta);
|
|
1364
|
+
const timestampOf = (message, fallback) => message.createdAt || fallback;
|
|
1365
|
+
function stableJson(value) {
|
|
1366
|
+
if (value === void 0) return "undefined";
|
|
1367
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "undefined";
|
|
1368
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
1369
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
1370
|
+
}
|
|
1371
|
+
function fingerprint(value) {
|
|
1372
|
+
let hash = 14695981039346656037n;
|
|
1373
|
+
for (const character of stableJson(value)) {
|
|
1374
|
+
hash ^= BigInt(character.codePointAt(0) ?? 0);
|
|
1375
|
+
hash = BigInt.asUintN(64, hash * 1099511628211n);
|
|
1376
|
+
}
|
|
1377
|
+
return hash.toString(16).padStart(16, "0");
|
|
1378
|
+
}
|
|
1379
|
+
function sourceFingerprint(turns) {
|
|
1380
|
+
return `${turns.length}:${fingerprint(turns)}`;
|
|
1381
|
+
}
|
|
1382
|
+
function projectNativeMessageMeta(message) {
|
|
1383
|
+
const source = messageMeta(message);
|
|
1384
|
+
const safe = {
|
|
1385
|
+
sourceSessionId: message.sourceSessionId,
|
|
1386
|
+
sequence: message.sequence,
|
|
1387
|
+
createdAt: message.createdAt
|
|
1388
|
+
};
|
|
1389
|
+
for (const key of [
|
|
1390
|
+
"agentSessionEntryId",
|
|
1391
|
+
"messageKind",
|
|
1392
|
+
"nativeApi",
|
|
1393
|
+
"generationStatus"
|
|
1394
|
+
]) if (source[key] !== void 0) safe[key] = source[key];
|
|
1395
|
+
const compaction = asMeta(source.compaction);
|
|
1396
|
+
if (Object.keys(compaction).length) safe.compaction = Object.fromEntries([
|
|
1397
|
+
"compactionId",
|
|
1398
|
+
"compactedAt",
|
|
1399
|
+
"tokensBefore",
|
|
1400
|
+
"firstKeptEntryId"
|
|
1401
|
+
].filter((key) => compaction[key] !== void 0).map((key) => [key, compaction[key]]));
|
|
1402
|
+
return {
|
|
1403
|
+
...safe,
|
|
1404
|
+
messageId: message.id,
|
|
1405
|
+
turnId: message.turnId
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
function projectPiBlock(block) {
|
|
1409
|
+
if (block.type === "text") return {
|
|
1410
|
+
type: "text",
|
|
1411
|
+
text: block.text
|
|
1412
|
+
};
|
|
1413
|
+
if (block.type === "thinking") return {
|
|
1414
|
+
type: "thinking",
|
|
1415
|
+
thinking: block.thinking,
|
|
1416
|
+
...block.signature ? { thinkingSignature: block.signature } : {}
|
|
1417
|
+
};
|
|
1418
|
+
if (block.type === "image" && block.source.type === "base64") return {
|
|
1419
|
+
type: "image",
|
|
1420
|
+
data: block.source.data,
|
|
1421
|
+
mimeType: block.source.media_type
|
|
1422
|
+
};
|
|
1423
|
+
if (block.type === "tool_use") return {
|
|
1424
|
+
type: "toolCall",
|
|
1425
|
+
id: block.id,
|
|
1426
|
+
name: block.name,
|
|
1427
|
+
arguments: block.input
|
|
1428
|
+
};
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
function containsExternalImage(content) {
|
|
1432
|
+
return content.some((block) => block.type === "image" && block.source.type === "url" || block.type === "tool_result" && Array.isArray(block.content) && containsExternalImage(block.content));
|
|
1433
|
+
}
|
|
1434
|
+
function projectPiMessage(message, warnings, toolNames) {
|
|
1435
|
+
const meta = projectNativeMessageMeta(message);
|
|
1436
|
+
if (containsExternalImage(message.content)) warnings.push({
|
|
1437
|
+
sourceMessageId: message.id,
|
|
1438
|
+
sourceTurnId: message.turnId,
|
|
1439
|
+
reason: "external image URL omitted from native projection"
|
|
1440
|
+
});
|
|
1441
|
+
if (message.role === "system") return [];
|
|
1442
|
+
if (message.role === "user") {
|
|
1443
|
+
const content = message.content.map(projectPiBlock).filter((block) => block !== null);
|
|
1444
|
+
return content.length ? [{
|
|
1445
|
+
role: "user",
|
|
1446
|
+
content,
|
|
1447
|
+
timestamp: Date.parse(message.createdAt) || 0,
|
|
1448
|
+
meta
|
|
1449
|
+
}] : [];
|
|
1450
|
+
}
|
|
1451
|
+
const calls = new Map(message.content.flatMap((block) => block.type === "tool_use" ? [[block.id, block.name]] : []));
|
|
1452
|
+
for (const [id, name] of calls) toolNames.set(id, name);
|
|
1453
|
+
const result = [];
|
|
1454
|
+
const content = message.content.map(projectPiBlock).filter((block) => block !== null);
|
|
1455
|
+
if (content.length) result.push({
|
|
1456
|
+
role: "assistant",
|
|
1457
|
+
content,
|
|
1458
|
+
api: typeof messageMeta(message).nativeApi === "string" ? messageMeta(message).nativeApi : "history",
|
|
1459
|
+
provider: message.provider ?? "history",
|
|
1460
|
+
model: message.model ?? "history",
|
|
1461
|
+
usage: message.usage ?? emptyUsage(),
|
|
1462
|
+
stopReason: message.stopReason === "tool_use" ? "toolUse" : message.stopReason ?? (calls.size ? "toolUse" : "stop"),
|
|
1463
|
+
...message.errorMessage ? { errorMessage: message.errorMessage } : {},
|
|
1464
|
+
timestamp: Date.parse(message.createdAt) || 0,
|
|
1465
|
+
meta
|
|
1466
|
+
});
|
|
1467
|
+
for (const block of message.content) {
|
|
1468
|
+
if (block.type !== "tool_result") continue;
|
|
1469
|
+
const toolName = calls.get(block.tool_use_id) ?? toolNames.get(block.tool_use_id);
|
|
1470
|
+
if (!toolName) continue;
|
|
1471
|
+
result.push({
|
|
1472
|
+
role: "toolResult",
|
|
1473
|
+
toolCallId: block.tool_use_id,
|
|
1474
|
+
toolName,
|
|
1475
|
+
content: typeof block.content === "string" ? [{
|
|
1476
|
+
type: "text",
|
|
1477
|
+
text: block.content
|
|
1478
|
+
}] : block.content.map(projectPiBlock).filter((item) => item !== null),
|
|
1479
|
+
isError: block.is_error ?? false,
|
|
1480
|
+
timestamp: Date.parse(message.createdAt) || 0,
|
|
1481
|
+
meta
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
return result;
|
|
1485
|
+
}
|
|
1486
|
+
function emptyUsage() {
|
|
1487
|
+
return {
|
|
1488
|
+
input: 0,
|
|
1489
|
+
output: 0,
|
|
1490
|
+
cacheRead: 0,
|
|
1491
|
+
cacheWrite: 0,
|
|
1492
|
+
totalTokens: 0,
|
|
1493
|
+
cost: null
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
function projectionMetadata(input) {
|
|
1497
|
+
return {
|
|
1498
|
+
schema: "cohub.projection.v2",
|
|
1499
|
+
spaceId: input.spaceId,
|
|
1500
|
+
sessionId: input.sessionId
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
function trimProjectionTurnsToCompaction(turns) {
|
|
1504
|
+
let boundaryTurn = -1;
|
|
1505
|
+
let boundaryMessage = -1;
|
|
1506
|
+
let boundaryOrder = Number.NEGATIVE_INFINITY;
|
|
1507
|
+
turns.forEach((turn, turnIndex) => {
|
|
1508
|
+
turn.messages.forEach((message, messageIndex) => {
|
|
1509
|
+
if (!isProjectionCompaction(message)) return;
|
|
1510
|
+
const compactedAt = asMeta(messageMeta(message).compaction).compactedAt;
|
|
1511
|
+
const parsed = typeof compactedAt === "string" ? Date.parse(compactedAt) : NaN;
|
|
1512
|
+
const order = Number.isFinite(parsed) ? parsed : turnIndex * 1e6 + messageIndex;
|
|
1513
|
+
if (order >= boundaryOrder) {
|
|
1514
|
+
boundaryTurn = turnIndex;
|
|
1515
|
+
boundaryMessage = messageIndex;
|
|
1516
|
+
boundaryOrder = order;
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
});
|
|
1520
|
+
if (boundaryTurn < 0) return turns;
|
|
1521
|
+
return turns.slice(boundaryTurn).flatMap((turn, index) => {
|
|
1522
|
+
const messages = index === 0 ? turn.messages.slice(boundaryMessage).filter((message, messageIndex) => messageIndex === 0 || !isProjectionCompaction(message)) : turn.messages.filter((message) => !isProjectionCompaction(message));
|
|
1523
|
+
return messages.length ? [{
|
|
1524
|
+
...turn,
|
|
1525
|
+
messages
|
|
1526
|
+
}] : [];
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
function piProjection(input) {
|
|
1530
|
+
const sourceTurns = trimProjectionTurnsToCompaction(input.turns);
|
|
1531
|
+
const fallbackTimestamp = input.timestamp ?? DEFAULT_TIMESTAMP;
|
|
1532
|
+
const records = [{
|
|
1533
|
+
key: `header:${input.nativeSessionId}`,
|
|
1534
|
+
sourceTurnId: null,
|
|
1535
|
+
sourceMessageId: null,
|
|
1536
|
+
record: {
|
|
1537
|
+
type: "session",
|
|
1538
|
+
version: 3,
|
|
1539
|
+
id: input.nativeSessionId,
|
|
1540
|
+
timestamp: fallbackTimestamp,
|
|
1541
|
+
cwd: input.cwd,
|
|
1542
|
+
affinity: {
|
|
1543
|
+
sessionId: input.sessionId,
|
|
1544
|
+
threadId: input.nativeSessionId
|
|
1545
|
+
},
|
|
1546
|
+
cohub: projectionMetadata(input)
|
|
1547
|
+
}
|
|
1548
|
+
}];
|
|
1549
|
+
const warnings = [];
|
|
1550
|
+
const nativeIds = /* @__PURE__ */ new Map();
|
|
1551
|
+
const toolNames = /* @__PURE__ */ new Map();
|
|
1552
|
+
const compactions = [];
|
|
1553
|
+
let parentId = null;
|
|
1554
|
+
for (const turn of sourceTurns) for (const message of turn.messages) {
|
|
1555
|
+
if (isPendingGeneration(message)) continue;
|
|
1556
|
+
if (isProjectionCompaction(message)) {
|
|
1557
|
+
const id = `cohub_${safeId(message.id)}_compaction`;
|
|
1558
|
+
const record = {
|
|
1559
|
+
type: "compaction",
|
|
1560
|
+
id,
|
|
1561
|
+
parentId,
|
|
1562
|
+
timestamp: timestampOf(message, fallbackTimestamp),
|
|
1563
|
+
summary: message.content.flatMap((block) => block.type === "system_note" ? [block.text] : []).join("\n"),
|
|
1564
|
+
firstKeptEntryId: "",
|
|
1565
|
+
tokensBefore: Number(asMeta(messageMeta(message).compaction).tokensBefore) || 0
|
|
1566
|
+
};
|
|
1567
|
+
const projectionRecord = {
|
|
1568
|
+
key: `${message.id}:${message.turnId}:compaction`,
|
|
1569
|
+
sourceTurnId: message.turnId,
|
|
1570
|
+
sourceMessageId: message.id,
|
|
1571
|
+
record
|
|
1572
|
+
};
|
|
1573
|
+
records.push(projectionRecord);
|
|
1574
|
+
compactions.push({
|
|
1575
|
+
record: projectionRecord,
|
|
1576
|
+
firstKeptEntryId: typeof asMeta(messageMeta(message).compaction).firstKeptEntryId === "string" ? String(asMeta(messageMeta(message).compaction).firstKeptEntryId) : null
|
|
1577
|
+
});
|
|
1578
|
+
parentId = id;
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
const projected = projectPiMessage(message, warnings, toolNames);
|
|
1582
|
+
projected.forEach((nativeMessage, ordinal) => {
|
|
1583
|
+
const id = entryId(message.id, ordinal);
|
|
1584
|
+
const record = {
|
|
1585
|
+
type: "message",
|
|
1586
|
+
id,
|
|
1587
|
+
parentId,
|
|
1588
|
+
timestamp: timestampOf(message, fallbackTimestamp),
|
|
1589
|
+
message: nativeMessage
|
|
1590
|
+
};
|
|
1591
|
+
records.push({
|
|
1592
|
+
key: `${message.id}:${message.turnId}:${ordinal}`,
|
|
1593
|
+
sourceTurnId: message.turnId,
|
|
1594
|
+
sourceMessageId: message.id,
|
|
1595
|
+
record
|
|
1596
|
+
});
|
|
1597
|
+
if (!nativeIds.has(message.id)) nativeIds.set(message.id, id);
|
|
1598
|
+
const sourceEntry = messageMeta(message).agentSessionEntryId;
|
|
1599
|
+
if (typeof sourceEntry === "string" && !nativeIds.has(sourceEntry)) nativeIds.set(sourceEntry, id);
|
|
1600
|
+
parentId = id;
|
|
1601
|
+
});
|
|
1602
|
+
if (projected.length === 0 && message.content.length) warnings.push({
|
|
1603
|
+
sourceMessageId: message.id,
|
|
1604
|
+
sourceTurnId: message.turnId,
|
|
1605
|
+
reason: "message has no native Pi representation"
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
for (const compaction of compactions) compaction.record.record.firstKeptEntryId = compaction.firstKeptEntryId ? nativeIds.get(compaction.firstKeptEntryId) ?? "" : "";
|
|
1609
|
+
return {
|
|
1610
|
+
target: "pi",
|
|
1611
|
+
records,
|
|
1612
|
+
cursor: {
|
|
1613
|
+
sourceSequence: input.turns.at(-1)?.sequence ?? null,
|
|
1614
|
+
sourceTurnId: input.turns.at(-1)?.sourceTurnId ?? null,
|
|
1615
|
+
sourceFingerprint: sourceFingerprint(input.turns)
|
|
1616
|
+
},
|
|
1617
|
+
warnings
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
function codexInputContent(block) {
|
|
1621
|
+
if (block.type === "text") return [{
|
|
1622
|
+
type: "input_text",
|
|
1623
|
+
text: block.text
|
|
1624
|
+
}];
|
|
1625
|
+
if (block.type === "image" && block.source.type === "base64") return [{
|
|
1626
|
+
type: "input_image",
|
|
1627
|
+
image_url: `data:${block.source.media_type};base64,${block.source.data}`
|
|
1628
|
+
}];
|
|
1629
|
+
return [];
|
|
1630
|
+
}
|
|
1631
|
+
function codexOutput(block) {
|
|
1632
|
+
return block.type === "text" ? [{
|
|
1633
|
+
type: "output_text",
|
|
1634
|
+
text: block.text
|
|
1635
|
+
}] : [];
|
|
1636
|
+
}
|
|
1637
|
+
function codexMessageRecords(input, message, warnings) {
|
|
1638
|
+
const timestamp = timestampOf(message, input.timestamp ?? DEFAULT_TIMESTAMP);
|
|
1639
|
+
const records = [];
|
|
1640
|
+
const metadata = { cohub: {
|
|
1641
|
+
...projectionMetadata(input),
|
|
1642
|
+
...projectNativeMessageMeta(message),
|
|
1643
|
+
turnId: message.turnId,
|
|
1644
|
+
messageId: message.id
|
|
1645
|
+
} };
|
|
1646
|
+
if (containsExternalImage(message.content)) warnings.push({
|
|
1647
|
+
sourceMessageId: message.id,
|
|
1648
|
+
sourceTurnId: message.turnId,
|
|
1649
|
+
reason: "external image URL omitted from native projection"
|
|
1650
|
+
});
|
|
1651
|
+
const push = (ordinal, payload) => records.push({
|
|
1652
|
+
key: `${message.id}:${message.turnId}:${ordinal}`,
|
|
1653
|
+
sourceTurnId: message.turnId,
|
|
1654
|
+
sourceMessageId: message.id,
|
|
1655
|
+
record: {
|
|
1656
|
+
timestamp,
|
|
1657
|
+
type: "response_item",
|
|
1658
|
+
payload,
|
|
1659
|
+
metadata
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
const plain = message.content.flatMap((block) => block.type === "text" ? [block.text] : []).join("\n");
|
|
1663
|
+
for (const block of message.content) {
|
|
1664
|
+
if (block.type === "image" && block.source.type === "url") continue;
|
|
1665
|
+
if (block.type === "text") push(records.length, {
|
|
1666
|
+
type: "message",
|
|
1667
|
+
role: message.role,
|
|
1668
|
+
content: message.role === "assistant" ? codexOutput(block) : codexInputContent(block)
|
|
1669
|
+
});
|
|
1670
|
+
else if (block.type === "image") {
|
|
1671
|
+
const content = codexInputContent(block);
|
|
1672
|
+
if (content.length) push(records.length, {
|
|
1673
|
+
type: "message",
|
|
1674
|
+
role: "user",
|
|
1675
|
+
content
|
|
1676
|
+
});
|
|
1677
|
+
} else if (block.type === "thinking") push(records.length, {
|
|
1678
|
+
type: "reasoning",
|
|
1679
|
+
summary: [{
|
|
1680
|
+
type: "summary_text",
|
|
1681
|
+
text: block.thinking
|
|
1682
|
+
}],
|
|
1683
|
+
content: null,
|
|
1684
|
+
encrypted_content: null
|
|
1685
|
+
});
|
|
1686
|
+
else if (block.type === "tool_use") push(records.length, {
|
|
1687
|
+
type: "function_call",
|
|
1688
|
+
name: block.name,
|
|
1689
|
+
arguments: JSON.stringify(block.input),
|
|
1690
|
+
call_id: block.id
|
|
1691
|
+
});
|
|
1692
|
+
else if (block.type === "tool_result") push(records.length, {
|
|
1693
|
+
type: "function_call_output",
|
|
1694
|
+
call_id: block.tool_use_id,
|
|
1695
|
+
output: typeof block.content === "string" ? block.content : block.content.flatMap((item) => item.type === "text" ? [item.text] : []).join("\n")
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
if (plain) records.push({
|
|
1699
|
+
key: `${message.id}:${message.turnId}:event`,
|
|
1700
|
+
sourceTurnId: message.turnId,
|
|
1701
|
+
sourceMessageId: message.id,
|
|
1702
|
+
record: {
|
|
1703
|
+
timestamp,
|
|
1704
|
+
type: "event_msg",
|
|
1705
|
+
payload: {
|
|
1706
|
+
type: message.role === "user" ? "user_message" : "agent_message",
|
|
1707
|
+
message: plain,
|
|
1708
|
+
kind: "plain"
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
return records;
|
|
1713
|
+
}
|
|
1714
|
+
function codexTerminalPayload(turn) {
|
|
1715
|
+
const lastAgentMessage = turn.assistantContent?.flatMap((block) => block.type === "text" ? [block.text] : []).join("\n") || null;
|
|
1716
|
+
if (turn.status === "completed") return {
|
|
1717
|
+
type: "turn_complete",
|
|
1718
|
+
turn_id: turn.id,
|
|
1719
|
+
last_agent_message: lastAgentMessage
|
|
1720
|
+
};
|
|
1721
|
+
return {
|
|
1722
|
+
type: "turn_aborted",
|
|
1723
|
+
turn_id: turn.id,
|
|
1724
|
+
reason: turn.status,
|
|
1725
|
+
last_agent_message: lastAgentMessage
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
function codexProjection(input) {
|
|
1729
|
+
const sourceTurns = trimProjectionTurnsToCompaction(input.turns);
|
|
1730
|
+
const fallbackTimestamp = input.timestamp ?? DEFAULT_TIMESTAMP;
|
|
1731
|
+
const records = [{
|
|
1732
|
+
key: `header:${input.nativeSessionId}`,
|
|
1733
|
+
sourceTurnId: null,
|
|
1734
|
+
sourceMessageId: null,
|
|
1735
|
+
record: {
|
|
1736
|
+
timestamp: fallbackTimestamp,
|
|
1737
|
+
type: "session_meta",
|
|
1738
|
+
payload: {
|
|
1739
|
+
id: input.nativeSessionId,
|
|
1740
|
+
session_id: input.nativeSessionId,
|
|
1741
|
+
timestamp: fallbackTimestamp,
|
|
1742
|
+
cwd: input.cwd,
|
|
1743
|
+
originator: "cohub",
|
|
1744
|
+
cli_version: "cohub-runtime",
|
|
1745
|
+
source: "cli",
|
|
1746
|
+
model_provider: input.provider ?? null,
|
|
1747
|
+
history_mode: "legacy",
|
|
1748
|
+
cohub: projectionMetadata(input)
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}];
|
|
1752
|
+
const warnings = [];
|
|
1753
|
+
for (const turn of sourceTurns) {
|
|
1754
|
+
const start = turn.startedAt ?? turn.createdAt;
|
|
1755
|
+
records.push({
|
|
1756
|
+
key: `turn:${turn.id}:started`,
|
|
1757
|
+
sourceTurnId: turn.sourceTurnId,
|
|
1758
|
+
sourceMessageId: null,
|
|
1759
|
+
record: {
|
|
1760
|
+
timestamp: start,
|
|
1761
|
+
type: "event_msg",
|
|
1762
|
+
payload: {
|
|
1763
|
+
type: "turn_started",
|
|
1764
|
+
turn_id: turn.id,
|
|
1765
|
+
started_at: Math.floor(Date.parse(start) / 1e3),
|
|
1766
|
+
model_context_window: null,
|
|
1767
|
+
collaboration_mode_kind: "default"
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
});
|
|
1771
|
+
for (const message of turn.messages) if (!isPendingGeneration(message)) records.push(...codexMessageRecords(input, message, warnings));
|
|
1772
|
+
records.push({
|
|
1773
|
+
key: `turn:${turn.id}:terminal`,
|
|
1774
|
+
sourceTurnId: turn.sourceTurnId,
|
|
1775
|
+
sourceMessageId: null,
|
|
1776
|
+
record: {
|
|
1777
|
+
timestamp: turn.completedAt ?? turn.createdAt,
|
|
1778
|
+
type: "event_msg",
|
|
1779
|
+
payload: codexTerminalPayload(turn)
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
return {
|
|
1784
|
+
target: "codex",
|
|
1785
|
+
records,
|
|
1786
|
+
cursor: {
|
|
1787
|
+
sourceSequence: input.turns.at(-1)?.sequence ?? null,
|
|
1788
|
+
sourceTurnId: input.turns.at(-1)?.sourceTurnId ?? null,
|
|
1789
|
+
sourceFingerprint: sourceFingerprint(input.turns)
|
|
1790
|
+
},
|
|
1791
|
+
warnings
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
function projectNativeSession(input, target) {
|
|
1795
|
+
return target === "pi" ? piProjection(input) : codexProjection(input);
|
|
1796
|
+
}
|
|
1797
|
+
function serializeProjectionRecords(records) {
|
|
1798
|
+
return records.length ? `${records.map((record) => JSON.stringify(record.record)).join("\n")}\n` : "";
|
|
1799
|
+
}
|
|
1800
|
+
function serializeProjection(projection) {
|
|
1801
|
+
return serializeProjectionRecords(projection.records);
|
|
1802
|
+
}
|
|
1803
|
+
function fingerprintProjectionTurns(turns) {
|
|
1804
|
+
return `${turns.length}:${fingerprint(turns)}`;
|
|
1805
|
+
}
|
|
1806
|
+
//#endregion
|
|
1347
1807
|
//#region ../protocol/dist/runtime/context.js
|
|
1348
1808
|
const createEmptyUsage = () => ({
|
|
1349
1809
|
input: 0,
|
|
@@ -1483,6 +1943,7 @@ function contextToPiMessages(messages, options = {}) {
|
|
|
1483
1943
|
//#region ../protocol/dist/runtime/index.js
|
|
1484
1944
|
const RUNTIME_PROTOCOL_VERSION = 1;
|
|
1485
1945
|
const RUNTIME_MAX_FRAME_BYTES = 33554432;
|
|
1946
|
+
const RUNTIME_RECOVERY_BATCH_SIZE = 64;
|
|
1486
1947
|
z.enum([
|
|
1487
1948
|
"cohub",
|
|
1488
1949
|
"pi",
|
|
@@ -1502,8 +1963,14 @@ const runtimeCapabilitiesSchema = z.object({
|
|
|
1502
1963
|
name: z.string().max(255)
|
|
1503
1964
|
})).max(2e3)
|
|
1504
1965
|
});
|
|
1966
|
+
const runtimePendingExecutionSchema = z.object({
|
|
1967
|
+
sessionId: z.string().uuid(),
|
|
1968
|
+
turnId: z.string().uuid(),
|
|
1969
|
+
harness: z.enum(["pi", "codex"])
|
|
1970
|
+
}).strict();
|
|
1505
1971
|
z.object({
|
|
1506
1972
|
connectionId: z.string().uuid(),
|
|
1973
|
+
runtimeId: z.string().uuid().optional(),
|
|
1507
1974
|
endpoint: z.url({ protocol: /^wss?$/ }),
|
|
1508
1975
|
ownerUserId: z.string().min(1),
|
|
1509
1976
|
capabilities: runtimeCapabilitiesSchema
|
|
@@ -1512,7 +1979,35 @@ const runtimeReadySchema = z.object({
|
|
|
1512
1979
|
type: z.literal("runtime.ready"),
|
|
1513
1980
|
connectionId: z.string().uuid()
|
|
1514
1981
|
});
|
|
1982
|
+
const runtimeTraceContextSchema = z.object({
|
|
1983
|
+
requestId: z.string().regex(/^[a-zA-Z0-9._:-]{1,128}$/).optional(),
|
|
1984
|
+
traceId: z.string().regex(/^[0-9a-f]{32}$/i).nullable().optional(),
|
|
1985
|
+
spanId: z.string().regex(/^[0-9a-f]{16}$/i).nullable().optional(),
|
|
1986
|
+
traceparent: z.string().regex(/^[0-9a-f]{2}-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/i).nullable().optional()
|
|
1987
|
+
}).strict();
|
|
1988
|
+
z.object({
|
|
1989
|
+
backend: z.enum([
|
|
1990
|
+
"fsevents",
|
|
1991
|
+
"fsnotify",
|
|
1992
|
+
"scan",
|
|
1993
|
+
"none"
|
|
1994
|
+
]),
|
|
1995
|
+
state: z.enum([
|
|
1996
|
+
"running",
|
|
1997
|
+
"degraded",
|
|
1998
|
+
"unavailable"
|
|
1999
|
+
]),
|
|
2000
|
+
reason: z.enum([
|
|
2001
|
+
"polling",
|
|
2002
|
+
"watch_error",
|
|
2003
|
+
"start_failed",
|
|
2004
|
+
"coverage_incomplete",
|
|
2005
|
+
"root_changed"
|
|
2006
|
+
]).optional(),
|
|
2007
|
+
observedAt: z.iso.datetime()
|
|
2008
|
+
});
|
|
1515
2009
|
z.object({
|
|
2010
|
+
expectedTurnId: z.string().uuid(),
|
|
1516
2011
|
revision: z.string().min(1),
|
|
1517
2012
|
confirmed: z.literal(true)
|
|
1518
2013
|
}).strict();
|
|
@@ -1577,8 +2072,7 @@ const runtimeEventSchema = z.discriminatedUnion("type", [
|
|
|
1577
2072
|
}),
|
|
1578
2073
|
z.object({
|
|
1579
2074
|
type: z.literal("context.required"),
|
|
1580
|
-
pendingTurnIds: z.array(id).max(2).optional()
|
|
1581
|
-
historyOnly: z.boolean().optional()
|
|
2075
|
+
pendingTurnIds: z.array(id).max(2).optional()
|
|
1582
2076
|
}),
|
|
1583
2077
|
z.object({ type: z.literal("turn.acknowledged") }),
|
|
1584
2078
|
z.object({
|
|
@@ -1594,7 +2088,11 @@ z.discriminatedUnion("type", [
|
|
|
1594
2088
|
spaceId: id,
|
|
1595
2089
|
token: z.string().min(1).max(16384),
|
|
1596
2090
|
capabilities: runtimeCapabilitiesSchema
|
|
1597
|
-
}),
|
|
2091
|
+
}).strict(),
|
|
2092
|
+
z.object({
|
|
2093
|
+
type: z.literal("runtime.recovery"),
|
|
2094
|
+
executions: z.array(runtimePendingExecutionSchema).min(1).max(64)
|
|
2095
|
+
}).strict(),
|
|
1598
2096
|
z.object({ type: z.literal("runtime.heartbeat") }),
|
|
1599
2097
|
z.object({
|
|
1600
2098
|
type: z.literal("runtime.auth"),
|
|
@@ -1639,7 +2137,8 @@ const runtimeCommandSchema = z.discriminatedUnion("type", [
|
|
|
1639
2137
|
sessionId: id,
|
|
1640
2138
|
turnId: id,
|
|
1641
2139
|
harness: z.enum(["pi", "codex"])
|
|
1642
|
-
})
|
|
2140
|
+
}),
|
|
2141
|
+
traceContext: runtimeTraceContextSchema.optional()
|
|
1643
2142
|
}),
|
|
1644
2143
|
z.object({
|
|
1645
2144
|
type: z.literal("turn.start"),
|
|
@@ -1661,6 +2160,8 @@ const runtimeCommandSchema = z.discriminatedUnion("type", [
|
|
|
1661
2160
|
provider: z.string().nullable().optional(),
|
|
1662
2161
|
model: z.string().nullable().optional(),
|
|
1663
2162
|
thinkingLevel: z.string().nullable().optional(),
|
|
2163
|
+
requestId: z.string().regex(/^[a-zA-Z0-9._:-]{1,128}$/).nullable().optional(),
|
|
2164
|
+
traceContext: runtimeTraceContextSchema.optional(),
|
|
1664
2165
|
accessMode: z.enum(["read_only", "full_access"])
|
|
1665
2166
|
}).refine((input) => {
|
|
1666
2167
|
const owner = input.messages.at(-1);
|
|
@@ -2823,50 +3324,97 @@ var ParentBridgeTransport = class {
|
|
|
2823
3324
|
});
|
|
2824
3325
|
}
|
|
2825
3326
|
};
|
|
2826
|
-
/**
|
|
2827
|
-
* Broker-mode transport for standalone-deployed apps. Opens a popup window to
|
|
2828
|
-
* the Cohub auth broker page, performs a ready-handshake, sends the request via
|
|
2829
|
-
* postMessage, and resolves with the broker's response. The popup is closed
|
|
2830
|
-
* after a single request is fulfilled (one-shot, per §7.2 of the plan).
|
|
2831
|
-
*
|
|
2832
|
-
* Non-interactive messages (`context`, `checkout-state`) are answered locally
|
|
2833
|
-
* without opening a popup — the app already knows its own appId, and
|
|
2834
|
-
* checkout state is not available on the app's own origin in broker mode.
|
|
2835
|
-
*/
|
|
2836
3327
|
var PopupBrokerTransport = class {
|
|
2837
3328
|
supportsNavigation = false;
|
|
3329
|
+
memoryOnly = true;
|
|
2838
3330
|
brokerOrigin;
|
|
2839
3331
|
appId;
|
|
2840
3332
|
getAppId;
|
|
3333
|
+
app;
|
|
3334
|
+
getApp;
|
|
3335
|
+
viewerGrants = [];
|
|
3336
|
+
viewerIdentity;
|
|
3337
|
+
getViewerIdentity() {
|
|
3338
|
+
return this.viewerIdentity;
|
|
3339
|
+
}
|
|
3340
|
+
observeViewerIdentity(next) {
|
|
3341
|
+
if (next !== void 0 && this.viewerIdentity !== void 0 && next !== this.viewerIdentity) this.viewerGrants = [];
|
|
3342
|
+
if (next !== void 0) this.viewerIdentity = next;
|
|
3343
|
+
}
|
|
2841
3344
|
constructor(config) {
|
|
2842
3345
|
this.brokerOrigin = config.brokerOrigin;
|
|
2843
3346
|
this.appId = config.appId;
|
|
2844
3347
|
this.getAppId = config.getAppId;
|
|
2845
|
-
|
|
3348
|
+
this.app = config.app;
|
|
3349
|
+
this.getApp = config.getApp;
|
|
3350
|
+
if (!this.app && this.getApp) this.getApp();
|
|
3351
|
+
else if (!this.appId && this.getAppId) this.getAppId();
|
|
3352
|
+
}
|
|
3353
|
+
async resolveApp() {
|
|
3354
|
+
if (this.app) return this.app;
|
|
3355
|
+
const resolved = await this.getApp?.();
|
|
3356
|
+
if (resolved) return resolved;
|
|
3357
|
+
const appId = this.appId ?? await this.getAppId?.() ?? null;
|
|
3358
|
+
return appId ? {
|
|
3359
|
+
id: appId,
|
|
3360
|
+
slug: "",
|
|
3361
|
+
url: null,
|
|
3362
|
+
homeSpace: {
|
|
3363
|
+
id: "",
|
|
3364
|
+
name: null
|
|
3365
|
+
},
|
|
3366
|
+
appScopes: []
|
|
3367
|
+
} : null;
|
|
2846
3368
|
}
|
|
2847
|
-
resolveAppId() {
|
|
2848
|
-
|
|
2849
|
-
if (this.getAppId) return this.getAppId();
|
|
2850
|
-
return Promise.resolve(null);
|
|
3369
|
+
async resolveAppId() {
|
|
3370
|
+
return (await this.resolveApp())?.id ?? null;
|
|
2851
3371
|
}
|
|
2852
3372
|
async request(message, options) {
|
|
2853
|
-
if (message.type === "cohub.app.context")
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
3373
|
+
if (message.type === "cohub.app.context") {
|
|
3374
|
+
const app = await this.resolveApp();
|
|
3375
|
+
if (!app) return null;
|
|
3376
|
+
const viewerScopes = Array.from(new Set(this.viewerGrants.flatMap((grant) => grant.scopes)));
|
|
3377
|
+
return {
|
|
3378
|
+
type: "cohub.app.context.result",
|
|
3379
|
+
context: {
|
|
3380
|
+
capabilities: {
|
|
3381
|
+
authorization: 2,
|
|
3382
|
+
serverGrants: true
|
|
3383
|
+
},
|
|
3384
|
+
mode: "broker",
|
|
3385
|
+
app: {
|
|
3386
|
+
id: app.id,
|
|
3387
|
+
slug: app.slug,
|
|
3388
|
+
url: app.url,
|
|
3389
|
+
homeSpace: { ...app.homeSpace }
|
|
3390
|
+
},
|
|
3391
|
+
space: {
|
|
3392
|
+
id: app.homeSpace.id,
|
|
3393
|
+
name: app.homeSpace.name
|
|
3394
|
+
},
|
|
3395
|
+
...this.viewerIdentity !== void 0 ? { viewer: this.viewerIdentity ? { userUuid: this.viewerIdentity } : null } : {},
|
|
3396
|
+
invocation: {
|
|
3397
|
+
surface: "broker",
|
|
3398
|
+
source: "route"
|
|
3399
|
+
},
|
|
3400
|
+
shell: {
|
|
3401
|
+
surface: "broker",
|
|
3402
|
+
space: null,
|
|
3403
|
+
session: null,
|
|
3404
|
+
turn: null
|
|
3405
|
+
},
|
|
3406
|
+
permissions: {
|
|
3407
|
+
scopes: Array.from(/* @__PURE__ */ new Set([...app.appScopes, ...viewerScopes])),
|
|
3408
|
+
appScopes: [...app.appScopes],
|
|
3409
|
+
viewerScopes,
|
|
3410
|
+
viewerGrants: this.viewerGrants.map((grant) => ({
|
|
3411
|
+
...grant,
|
|
3412
|
+
scopes: [...grant.scopes]
|
|
3413
|
+
}))
|
|
3414
|
+
}
|
|
2867
3415
|
}
|
|
2868
|
-
}
|
|
2869
|
-
}
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
2870
3418
|
if (message.type === "cohub.app.checkout-state") return {
|
|
2871
3419
|
type: "cohub.app.checkout-state.result",
|
|
2872
3420
|
status: null,
|
|
@@ -2883,7 +3431,7 @@ var PopupBrokerTransport = class {
|
|
|
2883
3431
|
}
|
|
2884
3432
|
const appOrigin = window.location.origin;
|
|
2885
3433
|
const brokerUrl = `${this.brokerOrigin}/app-auth?app=${encodeURIComponent(appId)}&origin=${encodeURIComponent(appOrigin)}`;
|
|
2886
|
-
const popup = window.open(brokerUrl,
|
|
3434
|
+
const popup = window.open(brokerUrl, `cohub-app-auth-${appId}`, "popup,width=480,height=640");
|
|
2887
3435
|
if (!popup) {
|
|
2888
3436
|
reject(/* @__PURE__ */ new Error("Failed to open authorization window. Please allow popups for this site."));
|
|
2889
3437
|
return;
|
|
@@ -2922,6 +3470,8 @@ var PopupBrokerTransport = class {
|
|
|
2922
3470
|
return;
|
|
2923
3471
|
}
|
|
2924
3472
|
if (data.type === "cohub.app.broker.ready" && !ready) {
|
|
3473
|
+
const brokerViewer = data.viewer;
|
|
3474
|
+
this.observeViewerIdentity(brokerViewer === null ? null : typeof brokerViewer?.userUuid === "string" ? brokerViewer.userUuid : void 0);
|
|
2925
3475
|
if (message.type === "cohub.app.authorize.v2" && data.authorizationVersion !== 2) {
|
|
2926
3476
|
finish(() => reject(new AppRuntimeError("unsupported", "This host does not support structured authorization.")));
|
|
2927
3477
|
return;
|
|
@@ -2945,6 +3495,29 @@ var PopupBrokerTransport = class {
|
|
|
2945
3495
|
reject(new AppRuntimeError(data.code ?? "request_failed", data.message, requestId));
|
|
2946
3496
|
return;
|
|
2947
3497
|
}
|
|
3498
|
+
const rawResult = data.result;
|
|
3499
|
+
if (message.type === "cohub.app.authorize.v2") {
|
|
3500
|
+
const parsedResult = appAuthorizationResultSchema.safeParse(rawResult);
|
|
3501
|
+
if (!parsedResult.success) {
|
|
3502
|
+
reject(new AppRuntimeError("invalid_response", "Invalid authorization result.", requestId));
|
|
3503
|
+
return;
|
|
3504
|
+
}
|
|
3505
|
+
const authorization = parsedResult.data;
|
|
3506
|
+
if (authorization.status === "granted") {
|
|
3507
|
+
const token = data.token;
|
|
3508
|
+
if (typeof token !== "string" || !token) {
|
|
3509
|
+
reject(new AppRuntimeError("invalid_response", "Authorization token is missing.", requestId));
|
|
3510
|
+
return;
|
|
3511
|
+
}
|
|
3512
|
+
const knownScopes = new Set(PERMISSIONS);
|
|
3513
|
+
const scopes = authorization.grant.scopes.filter((scope) => knownScopes.has(scope));
|
|
3514
|
+
const existing = this.viewerGrants.filter((grant) => grant.spaceId !== authorization.grant.spaceId);
|
|
3515
|
+
this.viewerGrants = [...existing, {
|
|
3516
|
+
spaceId: authorization.grant.spaceId,
|
|
3517
|
+
scopes
|
|
3518
|
+
}];
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
2948
3521
|
resolve(data);
|
|
2949
3522
|
});
|
|
2950
3523
|
};
|
|
@@ -3112,7 +3685,7 @@ var AppRuntimeApi = class {
|
|
|
3112
3685
|
return this.storageKeysReady;
|
|
3113
3686
|
}
|
|
3114
3687
|
readStoredToken() {
|
|
3115
|
-
if (this.transport
|
|
3688
|
+
if (this.transport.memoryOnly) {
|
|
3116
3689
|
this.writeStoredToken(null);
|
|
3117
3690
|
return null;
|
|
3118
3691
|
}
|
|
@@ -3124,7 +3697,7 @@ var AppRuntimeApi = class {
|
|
|
3124
3697
|
}
|
|
3125
3698
|
}
|
|
3126
3699
|
writeStoredToken(token) {
|
|
3127
|
-
if (this.transport
|
|
3700
|
+
if (this.transport.memoryOnly) token = null;
|
|
3128
3701
|
if (!this.tokenStorageKey || typeof localStorage === "undefined") return;
|
|
3129
3702
|
try {
|
|
3130
3703
|
if (token) localStorage.setItem(this.tokenStorageKey, token);
|
|
@@ -3132,6 +3705,12 @@ var AppRuntimeApi = class {
|
|
|
3132
3705
|
} catch {}
|
|
3133
3706
|
}
|
|
3134
3707
|
readStoredGrants() {
|
|
3708
|
+
if (this.transport.memoryOnly) {
|
|
3709
|
+
if (this.grantsStorageKey && typeof localStorage !== "undefined") try {
|
|
3710
|
+
localStorage.removeItem(this.grantsStorageKey);
|
|
3711
|
+
} catch {}
|
|
3712
|
+
return null;
|
|
3713
|
+
}
|
|
3135
3714
|
if (!this.grantsStorageKey || typeof localStorage === "undefined") return null;
|
|
3136
3715
|
try {
|
|
3137
3716
|
const raw = localStorage.getItem(this.grantsStorageKey);
|
|
@@ -3144,6 +3723,7 @@ var AppRuntimeApi = class {
|
|
|
3144
3723
|
}
|
|
3145
3724
|
}
|
|
3146
3725
|
writeStoredGrants(grants) {
|
|
3726
|
+
if (this.transport.memoryOnly) return;
|
|
3147
3727
|
if (!this.grantsStorageKey || typeof localStorage === "undefined") return;
|
|
3148
3728
|
try {
|
|
3149
3729
|
if (grants && grants.length > 0) localStorage.setItem(this.grantsStorageKey, JSON.stringify(grants));
|
|
@@ -3152,12 +3732,11 @@ var AppRuntimeApi = class {
|
|
|
3152
3732
|
}
|
|
3153
3733
|
serverGrants = false;
|
|
3154
3734
|
viewerIdentity;
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
const viewer = context.viewer?.userUuid ?? null;
|
|
3735
|
+
observeTransportIdentity() {
|
|
3736
|
+
const viewer = this.transport.getViewerIdentity?.();
|
|
3737
|
+
if (viewer !== void 0) this.observeViewer(viewer);
|
|
3738
|
+
}
|
|
3739
|
+
observeViewer(viewer) {
|
|
3161
3740
|
if (this.viewerIdentity !== void 0 && viewer !== this.viewerIdentity) {
|
|
3162
3741
|
this.identityVersion++;
|
|
3163
3742
|
this.token = null;
|
|
@@ -3167,7 +3746,15 @@ var AppRuntimeApi = class {
|
|
|
3167
3746
|
}
|
|
3168
3747
|
this.viewerIdentity = viewer;
|
|
3169
3748
|
}
|
|
3749
|
+
identityVersion = 0;
|
|
3750
|
+
contextUnsubscribe = null;
|
|
3751
|
+
observeContext(context) {
|
|
3752
|
+
if (context.capabilities?.serverGrants) this.serverGrants = true;
|
|
3753
|
+
if (context.viewer === void 0) return;
|
|
3754
|
+
this.observeViewer(context.viewer?.userUuid ?? null);
|
|
3755
|
+
}
|
|
3170
3756
|
async context() {
|
|
3757
|
+
this.observeTransportIdentity();
|
|
3171
3758
|
const response = await this.transport.request({ type: "cohub.app.context" }, {
|
|
3172
3759
|
timeoutMs: 8e3,
|
|
3173
3760
|
retryIntervalMs: 250
|
|
@@ -3253,6 +3840,7 @@ var AppRuntimeApi = class {
|
|
|
3253
3840
|
}
|
|
3254
3841
|
async getAccessToken(options) {
|
|
3255
3842
|
await this.ensureStorageKeys();
|
|
3843
|
+
this.observeTransportIdentity();
|
|
3256
3844
|
const identityVersion = this.identityVersion;
|
|
3257
3845
|
if (this.token && !options?.forceRefresh) return this.token;
|
|
3258
3846
|
if (options?.forceRefresh) {
|
|
@@ -3268,14 +3856,16 @@ var AppRuntimeApi = class {
|
|
|
3268
3856
|
scopes: grant.scopes,
|
|
3269
3857
|
spaceId: grant.spaceId
|
|
3270
3858
|
}, { timeoutMs: 12e4 });
|
|
3271
|
-
|
|
3859
|
+
this.observeTransportIdentity();
|
|
3860
|
+
if (identityVersion !== this.identityVersion && !this.transport.memoryOnly) return null;
|
|
3272
3861
|
const token = response?.token ?? null;
|
|
3273
3862
|
if (!token) continue;
|
|
3274
3863
|
this.token = token;
|
|
3275
3864
|
next = recordConsent(next, responseSpaceId(response?.space?.id) ?? grant.spaceId, grant.scopes);
|
|
3276
3865
|
refreshed = true;
|
|
3277
3866
|
} catch {
|
|
3278
|
-
|
|
3867
|
+
this.observeTransportIdentity();
|
|
3868
|
+
if (identityVersion !== this.identityVersion && !this.transport.memoryOnly) return null;
|
|
3279
3869
|
next = recordConsent(next, grant.spaceId, grant.scopes);
|
|
3280
3870
|
}
|
|
3281
3871
|
this.authorizedGrants = next;
|
|
@@ -3289,7 +3879,8 @@ var AppRuntimeApi = class {
|
|
|
3289
3879
|
type: "cohub.app.token",
|
|
3290
3880
|
forceRefresh: Boolean(options?.forceRefresh)
|
|
3291
3881
|
}, { timeoutMs: 2e4 });
|
|
3292
|
-
|
|
3882
|
+
this.observeTransportIdentity();
|
|
3883
|
+
if (identityVersion !== this.identityVersion && !this.transport.memoryOnly) return null;
|
|
3293
3884
|
this.token = response?.token ?? null;
|
|
3294
3885
|
this.writeStoredToken(this.token);
|
|
3295
3886
|
return this.token;
|
|
@@ -3306,7 +3897,8 @@ var AppRuntimeApi = class {
|
|
|
3306
3897
|
type: "cohub.app.authorize.v2",
|
|
3307
3898
|
...parsed.data
|
|
3308
3899
|
}, { timeoutMs: 12e4 });
|
|
3309
|
-
|
|
3900
|
+
this.observeTransportIdentity();
|
|
3901
|
+
if (identityVersion !== this.identityVersion && !this.transport.memoryOnly) throw new AppRuntimeError("session_changed", "The signed-in account changed. Please try again.");
|
|
3310
3902
|
if (!response) throw new AppRuntimeError("host_timeout", "Authorization host did not respond. It may not support structured authorization.");
|
|
3311
3903
|
const result = appAuthorizationResultSchema.safeParse(response.result);
|
|
3312
3904
|
if (!result.success) throw new AppRuntimeError("invalid_response", "Invalid authorization result.");
|
|
@@ -3332,6 +3924,7 @@ var AppRuntimeApi = class {
|
|
|
3332
3924
|
*/
|
|
3333
3925
|
async requestAuthorization(input) {
|
|
3334
3926
|
await this.ensureStorageKeys();
|
|
3927
|
+
this.observeTransportIdentity();
|
|
3335
3928
|
const response = await this.transport.request({
|
|
3336
3929
|
type: "cohub.app.authorize",
|
|
3337
3930
|
scopes: input.scopes,
|
|
@@ -3339,6 +3932,7 @@ var AppRuntimeApi = class {
|
|
|
3339
3932
|
spaceId: input.spaceId,
|
|
3340
3933
|
alwaysAsk: input.alwaysAsk
|
|
3341
3934
|
}, { timeoutMs: 12e4 });
|
|
3935
|
+
this.observeTransportIdentity();
|
|
3342
3936
|
const token = response?.token ?? null;
|
|
3343
3937
|
if (token) {
|
|
3344
3938
|
this.token = token;
|
|
@@ -3357,6 +3951,7 @@ var AppRuntimeApi = class {
|
|
|
3357
3951
|
*/
|
|
3358
3952
|
async requestSpaceAuthorization(input) {
|
|
3359
3953
|
await this.ensureStorageKeys();
|
|
3954
|
+
this.observeTransportIdentity();
|
|
3360
3955
|
const response = await this.transport.request({
|
|
3361
3956
|
type: "cohub.app.authorize",
|
|
3362
3957
|
scopes: input.scopes,
|
|
@@ -3364,6 +3959,7 @@ var AppRuntimeApi = class {
|
|
|
3364
3959
|
selectSpace: true,
|
|
3365
3960
|
alwaysAsk: input.alwaysAsk
|
|
3366
3961
|
}, { timeoutMs: 12e4 });
|
|
3962
|
+
this.observeTransportIdentity();
|
|
3367
3963
|
const token = response?.token ?? null;
|
|
3368
3964
|
const spaceId = typeof response?.space?.id === "string" ? response.space.id : null;
|
|
3369
3965
|
const spaceName = typeof response?.space?.name === "string" ? response.space.name : null;
|
|
@@ -3393,12 +3989,14 @@ var AppRuntimeApi = class {
|
|
|
3393
3989
|
*/
|
|
3394
3990
|
async requestCreateSpaceAuthorization(input) {
|
|
3395
3991
|
await this.ensureStorageKeys();
|
|
3992
|
+
this.observeTransportIdentity();
|
|
3396
3993
|
const response = await this.transport.request({
|
|
3397
3994
|
type: "cohub.app.authorize",
|
|
3398
3995
|
scopes: input.scopes,
|
|
3399
3996
|
reason: input.reason,
|
|
3400
3997
|
createSpace: input.space
|
|
3401
3998
|
}, { timeoutMs: 12e4 });
|
|
3999
|
+
this.observeTransportIdentity();
|
|
3402
4000
|
const token = response?.token ?? null;
|
|
3403
4001
|
const spaceId = typeof response?.space?.id === "string" ? response.space.id : null;
|
|
3404
4002
|
const spaceName = typeof response?.space?.name === "string" ? response.space.name : null;
|
|
@@ -3433,6 +4031,83 @@ var AppRuntimeApi = class {
|
|
|
3433
4031
|
}) ?? null;
|
|
3434
4032
|
}
|
|
3435
4033
|
};
|
|
4034
|
+
/** Resolves complete App identity from the browser's current standalone origin. */
|
|
4035
|
+
function createOriginAppResolver(deps) {
|
|
4036
|
+
let cached = null;
|
|
4037
|
+
return () => {
|
|
4038
|
+
if (cached) return cached;
|
|
4039
|
+
const run = (async () => {
|
|
4040
|
+
const doFetch = deps.fetch ?? globalThis.fetch;
|
|
4041
|
+
const origin = deps.origin?.() ?? (typeof window !== "undefined" ? window.location.origin : null);
|
|
4042
|
+
if (typeof doFetch !== "function" || !origin) return null;
|
|
4043
|
+
const response = await doFetch(`${deps.apiBaseUrl}/api/apps/by-origin`, {
|
|
4044
|
+
credentials: "omit",
|
|
4045
|
+
headers: { Accept: "application/json" }
|
|
4046
|
+
});
|
|
4047
|
+
if (response.status === 404) return null;
|
|
4048
|
+
if (!response.ok) throw new Error(`getByOrigin failed: ${response.status}`);
|
|
4049
|
+
const data = await response.json();
|
|
4050
|
+
const app = data?.app;
|
|
4051
|
+
const space = data?.space;
|
|
4052
|
+
if (typeof app?.id !== "string" || typeof app.slug !== "string" || typeof app.spaceId !== "string" || typeof space?.id !== "string" || space.id !== app.spaceId) return null;
|
|
4053
|
+
const knownScopes = new Set(PERMISSIONS);
|
|
4054
|
+
const appScopes = Array.isArray(app.appScopes) ? app.appScopes.filter((scope) => typeof scope === "string" && knownScopes.has(scope)) : [];
|
|
4055
|
+
return {
|
|
4056
|
+
id: app.id,
|
|
4057
|
+
slug: app.slug,
|
|
4058
|
+
url: origin,
|
|
4059
|
+
homeSpace: {
|
|
4060
|
+
id: space.id,
|
|
4061
|
+
name: typeof space.name === "string" ? space.name : null
|
|
4062
|
+
},
|
|
4063
|
+
appScopes
|
|
4064
|
+
};
|
|
4065
|
+
})().catch(() => {
|
|
4066
|
+
cached = null;
|
|
4067
|
+
return null;
|
|
4068
|
+
});
|
|
4069
|
+
cached = run;
|
|
4070
|
+
return run;
|
|
4071
|
+
};
|
|
4072
|
+
}
|
|
4073
|
+
/**
|
|
4074
|
+
* Defers standalone origin discovery until the runtime is first used.
|
|
4075
|
+
* Apps should initialize `context()` before click-triggered authorization so
|
|
4076
|
+
* discovery does not consume the browser's transient user activation.
|
|
4077
|
+
*/
|
|
4078
|
+
var OriginBrokerTransport = class {
|
|
4079
|
+
brokerOrigin;
|
|
4080
|
+
resolveApp;
|
|
4081
|
+
supportsNavigation = false;
|
|
4082
|
+
memoryOnly = true;
|
|
4083
|
+
transport = null;
|
|
4084
|
+
resolvedTransport = null;
|
|
4085
|
+
getViewerIdentity() {
|
|
4086
|
+
return this.resolvedTransport?.getViewerIdentity();
|
|
4087
|
+
}
|
|
4088
|
+
constructor(brokerOrigin, resolveApp) {
|
|
4089
|
+
this.brokerOrigin = brokerOrigin;
|
|
4090
|
+
this.resolveApp = resolveApp;
|
|
4091
|
+
}
|
|
4092
|
+
resolveTransport() {
|
|
4093
|
+
if (this.transport) return this.transport;
|
|
4094
|
+
const attempt = this.resolveApp().then((app) => app ? new PopupBrokerTransport({
|
|
4095
|
+
brokerOrigin: this.brokerOrigin,
|
|
4096
|
+
app
|
|
4097
|
+
}) : null).then((transport) => {
|
|
4098
|
+
this.resolvedTransport = transport;
|
|
4099
|
+
return transport;
|
|
4100
|
+
}).catch(() => null);
|
|
4101
|
+
this.transport = attempt;
|
|
4102
|
+
attempt.then((transport) => {
|
|
4103
|
+
if (!transport && this.transport === attempt) this.transport = null;
|
|
4104
|
+
});
|
|
4105
|
+
return attempt;
|
|
4106
|
+
}
|
|
4107
|
+
async request(message, options) {
|
|
4108
|
+
return (await this.resolveTransport())?.request(message, options) ?? null;
|
|
4109
|
+
}
|
|
4110
|
+
};
|
|
3436
4111
|
/**
|
|
3437
4112
|
* Builds a memoized appId resolver that reverse-looks-up the appId from the
|
|
3438
4113
|
* public slug triple via `GET /api/apps/by-slug/:username/:spaceSlug/:appSlug`
|
|
@@ -3461,11 +4136,10 @@ function createSlugAppIdResolver(deps) {
|
|
|
3461
4136
|
};
|
|
3462
4137
|
}
|
|
3463
4138
|
/**
|
|
3464
|
-
* Resolves the appropriate transport
|
|
3465
|
-
*
|
|
3466
|
-
* broker; otherwise → bridge (returns null for non-app contexts).
|
|
4139
|
+
* Resolves the appropriate transport. Iframes use the parent bridge;
|
|
4140
|
+
* standalone pages use explicit legacy configuration or Origin discovery.
|
|
3467
4141
|
*/
|
|
3468
|
-
function resolveAppTransport(config, appIdResolver) {
|
|
4142
|
+
function resolveAppTransport(config, appIdResolver, originAppResolver, defaultBrokerOrigin) {
|
|
3469
4143
|
const explicitMode = config?.mode;
|
|
3470
4144
|
const brokerOrigin = config?.brokerOrigin;
|
|
3471
4145
|
const appId = config?.appId;
|
|
@@ -3479,7 +4153,9 @@ function resolveAppTransport(config, appIdResolver) {
|
|
|
3479
4153
|
if (explicitMode === "bridge") return new ParentBridgeTransport();
|
|
3480
4154
|
if (explicitMode === "broker") return createBroker();
|
|
3481
4155
|
if (typeof window !== "undefined" && window.parent !== window) return new ParentBridgeTransport();
|
|
3482
|
-
|
|
4156
|
+
if (hasBrokerConfig) return createBroker();
|
|
4157
|
+
if (originAppResolver && defaultBrokerOrigin) return new OriginBrokerTransport(defaultBrokerOrigin, originAppResolver);
|
|
4158
|
+
return new ParentBridgeTransport();
|
|
3483
4159
|
}
|
|
3484
4160
|
const createAppRuntime = (transport, appId, appIdResolver) => new AppRuntimeApi(transport, appId, appIdResolver);
|
|
3485
4161
|
//#endregion
|
|
@@ -3532,7 +4208,10 @@ var CohubClient = class {
|
|
|
3532
4208
|
spaceSlug: appRuntime.spaceSlug,
|
|
3533
4209
|
appSlug: appRuntime.appSlug
|
|
3534
4210
|
}) : void 0;
|
|
3535
|
-
const appTransport = resolveAppTransport(appRuntime, appIdResolver
|
|
4211
|
+
const appTransport = resolveAppTransport(appRuntime, appIdResolver, createOriginAppResolver({
|
|
4212
|
+
apiBaseUrl,
|
|
4213
|
+
fetch: options.fetch
|
|
4214
|
+
}), resolveWebBaseUrl({ env: options.env }));
|
|
3536
4215
|
this.appRuntime = createAppRuntime(appTransport, appRuntime?.appId, appIdResolver);
|
|
3537
4216
|
const executionToken = resolveExecutionToken();
|
|
3538
4217
|
const getAccessToken = options.getAccessToken ?? (executionToken ? () => executionToken : (tokenOptions) => this.appRuntime.getAccessToken(tokenOptions));
|
|
@@ -4007,7 +4686,7 @@ const isDefinitiveAuthorizationFailure = (error) => error instanceof AppAuthoriz
|
|
|
4007
4686
|
401,
|
|
4008
4687
|
403,
|
|
4009
4688
|
404
|
|
4010
|
-
].includes(error.status);
|
|
4689
|
+
].includes(error.status) && error.code !== "host_unavailable";
|
|
4011
4690
|
/** A grant the server rejected (revoked / not found) — safe to drop from cache. */
|
|
4012
4691
|
const isGrantRejection = (error) => error instanceof AppAuthorizationError && [403, 404].includes(error.status);
|
|
4013
4692
|
/** A stale access token — never a revoked grant, so never clear the cache. */
|
|
@@ -4435,20 +5114,25 @@ function createAppBridgeCore(config) {
|
|
|
4435
5114
|
throw new AppLoginRedirect();
|
|
4436
5115
|
}
|
|
4437
5116
|
const authorizingViewer = await getViewerUuid();
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
5117
|
+
let response;
|
|
5118
|
+
try {
|
|
5119
|
+
response = await fetch(`${apiOrigin}/api/apps/${app.id}/authorize`, {
|
|
5120
|
+
method: "POST",
|
|
5121
|
+
signal: timeoutSignal(AUTHORIZE_TIMEOUT_MS),
|
|
5122
|
+
headers: {
|
|
5123
|
+
Authorization: `Bearer ${userToken}`,
|
|
5124
|
+
"Content-Type": "application/json"
|
|
5125
|
+
},
|
|
5126
|
+
body: JSON.stringify({
|
|
5127
|
+
scopes,
|
|
5128
|
+
...spaceId ? { spaceId } : {},
|
|
5129
|
+
...options?.silent ? { silent: true } : {},
|
|
5130
|
+
...incremental ? { scopeMode: "extend" } : {}
|
|
5131
|
+
})
|
|
5132
|
+
});
|
|
5133
|
+
} catch (error) {
|
|
5134
|
+
throw new AppAuthorizationError(error instanceof DOMException && error.name === "TimeoutError" ? "Authorization request timed out." : "Authorization request failed.", 503, "host_unavailable");
|
|
5135
|
+
}
|
|
4452
5136
|
const payload = await response.json().catch(() => null);
|
|
4453
5137
|
if (!response.ok) throw new AppAuthorizationError(typeof payload?.message === "string" ? payload.message : "Authorization failed.", response.status, typeof payload?.code === "string" ? payload.code : void 0);
|
|
4454
5138
|
if (authorizingViewer !== await getViewerUuid()) throw new AppAuthorizationError("The signed-in account changed. Please try again.", 409, "session_changed");
|
|
@@ -4489,6 +5173,55 @@ function createAppBridgeCore(config) {
|
|
|
4489
5173
|
}
|
|
4490
5174
|
}
|
|
4491
5175
|
/**
|
|
5176
|
+
* Silently renews the approved read-only scopes on the current Shell Space.
|
|
5177
|
+
*
|
|
5178
|
+
* Deciding that a request needs no dialog is the Host's job: it compares the
|
|
5179
|
+
* target against the Space it is actually showing. That check only decides
|
|
5180
|
+
* *whether to ask*; the server still decides *what may be granted* through
|
|
5181
|
+
* the silent path, so a revoked consent can never come back on its own.
|
|
5182
|
+
*/
|
|
5183
|
+
const shellAuthorizationInFlight = /* @__PURE__ */ new Map();
|
|
5184
|
+
const shellAuthorizationCache = /* @__PURE__ */ new Map();
|
|
5185
|
+
const SHELL_AUTH_CACHE_MS = 3e4;
|
|
5186
|
+
async function authorizeShellSilently(scopes, spaceId) {
|
|
5187
|
+
const shellContext = config.getShell?.() ?? config.shell;
|
|
5188
|
+
const shell = shellContext?.space ?? null;
|
|
5189
|
+
if (!shellContext || authorizationContext.surface === "broker" || !shell || shell.id !== spaceId) throw new AppAuthorizationError("Shell authorization is unavailable for this surface.", 403, "consent_required");
|
|
5190
|
+
const viewerUuid = await getViewerUuid();
|
|
5191
|
+
if (!viewerUuid) {
|
|
5192
|
+
await startSignIn();
|
|
5193
|
+
throw new AppLoginRedirect();
|
|
5194
|
+
}
|
|
5195
|
+
const cacheKey = `${viewerUuid}:${app.id}:${spaceId}:${[...scopes].sort().join(",")}`;
|
|
5196
|
+
const cached = shellAuthorizationCache.get(cacheKey);
|
|
5197
|
+
if (cached && cached.expiresAt > Date.now()) return cached.result;
|
|
5198
|
+
if (cached) shellAuthorizationCache.delete(cacheKey);
|
|
5199
|
+
const pending = shellAuthorizationInFlight.get(cacheKey);
|
|
5200
|
+
if (pending) return pending;
|
|
5201
|
+
const request = authorize(scopes, spaceId, { silent: true });
|
|
5202
|
+
shellAuthorizationInFlight.set(cacheKey, request);
|
|
5203
|
+
try {
|
|
5204
|
+
const result = await request;
|
|
5205
|
+
shellAuthorizationCache.set(cacheKey, {
|
|
5206
|
+
result,
|
|
5207
|
+
expiresAt: Date.now() + SHELL_AUTH_CACHE_MS
|
|
5208
|
+
});
|
|
5209
|
+
return result;
|
|
5210
|
+
} finally {
|
|
5211
|
+
shellAuthorizationInFlight.delete(cacheKey);
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5214
|
+
/**
|
|
5215
|
+
* Reads the Shell Space supplied by the current Host. Invocation context is
|
|
5216
|
+
* deliberately excluded: an App may be opened from one Space while the Shell
|
|
5217
|
+
* shows another; the Host/Shell broker remains the authorization authority.
|
|
5218
|
+
*/
|
|
5219
|
+
function resolveShellSpace() {
|
|
5220
|
+
const shell = config.getShell?.() ?? config.shell;
|
|
5221
|
+
if (!shell || shell.surface === "broker") return null;
|
|
5222
|
+
return shell.space;
|
|
5223
|
+
}
|
|
5224
|
+
/**
|
|
4492
5225
|
* Attempts silent renewal against each target in order. Returns true when the
|
|
4493
5226
|
* request was answered (success or terminal error); false when every target
|
|
4494
5227
|
* was a rejected grant, so the caller should fall back to consent.
|
|
@@ -4940,6 +5673,28 @@ function createAppBridgeCore(config) {
|
|
|
4940
5673
|
await startSignIn();
|
|
4941
5674
|
return;
|
|
4942
5675
|
}
|
|
5676
|
+
const shellSpace = resolveShellSpace();
|
|
5677
|
+
const shellTargetId = spaceId ?? shellSpace?.id;
|
|
5678
|
+
if (!alwaysAsk && !selectSpace && Boolean(shellTargetId && (!spaceId || shellSpace?.id === spaceId)) && scopes.every(isAppSilentShellScope) && shellTargetId) try {
|
|
5679
|
+
const result = await authorizeShellSilently(scopes, shellTargetId);
|
|
5680
|
+
if (state.pendingAuth !== reserved) return;
|
|
5681
|
+
setGrantedAppScopes(await getViewerUuid(), app.id, result.scopes, result.spaceId);
|
|
5682
|
+
replyPendingAuth(reserved, authorizeResult(result.token, result.spaceId, shellSpace?.name ?? null));
|
|
5683
|
+
resetDialogState();
|
|
5684
|
+
safeNotify();
|
|
5685
|
+
return;
|
|
5686
|
+
} catch (error) {
|
|
5687
|
+
if (error instanceof AppLoginRedirect) throw error;
|
|
5688
|
+
if (!isDefinitiveAuthorizationFailure(error)) {
|
|
5689
|
+
replyPendingAuth(reserved, {
|
|
5690
|
+
type: "cohub.app.error",
|
|
5691
|
+
message: error instanceof Error ? error.message : "Authorization failed."
|
|
5692
|
+
});
|
|
5693
|
+
resetDialogState();
|
|
5694
|
+
safeNotify();
|
|
5695
|
+
return;
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
4943
5698
|
const spaceLevel = Boolean(spaceId) || selectSpace || scopes.some((scope) => !isUserLevelPermission(scope));
|
|
4944
5699
|
const lastPicked = readLastPickedSpace();
|
|
4945
5700
|
const contextSpaceId = resolveContextSpaceId();
|
|
@@ -5419,4 +6174,4 @@ function createBoardExtensionRegistry() {
|
|
|
5419
6174
|
}
|
|
5420
6175
|
const BOARD_CHANNELS = BOARD_ANIMATION_CHANNELS;
|
|
5421
6176
|
//#endregion
|
|
5422
|
-
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, AppRuntimeError, AppSurfaceApi, AppsApi, BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_ANIMATION_CHANNEL_CAPABILITIES, BOARD_CHANNELS, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BillingApi, BoardAnimationSpecSchema, BoardAuthoringItemSchema, BoardClient, BoardCompositionInputSchema, BoardCompositionSchema, BoardDealParamsSchema, BoardEffectInputSchema, BoardEffectSchema, BoardExtensionRegistry, BoardItemPatchSchema, BoardPlaybackPolicySchema, BoardSemanticCommandSchema, 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, PERMISSIONS, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, RUNTIME_ARCHIVE_SEGMENT_BYTES, RUNTIME_MAX_FRAME_BYTES, RUNTIME_PROTOCOL_VERSION, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, SpacePublicFilesApi, SpaceWebhooksApi, USER_LEVEL_PERMISSIONS, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkSurfaceApi, assertGenerationRequestAllowedByPolicy, attachAppEmbed, buildAppSurfaceRequest, buildSpaceInvitePath, buildSpacePath, clearGrantedAppScopes, compileComposition, composition, contextToPiMessages, createAppBridgeCore, createAppBridgeCore as createWorkBridgeCore, createAppRuntime, createAppRuntime as createWorkRuntime, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugAppIdResolver, createSlugAppIdResolver as createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, filterSpacePickerItems, findGenerationModelPolicy, formatAppRef, formatWorkRef, getAllowedGenerationModelIds, getCohubContext, harnessArchiveIndexSchema, hasGrantedAppScopes, hasRequestSourceIdentity, isAppId, isBillingAccessBlockedCode, isBillingAccessBlockedError, isDesktopCallMethod, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isLocalHarness, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalDesktopCommandStatus, isUserLevelPermission, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeSpacePickerQuery, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, orderSpacePickerItems, parseAppRef, parseAppSurfaceReady, parseAppSurfaceResponse, parseAssistantMessageCommit, parseBoardCompositionInput, parseBoardEffectInput, parseBoardPlaybackPolicy, parseDesktopCommand, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseWorkRef, proceduralClip, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveAppTransport, resolveAppTransport as resolveWorkTransport, resolveCohubEnvironment, resolveExecutionAppId, resolveExecutionToken, resolveHarness, resolveOpenSurface, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, runtimeCommandSchema, runtimeEventSchema, runtimeReadySchema, sampleCompositionTracks, sampleEasing, sampleTrack, sanitizeAccessToken, scopeListHasPermission, selectRuntimeContextMessages, selectSpacePickerItems, setGrantedAppScopes, track, validateArchiveBoundary };
|
|
6177
|
+
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, AppRuntimeError, AppSurfaceApi, AppsApi, BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_ANIMATION_CHANNEL_CAPABILITIES, BOARD_CHANNELS, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BillingApi, BoardAnimationSpecSchema, BoardAuthoringItemSchema, BoardClient, BoardCompositionInputSchema, BoardCompositionSchema, BoardDealParamsSchema, BoardEffectInputSchema, BoardEffectSchema, BoardExtensionRegistry, BoardItemPatchSchema, BoardPlaybackPolicySchema, BoardSemanticCommandSchema, 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, OriginBrokerTransport, PERMISSIONS, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, RUNTIME_ARCHIVE_SEGMENT_BYTES, RUNTIME_MAX_FRAME_BYTES, RUNTIME_PROTOCOL_VERSION, RUNTIME_RECOVERY_BATCH_SIZE, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, SpacePublicFilesApi, SpaceWebhooksApi, USER_LEVEL_PERMISSIONS, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkSurfaceApi, assertGenerationRequestAllowedByPolicy, attachAppEmbed, buildAppSurfaceRequest, buildSpaceInvitePath, buildSpacePath, clearGrantedAppScopes, compileComposition, composition, contextToPiMessages, createAppBridgeCore, createAppBridgeCore as createWorkBridgeCore, createAppRuntime, createAppRuntime as createWorkRuntime, createBoardExtensionRegistry, createCohubClient, createHttpClient, createOriginAppResolver, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugAppIdResolver, createSlugAppIdResolver as createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, filterSpacePickerItems, findGenerationModelPolicy, fingerprintProjectionTurns, formatAppRef, formatWorkRef, getAllowedGenerationModelIds, getCohubContext, harnessArchiveIndexSchema, hasGrantedAppScopes, hasRequestSourceIdentity, isAppId, isBillingAccessBlockedCode, isBillingAccessBlockedError, isDesktopCallMethod, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isLocalHarness, isProjectionCompaction, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalDesktopCommandStatus, isUserLevelPermission, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeSpacePickerQuery, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, orderSpacePickerItems, parseAppRef, parseAppSurfaceReady, parseAppSurfaceResponse, parseAssistantMessageCommit, parseBoardCompositionInput, parseBoardEffectInput, parseBoardPlaybackPolicy, parseDesktopCommand, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseWorkRef, proceduralClip, projectNativeMessageMeta, projectNativeSession, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveAppTransport, resolveAppTransport as resolveWorkTransport, resolveCohubEnvironment, resolveExecutionAppId, resolveExecutionToken, resolveHarness, resolveOpenSurface, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebBaseUrl, resolveWebsocketUrl, runtimeCommandSchema, runtimeEventSchema, runtimeReadySchema, sampleCompositionTracks, sampleEasing, sampleTrack, sanitizeAccessToken, scopeListHasPermission, selectRuntimeContextMessages, selectSpacePickerItems, serializeProjection, serializeProjectionRecords, setGrantedAppScopes, track, trimProjectionTurnsToCompaction, validateArchiveBoundary };
|