@opengeni/sdk 3.3.2 → 3.4.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 +1007 -72
- package/dist/adapters/express.d.ts +31 -0
- package/dist/adapters/express.js +87 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/hono.d.ts +16 -0
- package/dist/adapters/hono.js +8 -0
- package/dist/adapters/hono.js.map +1 -0
- package/dist/adapters/next.d.ts +29 -0
- package/dist/adapters/next.js +22 -0
- package/dist/adapters/next.js.map +1 -0
- package/dist/allowance-refusal.d.ts +2 -0
- package/dist/allowance-refusal.js +50 -0
- package/dist/allowance-refusal.js.map +1 -0
- package/dist/app-links.d.ts +42 -0
- package/dist/app-links.js +66 -0
- package/dist/app-links.js.map +1 -0
- package/dist/artifact-catalog.d.ts +40 -0
- package/dist/artifact-client.d.ts +20 -8
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +8 -6
- package/dist/automations.js +8 -2
- package/dist/automations.js.map +1 -1
- package/dist/azure-live-transport.d.ts +32 -0
- package/dist/browser.d.ts +7 -2
- package/dist/browser.js +27 -5
- package/dist/chat/fold.d.ts +75 -0
- package/dist/chat/handler.d.ts +41 -0
- package/dist/chat/http.d.ts +67 -0
- package/dist/chat/ids.d.ts +34 -0
- package/dist/chat/index.d.ts +9 -0
- package/dist/chat/index.js +1369 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/openai.d.ts +30 -0
- package/dist/chat/opengeni.d.ts +129 -0
- package/dist/chat/types.d.ts +207 -0
- package/dist/chat/vercel.d.ts +49 -0
- package/dist/chats.d.ts +8 -0
- package/dist/chunk-4AHR4KGA.js +301 -0
- package/dist/chunk-4AHR4KGA.js.map +1 -0
- package/dist/chunk-6GYGMLX4.js +332 -0
- package/dist/chunk-6GYGMLX4.js.map +1 -0
- package/dist/chunk-7EWT3PV6.js +368 -0
- package/dist/chunk-7EWT3PV6.js.map +1 -0
- package/dist/chunk-FTWVVPR6.js +1665 -0
- package/dist/chunk-FTWVVPR6.js.map +1 -0
- package/dist/{chunk-VYCTL62C.js → chunk-GZQSWAKD.js} +1 -1
- package/dist/chunk-GZQSWAKD.js.map +1 -0
- package/dist/chunk-KGEABAID.js +71 -0
- package/dist/chunk-KGEABAID.js.map +1 -0
- package/dist/{chunk-YKZME56C.js → chunk-KWADQ227.js} +3 -3
- package/dist/{chunk-YKZME56C.js.map → chunk-KWADQ227.js.map} +1 -1
- package/dist/{chunk-JUS2UYRP.js → chunk-NCU5OAN7.js} +27 -2
- package/dist/chunk-NCU5OAN7.js.map +1 -0
- package/dist/{chunk-YXQX7RQT.js → chunk-OI5YLASH.js} +73 -2
- package/dist/chunk-OI5YLASH.js.map +1 -0
- package/dist/chunk-OV3POE62.js +359 -0
- package/dist/chunk-OV3POE62.js.map +1 -0
- package/dist/chunk-P7UWKORG.js +561 -0
- package/dist/chunk-P7UWKORG.js.map +1 -0
- package/dist/chunk-QGEIL4TR.js +195 -0
- package/dist/chunk-QGEIL4TR.js.map +1 -0
- package/dist/{chunk-TRNI7KEO.js → chunk-S2NLS3ES.js} +3669 -1281
- package/dist/chunk-S2NLS3ES.js.map +1 -0
- package/dist/chunk-UM4ZXXJR.js +9 -0
- package/dist/chunk-UM4ZXXJR.js.map +1 -0
- package/dist/{chunk-QT3KFVDP.js → chunk-UME3X7CP.js} +28 -32
- package/dist/chunk-UME3X7CP.js.map +1 -0
- package/dist/chunk-VT4YM4XQ.js +210 -0
- package/dist/chunk-VT4YM4XQ.js.map +1 -0
- package/dist/chunk-WSYYXONG.js +1602 -0
- package/dist/chunk-WSYYXONG.js.map +1 -0
- package/dist/{chunk-C2H7HBNP.js → chunk-WVCJRSGJ.js} +119 -14
- package/dist/chunk-WVCJRSGJ.js.map +1 -0
- package/dist/chunk-X5ITQVXE.js +15 -0
- package/dist/chunk-X5ITQVXE.js.map +1 -0
- package/dist/chunk-XA7HVIHI.js +126 -0
- package/dist/chunk-XA7HVIHI.js.map +1 -0
- package/dist/{chunk-KKRO2VCP.js → chunk-YJCHB7R3.js} +60 -12
- package/dist/chunk-YJCHB7R3.js.map +1 -0
- package/dist/chunk-YKIHNLWD.js +153 -0
- package/dist/chunk-YKIHNLWD.js.map +1 -0
- package/dist/chunk-ZDUBPV33.js +277 -0
- package/dist/chunk-ZDUBPV33.js.map +1 -0
- package/dist/client.d.ts +1068 -118
- package/dist/codex-realtime-controller.d.ts +13 -0
- package/dist/codex-realtime-controller.js +4 -2
- package/dist/codex-realtime-v3-wire.d.ts +3 -2
- package/dist/codex-realtime.d.ts +5 -1
- package/dist/company-brain.d.ts +1 -11
- package/dist/core.d.ts +7 -2
- package/dist/core.js +25 -6
- package/dist/deprecation.d.ts +47 -0
- package/dist/document-authority-client.d.ts +24 -2
- package/dist/document-authority.js +7 -5
- package/dist/editable-artifacts/index.d.ts +1 -1
- package/dist/editable-artifacts/types.d.ts +12 -2
- package/dist/editable-artifacts.js +19 -3
- package/dist/editable-artifacts.js.map +1 -1
- package/dist/embedding-client.d.ts +129 -0
- package/dist/errors.d.ts +76 -1
- package/dist/external-vault-receiver.d.ts +203 -0
- package/dist/external-vault-receiver.js +564 -0
- package/dist/external-vault-receiver.js.map +1 -0
- package/dist/feedback.d.ts +23 -0
- package/dist/gateway-realtime-transport.js +1 -1
- package/dist/github-repositories.d.ts +10 -0
- package/dist/github-repositories.js +36 -0
- package/dist/github-repositories.js.map +1 -0
- package/dist/index.d.ts +62 -11
- package/dist/index.js +557 -338
- package/dist/index.js.map +1 -1
- package/dist/insights-usage.d.ts +33 -0
- package/dist/insights-usage.js +7 -0
- package/dist/insights-usage.js.map +1 -0
- package/dist/interaction.d.ts +165 -71
- package/dist/interaction.js +2 -2
- package/dist/knowledge.d.ts +7 -0
- package/dist/knowledge.js +20 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/known-tools.d.ts +4 -0
- package/dist/message-links.d.ts +68 -0
- package/dist/model-context.d.ts +68 -0
- package/dist/model-display.d.ts +2 -0
- package/dist/model-display.js +17 -0
- package/dist/model-display.js.map +1 -0
- package/dist/model-picker-order.d.ts +1 -0
- package/dist/model-picker-order.js +10 -0
- package/dist/model-picker-order.js.map +1 -0
- package/dist/organization-integration-policy.d.ts +26 -0
- package/dist/organization-integration-policy.js +26 -0
- package/dist/organization-integration-policy.js.map +1 -0
- package/dist/pr-review-client.d.ts +1 -1
- package/dist/pr-review.d.ts +1 -1
- package/dist/pr-review.js.map +1 -1
- package/dist/preference-registry.d.ts +1 -1
- package/dist/prepared-mcp-setup.d.ts +5 -0
- package/dist/proxy.d.ts +13 -11
- package/dist/realtime.d.ts +1 -1
- package/dist/realtime.js +253 -7
- package/dist/realtime.js.map +1 -1
- package/dist/session-history-import.d.ts +22 -0
- package/dist/session-history-import.js +36 -0
- package/dist/session-history-import.js.map +1 -0
- package/dist/session-list-entries.d.ts +1 -0
- package/dist/session-list-entries.js +7 -0
- package/dist/session-list-entries.js.map +1 -0
- package/dist/session-message-search.d.ts +53 -0
- package/dist/session-proxy-client.d.ts +28 -0
- package/dist/session-proxy.d.ts +334 -0
- package/dist/session-proxy.js +24 -0
- package/dist/session-proxy.js.map +1 -0
- package/dist/session-titles.d.ts +2 -28
- package/dist/session-usage-limits.d.ts +10 -0
- package/dist/session-usage-limits.js +43 -0
- package/dist/session-usage-limits.js.map +1 -0
- package/dist/shared-catalog-loader.d.ts +34 -0
- package/dist/site-ai.d.ts +35 -0
- package/dist/site-bridge-host.d.ts +112 -0
- package/dist/site-bridge-host.js +286 -0
- package/dist/site-bridge-host.js.map +1 -0
- package/dist/site-browser-entry.d.ts +1 -0
- package/dist/site-browser-runtime.gen.d.ts +1 -0
- package/dist/site-data.d.ts +178 -0
- package/dist/site-document.d.ts +5 -0
- package/dist/site-document.js +20 -0
- package/dist/site-document.js.map +1 -0
- package/dist/site-document.test.d.ts +1 -0
- package/dist/site-http.d.ts +17 -0
- package/dist/site-sql.d.ts +31 -0
- package/dist/site-tool-bridge.d.ts +57 -0
- package/dist/site.d.ts +186 -0
- package/dist/site.js +63 -0
- package/dist/site.js.map +1 -0
- package/dist/skills.d.ts +75 -0
- package/dist/sse.d.ts +1 -1
- package/dist/stream.d.ts +2 -0
- package/dist/tenant-workspaces.d.ts +47 -0
- package/dist/tenant-workspaces.js +8 -0
- package/dist/tenant-workspaces.js.map +1 -0
- package/dist/tool-auth.d.ts +87 -0
- package/dist/tool-auth.js +23 -0
- package/dist/tool-auth.js.map +1 -0
- package/dist/tool-display-metadata.d.ts +3 -0
- package/dist/tools-37DCTCNZ.js +12 -0
- package/dist/tools-37DCTCNZ.js.map +1 -0
- package/dist/tools.d.ts +81 -0
- package/dist/transcription.d.ts +1 -1
- package/dist/types.d.ts +2807 -583
- package/dist/usage-allowances.d.ts +36 -0
- package/dist/usage-allowances.js +80 -0
- package/dist/usage-allowances.js.map +1 -0
- package/dist/vault-types.d.ts +324 -0
- package/dist/vaults-api.d.ts +65 -0
- package/dist/workspace-artifacts.d.ts +47 -5
- package/dist/workspace-instruction-policies.d.ts +2 -2
- package/dist/workspace-integrations.d.ts +74 -0
- package/dist/workspace-integrations.js +67 -0
- package/dist/workspace-integrations.js.map +1 -0
- package/dist/workspace-state.d.ts +8 -71
- package/package.json +103 -5
- package/src/adapters/express.ts +136 -0
- package/src/adapters/hono.ts +18 -0
- package/src/adapters/next.ts +42 -0
- package/src/allowance-refusal.ts +5 -0
- package/src/app-links.ts +117 -0
- package/src/artifact-catalog.ts +51 -0
- package/src/artifact-client.ts +40 -49
- package/src/artifacts.ts +1 -0
- package/src/automations-client.ts +8 -2
- package/src/azure-live-transport.ts +286 -0
- package/src/browser.ts +37 -0
- package/src/chat/fold.ts +431 -0
- package/src/chat/handler.ts +240 -0
- package/src/chat/http.ts +257 -0
- package/src/chat/ids.ts +85 -0
- package/src/chat/index.ts +88 -0
- package/src/chat/openai.ts +439 -0
- package/src/chat/opengeni.ts +737 -0
- package/src/chat/types.ts +219 -0
- package/src/chat/vercel.ts +204 -0
- package/src/chats.ts +16 -0
- package/src/client.ts +5697 -1833
- package/src/codex-realtime-controller.ts +163 -8
- package/src/codex-realtime-v3-wire.ts +4 -3
- package/src/codex-realtime-v3.ts +12 -3
- package/src/codex-realtime.ts +11 -3
- package/src/company-brain.ts +0 -40
- package/src/core.ts +20 -0
- package/src/deprecation.ts +149 -0
- package/src/document-authority-client.ts +104 -1
- package/src/editable-artifacts/controller.ts +36 -2
- package/src/editable-artifacts/index.ts +1 -0
- package/src/editable-artifacts/types.ts +29 -16
- package/src/embedding-client.ts +519 -0
- package/src/errors.ts +303 -4
- package/src/external-vault-receiver.ts +914 -0
- package/src/feedback.ts +20 -0
- package/src/gateway-realtime-transport.ts +3 -3
- package/src/github-repositories.ts +60 -0
- package/src/index.ts +603 -65
- package/src/insights-usage.ts +64 -0
- package/src/interaction.ts +315 -93
- package/src/knowledge.ts +72 -0
- package/src/known-tools.ts +279 -0
- package/src/message-links.ts +178 -0
- package/src/model-context.ts +113 -0
- package/src/model-display.ts +11 -0
- package/src/model-picker-order.ts +6 -0
- package/src/organization-integration-policy.ts +54 -0
- package/src/pr-review-client.ts +1 -1
- package/src/pr-review.ts +1 -1
- package/src/preference-registry.ts +2 -0
- package/src/prepared-mcp-setup.ts +123 -0
- package/src/proxy.ts +23 -12
- package/src/realtime.ts +15 -6
- package/src/session-history-import.ts +84 -0
- package/src/session-list-entries.ts +1 -0
- package/src/session-message-search.ts +47 -0
- package/src/session-proxy-client.ts +68 -0
- package/src/session-proxy.ts +2098 -0
- package/src/session-titles.ts +8 -82
- package/src/session-usage-limits.ts +71 -0
- package/src/shared-catalog-loader.ts +169 -0
- package/src/site-ai.ts +284 -0
- package/src/site-bridge-host.ts +422 -0
- package/src/site-browser-entry.ts +6 -0
- package/src/site-browser-runtime.gen.ts +2 -0
- package/src/site-data.ts +372 -0
- package/src/site-document.test.ts +64 -0
- package/src/site-document.ts +18 -0
- package/src/site-http.ts +171 -0
- package/src/site-sql.ts +153 -0
- package/src/site-tool-bridge.ts +401 -0
- package/src/site.ts +878 -0
- package/src/skills.ts +69 -0
- package/src/sse.ts +9 -3
- package/src/stream.ts +33 -10
- package/src/tenant-workspaces.ts +148 -0
- package/src/tool-auth.ts +301 -0
- package/src/tool-display-metadata.ts +27 -0
- package/src/tools.ts +452 -0
- package/src/transcription.ts +3 -0
- package/src/types.ts +3170 -582
- package/src/usage-allowances.ts +189 -0
- package/src/vault-types.ts +379 -0
- package/src/vaults-api.ts +339 -0
- package/src/workspace-artifacts.ts +55 -6
- package/src/workspace-instruction-policies.ts +2 -1
- package/src/workspace-integrations.ts +402 -0
- package/src/workspace-state.ts +8 -82
- package/dist/chunk-C2H7HBNP.js.map +0 -1
- package/dist/chunk-C4DJPZRU.js +0 -119
- package/dist/chunk-C4DJPZRU.js.map +0 -1
- package/dist/chunk-JUS2UYRP.js.map +0 -1
- package/dist/chunk-KKRO2VCP.js.map +0 -1
- package/dist/chunk-QT3KFVDP.js.map +0 -1
- package/dist/chunk-TRNI7KEO.js.map +0 -1
- package/dist/chunk-VYCTL62C.js.map +0 -1
- package/dist/chunk-YXQX7RQT.js.map +0 -1
package/src/site.ts
ADDED
|
@@ -0,0 +1,878 @@
|
|
|
1
|
+
export { createSiteToolBridge, isSiteCatalogStaleError } from "./site-tool-bridge";
|
|
2
|
+
export type {
|
|
3
|
+
SiteToolBridge,
|
|
4
|
+
SiteToolCaller,
|
|
5
|
+
SiteToolCallRequest,
|
|
6
|
+
CreateSiteToolBridgeOptions,
|
|
7
|
+
} from "./site-tool-bridge";
|
|
8
|
+
import {
|
|
9
|
+
OpenGeniToolsClient,
|
|
10
|
+
type OpenGeniToolTransport,
|
|
11
|
+
type OpenGeniWorkspaceTools,
|
|
12
|
+
} from "./tools";
|
|
13
|
+
import { OpenGeniClient } from "./client";
|
|
14
|
+
import { sitePortFetch, siteSessionPath, SITE_WORKSPACE } from "./site-http";
|
|
15
|
+
import { createOpenGeniSiteData, type OpenGeniSiteData, type OpenGeniSiteFiles } from "./site-data";
|
|
16
|
+
import { createOpenGeniSiteSql, type OpenGeniSiteSql } from "./site-sql";
|
|
17
|
+
import { createOpenGeniSiteAi, type OpenGeniSiteAi } from "./site-ai";
|
|
18
|
+
export type {
|
|
19
|
+
OpenGeniSiteAi,
|
|
20
|
+
OpenGeniSiteAiRequest,
|
|
21
|
+
OpenGeniSiteAiResult,
|
|
22
|
+
OpenGeniSiteAiOptions,
|
|
23
|
+
} from "./site-ai";
|
|
24
|
+
export {
|
|
25
|
+
OpenGeniSiteSqlError,
|
|
26
|
+
type OpenGeniSiteSql,
|
|
27
|
+
type OpenGeniSiteSqlErrorCode,
|
|
28
|
+
type OpenGeniSiteSqlResult,
|
|
29
|
+
type OpenGeniSiteSqlValue,
|
|
30
|
+
} from "./site-sql";
|
|
31
|
+
export {
|
|
32
|
+
OpenGeniSiteDataError,
|
|
33
|
+
SITE_DATA_PATH_PREFIX,
|
|
34
|
+
type OpenGeniSiteData,
|
|
35
|
+
type OpenGeniSiteDataCollection,
|
|
36
|
+
type OpenGeniSiteDataErrorCode,
|
|
37
|
+
type OpenGeniSiteDataFilter,
|
|
38
|
+
type OpenGeniSiteDataListOptions,
|
|
39
|
+
type OpenGeniSiteDataOperation,
|
|
40
|
+
type OpenGeniSiteDataPage,
|
|
41
|
+
type OpenGeniSiteDataRecord,
|
|
42
|
+
type OpenGeniSiteDataRef,
|
|
43
|
+
type OpenGeniSiteDataScalar,
|
|
44
|
+
type OpenGeniSiteDataWriteResult,
|
|
45
|
+
type OpenGeniSiteFile,
|
|
46
|
+
type OpenGeniSiteFiles,
|
|
47
|
+
} from "./site-data";
|
|
48
|
+
export {
|
|
49
|
+
isSiteHttpRequest,
|
|
50
|
+
serveSiteHttp,
|
|
51
|
+
siteSessionPath,
|
|
52
|
+
type SiteHttpRequest,
|
|
53
|
+
} from "./site-http";
|
|
54
|
+
import type { SiteViewer, SiteViewerCapabilities } from "@opengeni/contracts";
|
|
55
|
+
import type {
|
|
56
|
+
ToolGatewayCallRequest,
|
|
57
|
+
ToolGatewayCallResponse,
|
|
58
|
+
ToolGatewayCatalog,
|
|
59
|
+
ToolGatewayDeclarationsResponse,
|
|
60
|
+
ToolGatewayResolveRequest,
|
|
61
|
+
ToolGatewayResolvedTool,
|
|
62
|
+
ToolGatewayInvokeRequest,
|
|
63
|
+
ToolGatewayInvokeResponse,
|
|
64
|
+
} from "./types";
|
|
65
|
+
|
|
66
|
+
export const OPENGENI_SITE_BRIDGE_VERSION = 2 as const;
|
|
67
|
+
export const OPENGENI_SITE_BRIDGE_CONNECT = "opengeni.site.connect" as const;
|
|
68
|
+
export const OPENGENI_SITE_BRIDGE_READY = "opengeni.site.ready" as const;
|
|
69
|
+
export const OPENGENI_SITE_BRIDGE_REQUEST = "opengeni.site.request" as const;
|
|
70
|
+
export const OPENGENI_SITE_BRIDGE_RESPONSE = "opengeni.site.response" as const;
|
|
71
|
+
export const OPENGENI_SITE_BRIDGE_CANCEL = "opengeni.site.cancel" as const;
|
|
72
|
+
export const OPENGENI_SITE_BRIDGE_BOOTSTRAP_GLOBAL = "__opengeniSiteBridgeBootstrapV2" as const;
|
|
73
|
+
export const OPENGENI_SITE_LOCAL_CODEMODE_PATH = "/__opengeni/site-tools" as const;
|
|
74
|
+
/** Bridge request method that returns the viewer (advertised by `viewer: 1`). */
|
|
75
|
+
export const OPENGENI_SITE_BRIDGE_VIEWER_METHOD = "viewer" as const;
|
|
76
|
+
/** Bridge request method that asks the host to sign the viewer in (advertised by `viewer: 1`). */
|
|
77
|
+
export const OPENGENI_SITE_BRIDGE_SIGN_IN_METHOD = "auth.signIn" as const;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The person viewing a Site, as the host reports it. Guests of public
|
|
81
|
+
* published Sites have no name or email. `id` is stable per Site and is the
|
|
82
|
+
* Site Data owner principal; it is not an Opengeni subject id usable elsewhere.
|
|
83
|
+
*/
|
|
84
|
+
export type OpenGeniSiteViewer = SiteViewer;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* What the current viewer may do on this published Site, so a page can hide
|
|
88
|
+
* features instead of failing: `agent`, `workspaceTools` (workspace members
|
|
89
|
+
* only), `siteData` (`"guest"` when a collection's guest access applies),
|
|
90
|
+
* the Site SQL query audiences the viewer may call, and `viewAs` while a
|
|
91
|
+
* member previews the Site as a visitor. Anything refused at call time
|
|
92
|
+
* still answers with the error code `not_available_for_viewer`.
|
|
93
|
+
*/
|
|
94
|
+
export type OpenGeniSiteCapabilities = SiteViewerCapabilities;
|
|
95
|
+
|
|
96
|
+
/** Result of `site.auth.signIn()`: `started` is true when the host began a sign-in navigation. */
|
|
97
|
+
export type OpenGeniSiteSignInResult = { started: boolean };
|
|
98
|
+
|
|
99
|
+
export type OpenGeniSiteBridgeConnectMessage = {
|
|
100
|
+
type: typeof OPENGENI_SITE_BRIDGE_CONNECT;
|
|
101
|
+
version: typeof OPENGENI_SITE_BRIDGE_VERSION;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export type OpenGeniSiteBridgeReadyMessage = {
|
|
105
|
+
type: typeof OPENGENI_SITE_BRIDGE_READY;
|
|
106
|
+
version: typeof OPENGENI_SITE_BRIDGE_VERSION;
|
|
107
|
+
targetTools?: 1;
|
|
108
|
+
/** The host serves Site Data for this published Site over the Site HTTP channel. */
|
|
109
|
+
siteData?: 1;
|
|
110
|
+
/** The host answers the `viewer` and `auth.signIn` bridge methods. */
|
|
111
|
+
viewer?: 1;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** Site-originated calls cannot carry host-only approval tokens or Site-version authority. */
|
|
115
|
+
export type OpenGeniSiteToolCallRequest = Omit<
|
|
116
|
+
ToolGatewayCallRequest,
|
|
117
|
+
"approvalToken" | "siteArtifactId" | "siteVersionId"
|
|
118
|
+
>;
|
|
119
|
+
|
|
120
|
+
/** Host-filtered catalog projection. Tenant routing context never enters Site code. */
|
|
121
|
+
export type OpenGeniSiteToolCatalog = Omit<ToolGatewayCatalog, "accountId" | "workspaceId">;
|
|
122
|
+
|
|
123
|
+
export type OpenGeniSiteWorkspaceTools = OpenGeniWorkspaceTools<OpenGeniSiteToolCatalog>;
|
|
124
|
+
|
|
125
|
+
export type OpenGeniSiteBridgeRequestMessage = {
|
|
126
|
+
type: typeof OPENGENI_SITE_BRIDGE_REQUEST;
|
|
127
|
+
version: typeof OPENGENI_SITE_BRIDGE_VERSION;
|
|
128
|
+
requestId: string;
|
|
129
|
+
} & (
|
|
130
|
+
| { method: "catalog" }
|
|
131
|
+
| { method: "call"; payload: OpenGeniSiteToolCallRequest }
|
|
132
|
+
| { method: "declarations" }
|
|
133
|
+
| { method: "resolve"; payload: ToolGatewayResolveRequest }
|
|
134
|
+
| { method: "invoke"; payload: ToolGatewayInvokeRequest }
|
|
135
|
+
| { method: typeof OPENGENI_SITE_BRIDGE_VIEWER_METHOD }
|
|
136
|
+
| { method: typeof OPENGENI_SITE_BRIDGE_SIGN_IN_METHOD }
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
export type OpenGeniSiteBridgeCancelMessage = {
|
|
140
|
+
type: typeof OPENGENI_SITE_BRIDGE_CANCEL;
|
|
141
|
+
version: typeof OPENGENI_SITE_BRIDGE_VERSION;
|
|
142
|
+
requestId: string;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export type OpenGeniSiteBridgeResponseMessage = {
|
|
146
|
+
type: typeof OPENGENI_SITE_BRIDGE_RESPONSE;
|
|
147
|
+
version: typeof OPENGENI_SITE_BRIDGE_VERSION;
|
|
148
|
+
requestId: string;
|
|
149
|
+
} & (
|
|
150
|
+
| {
|
|
151
|
+
ok: true;
|
|
152
|
+
value:
|
|
153
|
+
| OpenGeniSiteToolCatalog
|
|
154
|
+
| ToolGatewayCallResponse
|
|
155
|
+
| ToolGatewayDeclarationsResponse
|
|
156
|
+
| ToolGatewayResolvedTool
|
|
157
|
+
| ToolGatewayInvokeResponse
|
|
158
|
+
| OpenGeniSiteViewer
|
|
159
|
+
| OpenGeniSiteSignInResult;
|
|
160
|
+
}
|
|
161
|
+
| {
|
|
162
|
+
ok: false;
|
|
163
|
+
error: {
|
|
164
|
+
code: string;
|
|
165
|
+
message: string;
|
|
166
|
+
retryable: boolean;
|
|
167
|
+
outcomeUnknown?: boolean;
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
export type OpenGeniSiteClient = {
|
|
173
|
+
/** Ordinary SDK, including React session/composer methods and SSE streams. */
|
|
174
|
+
readonly client: OpenGeniClient;
|
|
175
|
+
/** Inline AI over ordinary Site sessions. No conversation component is required. */
|
|
176
|
+
readonly ai: OpenGeniSiteAi;
|
|
177
|
+
/** Host-bound routing alias, not a credential or a caller-selected tenant. */
|
|
178
|
+
readonly workspaceId: string;
|
|
179
|
+
/** Workspace-bound typed facade. The parent host owns workspace identity and credentials. */
|
|
180
|
+
readonly tools: OpenGeniSiteWorkspaceTools;
|
|
181
|
+
/** Persistent collections of JSON records that belong to this published Site. */
|
|
182
|
+
readonly data: OpenGeniSiteData;
|
|
183
|
+
/** Files stored with this published Site. */
|
|
184
|
+
readonly files: OpenGeniSiteFiles;
|
|
185
|
+
/**
|
|
186
|
+
* The Site's optional SQL database: run the named queries its owner
|
|
187
|
+
* defined (`await site.sql.run("top_votes", { limit: 10 })`). Served by
|
|
188
|
+
* hosts bound to a published Site, like `data`; the owner turns it on.
|
|
189
|
+
*/
|
|
190
|
+
readonly sql: OpenGeniSiteSql;
|
|
191
|
+
/**
|
|
192
|
+
* The person viewing this Site, resolved once from the host and cached for
|
|
193
|
+
* the life of the client (`await site.viewer`). In the console this is the
|
|
194
|
+
* signed-in workspace member; on a published Site it is the signed-in
|
|
195
|
+
* viewer or an anonymous guest (`kind: "guest"`, `signedIn: false`).
|
|
196
|
+
* Resolves `null` when the host does not report viewers (older hosts and
|
|
197
|
+
* local previews). A failed lookup is not cached; reading again retries.
|
|
198
|
+
*/
|
|
199
|
+
readonly viewer: Promise<OpenGeniSiteViewer | null>;
|
|
200
|
+
/**
|
|
201
|
+
* What this viewer may do on the published Site (`await site.capabilities`),
|
|
202
|
+
* resolved once and cached like `viewer`. Resolves `null` when the host
|
|
203
|
+
* does not report it (the console, older hosts and local previews): treat
|
|
204
|
+
* null as "everything the Site declares", as for a workspace member.
|
|
205
|
+
*/
|
|
206
|
+
readonly capabilities: Promise<OpenGeniSiteCapabilities | null>;
|
|
207
|
+
readonly auth: {
|
|
208
|
+
/**
|
|
209
|
+
* Ask the host to sign the viewer in. On a published Site the host
|
|
210
|
+
* navigates the whole page to sign-in and returns `{ started: true }`;
|
|
211
|
+
* the page reloads signed in afterwards. Resolves `{ started: false }`
|
|
212
|
+
* when the viewer is already signed in or the host has no sign-in.
|
|
213
|
+
*/
|
|
214
|
+
signIn(): Promise<OpenGeniSiteSignInResult>;
|
|
215
|
+
};
|
|
216
|
+
close(): void;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export type OpenGeniSiteClientOptions = {
|
|
220
|
+
/** Explicit compatibility with hosts predating targeted tool calls; never an automatic fallback. */
|
|
221
|
+
toolGatewayMode?: "catalog" | "target";
|
|
222
|
+
/** Advanced embedding seam; ordinary Sites accept bootstrap only from their exact parent. */
|
|
223
|
+
parentWindow?: MessageEventSource;
|
|
224
|
+
/** Advanced embedding seam for listening to the host's document-bound bootstrap message. */
|
|
225
|
+
siteWindow?: Pick<Window, "parent" | "addEventListener" | "removeEventListener">;
|
|
226
|
+
/** Test/embedding seam for an already document-bound host bootstrap port. */
|
|
227
|
+
bootstrapPort?: MessagePort;
|
|
228
|
+
connectTimeoutMs?: number;
|
|
229
|
+
requestTimeoutMs?: number;
|
|
230
|
+
createMessageChannel?: () => MessageChannel;
|
|
231
|
+
/** Local-preview seam. Top-level pages use this same-origin Codemode proxy automatically. */
|
|
232
|
+
localCodemodePath?: string | false;
|
|
233
|
+
/** Test/embedding seam for local-preview requests. */
|
|
234
|
+
fetch?: typeof globalThis.fetch;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export class OpenGeniSiteBridgeError extends Error {
|
|
238
|
+
constructor(
|
|
239
|
+
readonly code: string,
|
|
240
|
+
message: string,
|
|
241
|
+
readonly retryable = false,
|
|
242
|
+
readonly outcomeUnknown = false,
|
|
243
|
+
) {
|
|
244
|
+
super(message);
|
|
245
|
+
this.name = "OpenGeniSiteBridgeError";
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Create the browser client used by a Site. Published iframes use the parent
|
|
251
|
+
* MessagePort; top-level sandbox previews use the same-origin Codemode adapter.
|
|
252
|
+
* Credentials remain in the selected host and never enter Site code.
|
|
253
|
+
*/
|
|
254
|
+
export function createOpenGeniSiteClient(
|
|
255
|
+
options: OpenGeniSiteClientOptions = {},
|
|
256
|
+
): OpenGeniSiteClient {
|
|
257
|
+
const transport = shouldUseLocalCodemode(options)
|
|
258
|
+
? new OpenGeniSiteLocalCodemodeTransport(options)
|
|
259
|
+
: new OpenGeniSiteBridgeTransport(options);
|
|
260
|
+
const siteData = createOpenGeniSiteData(
|
|
261
|
+
(path, init) => transport.fetch(`https://site.opengeni.invalid${path}`, init),
|
|
262
|
+
() => transport.siteDataSupported(),
|
|
263
|
+
);
|
|
264
|
+
const siteSql = createOpenGeniSiteSql(
|
|
265
|
+
(path, init) => transport.fetch(`https://site.opengeni.invalid${path}`, init),
|
|
266
|
+
() => transport.siteDataSupported(),
|
|
267
|
+
);
|
|
268
|
+
let viewer: Promise<OpenGeniSiteViewer | null> | null = null;
|
|
269
|
+
let capabilities: Promise<OpenGeniSiteCapabilities | null> | null = null;
|
|
270
|
+
const client = new OpenGeniClient({
|
|
271
|
+
baseUrl: "https://site.opengeni.invalid",
|
|
272
|
+
toolGatewayMode: options.toolGatewayMode ?? "target",
|
|
273
|
+
fetch: (input, init) => transport.fetch(input, init),
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
client,
|
|
277
|
+
ai: createOpenGeniSiteAi(client, SITE_WORKSPACE),
|
|
278
|
+
workspaceId: SITE_WORKSPACE,
|
|
279
|
+
tools: new OpenGeniToolsClient(transport).forWorkspace("site-host"),
|
|
280
|
+
data: siteData.data,
|
|
281
|
+
files: siteData.files,
|
|
282
|
+
sql: siteSql,
|
|
283
|
+
get viewer() {
|
|
284
|
+
if (!viewer) {
|
|
285
|
+
const pending = transport.viewer();
|
|
286
|
+
viewer = pending;
|
|
287
|
+
pending.catch(() => {
|
|
288
|
+
if (viewer === pending) viewer = null;
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
return viewer;
|
|
292
|
+
},
|
|
293
|
+
get capabilities() {
|
|
294
|
+
capabilities ??= readSiteCapabilities(transport);
|
|
295
|
+
return capabilities;
|
|
296
|
+
},
|
|
297
|
+
auth: { signIn: () => transport.signIn() },
|
|
298
|
+
close: () => transport.close(),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
class OpenGeniSiteLocalCodemodeTransport implements OpenGeniToolTransport {
|
|
303
|
+
readonly toolGatewayMode: "catalog" | "target";
|
|
304
|
+
private readonly fetchImpl: typeof globalThis.fetch;
|
|
305
|
+
private readonly basePath: string;
|
|
306
|
+
|
|
307
|
+
constructor(options: OpenGeniSiteClientOptions) {
|
|
308
|
+
this.toolGatewayMode = options.toolGatewayMode ?? "target";
|
|
309
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
310
|
+
const configured = options.localCodemodePath;
|
|
311
|
+
this.basePath = (
|
|
312
|
+
typeof configured === "string" ? configured : OPENGENI_SITE_LOCAL_CODEMODE_PATH
|
|
313
|
+
).replace(/\/+$/u, "");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
close(): void {}
|
|
317
|
+
|
|
318
|
+
/** Local previews have no published Site, so no Site Data. */
|
|
319
|
+
async siteDataSupported(): Promise<boolean> {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** Local previews run outside any host that knows the viewer. */
|
|
324
|
+
async viewer(): Promise<OpenGeniSiteViewer | null> {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async signIn(): Promise<OpenGeniSiteSignInResult> {
|
|
329
|
+
return { started: false };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
|
333
|
+
const request = new Request(input, init);
|
|
334
|
+
const url = new URL(request.url);
|
|
335
|
+
const path = `${url.pathname}${url.search}`;
|
|
336
|
+
siteSessionPath(path, SITE_WORKSPACE, request.method);
|
|
337
|
+
return this.fetchImpl(`${this.basePath}/sdk${path}`, {
|
|
338
|
+
method: request.method,
|
|
339
|
+
headers: request.headers,
|
|
340
|
+
signal: request.signal,
|
|
341
|
+
...(request.body ? { body: await request.text() } : {}),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async requestJson<T>(
|
|
346
|
+
method: string,
|
|
347
|
+
path: string,
|
|
348
|
+
body?: unknown,
|
|
349
|
+
_query?: Record<string, string>,
|
|
350
|
+
options: { signal?: AbortSignal } = {},
|
|
351
|
+
): Promise<T> {
|
|
352
|
+
const suffix = localCodemodeSuffix(method, path);
|
|
353
|
+
const response = await this.fetchImpl(`${this.basePath}${suffix}`, {
|
|
354
|
+
method,
|
|
355
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
356
|
+
...(body === undefined
|
|
357
|
+
? {}
|
|
358
|
+
: {
|
|
359
|
+
headers: { "content-type": "application/json" },
|
|
360
|
+
body: JSON.stringify(body),
|
|
361
|
+
}),
|
|
362
|
+
});
|
|
363
|
+
const value = (await response.json()) as unknown;
|
|
364
|
+
if (response.ok) return value as T;
|
|
365
|
+
const envelope = isRecord(value) && isRecord(value.error) ? value.error : {};
|
|
366
|
+
throw new OpenGeniSiteBridgeError(
|
|
367
|
+
typeof envelope.code === "string" ? envelope.code : "local_codemode_error",
|
|
368
|
+
typeof envelope.message === "string"
|
|
369
|
+
? envelope.message
|
|
370
|
+
: `Local Site tool request failed with HTTP ${response.status}`,
|
|
371
|
+
envelope.retryable === true,
|
|
372
|
+
envelope.outcomeUnknown === true,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function shouldUseLocalCodemode(options: OpenGeniSiteClientOptions): boolean {
|
|
378
|
+
if (options.localCodemodePath === false || options.bootstrapPort) return false;
|
|
379
|
+
if (typeof options.localCodemodePath === "string") return true;
|
|
380
|
+
const siteWindow = options.siteWindow ?? globalThis.window;
|
|
381
|
+
return Boolean(siteWindow && siteWindow.parent === siteWindow);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function localCodemodeSuffix(method: string, path: string): string {
|
|
385
|
+
if (method === "GET" && path.endsWith("/tools/catalog")) return "/catalog";
|
|
386
|
+
if (method === "GET" && path.endsWith("/tools/declarations")) return "/declarations";
|
|
387
|
+
if (method === "POST" && path.endsWith("/tools/calls")) return "/calls";
|
|
388
|
+
if (method === "POST" && path.endsWith("/tools/resolve")) return "/resolve";
|
|
389
|
+
if (method === "POST" && path.endsWith("/tools/invoke")) return "/invoke";
|
|
390
|
+
throw new OpenGeniSiteBridgeError("unsupported_request", "Unsupported Site tool request");
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function isOpenGeniSiteBridgeConnectMessage(
|
|
394
|
+
value: unknown,
|
|
395
|
+
): value is OpenGeniSiteBridgeConnectMessage {
|
|
396
|
+
return (
|
|
397
|
+
isRecord(value) &&
|
|
398
|
+
value.type === OPENGENI_SITE_BRIDGE_CONNECT &&
|
|
399
|
+
value.version === OPENGENI_SITE_BRIDGE_VERSION
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export function isOpenGeniSiteBridgeRequestMessage(
|
|
404
|
+
value: unknown,
|
|
405
|
+
): value is OpenGeniSiteBridgeRequestMessage {
|
|
406
|
+
if (
|
|
407
|
+
!isRecord(value) ||
|
|
408
|
+
value.type !== OPENGENI_SITE_BRIDGE_REQUEST ||
|
|
409
|
+
value.version !== OPENGENI_SITE_BRIDGE_VERSION ||
|
|
410
|
+
typeof value.requestId !== "string" ||
|
|
411
|
+
!value.requestId
|
|
412
|
+
) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
if (
|
|
416
|
+
value.method === "catalog" ||
|
|
417
|
+
value.method === "declarations" ||
|
|
418
|
+
value.method === OPENGENI_SITE_BRIDGE_VIEWER_METHOD ||
|
|
419
|
+
value.method === OPENGENI_SITE_BRIDGE_SIGN_IN_METHOD
|
|
420
|
+
)
|
|
421
|
+
return true;
|
|
422
|
+
return ["call", "resolve", "invoke"].includes(String(value.method)) && isRecord(value.payload);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export function isOpenGeniSiteBridgeCancelMessage(
|
|
426
|
+
value: unknown,
|
|
427
|
+
): value is OpenGeniSiteBridgeCancelMessage {
|
|
428
|
+
return (
|
|
429
|
+
isRecord(value) &&
|
|
430
|
+
value.type === OPENGENI_SITE_BRIDGE_CANCEL &&
|
|
431
|
+
value.version === OPENGENI_SITE_BRIDGE_VERSION &&
|
|
432
|
+
typeof value.requestId === "string" &&
|
|
433
|
+
Boolean(value.requestId)
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** Strip every host-only field before a call crosses the Site boundary. */
|
|
438
|
+
export function sanitizeOpenGeniSiteToolCallRequest(
|
|
439
|
+
value: ToolGatewayCallRequest,
|
|
440
|
+
): OpenGeniSiteToolCallRequest {
|
|
441
|
+
return {
|
|
442
|
+
...(value.operationId === undefined ? {} : { operationId: value.operationId }),
|
|
443
|
+
catalogDigest: value.catalogDigest,
|
|
444
|
+
identity: value.identity,
|
|
445
|
+
arguments: value.arguments,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function sanitizeOpenGeniSiteToolInvokeRequest(
|
|
450
|
+
value: ToolGatewayInvokeRequest,
|
|
451
|
+
): ToolGatewayInvokeRequest {
|
|
452
|
+
return {
|
|
453
|
+
target: value.target,
|
|
454
|
+
operationId: value.operationId,
|
|
455
|
+
arguments: value.arguments,
|
|
456
|
+
...(value.expectedDefinitionDigest === undefined
|
|
457
|
+
? {}
|
|
458
|
+
: { expectedDefinitionDigest: value.expectedDefinitionDigest }),
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
class OpenGeniSiteBridgeTransport implements OpenGeniToolTransport {
|
|
463
|
+
readonly toolGatewayMode: "catalog" | "target";
|
|
464
|
+
private targetTools = false;
|
|
465
|
+
private siteData = false;
|
|
466
|
+
private viewerSupported = false;
|
|
467
|
+
private readonly options: OpenGeniSiteClientOptions;
|
|
468
|
+
private readonly bootstrap: SiteBridgeBootstrap;
|
|
469
|
+
private port: MessagePort | null = null;
|
|
470
|
+
private connecting: Promise<MessagePort> | null = null;
|
|
471
|
+
private closed = false;
|
|
472
|
+
|
|
473
|
+
constructor(options: OpenGeniSiteClientOptions) {
|
|
474
|
+
this.toolGatewayMode = options.toolGatewayMode ?? "target";
|
|
475
|
+
this.options = options;
|
|
476
|
+
this.bootstrap = createSiteBridgeBootstrap(options);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
|
480
|
+
return sitePortFetch(await this.connect(), input, init);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
async siteDataSupported(): Promise<boolean> {
|
|
484
|
+
await this.connect();
|
|
485
|
+
return this.siteData;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
async viewer(): Promise<OpenGeniSiteViewer | null> {
|
|
489
|
+
await this.connect();
|
|
490
|
+
if (!this.viewerSupported) return null;
|
|
491
|
+
const value = await this.request({ method: OPENGENI_SITE_BRIDGE_VIEWER_METHOD }, {});
|
|
492
|
+
if (!isOpenGeniSiteViewer(value))
|
|
493
|
+
throw new OpenGeniSiteBridgeError(
|
|
494
|
+
"invalid_viewer",
|
|
495
|
+
"The Site host returned an invalid viewer",
|
|
496
|
+
);
|
|
497
|
+
return value;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
async signIn(): Promise<OpenGeniSiteSignInResult> {
|
|
501
|
+
await this.connect();
|
|
502
|
+
if (!this.viewerSupported) return { started: false };
|
|
503
|
+
const value = await this.request({ method: OPENGENI_SITE_BRIDGE_SIGN_IN_METHOD }, {});
|
|
504
|
+
return { started: isRecord(value) && value.started === true };
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async requestJson<T>(
|
|
508
|
+
method: string,
|
|
509
|
+
path: string,
|
|
510
|
+
body?: unknown,
|
|
511
|
+
_query?: Record<string, string>,
|
|
512
|
+
requestOptions: { signal?: AbortSignal; timeoutMs?: number } = {},
|
|
513
|
+
): Promise<T> {
|
|
514
|
+
if (method === "POST" && (path.endsWith("/tools/resolve") || path.endsWith("/tools/invoke"))) {
|
|
515
|
+
await this.connect();
|
|
516
|
+
if (!this.targetTools)
|
|
517
|
+
throw new OpenGeniSiteBridgeError(
|
|
518
|
+
"target_tools_unsupported",
|
|
519
|
+
"Upgrade the Site host to use targeted tools",
|
|
520
|
+
);
|
|
521
|
+
if (!isRecord(body)) throw new TypeError("Site target payload is required");
|
|
522
|
+
const request = body as ToolGatewayInvokeRequest;
|
|
523
|
+
return (await this.request(
|
|
524
|
+
path.endsWith("/tools/resolve")
|
|
525
|
+
? { method: "resolve", payload: { target: request.target } }
|
|
526
|
+
: { method: "invoke", payload: sanitizeOpenGeniSiteToolInvokeRequest(request) },
|
|
527
|
+
requestOptions,
|
|
528
|
+
)) as T;
|
|
529
|
+
}
|
|
530
|
+
if (method === "GET" && path.endsWith("/tools/catalog")) {
|
|
531
|
+
return (await this.request({ method: "catalog" }, requestOptions)) as T;
|
|
532
|
+
}
|
|
533
|
+
if (method === "GET" && path.endsWith("/tools/declarations")) {
|
|
534
|
+
return (await this.request({ method: "declarations" }, requestOptions)) as T;
|
|
535
|
+
}
|
|
536
|
+
if (method === "POST" && path.endsWith("/tools/calls")) {
|
|
537
|
+
if (!isRecord(body)) throw new TypeError("Site tool call payload is required");
|
|
538
|
+
return (await this.request(
|
|
539
|
+
{
|
|
540
|
+
method: "call",
|
|
541
|
+
payload: sanitizeOpenGeniSiteToolCallRequest(body as ToolGatewayCallRequest),
|
|
542
|
+
},
|
|
543
|
+
requestOptions,
|
|
544
|
+
)) as T;
|
|
545
|
+
}
|
|
546
|
+
throw new OpenGeniSiteBridgeError("unsupported_request", "Unsupported Site bridge request");
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
close(): void {
|
|
550
|
+
if (this.closed) return;
|
|
551
|
+
this.closed = true;
|
|
552
|
+
this.bootstrap.close();
|
|
553
|
+
this.port?.close();
|
|
554
|
+
this.port = null;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
private async request(
|
|
558
|
+
request:
|
|
559
|
+
| Omit<
|
|
560
|
+
Extract<OpenGeniSiteBridgeRequestMessage, { method: "resolve" }>,
|
|
561
|
+
"type" | "version" | "requestId"
|
|
562
|
+
>
|
|
563
|
+
| Omit<
|
|
564
|
+
Extract<OpenGeniSiteBridgeRequestMessage, { method: "invoke" }>,
|
|
565
|
+
"type" | "version" | "requestId"
|
|
566
|
+
>
|
|
567
|
+
| { method: "catalog" }
|
|
568
|
+
| { method: "call"; payload: OpenGeniSiteToolCallRequest }
|
|
569
|
+
| { method: "declarations" }
|
|
570
|
+
| { method: typeof OPENGENI_SITE_BRIDGE_VIEWER_METHOD }
|
|
571
|
+
| { method: typeof OPENGENI_SITE_BRIDGE_SIGN_IN_METHOD },
|
|
572
|
+
options: { signal?: AbortSignal; timeoutMs?: number },
|
|
573
|
+
): Promise<unknown> {
|
|
574
|
+
const port = await this.connect();
|
|
575
|
+
const requestId = crypto.randomUUID();
|
|
576
|
+
const timeoutMs = positiveTimeout(
|
|
577
|
+
options.timeoutMs ?? this.options.requestTimeoutMs ?? 120_000,
|
|
578
|
+
"requestTimeoutMs",
|
|
579
|
+
);
|
|
580
|
+
return await new Promise((resolve, reject) => {
|
|
581
|
+
const timeout = setTimeout(() => {
|
|
582
|
+
cleanup();
|
|
583
|
+
cancel(port, requestId);
|
|
584
|
+
reject(
|
|
585
|
+
request.method === "call" || request.method === "invoke"
|
|
586
|
+
? new OpenGeniSiteBridgeError(
|
|
587
|
+
"timeout",
|
|
588
|
+
"Site tool request timed out after execution may have started",
|
|
589
|
+
false,
|
|
590
|
+
true,
|
|
591
|
+
)
|
|
592
|
+
: new OpenGeniSiteBridgeError("timeout", "Site tool request timed out", true),
|
|
593
|
+
);
|
|
594
|
+
}, timeoutMs);
|
|
595
|
+
const abort = () => {
|
|
596
|
+
cleanup();
|
|
597
|
+
cancel(port, requestId);
|
|
598
|
+
reject(options.signal?.reason ?? new DOMException("Request aborted", "AbortError"));
|
|
599
|
+
};
|
|
600
|
+
const onMessage = (event: MessageEvent<unknown>) => {
|
|
601
|
+
const response = event.data;
|
|
602
|
+
if (
|
|
603
|
+
!isRecord(response) ||
|
|
604
|
+
response.type !== OPENGENI_SITE_BRIDGE_RESPONSE ||
|
|
605
|
+
response.version !== OPENGENI_SITE_BRIDGE_VERSION ||
|
|
606
|
+
response.requestId !== requestId ||
|
|
607
|
+
typeof response.ok !== "boolean"
|
|
608
|
+
) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
cleanup();
|
|
612
|
+
if (response.ok) {
|
|
613
|
+
resolve(response.value);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const error = isRecord(response.error) ? response.error : {};
|
|
617
|
+
reject(
|
|
618
|
+
new OpenGeniSiteBridgeError(
|
|
619
|
+
typeof error.code === "string" ? error.code : "bridge_error",
|
|
620
|
+
typeof error.message === "string" ? error.message : "Site tool request failed",
|
|
621
|
+
error.retryable === true,
|
|
622
|
+
error.outcomeUnknown === true,
|
|
623
|
+
),
|
|
624
|
+
);
|
|
625
|
+
};
|
|
626
|
+
const cleanup = () => {
|
|
627
|
+
clearTimeout(timeout);
|
|
628
|
+
options.signal?.removeEventListener("abort", abort);
|
|
629
|
+
port.removeEventListener("message", onMessage);
|
|
630
|
+
};
|
|
631
|
+
if (options.signal?.aborted) {
|
|
632
|
+
abort();
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
636
|
+
port.addEventListener("message", onMessage);
|
|
637
|
+
port.postMessage({
|
|
638
|
+
type: OPENGENI_SITE_BRIDGE_REQUEST,
|
|
639
|
+
version: OPENGENI_SITE_BRIDGE_VERSION,
|
|
640
|
+
requestId,
|
|
641
|
+
...request,
|
|
642
|
+
} satisfies OpenGeniSiteBridgeRequestMessage);
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
private async connect(): Promise<MessagePort> {
|
|
647
|
+
if (this.closed) throw new OpenGeniSiteBridgeError("closed", "Site bridge is closed");
|
|
648
|
+
if (this.port) return this.port;
|
|
649
|
+
if (this.connecting) return await this.connecting;
|
|
650
|
+
const channel = (this.options.createMessageChannel ?? (() => new MessageChannel()))();
|
|
651
|
+
const timeoutMs = positiveTimeout(this.options.connectTimeoutMs ?? 10_000, "connectTimeoutMs");
|
|
652
|
+
this.connecting = new Promise<MessagePort>((resolve, reject) => {
|
|
653
|
+
let active = true;
|
|
654
|
+
const timeout = setTimeout(() => {
|
|
655
|
+
cleanup();
|
|
656
|
+
channel.port1.close();
|
|
657
|
+
reject(new OpenGeniSiteBridgeError("host_timeout", "Opengeni Site host did not respond"));
|
|
658
|
+
}, timeoutMs);
|
|
659
|
+
const onMessage = (event: MessageEvent<unknown>) => {
|
|
660
|
+
const message = event.data;
|
|
661
|
+
if (
|
|
662
|
+
!isRecord(message) ||
|
|
663
|
+
message.type !== OPENGENI_SITE_BRIDGE_READY ||
|
|
664
|
+
message.version !== OPENGENI_SITE_BRIDGE_VERSION
|
|
665
|
+
) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
cleanup();
|
|
669
|
+
this.targetTools = message.targetTools === 1;
|
|
670
|
+
this.siteData = message.siteData === 1;
|
|
671
|
+
this.viewerSupported = message.viewer === 1;
|
|
672
|
+
this.port = channel.port1;
|
|
673
|
+
resolve(channel.port1);
|
|
674
|
+
};
|
|
675
|
+
const cleanup = () => {
|
|
676
|
+
active = false;
|
|
677
|
+
clearTimeout(timeout);
|
|
678
|
+
channel.port1.removeEventListener("message", onMessage);
|
|
679
|
+
};
|
|
680
|
+
channel.port1.addEventListener("message", onMessage);
|
|
681
|
+
channel.port1.start();
|
|
682
|
+
void this.bootstrap
|
|
683
|
+
.port()
|
|
684
|
+
.then((bootstrapPort) => {
|
|
685
|
+
if (!active) return;
|
|
686
|
+
if (this.closed) throw new OpenGeniSiteBridgeError("closed", "Site bridge is closed");
|
|
687
|
+
bootstrapPort.postMessage(
|
|
688
|
+
{
|
|
689
|
+
type: OPENGENI_SITE_BRIDGE_CONNECT,
|
|
690
|
+
version: OPENGENI_SITE_BRIDGE_VERSION,
|
|
691
|
+
} satisfies OpenGeniSiteBridgeConnectMessage,
|
|
692
|
+
[channel.port2],
|
|
693
|
+
);
|
|
694
|
+
})
|
|
695
|
+
.catch((error) => {
|
|
696
|
+
cleanup();
|
|
697
|
+
channel.port1.close();
|
|
698
|
+
reject(error);
|
|
699
|
+
});
|
|
700
|
+
}).finally(() => {
|
|
701
|
+
this.connecting = null;
|
|
702
|
+
});
|
|
703
|
+
return await this.connecting;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
type SiteBridgeBootstrap = {
|
|
708
|
+
port: () => Promise<MessagePort>;
|
|
709
|
+
close: () => void;
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
function createSiteBridgeBootstrap(options: OpenGeniSiteClientOptions): SiteBridgeBootstrap {
|
|
713
|
+
if (options.bootstrapPort) {
|
|
714
|
+
return {
|
|
715
|
+
port: async () => options.bootstrapPort!,
|
|
716
|
+
close: () => options.bootstrapPort?.close(),
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
const siteWindow = options.siteWindow ?? globalThis.window;
|
|
720
|
+
const parentWindow = options.parentWindow ?? siteWindow?.parent;
|
|
721
|
+
if (!siteWindow || !parentWindow || parentWindow === siteWindow) {
|
|
722
|
+
return {
|
|
723
|
+
port: async () => {
|
|
724
|
+
throw new OpenGeniSiteBridgeError(
|
|
725
|
+
"host_unavailable",
|
|
726
|
+
"Opengeni Site client must run inside a hosted iframe",
|
|
727
|
+
);
|
|
728
|
+
},
|
|
729
|
+
close: () => undefined,
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
const retainedPort = retainedSiteBridgeBootstrapPort(siteWindow);
|
|
733
|
+
if (retainedPort) {
|
|
734
|
+
return {
|
|
735
|
+
port: async () => retainedPort,
|
|
736
|
+
close: () => undefined,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
let settledPort: MessagePort | null = null;
|
|
740
|
+
let rejectBootstrap: ((reason?: unknown) => void) | null = null;
|
|
741
|
+
const onMessage = (event: MessageEvent<unknown>) => {
|
|
742
|
+
if (
|
|
743
|
+
event.source !== parentWindow ||
|
|
744
|
+
!isOpenGeniSiteBridgeReadyMessage(event.data) ||
|
|
745
|
+
event.ports.length !== 1
|
|
746
|
+
) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
cleanup();
|
|
750
|
+
settledPort = event.ports[0]!;
|
|
751
|
+
settledPort.start();
|
|
752
|
+
retainSiteBridgeBootstrapPort(siteWindow, settledPort);
|
|
753
|
+
resolveBootstrap?.(settledPort);
|
|
754
|
+
};
|
|
755
|
+
let resolveBootstrap: ((port: MessagePort) => void) | null = null;
|
|
756
|
+
const cleanup = () => siteWindow.removeEventListener("message", onMessage as EventListener);
|
|
757
|
+
const port = new Promise<MessagePort>((resolve, reject) => {
|
|
758
|
+
resolveBootstrap = resolve;
|
|
759
|
+
rejectBootstrap = reject;
|
|
760
|
+
siteWindow.addEventListener("message", onMessage as EventListener);
|
|
761
|
+
});
|
|
762
|
+
void port.catch(() => undefined);
|
|
763
|
+
return {
|
|
764
|
+
port: async () => await port,
|
|
765
|
+
close: () => {
|
|
766
|
+
cleanup();
|
|
767
|
+
rejectBootstrap?.(new OpenGeniSiteBridgeError("closed", "Site bridge is closed"));
|
|
768
|
+
resolveBootstrap = null;
|
|
769
|
+
rejectBootstrap = null;
|
|
770
|
+
},
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function retainedSiteBridgeBootstrapPort(
|
|
775
|
+
siteWindow: OpenGeniSiteClientOptions["siteWindow"],
|
|
776
|
+
): MessagePort | null {
|
|
777
|
+
const state = (siteWindow as unknown as Record<string, unknown>)[
|
|
778
|
+
OPENGENI_SITE_BRIDGE_BOOTSTRAP_GLOBAL
|
|
779
|
+
];
|
|
780
|
+
if (!isRecord(state)) return null;
|
|
781
|
+
const candidate = state.port as Partial<MessagePort> | undefined;
|
|
782
|
+
return candidate &&
|
|
783
|
+
typeof candidate.postMessage === "function" &&
|
|
784
|
+
typeof candidate.addEventListener === "function" &&
|
|
785
|
+
typeof candidate.start === "function"
|
|
786
|
+
? (candidate as MessagePort)
|
|
787
|
+
: null;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function retainSiteBridgeBootstrapPort(
|
|
791
|
+
siteWindow: OpenGeniSiteClientOptions["siteWindow"],
|
|
792
|
+
port: MessagePort,
|
|
793
|
+
): void {
|
|
794
|
+
try {
|
|
795
|
+
Object.defineProperty(siteWindow, OPENGENI_SITE_BRIDGE_BOOTSTRAP_GLOBAL, {
|
|
796
|
+
configurable: true,
|
|
797
|
+
value: { port },
|
|
798
|
+
});
|
|
799
|
+
} catch {
|
|
800
|
+
// The bootstrap event still serves the current client when the embedding
|
|
801
|
+
// window refuses extension; only late construction loses this fallback.
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function isOpenGeniSiteBridgeReadyMessage(value: unknown): value is OpenGeniSiteBridgeReadyMessage {
|
|
806
|
+
return (
|
|
807
|
+
isRecord(value) &&
|
|
808
|
+
value.type === OPENGENI_SITE_BRIDGE_READY &&
|
|
809
|
+
value.version === OPENGENI_SITE_BRIDGE_VERSION
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const SITE_VIEWER_KINDS: readonly string[] = ["member", "invited", "user", "guest"];
|
|
814
|
+
|
|
815
|
+
/** `GET /site/config` over the Site HTTP channel; null when the host does not serve it. */
|
|
816
|
+
async function readSiteCapabilities(transport: {
|
|
817
|
+
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
818
|
+
siteDataSupported(): Promise<boolean>;
|
|
819
|
+
}): Promise<OpenGeniSiteCapabilities | null> {
|
|
820
|
+
try {
|
|
821
|
+
if (!(await transport.siteDataSupported())) return null;
|
|
822
|
+
const response = await transport.fetch(
|
|
823
|
+
"https://site.opengeni.invalid/v1/workspaces/site-host/site/config",
|
|
824
|
+
{ method: "GET", headers: { accept: "application/json" } },
|
|
825
|
+
);
|
|
826
|
+
if (!response.ok) {
|
|
827
|
+
await response.body?.cancel();
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
const value = ((await response.json()) as { capabilities?: unknown } | null)?.capabilities;
|
|
831
|
+
return isOpenGeniSiteCapabilities(value) ? value : null;
|
|
832
|
+
} catch {
|
|
833
|
+
return null;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function isOpenGeniSiteCapabilities(value: unknown): value is OpenGeniSiteCapabilities {
|
|
838
|
+
return (
|
|
839
|
+
isRecord(value) &&
|
|
840
|
+
typeof value.agent === "boolean" &&
|
|
841
|
+
typeof value.workspaceTools === "boolean" &&
|
|
842
|
+
(value.siteData === "viewer" || value.siteData === "guest") &&
|
|
843
|
+
Array.isArray(value.sqlQueryAudiences) &&
|
|
844
|
+
value.sqlQueryAudiences.every(
|
|
845
|
+
(audience) => audience === "everyone" || audience === "signed_in",
|
|
846
|
+
) &&
|
|
847
|
+
(value.viewAs === null || value.viewAs === "invited" || value.viewAs === "public")
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function isOpenGeniSiteViewer(value: unknown): value is OpenGeniSiteViewer {
|
|
852
|
+
return (
|
|
853
|
+
isRecord(value) &&
|
|
854
|
+
SITE_VIEWER_KINDS.includes(String(value.kind)) &&
|
|
855
|
+
typeof value.id === "string" &&
|
|
856
|
+
value.id.length > 0 &&
|
|
857
|
+
(value.name === null || typeof value.name === "string") &&
|
|
858
|
+
(value.email === null || typeof value.email === "string") &&
|
|
859
|
+
typeof value.signedIn === "boolean"
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function cancel(port: MessagePort, requestId: string): void {
|
|
864
|
+
port.postMessage({
|
|
865
|
+
type: OPENGENI_SITE_BRIDGE_CANCEL,
|
|
866
|
+
version: OPENGENI_SITE_BRIDGE_VERSION,
|
|
867
|
+
requestId,
|
|
868
|
+
} satisfies OpenGeniSiteBridgeCancelMessage);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function positiveTimeout(value: number, label: string): number {
|
|
872
|
+
if (!Number.isFinite(value) || value <= 0) throw new RangeError(`${label} must be positive`);
|
|
873
|
+
return value;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function isRecord(value: unknown): value is Record<string, any> {
|
|
877
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
878
|
+
}
|