@m6d/cortex-angular 1.3.0 → 1.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 +18 -11
- package/fesm2022/m6d-cortex-angular.mjs +19 -4
- package/package.json +1 -1
- package/types/m6d-cortex-angular.d.ts +10 -0
package/README.md
CHANGED
|
@@ -27,16 +27,17 @@ No root-level providers needed — each widget instance is fully self-contained.
|
|
|
27
27
|
|
|
28
28
|
### Configuration
|
|
29
29
|
|
|
30
|
-
| Property
|
|
31
|
-
|
|
|
32
|
-
| `locale`
|
|
33
|
-
| `transport.baseUrl`
|
|
34
|
-
| `transport.getHeaders`
|
|
35
|
-
| `wsUrl`
|
|
36
|
-
| `theme`
|
|
37
|
-
| `showDebugButton`
|
|
38
|
-
| `toolComponents`
|
|
39
|
-
| `onToolCall`
|
|
30
|
+
| Property | Type | Description |
|
|
31
|
+
| ----------------------- | ------------------------------- | ------------------------------------------------------------------------------- |
|
|
32
|
+
| `locale` | `Signal<string>` | Active locale (`"en"`, `"ar"`) |
|
|
33
|
+
| `transport.baseUrl` | `string \| Signal<string>` | Agent API base URL |
|
|
34
|
+
| `transport.getHeaders` | `() => Record<string, string>` | Request headers provider |
|
|
35
|
+
| `wsUrl` | `string` | WebSocket URL for real-time events (optional) |
|
|
36
|
+
| `theme` | `CortexClientTheme` | `'light' \| 'dark' \| 'system'` (default: `'light'`) |
|
|
37
|
+
| `showDebugButton` | `boolean` | Show/hide the debug toggle button (default: `false`) |
|
|
38
|
+
| `toolComponents` | `Record<string, Type<unknown>>` | Custom Angular components for tool rendering (optional) |
|
|
39
|
+
| `onToolCall` | `(toolCall) => unknown` | Client-side tool call handler (optional) |
|
|
40
|
+
| `trustSameOriginEmbeds` | `boolean` | Allow embedded tool pages served from this page's own origin (default: `false`) |
|
|
40
41
|
|
|
41
42
|
`transport.getHeaders` can return any request headers needed by the host app. WebSocket auth continues to derive the `token` query param from a bearer `Authorization` header when present.
|
|
42
43
|
|
|
@@ -48,7 +49,13 @@ initiate, shows the returned page in a sandboxed iframe (inline card or
|
|
|
48
49
|
modal, per tool config), and submits the page's `cortex:client-tool`
|
|
49
50
|
postMessage outcome back to the run as-is — anything that must actually be
|
|
50
51
|
true lives in the integrating backend's own records. No widget configuration
|
|
51
|
-
is needed
|
|
52
|
+
is needed. One caveat: an embed served from the host page's **own origin** is
|
|
53
|
+
refused by default, because a same-origin iframe cannot be sandboxed — it
|
|
54
|
+
could reach into the host page, and embed URLs come from Control Center tool
|
|
55
|
+
config, whose administrator is not necessarily the site owner. When both are
|
|
56
|
+
the same party and the embeds are the site's own pages, set
|
|
57
|
+
`trustSameOriginEmbeds: true` to render them anyway (with full access, as any
|
|
58
|
+
of the site's own pages has). Other pending tool calls keep the existing `toolComponents` /
|
|
52
59
|
`hooks.onToolCall` behavior. That hook is also how static client tools work:
|
|
53
60
|
declare an executor-less tool in the server config and answer it here by
|
|
54
61
|
returning its output from `hooks.onToolCall` for that tool name. Consumer `hooks.onToolCall` implementations must
|
|
@@ -483,14 +483,22 @@ function parseClientToolHandshake(data, origin, embedOrigin) {
|
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
// src/internal/client/src/client-tools.ts
|
|
486
|
-
function embedSandbox(embedUrl, pageOrigin) {
|
|
486
|
+
function embedSandbox(embedUrl, pageOrigin, trustSameOrigin) {
|
|
487
487
|
const base = "allow-scripts allow-forms allow-popups";
|
|
488
488
|
try {
|
|
489
|
-
|
|
489
|
+
const sameOrigin = new URL(embedUrl).origin === pageOrigin;
|
|
490
|
+
return sameOrigin && !trustSameOrigin ? base : `${base} allow-same-origin`;
|
|
490
491
|
} catch {
|
|
491
492
|
return base;
|
|
492
493
|
}
|
|
493
494
|
}
|
|
495
|
+
function embedTrustedOnHostOrigin(embedUrl, pageOrigin, trustSameOrigin) {
|
|
496
|
+
try {
|
|
497
|
+
return trustSameOrigin === true && new URL(embedUrl).origin === pageOrigin;
|
|
498
|
+
} catch {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
494
502
|
function frameBreachedHostOrigin(frame, pageOrigin) {
|
|
495
503
|
try {
|
|
496
504
|
return frame.contentWindow?.location.origin === pageOrigin;
|
|
@@ -3464,8 +3472,10 @@ class ClientToolEmbedComponent {
|
|
|
3464
3472
|
}, ...ngDevMode ? [{ debugName: "isModal" }] : []);
|
|
3465
3473
|
sandbox = computed(() => {
|
|
3466
3474
|
const state = this.state();
|
|
3467
|
-
return state.phase === "embedded" ? embedSandbox(state.embedUrl, window.location.origin) : "";
|
|
3475
|
+
return state.phase === "embedded" ? embedSandbox(state.embedUrl, window.location.origin, this.trustSameOriginEmbeds) : "";
|
|
3468
3476
|
}, ...ngDevMode ? [{ debugName: "sandbox" }] : []);
|
|
3477
|
+
config = inject(CORTEX_CLIENT_CONFIG);
|
|
3478
|
+
trustSameOriginEmbeds;
|
|
3469
3479
|
api = inject(CortexApiClient);
|
|
3470
3480
|
chatService = inject(CortexChatService);
|
|
3471
3481
|
sanitizer = inject(DomSanitizer);
|
|
@@ -3486,6 +3496,7 @@ class ClientToolEmbedComponent {
|
|
|
3486
3496
|
});
|
|
3487
3497
|
}
|
|
3488
3498
|
ngOnInit() {
|
|
3499
|
+
this.trustSameOriginEmbeds = this.config().trustSameOriginEmbeds;
|
|
3489
3500
|
const threadId = this.chatService.selectedThread()?.id;
|
|
3490
3501
|
const part = this.toolCallPart();
|
|
3491
3502
|
if (!threadId) {
|
|
@@ -3516,7 +3527,11 @@ class ClientToolEmbedComponent {
|
|
|
3516
3527
|
const frame = event.target;
|
|
3517
3528
|
if (!(frame instanceof HTMLIFrameElement))
|
|
3518
3529
|
return;
|
|
3519
|
-
|
|
3530
|
+
const state = this.state();
|
|
3531
|
+
if (state.phase !== "embedded")
|
|
3532
|
+
return;
|
|
3533
|
+
const trusted = embedTrustedOnHostOrigin(state.embedUrl, window.location.origin, this.trustSameOriginEmbeds);
|
|
3534
|
+
if (!trusted && frameBreachedHostOrigin(frame, window.location.origin)) {
|
|
3520
3535
|
this.embed?.terminate();
|
|
3521
3536
|
}
|
|
3522
3537
|
}
|
package/package.json
CHANGED
|
@@ -106,6 +106,16 @@ export type CortexConfig = {
|
|
|
106
106
|
/** Color scheme; `'system'` follows the OS preference. Defaults to `'light'`. */
|
|
107
107
|
theme?: CortexTheme;
|
|
108
108
|
showDebugButton?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Render embedded tool pages served from this page's own origin with their
|
|
111
|
+
* full identity — which, same-origin, means no effective sandbox at all: such
|
|
112
|
+
* an embed can reach into the host page. By default they are refused
|
|
113
|
+
* (rendered inert), because embed URLs come from Control Center tool config,
|
|
114
|
+
* and whoever administers that is not necessarily whoever owns this site.
|
|
115
|
+
* Enable only when both are the same party and the embeds are this site's
|
|
116
|
+
* own pages — then a second origin just for embeds is no longer needed.
|
|
117
|
+
*/
|
|
118
|
+
trustSameOriginEmbeds?: boolean;
|
|
109
119
|
hooks?: CortexHooks;
|
|
110
120
|
};
|
|
111
121
|
export type QueuedAttachment = {
|