@agent-native/core 0.221.0 → 0.221.1-nightly-20261028032829
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/agent-chat.js +6 -1
- package/dist/client/api-path.d.ts +2 -0
- package/dist/client/api-path.js +76 -55
- package/dist/client/application-state.js +12 -6
- package/dist/client/browser-tab-id.d.ts +2 -0
- package/dist/client/browser-tab-id.js +113 -13
- package/dist/client/collab/index.d.ts +1 -1
- package/dist/client/embed-auth.js +145 -26
- package/dist/client/route-state.js +15 -4
- package/dist/client/sse-event-processor.js +3 -0
- package/dist/client/use-change-version.js +6 -1
- package/dist/client/use-db-sync.js +70 -11
- package/dist/collab/client.d.ts +34 -0
- package/dist/collab/client.js +506 -53
- package/dist/deploy/build.js +10 -0
- package/dist/mcp/embed-app.js +165 -16
- package/dist/provider-api/actions/custom-provider-registration.d.ts +3 -3
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/server/app-origin-config.d.ts +2 -0
- package/dist/server/app-origin-config.js +10 -0
- package/dist/shared/mcp-app-widget-embed.d.ts +23 -1
- package/dist/shared/mcp-app-widget-embed.js +178 -5
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +41 -18
- package/package.json +4 -4
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +41 -18
package/dist/deploy/build.js
CHANGED
|
@@ -1747,6 +1747,14 @@ function getAppOriginClientConfigScript() {
|
|
|
1747
1747
|
const workspaceAppPath =
|
|
1748
1748
|
workspaceAppMountConfig?.currentPath ??
|
|
1749
1749
|
(workspaceRuntime ? configuredWorkspaceAppPath : "");
|
|
1750
|
+
const explicitRootPath =
|
|
1751
|
+
workspaceAppMountConfig?.currentPath === "/" ||
|
|
1752
|
+
(typeof appConfig.app.basePath === "string" &&
|
|
1753
|
+
appConfig.app.basePath.trim().length > 0 &&
|
|
1754
|
+
normalizeAppBasePath(appConfig.app.basePath) === "");
|
|
1755
|
+
const frameworkRoutePrefix = (
|
|
1756
|
+
process.env.AGENT_NATIVE_CONFIG_RUNTIME_FRAMEWORK_ROUTE_PREFIX || ""
|
|
1757
|
+
).trim();
|
|
1750
1758
|
const config = {
|
|
1751
1759
|
...(appConfig.app.id ? { appId: appConfig.app.id } : {}),
|
|
1752
1760
|
...(appConfig.app.workspaceId
|
|
@@ -1764,6 +1772,8 @@ function getAppOriginClientConfigScript() {
|
|
|
1764
1772
|
...(workspaceAppMountConfig?.paths
|
|
1765
1773
|
? { workspaceAppMountPaths: workspaceAppMountConfig.paths }
|
|
1766
1774
|
: {}),
|
|
1775
|
+
...(explicitRootPath ? { explicitRootPath: true } : {}),
|
|
1776
|
+
...(frameworkRoutePrefix ? { frameworkRoutePrefix } : {}),
|
|
1767
1777
|
};
|
|
1768
1778
|
const toUnicodeEscape = (character) =>
|
|
1769
1779
|
String.fromCharCode(92) +
|
package/dist/mcp/embed-app.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from "../shared/embed-auth.js";
|
|
2
2
|
import { MCP_APP_HOST_FILL_ATTRIBUTE, MCP_APP_PANE_FILL_MAX_HEIGHT, } from "../shared/mcp-app-display.js";
|
|
3
|
+
import { EMBED_TOKEN_STORAGE_KEY, MCP_CHAT_BRIDGE_STORAGE_KEY, resolveMcpAppEmbedBasePath, } from "../shared/mcp-app-widget-embed.js";
|
|
3
4
|
import { EXTERNAL_EMBED_WINDOW_KEY } from "../shared/route-chunk-recovery-bootstrap.js";
|
|
4
5
|
import { HIDDEN_EMBED_URL } from "./embed-redaction.js";
|
|
5
6
|
const MCP_APP_IMPORT = "https://esm.sh/@modelcontextprotocol/ext-apps@1.7.5/app-with-deps";
|
|
@@ -656,7 +657,28 @@ export function embedApp(options = {}) {
|
|
|
656
657
|
);
|
|
657
658
|
}
|
|
658
659
|
|
|
659
|
-
function
|
|
660
|
+
function appOriginConfigForDocument(parsed) {
|
|
661
|
+
const script = parsed.querySelector("script[data-agent-native-app-origin-config]");
|
|
662
|
+
if (!script) return {};
|
|
663
|
+
const source = script.textContent || "";
|
|
664
|
+
const prefix = "window.__AGENT_NATIVE_CONFIG__=Object.assign({},window.__AGENT_NATIVE_CONFIG__,";
|
|
665
|
+
const suffix = ");";
|
|
666
|
+
if (!source.startsWith(prefix) || !source.endsWith(suffix)) {
|
|
667
|
+
throw new Error("Embedded app origin config is malformed.");
|
|
668
|
+
}
|
|
669
|
+
let appConfig;
|
|
670
|
+
try {
|
|
671
|
+
appConfig = JSON.parse(source.slice(prefix.length, -suffix.length));
|
|
672
|
+
} catch (_err) {
|
|
673
|
+
throw new Error("Embedded app origin config is not valid JSON.");
|
|
674
|
+
}
|
|
675
|
+
if (!appConfig || typeof appConfig !== "object" || Array.isArray(appConfig)) {
|
|
676
|
+
throw new Error("Embedded app origin config must be an object.");
|
|
677
|
+
}
|
|
678
|
+
return appConfig;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function embedConfigForAppUrl(appUrl, appConfig) {
|
|
660
682
|
const sanitizedTarget = localPathFromUrl(appUrl, false);
|
|
661
683
|
return {
|
|
662
684
|
origin: appUrl.origin,
|
|
@@ -667,7 +689,8 @@ export function embedApp(options = {}) {
|
|
|
667
689
|
chatBridgeActive: appUrl.searchParams.get(chatBridgeParam) === "1",
|
|
668
690
|
chatBridgeParam,
|
|
669
691
|
embedTokenParam: "__an_embed_token",
|
|
670
|
-
embedTargetHeader: "x-agent-native-embed-target"
|
|
692
|
+
embedTargetHeader: "x-agent-native-embed-target",
|
|
693
|
+
appConfig
|
|
671
694
|
};
|
|
672
695
|
}
|
|
673
696
|
|
|
@@ -736,27 +759,108 @@ export function embedApp(options = {}) {
|
|
|
736
759
|
window.history.replaceState(window.history.state, "", config.target);
|
|
737
760
|
}
|
|
738
761
|
} catch (_err) {}
|
|
762
|
+
// Keep transplanted auth storage on the same mount resolver as its reader.
|
|
763
|
+
var resolveMcpAppEmbedBasePath = ${resolveMcpAppEmbedBasePath.toString()};
|
|
764
|
+
function activeConfig() {
|
|
765
|
+
return window[${JSON.stringify(EXTERNAL_EMBED_WINDOW_KEY)}] || config;
|
|
766
|
+
}
|
|
767
|
+
function workspaceMountOptions(active) {
|
|
768
|
+
var appConfig = active.appConfig || {};
|
|
769
|
+
return {
|
|
770
|
+
workspaceRuntime: true,
|
|
771
|
+
routerBasePath: window.__reactRouterContext && window.__reactRouterContext.basename,
|
|
772
|
+
workspaceAppPath: appConfig.workspaceAppPath,
|
|
773
|
+
workspaceAppMountPaths: appConfig.workspaceAppMountPaths,
|
|
774
|
+
explicitRootPath: appConfig.explicitRootPath === true,
|
|
775
|
+
frameworkRoutePrefix: appConfig.frameworkRoutePrefix
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
function embedStorageKey(key) {
|
|
779
|
+
var active = activeConfig();
|
|
780
|
+
var appConfig = active.appConfig || {};
|
|
781
|
+
if (appConfig.workspaceRuntime !== true) return key;
|
|
782
|
+
var basePath = resolveMcpAppEmbedBasePath(
|
|
783
|
+
"external-target",
|
|
784
|
+
active.target || location.pathname,
|
|
785
|
+
workspaceMountOptions(active)
|
|
786
|
+
);
|
|
787
|
+
if (typeof basePath !== "string") return undefined;
|
|
788
|
+
return key + ":" + (basePath || "/");
|
|
789
|
+
}
|
|
739
790
|
try {
|
|
740
791
|
if (config.token) {
|
|
741
|
-
|
|
792
|
+
var tokenStorageKey = embedStorageKey(${JSON.stringify(EMBED_TOKEN_STORAGE_KEY)});
|
|
793
|
+
if (tokenStorageKey) sessionStorage.setItem(tokenStorageKey, config.token);
|
|
742
794
|
}
|
|
743
795
|
if (config.chatBridgeActive && config.token) {
|
|
744
|
-
|
|
796
|
+
var bridgeStorageKey = embedStorageKey(${JSON.stringify(MCP_CHAT_BRIDGE_STORAGE_KEY)});
|
|
797
|
+
if (bridgeStorageKey) sessionStorage.setItem(bridgeStorageKey, config.token);
|
|
745
798
|
}
|
|
746
799
|
} catch (_err) {}
|
|
747
800
|
if (window.__agentNativeExternalEmbedRuntimeInstalled) return;
|
|
748
801
|
window.__agentNativeExternalEmbedRuntimeInstalled = true;
|
|
749
802
|
function appOrigin() {
|
|
750
803
|
try {
|
|
751
|
-
return new URL(
|
|
804
|
+
return new URL(activeConfig().origin).origin;
|
|
752
805
|
} catch (_err) {
|
|
753
806
|
return "";
|
|
754
807
|
}
|
|
755
808
|
}
|
|
756
809
|
function targetPath() {
|
|
757
|
-
|
|
810
|
+
var active = activeConfig();
|
|
811
|
+
return active.target || location.pathname + location.search;
|
|
812
|
+
}
|
|
813
|
+
function authorizedTargetPath() {
|
|
814
|
+
var active = activeConfig();
|
|
815
|
+
var appConfig = active.appConfig || {};
|
|
816
|
+
var target = targetPath();
|
|
817
|
+
if (appConfig.workspaceRuntime !== true) return target;
|
|
818
|
+
var options = workspaceMountOptions(active);
|
|
819
|
+
var targetMount = resolveMcpAppEmbedBasePath(
|
|
820
|
+
"external-target",
|
|
821
|
+
active.target || location.pathname,
|
|
822
|
+
options
|
|
823
|
+
);
|
|
824
|
+
var currentMount = resolveMcpAppEmbedBasePath(
|
|
825
|
+
"workspace-path",
|
|
826
|
+
location.pathname,
|
|
827
|
+
Object.assign({}, options, { preferDeclaredMounts: true })
|
|
828
|
+
);
|
|
829
|
+
if (typeof targetMount !== "string" || targetMount !== currentMount) {
|
|
830
|
+
return null;
|
|
831
|
+
}
|
|
832
|
+
return target;
|
|
833
|
+
}
|
|
834
|
+
function requestBelongsToTargetMount(value, target) {
|
|
835
|
+
var active = activeConfig();
|
|
836
|
+
var appConfig = active.appConfig || {};
|
|
837
|
+
if (appConfig.workspaceRuntime !== true) return true;
|
|
838
|
+
var options = workspaceMountOptions(active);
|
|
839
|
+
var targetMount = resolveMcpAppEmbedBasePath(
|
|
840
|
+
"external-target",
|
|
841
|
+
target,
|
|
842
|
+
options
|
|
843
|
+
);
|
|
844
|
+
if (typeof targetMount !== "string") return false;
|
|
845
|
+
var parsedUrl;
|
|
846
|
+
try {
|
|
847
|
+
parsedUrl = { url: new URL(value, location.href) };
|
|
848
|
+
} catch (error) {
|
|
849
|
+
parsedUrl = { error: error };
|
|
850
|
+
}
|
|
851
|
+
if (!("url" in parsedUrl)) return false;
|
|
852
|
+
var url = parsedUrl.url;
|
|
853
|
+
const origin = appOrigin();
|
|
854
|
+
if (url.origin !== location.origin && url.origin !== origin) return false;
|
|
855
|
+
var requestMount = resolveMcpAppEmbedBasePath(
|
|
856
|
+
"workspace-path",
|
|
857
|
+
url.pathname,
|
|
858
|
+
Object.assign({}, options, { preferDeclaredMounts: true })
|
|
859
|
+
);
|
|
860
|
+
return typeof requestMount === "string" && requestMount === targetMount;
|
|
758
861
|
}
|
|
759
862
|
function rewrittenUrl(value, appendToken) {
|
|
863
|
+
var active = activeConfig();
|
|
760
864
|
const origin = appOrigin();
|
|
761
865
|
if (!origin) return null;
|
|
762
866
|
let url;
|
|
@@ -771,20 +875,49 @@ export function embedApp(options = {}) {
|
|
|
771
875
|
url.protocol = app.protocol;
|
|
772
876
|
url.host = app.host;
|
|
773
877
|
}
|
|
774
|
-
|
|
775
|
-
|
|
878
|
+
const appConfig = active.appConfig || {};
|
|
879
|
+
var frameworkRoutePrefix = appConfig.frameworkRoutePrefix || "/_agent-native";
|
|
880
|
+
if (frameworkRoutePrefix.endsWith("/")) {
|
|
881
|
+
frameworkRoutePrefix = frameworkRoutePrefix.slice(0, -1);
|
|
882
|
+
}
|
|
883
|
+
const eventsPath = frameworkRoutePrefix + "/events";
|
|
884
|
+
const authorizedTarget = authorizedTargetPath();
|
|
885
|
+
if (
|
|
886
|
+
appendToken &&
|
|
887
|
+
authorizedTarget !== null &&
|
|
888
|
+
requestBelongsToTargetMount(value, authorizedTarget) &&
|
|
889
|
+
active.token &&
|
|
890
|
+
url.pathname.endsWith(eventsPath)
|
|
891
|
+
) {
|
|
892
|
+
url.searchParams.set(active.embedTokenParam, active.token);
|
|
776
893
|
}
|
|
777
894
|
return url.toString();
|
|
778
895
|
}
|
|
779
896
|
function authHeaders(input, init) {
|
|
897
|
+
var active = activeConfig();
|
|
898
|
+
var authorizedTarget = authorizedTargetPath();
|
|
899
|
+
var requestUrl = input instanceof Request ? input.url : input;
|
|
900
|
+
if (
|
|
901
|
+
authorizedTarget !== null &&
|
|
902
|
+
!requestBelongsToTargetMount(requestUrl, authorizedTarget)
|
|
903
|
+
) {
|
|
904
|
+
authorizedTarget = null;
|
|
905
|
+
}
|
|
780
906
|
const headers = new Headers(
|
|
781
907
|
init && init.headers ? init.headers : input instanceof Request ? input.headers : undefined
|
|
782
908
|
);
|
|
783
|
-
if (
|
|
784
|
-
|
|
909
|
+
if (
|
|
910
|
+
authorizedTarget !== null &&
|
|
911
|
+
active.token &&
|
|
912
|
+
!headers.has("Authorization")
|
|
913
|
+
) {
|
|
914
|
+
headers.set("Authorization", "Bearer " + active.token);
|
|
785
915
|
}
|
|
786
|
-
if (
|
|
787
|
-
|
|
916
|
+
if (
|
|
917
|
+
authorizedTarget !== null &&
|
|
918
|
+
!headers.has(active.embedTargetHeader)
|
|
919
|
+
) {
|
|
920
|
+
headers.set(active.embedTargetHeader, authorizedTarget);
|
|
788
921
|
}
|
|
789
922
|
return headers;
|
|
790
923
|
}
|
|
@@ -810,6 +943,7 @@ export function embedApp(options = {}) {
|
|
|
810
943
|
XMLHttpRequest.prototype.open = function(method, url) {
|
|
811
944
|
const rewritten = rewrittenUrl(url, false);
|
|
812
945
|
this.__agentNativeExternalEmbed = !!rewritten;
|
|
946
|
+
this.__agentNativeExternalEmbedUrl = url;
|
|
813
947
|
return originalOpen.call(
|
|
814
948
|
this,
|
|
815
949
|
method,
|
|
@@ -822,8 +956,20 @@ export function embedApp(options = {}) {
|
|
|
822
956
|
XMLHttpRequest.prototype.send = function(body) {
|
|
823
957
|
if (this.__agentNativeExternalEmbed) {
|
|
824
958
|
try {
|
|
825
|
-
|
|
826
|
-
|
|
959
|
+
var active = activeConfig();
|
|
960
|
+
var authorizedTarget = authorizedTargetPath();
|
|
961
|
+
if (
|
|
962
|
+
authorizedTarget !== null &&
|
|
963
|
+
requestBelongsToTargetMount(
|
|
964
|
+
this.__agentNativeExternalEmbedUrl,
|
|
965
|
+
authorizedTarget
|
|
966
|
+
)
|
|
967
|
+
) {
|
|
968
|
+
if (active.token) {
|
|
969
|
+
this.setRequestHeader("Authorization", "Bearer " + active.token);
|
|
970
|
+
}
|
|
971
|
+
this.setRequestHeader(active.embedTargetHeader, authorizedTarget);
|
|
972
|
+
}
|
|
827
973
|
} catch (_err) {}
|
|
828
974
|
}
|
|
829
975
|
return originalSend.call(this, body);
|
|
@@ -1056,12 +1202,15 @@ export function embedApp(options = {}) {
|
|
|
1056
1202
|
}
|
|
1057
1203
|
|
|
1058
1204
|
async function mountTransplantedHtml(html, appUrl) {
|
|
1059
|
-
const config = embedConfigForAppUrl(appUrl);
|
|
1060
|
-
installExternalEmbedRuntime(config);
|
|
1061
1205
|
const parsed = new DOMParser().parseFromString(
|
|
1062
1206
|
rewriteRootRelativeHtmlUrls(removeHtmlCspMeta(html), appUrl.origin),
|
|
1063
1207
|
"text/html"
|
|
1064
1208
|
);
|
|
1209
|
+
const config = embedConfigForAppUrl(
|
|
1210
|
+
appUrl,
|
|
1211
|
+
appOriginConfigForDocument(parsed)
|
|
1212
|
+
);
|
|
1213
|
+
installExternalEmbedRuntime(config);
|
|
1065
1214
|
const scripts = Array.from(parsed.querySelectorAll("script"));
|
|
1066
1215
|
copyDocumentElementAttributes(parsed.documentElement);
|
|
1067
1216
|
importHeadChildrenWithBase(parsed.head, config.baseHref);
|
|
@@ -76,8 +76,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
78
|
id?: undefined;
|
|
79
|
-
deleted?: undefined;
|
|
80
79
|
provider?: undefined;
|
|
80
|
+
deleted?: undefined;
|
|
81
81
|
message?: undefined;
|
|
82
82
|
providers: {
|
|
83
83
|
id: string;
|
|
@@ -103,8 +103,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
103
103
|
registered?: undefined;
|
|
104
104
|
label?: undefined;
|
|
105
105
|
} | {
|
|
106
|
-
deleted?: undefined;
|
|
107
106
|
provider?: undefined;
|
|
107
|
+
deleted?: undefined;
|
|
108
108
|
count?: undefined;
|
|
109
109
|
message?: undefined;
|
|
110
110
|
providers?: undefined;
|
|
@@ -123,8 +123,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
123
123
|
registered?: undefined;
|
|
124
124
|
label?: undefined;
|
|
125
125
|
} | {
|
|
126
|
-
deleted?: undefined;
|
|
127
126
|
provider?: undefined;
|
|
127
|
+
deleted?: undefined;
|
|
128
128
|
count?: undefined;
|
|
129
129
|
providers?: undefined;
|
|
130
130
|
found?: undefined;
|
|
@@ -316,6 +316,7 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
316
316
|
notes?: string | undefined;
|
|
317
317
|
scope?: "org" | "user" | undefined;
|
|
318
318
|
}, {
|
|
319
|
+
message?: undefined;
|
|
319
320
|
deleted?: undefined;
|
|
320
321
|
id?: undefined;
|
|
321
322
|
providers: {
|
|
@@ -332,8 +333,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
332
333
|
found?: undefined;
|
|
333
334
|
registered?: undefined;
|
|
334
335
|
label?: undefined;
|
|
335
|
-
message?: undefined;
|
|
336
336
|
} | {
|
|
337
|
+
message?: undefined;
|
|
337
338
|
deleted?: undefined;
|
|
338
339
|
id?: undefined;
|
|
339
340
|
providers?: undefined;
|
|
@@ -342,8 +343,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
342
343
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
343
344
|
registered?: undefined;
|
|
344
345
|
label?: undefined;
|
|
345
|
-
message?: undefined;
|
|
346
346
|
} | {
|
|
347
|
+
message?: undefined;
|
|
347
348
|
deleted?: undefined;
|
|
348
349
|
providers?: undefined;
|
|
349
350
|
count?: undefined;
|
|
@@ -352,8 +353,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
352
353
|
id: string;
|
|
353
354
|
registered?: undefined;
|
|
354
355
|
label?: undefined;
|
|
355
|
-
message?: undefined;
|
|
356
356
|
} | {
|
|
357
|
+
message?: undefined;
|
|
357
358
|
providers?: undefined;
|
|
358
359
|
count?: undefined;
|
|
359
360
|
provider?: undefined;
|
|
@@ -362,7 +363,6 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
362
363
|
id: string;
|
|
363
364
|
registered?: undefined;
|
|
364
365
|
label?: undefined;
|
|
365
|
-
message?: undefined;
|
|
366
366
|
} | {
|
|
367
367
|
deleted?: undefined;
|
|
368
368
|
providers?: undefined;
|
|
@@ -8,5 +8,7 @@ export declare function resolvePublicAppOriginConfig(): {
|
|
|
8
8
|
workspaceOAuthOrigin?: string;
|
|
9
9
|
workspaceRuntime?: boolean;
|
|
10
10
|
workspaceAppMountPaths?: string[];
|
|
11
|
+
frameworkRoutePrefix?: string;
|
|
12
|
+
explicitRootPath?: boolean;
|
|
11
13
|
} | null;
|
|
12
14
|
export declare function getAppOriginClientConfigScript(): string | null;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getAppConfig, resolveAppHomePath } from "../app-config/index.js";
|
|
2
2
|
import { safeJsonForHtml } from "../shared/agent-readable-resource.js";
|
|
3
3
|
import { normalizeAppBasePath } from "./app-base-path.js";
|
|
4
|
+
import { FRAMEWORK_INTERNAL_ROUTE_PREFIX, getFrameworkRoutePrefix, } from "./framework-route-prefix.js";
|
|
4
5
|
function workspaceAppMountConfigFromJson(value, workspaceAppId) {
|
|
5
6
|
if (!value?.trim())
|
|
6
7
|
return undefined;
|
|
@@ -50,6 +51,7 @@ function workspaceAppMountConfigFromJson(value, workspaceAppId) {
|
|
|
50
51
|
}
|
|
51
52
|
export function resolvePublicAppOriginConfig() {
|
|
52
53
|
const config = getAppConfig();
|
|
54
|
+
const frameworkRoutePrefix = getFrameworkRoutePrefix();
|
|
53
55
|
const workspaceRuntime = config.workspace.isWorkspace === true ||
|
|
54
56
|
typeof config.workspace.appsJson === "string";
|
|
55
57
|
const workspaceAppMountConfig = workspaceAppMountConfigFromJson(config.workspace.appsJson, config.app.workspaceId);
|
|
@@ -58,6 +60,10 @@ export function resolvePublicAppOriginConfig() {
|
|
|
58
60
|
: undefined;
|
|
59
61
|
const workspaceAppPath = workspaceAppMountConfig?.currentPath ??
|
|
60
62
|
(workspaceRuntime ? configuredWorkspaceAppPath : undefined);
|
|
63
|
+
const explicitRootPath = workspaceAppMountConfig?.currentPath === "/" ||
|
|
64
|
+
(typeof config.app.basePath === "string" &&
|
|
65
|
+
config.app.basePath.trim().length > 0 &&
|
|
66
|
+
normalizeAppBasePath(config.app.basePath) === "");
|
|
61
67
|
const resolved = {
|
|
62
68
|
...(config.app.id ? { appId: config.app.id } : {}),
|
|
63
69
|
...(config.app.workspaceId
|
|
@@ -76,6 +82,10 @@ export function resolvePublicAppOriginConfig() {
|
|
|
76
82
|
...(workspaceAppMountConfig?.paths
|
|
77
83
|
? { workspaceAppMountPaths: workspaceAppMountConfig.paths }
|
|
78
84
|
: {}),
|
|
85
|
+
...(explicitRootPath ? { explicitRootPath: true } : {}),
|
|
86
|
+
...(frameworkRoutePrefix !== FRAMEWORK_INTERNAL_ROUTE_PREFIX
|
|
87
|
+
? { frameworkRoutePrefix }
|
|
88
|
+
: {}),
|
|
79
89
|
};
|
|
80
90
|
return Object.keys(resolved).length > 0 ? resolved : null;
|
|
81
91
|
}
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
export declare const EMBED_TOKEN_STORAGE_KEY = "agent-native:embed-auth-token";
|
|
2
2
|
export declare const MCP_CHAT_BRIDGE_STORAGE_KEY = "agent-native:mcp-chat-bridge";
|
|
3
|
+
export interface WorkspaceAppMountPathOptions {
|
|
4
|
+
explicitRootPath?: boolean;
|
|
5
|
+
fallbackPath?: string;
|
|
6
|
+
frameworkRoutePrefix?: string;
|
|
7
|
+
preferDeclaredMounts?: boolean;
|
|
8
|
+
routerBasePath?: unknown;
|
|
9
|
+
workspaceAppMountPaths?: unknown;
|
|
10
|
+
workspaceAppPath?: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Self-contained so its source can safely be embedded in browser scripts.
|
|
14
|
+
* Keep both paths here: a serialized function cannot close over module locals.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveMcpAppEmbedBasePath(mode: "workspace-path" | "external-target", value: unknown, options?: WorkspaceAppMountPathOptions & {
|
|
17
|
+
workspaceRuntime?: boolean;
|
|
18
|
+
}): string | undefined;
|
|
19
|
+
export declare function resolveWorkspaceAppMountPath(pathname: string, options?: WorkspaceAppMountPathOptions): string | undefined;
|
|
20
|
+
export declare function resolveExternalEmbedTargetBasePath(target: unknown, options?: WorkspaceAppMountPathOptions & {
|
|
21
|
+
workspaceRuntime?: boolean;
|
|
22
|
+
}): string | undefined;
|
|
23
|
+
export declare function embedTokenStorageKey(basePath: string | undefined, workspaceRuntime: boolean): string;
|
|
24
|
+
export declare function mcpChatBridgeStorageKey(basePath: string | undefined, workspaceRuntime: boolean): string;
|
|
3
25
|
/**
|
|
4
26
|
* Set on `<html>` for the life of a document that is an app nested in an MCP
|
|
5
27
|
* App widget (a ChatGPT, Codex, or Claude card or side pane). Layout reads it
|
|
@@ -15,4 +37,4 @@ export declare const MCP_APP_WIDGET_EMBED_ATTRIBUTE = "data-agent-native-mcp-wid
|
|
|
15
37
|
* embed credential in the URL, stored, or `embedded=1`); `mcp-app-host.spec.ts`
|
|
16
38
|
* runs both over one table so they cannot drift.
|
|
17
39
|
*/
|
|
18
|
-
export declare function getMcpAppWidgetEmbedBootScriptBody(): string;
|
|
40
|
+
export declare function getMcpAppWidgetEmbedBootScriptBody(basePath?: string, workspaceRuntime?: boolean): string;
|
|
@@ -1,7 +1,140 @@
|
|
|
1
1
|
import { safeJsonForHtml } from "./agent-readable-resource.js";
|
|
2
2
|
import { EMBED_MODE_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, MCP_APP_CHAT_BRIDGE_QUERY_PARAM, } from "./embed-auth.js";
|
|
3
|
+
import { EXTERNAL_EMBED_WINDOW_KEY } from "./route-chunk-recovery-bootstrap.js";
|
|
3
4
|
export const EMBED_TOKEN_STORAGE_KEY = "agent-native:embed-auth-token";
|
|
4
5
|
export const MCP_CHAT_BRIDGE_STORAGE_KEY = "agent-native:mcp-chat-bridge";
|
|
6
|
+
/**
|
|
7
|
+
* Self-contained so its source can safely be embedded in browser scripts.
|
|
8
|
+
* Keep both paths here: a serialized function cannot close over module locals.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveMcpAppEmbedBasePath(mode, value, options = {}) {
|
|
11
|
+
const resolveWorkspacePath = (pathname, options) => {
|
|
12
|
+
const normalize = (value) => {
|
|
13
|
+
if (typeof value !== "string")
|
|
14
|
+
return "";
|
|
15
|
+
const trimmed = value.trim();
|
|
16
|
+
if (!trimmed || trimmed === "/")
|
|
17
|
+
return "";
|
|
18
|
+
return `/${trimmed.replace(/^\/+/, "").replace(/\/+$/, "")}`;
|
|
19
|
+
};
|
|
20
|
+
const matches = (basePath) => Boolean(basePath) &&
|
|
21
|
+
(pathname === basePath || pathname.startsWith(`${basePath}/`));
|
|
22
|
+
const configuredPath = normalize(options.workspaceAppPath);
|
|
23
|
+
const explicitRootPath = options.explicitRootPath === true ||
|
|
24
|
+
(typeof options.workspaceAppPath === "string" &&
|
|
25
|
+
options.workspaceAppPath.trim().length > 0 &&
|
|
26
|
+
configuredPath === "");
|
|
27
|
+
const fallbackPath = normalize(options.fallbackPath);
|
|
28
|
+
const routerBasePath = normalize(options.routerBasePath);
|
|
29
|
+
const matchingHostPaths = [routerBasePath, configuredPath]
|
|
30
|
+
.filter((basePath) => basePath && matches(basePath))
|
|
31
|
+
.sort((left, right) => right.length - left.length);
|
|
32
|
+
const matchingHostPath = matchingHostPaths[0];
|
|
33
|
+
if (matchingHostPath && !options.preferDeclaredMounts) {
|
|
34
|
+
return matchingHostPath;
|
|
35
|
+
}
|
|
36
|
+
const declaredMounts = (Array.isArray(options.workspaceAppMountPaths)
|
|
37
|
+
? options.workspaceAppMountPaths
|
|
38
|
+
: [])
|
|
39
|
+
.filter((value) => typeof value === "string")
|
|
40
|
+
.map(normalize)
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
const matchingMount = declaredMounts
|
|
43
|
+
.filter((basePath) => matches(basePath))
|
|
44
|
+
.sort((left, right) => right.length - left.length)[0];
|
|
45
|
+
if (matchingHostPath && options.preferDeclaredMounts) {
|
|
46
|
+
return [matchingHostPath, matchingMount]
|
|
47
|
+
.filter((basePath) => Boolean(basePath))
|
|
48
|
+
.sort((left, right) => right.length - left.length)[0];
|
|
49
|
+
}
|
|
50
|
+
const segment = pathname.split("/").find(Boolean);
|
|
51
|
+
if (!segment) {
|
|
52
|
+
if (explicitRootPath)
|
|
53
|
+
return "";
|
|
54
|
+
return fallbackPath || undefined;
|
|
55
|
+
}
|
|
56
|
+
const frameworkRoutePrefix = normalize(options.frameworkRoutePrefix) || "/_agent-native";
|
|
57
|
+
if (`/${segment}` === "/_agent-native" ||
|
|
58
|
+
`/${segment}` === frameworkRoutePrefix ||
|
|
59
|
+
segment === "api") {
|
|
60
|
+
if (options.preferDeclaredMounts) {
|
|
61
|
+
return explicitRootPath ? "" : undefined;
|
|
62
|
+
}
|
|
63
|
+
return fallbackPath;
|
|
64
|
+
}
|
|
65
|
+
if (matchingMount)
|
|
66
|
+
return matchingMount;
|
|
67
|
+
if (explicitRootPath)
|
|
68
|
+
return "";
|
|
69
|
+
if (options.preferDeclaredMounts &&
|
|
70
|
+
Array.isArray(options.workspaceAppMountPaths)) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
return fallbackPath || undefined;
|
|
74
|
+
};
|
|
75
|
+
if (mode === "workspace-path") {
|
|
76
|
+
return typeof value === "string"
|
|
77
|
+
? resolveWorkspacePath(value, options)
|
|
78
|
+
: undefined;
|
|
79
|
+
}
|
|
80
|
+
if (typeof value !== "string" || !value.startsWith("/"))
|
|
81
|
+
return undefined;
|
|
82
|
+
const url = new URL(value, "http://agent-native.invalid");
|
|
83
|
+
const markers = [
|
|
84
|
+
options.frameworkRoutePrefix || "/_agent-native",
|
|
85
|
+
"/_agent-native",
|
|
86
|
+
];
|
|
87
|
+
for (const marker of markers) {
|
|
88
|
+
for (let index = url.pathname.indexOf(marker); index > 0; index = url.pathname.indexOf(marker, index + 1)) {
|
|
89
|
+
const end = index + marker.length;
|
|
90
|
+
if (end === url.pathname.length || url.pathname[end] === "/") {
|
|
91
|
+
const prefix = url.pathname.slice(0, index).trim();
|
|
92
|
+
return prefix ? `/${prefix.replace(/^\/+|\/+$/g, "")}` : "";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!options.workspaceRuntime)
|
|
97
|
+
return undefined;
|
|
98
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
99
|
+
const segment = segments[0];
|
|
100
|
+
const frameworkRoutePrefix = options.frameworkRoutePrefix || "/_agent-native";
|
|
101
|
+
const fallbackPath = segments.length > 1 &&
|
|
102
|
+
segment &&
|
|
103
|
+
`/${segment}` !== "/_agent-native" &&
|
|
104
|
+
`/${segment}` !== frameworkRoutePrefix &&
|
|
105
|
+
segment !== "api"
|
|
106
|
+
? `/${segment}`
|
|
107
|
+
: "";
|
|
108
|
+
return resolveWorkspacePath(url.pathname, {
|
|
109
|
+
...options,
|
|
110
|
+
fallbackPath,
|
|
111
|
+
preferDeclaredMounts: true,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
export function resolveWorkspaceAppMountPath(pathname, options = {}) {
|
|
115
|
+
return resolveMcpAppEmbedBasePath("workspace-path", pathname, options);
|
|
116
|
+
}
|
|
117
|
+
export function resolveExternalEmbedTargetBasePath(target, options = {}) {
|
|
118
|
+
return resolveMcpAppEmbedBasePath("external-target", target, options);
|
|
119
|
+
}
|
|
120
|
+
function normalizedStorageBasePath(basePath) {
|
|
121
|
+
const trimmed = basePath?.trim();
|
|
122
|
+
if (!trimmed || trimmed === "/")
|
|
123
|
+
return "";
|
|
124
|
+
return `/${trimmed.replace(/^\/+/, "").replace(/\/+$/, "")}`;
|
|
125
|
+
}
|
|
126
|
+
function appScopedStorageKey(key, basePath, workspaceRuntime) {
|
|
127
|
+
if (!workspaceRuntime)
|
|
128
|
+
return key;
|
|
129
|
+
const normalizedBasePath = normalizedStorageBasePath(basePath);
|
|
130
|
+
return `${key}:${normalizedBasePath || "/"}`;
|
|
131
|
+
}
|
|
132
|
+
export function embedTokenStorageKey(basePath, workspaceRuntime) {
|
|
133
|
+
return appScopedStorageKey(EMBED_TOKEN_STORAGE_KEY, basePath, workspaceRuntime);
|
|
134
|
+
}
|
|
135
|
+
export function mcpChatBridgeStorageKey(basePath, workspaceRuntime) {
|
|
136
|
+
return appScopedStorageKey(MCP_CHAT_BRIDGE_STORAGE_KEY, basePath, workspaceRuntime);
|
|
137
|
+
}
|
|
5
138
|
/**
|
|
6
139
|
* Set on `<html>` for the life of a document that is an app nested in an MCP
|
|
7
140
|
* App widget (a ChatGPT, Codex, or Claude card or side pane). Layout reads it
|
|
@@ -17,22 +150,62 @@ export const MCP_APP_WIDGET_EMBED_ATTRIBUTE = "data-agent-native-mcp-widget";
|
|
|
17
150
|
* embed credential in the URL, stored, or `embedded=1`); `mcp-app-host.spec.ts`
|
|
18
151
|
* runs both over one table so they cannot drift.
|
|
19
152
|
*/
|
|
20
|
-
export function getMcpAppWidgetEmbedBootScriptBody() {
|
|
153
|
+
export function getMcpAppWidgetEmbedBootScriptBody(basePath, workspaceRuntime = false) {
|
|
154
|
+
const dynamicWorkspaceBasePath = workspaceRuntime && basePath === undefined;
|
|
155
|
+
const tokenStorageKey = dynamicWorkspaceBasePath
|
|
156
|
+
? EMBED_TOKEN_STORAGE_KEY
|
|
157
|
+
: embedTokenStorageKey(basePath ?? "", workspaceRuntime);
|
|
158
|
+
const bridgeStorageKey = dynamicWorkspaceBasePath
|
|
159
|
+
? MCP_CHAT_BRIDGE_STORAGE_KEY
|
|
160
|
+
: mcpChatBridgeStorageKey(basePath ?? "", workspaceRuntime);
|
|
21
161
|
return `(function __anMcpWidgetBoot() {
|
|
22
162
|
try {
|
|
23
163
|
var w = window;
|
|
24
164
|
if (w.parent === w) return;
|
|
165
|
+
var tokenStorageKey = ${safeJsonForHtml(tokenStorageKey)};
|
|
166
|
+
var bridgeStorageKey = ${safeJsonForHtml(bridgeStorageKey)};
|
|
167
|
+
var external = w[${safeJsonForHtml(EXTERNAL_EMBED_WINDOW_KEY)}];
|
|
168
|
+
var storageScopeAvailable = true;
|
|
169
|
+
${dynamicWorkspaceBasePath
|
|
170
|
+
? `var appConfig = w.__AGENT_NATIVE_CONFIG__ || {};
|
|
171
|
+
var runtimeConfig = appConfig.runtime && typeof appConfig.runtime === "object"
|
|
172
|
+
? appConfig.runtime
|
|
173
|
+
: {};
|
|
174
|
+
var resolveMcpAppEmbedBasePath = ${resolveMcpAppEmbedBasePath.toString()};
|
|
175
|
+
var workspaceOptions = {
|
|
176
|
+
workspaceAppPath: appConfig.workspaceAppPath,
|
|
177
|
+
workspaceAppMountPaths: appConfig.workspaceAppMountPaths,
|
|
178
|
+
explicitRootPath: appConfig.explicitRootPath === true,
|
|
179
|
+
routerBasePath: w.__reactRouterContext && w.__reactRouterContext.basename,
|
|
180
|
+
frameworkRoutePrefix: runtimeConfig.frameworkRoutePrefix || appConfig.frameworkRoutePrefix
|
|
181
|
+
};
|
|
182
|
+
var basePath = external && typeof external.target === "string"
|
|
183
|
+
? resolveMcpAppEmbedBasePath("external-target", external.target, Object.assign({ workspaceRuntime: true }, workspaceOptions))
|
|
184
|
+
: resolveMcpAppEmbedBasePath("workspace-path", w.location.pathname, workspaceOptions);
|
|
185
|
+
if (typeof basePath !== "string") {
|
|
186
|
+
storageScopeAvailable = false;
|
|
187
|
+
} else {
|
|
188
|
+
var normalizedBasePath = basePath.trim();
|
|
189
|
+
normalizedBasePath = !normalizedBasePath || normalizedBasePath === "/"
|
|
190
|
+
? ""
|
|
191
|
+
: "/" + normalizedBasePath.replace(/^\\/+/, "").replace(/\\/+$/, "");
|
|
192
|
+
tokenStorageKey += ":" + (normalizedBasePath || "/");
|
|
193
|
+
bridgeStorageKey += ":" + (normalizedBasePath || "/");
|
|
194
|
+
}`
|
|
195
|
+
: ""}
|
|
25
196
|
var params = new URLSearchParams(w.location.search);
|
|
26
197
|
var stored = function (key) {
|
|
198
|
+
if (!storageScopeAvailable) return null;
|
|
27
199
|
// coercion-ok: denied storage reads as absent, exactly as isEmbedMcpChatBridgeActive does.
|
|
28
200
|
try { return w.sessionStorage.getItem(key); } catch (e) { return null; }
|
|
29
201
|
};
|
|
30
|
-
var token = params.get(${safeJsonForHtml(EMBED_TOKEN_QUERY_PARAM)}) || stored(
|
|
202
|
+
var token = params.get(${safeJsonForHtml(EMBED_TOKEN_QUERY_PARAM)}) || stored(tokenStorageKey) || (external && typeof external.token === "string" ? external.token : "");
|
|
31
203
|
var mode = params.get(${safeJsonForHtml(EMBED_MODE_QUERY_PARAM)});
|
|
32
|
-
if (!token && mode !== "1" && mode !== "true") return;
|
|
33
204
|
var flag = params.get(${safeJsonForHtml(MCP_APP_CHAT_BRIDGE_QUERY_PARAM)});
|
|
34
|
-
var
|
|
35
|
-
if (
|
|
205
|
+
var externalBridgeActive = external && external.chatBridgeActive === true;
|
|
206
|
+
if (!token && mode !== "1" && mode !== "true" && !externalBridgeActive) return;
|
|
207
|
+
var scope = stored(bridgeStorageKey);
|
|
208
|
+
if (flag === "1" || flag === "true" || externalBridgeActive || (scope && (!token || scope === token))) {
|
|
36
209
|
document.documentElement.setAttribute(${safeJsonForHtml(MCP_APP_WIDGET_EMBED_ATTRIBUTE)}, "1");
|
|
37
210
|
}
|
|
38
211
|
// coercion-ok: the attribute is a first-paint hint; isMcpAppWidgetEmbed sets it again once the app runs.
|