@agent-native/dispatch 0.29.1 → 0.29.2
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/components/workspace-app-host.d.ts +4 -2
- package/dist/components/workspace-app-host.d.ts.map +1 -1
- package/dist/components/workspace-app-host.js +37 -5
- package/dist/components/workspace-app-host.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +2 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -1
- package/dist/lib/workspace-apps.js +23 -0
- package/dist/lib/workspace-apps.js.map +1 -1
- package/dist/lib/workspace-apps.test.js +60 -2
- package/dist/lib/workspace-apps.test.js.map +1 -1
- package/dist/routes/pages/$appId.d.ts.map +1 -1
- package/dist/routes/pages/$appId.js +15 -6
- package/dist/routes/pages/$appId.js.map +1 -1
- package/dist/routes/pages/apps.$appId.d.ts +1 -0
- package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
- package/dist/routes/pages/apps.$appId.js +5 -1
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/package.json +2 -2
- package/src/components/workspace-app-host.spec.tsx +118 -0
- package/src/components/workspace-app-host.tsx +54 -3
- package/src/lib/workspace-apps.test.ts +70 -1
- package/src/lib/workspace-apps.ts +24 -0
- package/src/routes/pages/$appId.spec.tsx +115 -0
- package/src/routes/pages/$appId.tsx +28 -5
- package/src/routes/pages/apps.$appId.spec.tsx +66 -0
- package/src/routes/pages/apps.$appId.tsx +11 -1
|
@@ -31,15 +31,17 @@ export interface WorkspaceAppFrameApp {
|
|
|
31
31
|
}
|
|
32
32
|
interface WorkspaceAppFrameProps {
|
|
33
33
|
app: WorkspaceAppFrameApp;
|
|
34
|
+
navigateToTopWindow?: (href: string) => boolean | void;
|
|
34
35
|
/** Chat-first app tabs use their own route while standalone hosts use app metadata. */
|
|
35
36
|
embedPath?: string;
|
|
36
37
|
/** Chat-first app surfaces own the parent chat rail around the iframe. */
|
|
37
38
|
chatSidebar?: boolean;
|
|
38
39
|
copy?: ChatFirstCopy;
|
|
39
40
|
}
|
|
40
|
-
export declare function WorkspaceAppFrame({ app, embedPath, chatSidebar, copy, }: WorkspaceAppFrameProps): import("node_modules/@types/react").JSX.Element;
|
|
41
|
-
export declare function WorkspaceAppHost({ appId }: {
|
|
41
|
+
export declare function WorkspaceAppFrame({ app, navigateToTopWindow, embedPath, chatSidebar, copy, }: WorkspaceAppFrameProps): import("node_modules/@types/react").JSX.Element;
|
|
42
|
+
export declare function WorkspaceAppHost({ appId, navigateToTopWindow, }: {
|
|
42
43
|
appId?: string;
|
|
44
|
+
navigateToTopWindow?: (href: string) => boolean | void;
|
|
43
45
|
}): import("node_modules/@types/react").JSX.Element;
|
|
44
46
|
export declare function WorkspaceAppKeepAlive({ activeAppId, }: {
|
|
45
47
|
activeAppId: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-app-host.d.ts","sourceRoot":"","sources":["../../src/components/workspace-app-host.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,sCAAsC,CAAC;AAc9C,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace-app-host.d.ts","sourceRoot":"","sources":["../../src/components/workspace-app-host.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,sCAAsC,CAAC;AAc9C,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAyBf,UAAU,iBAAiB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;CACnB;AAqBD,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,iBAAiB,CAEnB;AAkED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,OAAO,EACP,QAAQ,EACR,IAA2B,EAC3B,aAAa,EACb,mBAAmB,GACpB,EAAE,yBAAyB,mDAiE3B;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,UAAU,sBAAsB;IAC9B,GAAG,EAAE,oBAAoB,CAAC;IAC1B,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;IACvD,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,GAAG,EACH,mBAA4C,EAC5C,SAAS,EACT,WAAmB,EACnB,IAA2B,GAC5B,EAAE,sBAAsB,mDA4MxB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,mBAA4C,GAC7C,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;CACxD,mDA8JA;AAID,wBAAgB,qBAAqB,CAAC,EACpC,WAAW,GACZ,EAAE;IACD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,mDAwCA"}
|
|
@@ -11,7 +11,7 @@ import { useTheme } from "next-themes";
|
|
|
11
11
|
import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
12
12
|
import { Link } from "react-router";
|
|
13
13
|
import { isEmbedSessionExpiredMessage } from "../lib/embed-session-recovery.js";
|
|
14
|
-
import { mergeChatFirstWorkspaceApps, workspaceAppDirectHref, workspaceAppEmbedTarget, workspaceAppHref, } from "../lib/workspace-apps.js";
|
|
14
|
+
import { mergeChatFirstWorkspaceApps, navigateToWorkspaceApp, shouldOpenWorkspaceAppInTopWindow, workspaceAppDirectHref, workspaceAppEmbedTarget, workspaceAppHref, } from "../lib/workspace-apps.js";
|
|
15
15
|
import { DISPATCH_WORKSPACE_SSO_FLAG } from "../shared/feature-flags.js";
|
|
16
16
|
import { workspaceAppChatProxyPath } from "../shared/workspace-app-chat.js";
|
|
17
17
|
import { ActionQueryError } from "./action-query-error.js";
|
|
@@ -113,7 +113,7 @@ export function WorkspaceAppChatRail({ appId, appName, children, copy = defaultC
|
|
|
113
113
|
// rather than a copy maintained inside Dispatch.
|
|
114
114
|
apiUrl: appChat.apiUrl, agentChatSurface: "app", showTabBar: true, suppressInlineOpenApp: true, dynamicSuggestions: false, suggestions: [], emptyStateText: `Ask about ${appName}`, ...(agentPageHref ? { agentPageHref } : {}), ...(onFullscreenRequest ? { onFullscreenRequest } : {}), children: children }));
|
|
115
115
|
}
|
|
116
|
-
export function WorkspaceAppFrame({ app, embedPath, chatSidebar = false, copy = defaultChatFirstCopy, }) {
|
|
116
|
+
export function WorkspaceAppFrame({ app, navigateToTopWindow = navigateToWorkspaceApp, embedPath, chatSidebar = false, copy = defaultChatFirstCopy, }) {
|
|
117
117
|
const { resolvedTheme } = useTheme();
|
|
118
118
|
const theme = resolvedTheme === "dark" || resolvedTheme === "light"
|
|
119
119
|
? resolvedTheme
|
|
@@ -125,6 +125,7 @@ export function WorkspaceAppFrame({ app, embedPath, chatSidebar = false, copy =
|
|
|
125
125
|
const [embedError, setEmbedError] = useState(null);
|
|
126
126
|
const [isDirectFallback, setIsDirectFallback] = useState(false);
|
|
127
127
|
const [embedAttempt, setEmbedAttempt] = useState(0);
|
|
128
|
+
const [topWindowNavigationFailed, setTopWindowNavigationFailed] = useState(false);
|
|
128
129
|
const embedFrameRef = useRef(null);
|
|
129
130
|
const postThemeToFrame = useCallback(() => {
|
|
130
131
|
embedFrameRef.current?.contentWindow?.postMessage(buildWorkspaceAppThemeUpdate(theme), "*");
|
|
@@ -147,6 +148,17 @@ export function WorkspaceAppFrame({ app, embedPath, chatSidebar = false, copy =
|
|
|
147
148
|
path: app.path ?? "",
|
|
148
149
|
url: app.url,
|
|
149
150
|
});
|
|
151
|
+
const topWindowHref = useMemo(() => {
|
|
152
|
+
if (embedPath !== undefined) {
|
|
153
|
+
return workspaceAppDirectHref({ path: app.path, url: app.url }, embedPath);
|
|
154
|
+
}
|
|
155
|
+
const target = workspaceAppEmbedTarget({
|
|
156
|
+
path: app.path ?? "",
|
|
157
|
+
url: app.url,
|
|
158
|
+
});
|
|
159
|
+
return target.url ?? target.path ?? null;
|
|
160
|
+
}, [app.path, app.url, embedPath]);
|
|
161
|
+
const openInTopWindow = shouldOpenWorkspaceAppInTopWindow();
|
|
150
162
|
const embedInput = useMemo(() => {
|
|
151
163
|
if (embedPath !== undefined) {
|
|
152
164
|
return buildChatFirstEmbedSessionInput(app.id, embedPath);
|
|
@@ -160,7 +172,25 @@ export function WorkspaceAppFrame({ app, embedPath, chatSidebar = false, copy =
|
|
|
160
172
|
};
|
|
161
173
|
}, [app.id, app.path, app.url, appHref, embedPath]);
|
|
162
174
|
useEffect(() => {
|
|
163
|
-
if (!
|
|
175
|
+
if (!openInTopWindow) {
|
|
176
|
+
setTopWindowNavigationFailed(false);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (!topWindowHref) {
|
|
180
|
+
setTopWindowNavigationFailed(true);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
let didNavigate = false;
|
|
184
|
+
try {
|
|
185
|
+
didNavigate = navigateToTopWindow(topWindowHref) !== false;
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
didNavigate = false;
|
|
189
|
+
}
|
|
190
|
+
setTopWindowNavigationFailed(!didNavigate);
|
|
191
|
+
}, [navigateToTopWindow, openInTopWindow, topWindowHref]);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (!embedInput || (openInTopWindow && !topWindowNavigationFailed))
|
|
164
194
|
return;
|
|
165
195
|
let cancelled = false;
|
|
166
196
|
setEmbedUrl(null);
|
|
@@ -205,6 +235,8 @@ export function WorkspaceAppFrame({ app, embedPath, chatSidebar = false, copy =
|
|
|
205
235
|
embedInput,
|
|
206
236
|
embedPath,
|
|
207
237
|
embedAttempt,
|
|
238
|
+
openInTopWindow,
|
|
239
|
+
topWindowNavigationFailed,
|
|
208
240
|
workspaceSsoEnabled,
|
|
209
241
|
]);
|
|
210
242
|
useEffect(() => {
|
|
@@ -231,7 +263,7 @@ export function WorkspaceAppFrame({ app, embedPath, chatSidebar = false, copy =
|
|
|
231
263
|
return appPane;
|
|
232
264
|
return (_jsx(WorkspaceAppChatRail, { appId: app.id, appName: app.name, copy: copy, children: appPane }));
|
|
233
265
|
}
|
|
234
|
-
export function WorkspaceAppHost({ appId }) {
|
|
266
|
+
export function WorkspaceAppHost({ appId, navigateToTopWindow = navigateToWorkspaceApp, }) {
|
|
235
267
|
const t = useT();
|
|
236
268
|
const workspaceAppsQuery = useActionQuery("list-workspace-apps", { includeAgentCards: false });
|
|
237
269
|
const grantedAppsQuery = useActionQuery("list_apps", {});
|
|
@@ -281,7 +313,7 @@ export function WorkspaceAppHost({ appId }) {
|
|
|
281
313
|
defaultValue: "Open in Builder",
|
|
282
314
|
}) }) })) : null] })] }) }));
|
|
283
315
|
}
|
|
284
|
-
return (_jsx("div", { "data-dispatch-workspace-app-host": true, className: "flex h-full min-h-0 flex-col bg-background", children: _jsx("div", { className: "min-h-0 flex-1 bg-muted/20", children: _jsx(WorkspaceAppFrame, { app: app }) }) }));
|
|
316
|
+
return (_jsx("div", { "data-dispatch-workspace-app-host": true, className: "flex h-full min-h-0 flex-col bg-background", children: _jsx("div", { className: "min-h-0 flex-1 bg-muted/20", children: _jsx(WorkspaceAppFrame, { app: app, navigateToTopWindow: navigateToTopWindow }) }) }));
|
|
285
317
|
}
|
|
286
318
|
const MAX_KEEP_ALIVE_APPS = 3;
|
|
287
319
|
export function WorkspaceAppKeepAlive({ activeAppId, }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-app-host.js","sourceRoot":"","sources":["../../src/components/workspace-app-host.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AgentSidebar } from \"@agent-native/core/client/agent-chat\";\nimport { agentNativePath } from \"@agent-native/core/client/api-path\";\nimport {\n ChatFirstAppPane,\n defaultChatFirstCopy,\n type ChatFirstCopy,\n} from \"@agent-native/core/client/chat-first\";\nimport { useFeatureFlag } from \"@agent-native/core/client/feature-flags\";\nimport {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport { withBuilderUtmTrackingParams } from \"@agent-native/core/shared/builder-link-tracking\";\nimport {\n IconAlertTriangle,\n IconArrowLeft,\n IconClockHour4,\n} from \"@tabler/icons-react\";\nimport { useTheme } from \"next-themes\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { Link } from \"react-router\";\n\nimport { isEmbedSessionExpiredMessage } from \"../lib/embed-session-recovery\";\nimport {\n mergeChatFirstWorkspaceApps,\n workspaceAppDirectHref,\n workspaceAppEmbedTarget,\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../lib/workspace-apps\";\nimport { DISPATCH_WORKSPACE_SSO_FLAG } from \"../shared/feature-flags\";\nimport { workspaceAppChatProxyPath } from \"../shared/workspace-app-chat\";\nimport { ActionQueryError } from \"./action-query-error\";\nimport { Alert, AlertDescription } from \"./ui/alert\";\nimport { Badge } from \"./ui/badge\";\nimport { Button } from \"./ui/button\";\nimport { Skeleton } from \"./ui/skeleton\";\n\ninterface EmbedSessionResult {\n startUrl: string;\n}\n\ninterface EmbedSessionInput {\n app?: string;\n path?: string;\n url?: string;\n chrome: \"minimal\";\n}\n\ninterface GrantedWorkspaceAppSummary {\n id: string;\n name: string;\n url?: string | null;\n}\n\ninterface GrantedWorkspaceAppsResult {\n apps: GrantedWorkspaceAppSummary[];\n}\n\ntype WorkspaceAppTheme = \"light\" | \"dark\";\nfunction buildWorkspaceAppThemeUpdate(theme: WorkspaceAppTheme) {\n return {\n type: \"agent-native-theme-update\" as const,\n theme,\n isDark: theme === \"dark\",\n };\n}\n\nexport function buildChatFirstEmbedSessionInput(\n appId: string,\n path: string,\n): EmbedSessionInput {\n return { app: appId, path, chrome: \"minimal\" };\n}\n\nasync function readWorkspaceAppChatProxyError(\n response: Response,\n): Promise<string> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n // coercion-ok: an unreadable body is reported as such, not as an empty error.\n return `Agent chat proxy returned ${response.status} with an unreadable body.`;\n }\n try {\n const parsed = JSON.parse(body) as { error?: unknown };\n if (typeof parsed.error === \"string\" && parsed.error) return parsed.error;\n } catch {\n // coercion-ok: a non-JSON body is still reportable as the status line.\n }\n return body.trim() || `Agent chat proxy returned ${response.status}.`;\n}\n\n/**\n * Point the app pane's chat rail at the app's OWN agent through the Dispatch\n * proxy, and prove the proxy answers before claiming it works. A rail that\n * quietly fell back to Dispatch's agent would look identical while running the\n * wrong tools, instructions, and app resources, so a failed probe is a visible\n * error state instead.\n */\nfunction useWorkspaceAppChatApi(appId: string) {\n const apiUrl = useMemo(\n () => agentNativePath(workspaceAppChatProxyPath(appId)),\n [appId],\n );\n const [attempt, setAttempt] = useState(0);\n const [unavailable, setUnavailable] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n setUnavailable(false);\n // `/mode` is the app's own dev-mode surface: reaching it proves the proxy\n // minted an app session and the app's agent-chat routes answer.\n void fetch(`${apiUrl}/mode`, { credentials: \"include\" })\n .then(async (response) => {\n if (response.ok) return;\n throw new Error(await readWorkspaceAppChatProxyError(response));\n })\n .catch((cause: unknown) => {\n if (cancelled) return;\n console.warn(\n `[dispatch] app chat proxy unavailable for ${appId}`,\n cause,\n );\n setUnavailable(true);\n });\n return () => {\n cancelled = true;\n };\n }, [apiUrl, appId, attempt]);\n\n return {\n apiUrl,\n unavailable,\n retry: useCallback(() => setAttempt((value) => value + 1), []),\n };\n}\n\nexport interface WorkspaceAppChatRailProps {\n appId: string;\n appName: string;\n children: ReactNode;\n copy?: ChatFirstCopy;\n agentPageHref?: string;\n onFullscreenRequest?: () => void;\n}\n\n/**\n * The chat beside an open workspace app. Every surface that hosts an app pane\n * must go through here so the rail is always the app's own agent — same tools,\n * AGENTS.md, skills, app-scoped resources, and dev-mode surface as the app's\n * native chat — and so an unreachable app is one visible error state rather\n * than a per-surface silent handoff back to Dispatch's agent.\n */\nexport function WorkspaceAppChatRail({\n appId,\n appName,\n children,\n copy = defaultChatFirstCopy,\n agentPageHref,\n onFullscreenRequest,\n}: WorkspaceAppChatRailProps) {\n const t = useT();\n const appChat = useWorkspaceAppChatApi(appId);\n\n if (appChat.unavailable) {\n return (\n <div className=\"flex h-full min-h-0\">\n <div\n data-dispatch-app-chat-unavailable\n className=\"w-88 shrink-0 overflow-auto border-r p-4\"\n >\n <Alert variant=\"destructive\">\n <IconAlertTriangle className=\"size-4\" />\n <AlertDescription className=\"flex flex-wrap items-center justify-between gap-3\">\n <span>\n {t(\"dispatch.pages.appChatUnavailable\", {\n defaultValue:\n \"Dispatch could not connect to {{name}}'s agent, so its chat is unavailable here.\",\n name: appName,\n })}\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={appChat.retry}\n >\n {copy(\"retry\")}\n </Button>\n </AlertDescription>\n </Alert>\n </div>\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n );\n }\n\n return (\n <AgentSidebar\n position=\"left\"\n defaultOpen\n openStorageKey=\"dispatch-app-chat\"\n storageKey={`dispatch-app-chat:${appId}`}\n scope={{\n type: \"workspace-app\",\n id: appId,\n label: appName,\n contextKey: `workspace-app:${appId}`,\n }}\n // The app's own server answers this chat, so its tools, AGENTS.md,\n // skills, app-scoped resources, and dev-mode surface are the real ones\n // rather than a copy maintained inside Dispatch.\n apiUrl={appChat.apiUrl}\n agentChatSurface=\"app\"\n showTabBar\n suppressInlineOpenApp\n dynamicSuggestions={false}\n suggestions={[]}\n emptyStateText={`Ask about ${appName}`}\n {...(agentPageHref ? { agentPageHref } : {})}\n {...(onFullscreenRequest ? { onFullscreenRequest } : {})}\n >\n {children}\n </AgentSidebar>\n );\n}\n\nexport interface WorkspaceAppFrameApp {\n id: string;\n name: string;\n path?: string | null;\n url?: string | null;\n}\n\ninterface WorkspaceAppFrameProps {\n app: WorkspaceAppFrameApp;\n /** Chat-first app tabs use their own route while standalone hosts use app metadata. */\n embedPath?: string;\n /** Chat-first app surfaces own the parent chat rail around the iframe. */\n chatSidebar?: boolean;\n copy?: ChatFirstCopy;\n}\n\nexport function WorkspaceAppFrame({\n app,\n embedPath,\n chatSidebar = false,\n copy = defaultChatFirstCopy,\n}: WorkspaceAppFrameProps) {\n const { resolvedTheme } = useTheme();\n const theme: WorkspaceAppTheme =\n resolvedTheme === \"dark\" || resolvedTheme === \"light\"\n ? resolvedTheme\n : typeof document !== \"undefined\" &&\n document.documentElement.classList.contains(\"dark\")\n ? \"dark\"\n : \"light\";\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [embedError, setEmbedError] = useState<Error | null>(null);\n const [isDirectFallback, setIsDirectFallback] = useState(false);\n const [embedAttempt, setEmbedAttempt] = useState(0);\n const embedFrameRef = useRef<HTMLIFrameElement>(null);\n const postThemeToFrame = useCallback(() => {\n embedFrameRef.current?.contentWindow?.postMessage(\n buildWorkspaceAppThemeUpdate(theme),\n \"*\",\n );\n }, [theme]);\n const handleFrameLoad = useCallback(() => {\n postThemeToFrame();\n if (isDirectFallback) setEmbedError(null);\n }, [isDirectFallback, postThemeToFrame]);\n const workspaceSsoEnabled = useFeatureFlag(DISPATCH_WORKSPACE_SSO_FLAG.key);\n const createEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create_embed_session\", {\n skipActionQueryInvalidation: true,\n });\n const createWorkspaceSsoEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create-workspace-app-embed-session\", {\n skipActionQueryInvalidation: true,\n });\n const appHref = workspaceAppHref({\n id: app.id,\n name: app.name,\n path: app.path ?? \"\",\n url: app.url,\n });\n const embedInput = useMemo<EmbedSessionInput | null>(() => {\n if (embedPath !== undefined) {\n return buildChatFirstEmbedSessionInput(app.id, embedPath);\n }\n if (!appHref) return null;\n return {\n app: app.id,\n ...workspaceAppEmbedTarget({ path: app.path ?? \"\", url: app.url }),\n chrome: \"minimal\",\n };\n }, [app.id, app.path, app.url, appHref, embedPath]);\n\n useEffect(() => {\n if (!embedInput) return;\n let cancelled = false;\n setEmbedUrl(null);\n setEmbedError(null);\n setIsDirectFallback(false);\n const createSession = workspaceSsoEnabled\n ? createWorkspaceSsoEmbedSession\n : createEmbedSession;\n void createSession\n .mutateAsync(embedInput)\n .then((result) => {\n if (!cancelled) setEmbedUrl(result.startUrl);\n })\n .catch((cause: unknown) => {\n if (cancelled) return;\n const error = cause instanceof Error ? cause : new Error(String(cause));\n if (workspaceSsoEnabled) {\n // An SSO-enabled pane must never fall back to the child app's\n // unauthenticated shell. Keep the parent-owned retry surface in\n // place so a transient exchange failure cannot expose another\n // login form.\n setIsDirectFallback(false);\n setEmbedUrl(null);\n setEmbedError(error);\n return;\n }\n setIsDirectFallback(true);\n setEmbedUrl(\n workspaceAppDirectHref(\n { path: app.path ?? \"\", url: app.url },\n embedPath ?? \"/\",\n ),\n );\n setEmbedError(error);\n });\n return () => {\n cancelled = true;\n };\n }, [\n app.id,\n app.path,\n app.url,\n createEmbedSession.mutateAsync,\n createWorkspaceSsoEmbedSession.mutateAsync,\n embedInput,\n embedPath,\n embedAttempt,\n workspaceSsoEnabled,\n ]);\n\n useEffect(() => {\n const handleEmbedSessionExpired = (event: MessageEvent) => {\n if (\n !isEmbedSessionExpiredMessage(event, embedFrameRef.current, embedUrl)\n ) {\n return;\n }\n setEmbedAttempt((attempt) => attempt + 1);\n };\n\n window.addEventListener(\"message\", handleEmbedSessionExpired);\n return () =>\n window.removeEventListener(\"message\", handleEmbedSessionExpired);\n }, [embedUrl]);\n\n useEffect(() => {\n postThemeToFrame();\n }, [embedUrl, postThemeToFrame]);\n\n const appPane = (\n <ChatFirstAppPane\n app={app}\n status={\n embedUrl\n ? \"ready\"\n : embedError\n ? \"error\"\n : embedInput\n ? \"loading\"\n : \"unresolved\"\n }\n embedUrl={embedUrl}\n errorMessage={embedError?.message}\n onRetry={\n embedInput ? () => setEmbedAttempt((attempt) => attempt + 1) : undefined\n }\n renderEmbed={({ url, title }) => (\n <iframe\n key={url + \":\" + embedAttempt}\n data-dispatch-workspace-app-frame\n src={url}\n title={title ?? app.name}\n ref={embedFrameRef}\n onLoad={handleFrameLoad}\n referrerPolicy=\"no-referrer\"\n allow=\"clipboard-read; clipboard-write\"\n className=\"h-full w-full border-0 bg-background\"\n />\n )}\n copy={copy}\n />\n );\n\n if (!chatSidebar) return appPane;\n\n return (\n <WorkspaceAppChatRail appId={app.id} appName={app.name} copy={copy}>\n {appPane}\n </WorkspaceAppChatRail>\n );\n}\n\nexport function WorkspaceAppHost({ appId }: { appId?: string }) {\n const t = useT();\n const workspaceAppsQuery = useActionQuery<WorkspaceAppSummary[]>(\n \"list-workspace-apps\",\n { includeAgentCards: false },\n );\n const grantedAppsQuery = useActionQuery<GrantedWorkspaceAppsResult>(\n \"list_apps\",\n {},\n );\n const apps = useMemo(() => {\n const merged = new Map<string, WorkspaceAppSummary>();\n\n for (const app of mergeChatFirstWorkspaceApps(workspaceAppsQuery.data)) {\n merged.set(app.id.trim().toLowerCase(), app);\n }\n for (const app of grantedAppsQuery.data?.apps ?? []) {\n const id = app.id.trim();\n if (!id || merged.has(id.toLowerCase())) continue;\n merged.set(id.toLowerCase(), {\n id,\n name: app.name.trim() || id,\n path: \"\",\n url: app.url?.trim() || null,\n status: \"ready\",\n });\n }\n\n return [...merged.values()];\n }, [grantedAppsQuery.data?.apps, workspaceAppsQuery.data]);\n const app = useMemo(\n () =>\n apps.find(\n (item) => item.id.trim().toLowerCase() === appId?.trim().toLowerCase(),\n ) ?? null,\n [appId, apps],\n );\n const isLoading = workspaceAppsQuery.isLoading || grantedAppsQuery.isLoading;\n const queryError = workspaceAppsQuery.isError\n ? workspaceAppsQuery.error\n : grantedAppsQuery.isError\n ? grantedAppsQuery.error\n : null;\n\n if (queryError && !app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl\">\n <ActionQueryError\n error={queryError}\n onRetry={() => {\n void workspaceAppsQuery.refetch();\n void grantedAppsQuery.refetch();\n }}\n />\n </div>\n </div>\n );\n }\n\n if (isLoading && !app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl space-y-3 rounded-xl border bg-card p-6\">\n <Skeleton className=\"h-5 w-48\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-2/3\" />\n </div>\n </div>\n );\n }\n\n if (!app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl rounded-xl border bg-card p-6\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.appNotFound\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.pageNotFoundDescription\")}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (app.status === \"pending\") {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl rounded-xl border bg-card p-6\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n {t(\"dispatch.pages.building\")}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.appBuildingPrefix\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n {t(\"dispatch.pages.appBuildingSuffix\")}\n </p>\n {app.builderUrl ? (\n <Button asChild>\n <a\n href={withBuilderUtmTrackingParams(app.builderUrl, {\n campaign: \"product\",\n content: \"dispatch_branch\",\n })}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {t(\"dispatch.pages.openBuilderBranch\", {\n defaultValue: \"Open in Builder\",\n })}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-dispatch-workspace-app-host\n className=\"flex h-full min-h-0 flex-col bg-background\"\n >\n <div className=\"min-h-0 flex-1 bg-muted/20\">\n <WorkspaceAppFrame app={app} />\n </div>\n </div>\n );\n}\n\nconst MAX_KEEP_ALIVE_APPS = 3;\n\nexport function WorkspaceAppKeepAlive({\n activeAppId,\n}: {\n activeAppId: string | null;\n}) {\n const [visitedAppIds, setVisitedAppIds] = useState<string[]>(() =>\n activeAppId ? [activeAppId] : [],\n );\n\n useEffect(() => {\n if (!activeAppId) return;\n setVisitedAppIds((current) =>\n [activeAppId, ...current.filter((appId) => appId !== activeAppId)].slice(\n 0,\n MAX_KEEP_ALIVE_APPS,\n ),\n );\n }, [activeAppId]);\n\n const renderedAppIds =\n activeAppId && !visitedAppIds.includes(activeAppId)\n ? [activeAppId, ...visitedAppIds]\n : visitedAppIds;\n\n return (\n <div\n data-dispatch-workspace-app-cache\n className={activeAppId ? \"absolute inset-0 overflow-hidden\" : \"hidden\"}\n >\n {renderedAppIds.map((appId) => {\n const active = appId === activeAppId;\n return (\n <div\n key={appId}\n data-dispatch-workspace-app-cache-entry={appId}\n aria-hidden={!active}\n className={active ? \"h-full min-h-0\" : \"hidden\"}\n >\n <WorkspaceAppHost appId={appId} />\n </div>\n );\n })}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workspace-app-host.js","sourceRoot":"","sources":["../../src/components/workspace-app-host.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AgentSidebar } from \"@agent-native/core/client/agent-chat\";\nimport { agentNativePath } from \"@agent-native/core/client/api-path\";\nimport {\n ChatFirstAppPane,\n defaultChatFirstCopy,\n type ChatFirstCopy,\n} from \"@agent-native/core/client/chat-first\";\nimport { useFeatureFlag } from \"@agent-native/core/client/feature-flags\";\nimport {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport { withBuilderUtmTrackingParams } from \"@agent-native/core/shared/builder-link-tracking\";\nimport {\n IconAlertTriangle,\n IconArrowLeft,\n IconClockHour4,\n} from \"@tabler/icons-react\";\nimport { useTheme } from \"next-themes\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { Link } from \"react-router\";\n\nimport { isEmbedSessionExpiredMessage } from \"../lib/embed-session-recovery\";\nimport {\n mergeChatFirstWorkspaceApps,\n navigateToWorkspaceApp,\n shouldOpenWorkspaceAppInTopWindow,\n workspaceAppDirectHref,\n workspaceAppEmbedTarget,\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../lib/workspace-apps\";\nimport { DISPATCH_WORKSPACE_SSO_FLAG } from \"../shared/feature-flags\";\nimport { workspaceAppChatProxyPath } from \"../shared/workspace-app-chat\";\nimport { ActionQueryError } from \"./action-query-error\";\nimport { Alert, AlertDescription } from \"./ui/alert\";\nimport { Badge } from \"./ui/badge\";\nimport { Button } from \"./ui/button\";\nimport { Skeleton } from \"./ui/skeleton\";\n\ninterface EmbedSessionResult {\n startUrl: string;\n}\n\ninterface EmbedSessionInput {\n app?: string;\n path?: string;\n url?: string;\n chrome: \"minimal\";\n}\n\ninterface GrantedWorkspaceAppSummary {\n id: string;\n name: string;\n url?: string | null;\n}\n\ninterface GrantedWorkspaceAppsResult {\n apps: GrantedWorkspaceAppSummary[];\n}\n\ntype WorkspaceAppTheme = \"light\" | \"dark\";\nfunction buildWorkspaceAppThemeUpdate(theme: WorkspaceAppTheme) {\n return {\n type: \"agent-native-theme-update\" as const,\n theme,\n isDark: theme === \"dark\",\n };\n}\n\nexport function buildChatFirstEmbedSessionInput(\n appId: string,\n path: string,\n): EmbedSessionInput {\n return { app: appId, path, chrome: \"minimal\" };\n}\n\nasync function readWorkspaceAppChatProxyError(\n response: Response,\n): Promise<string> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n // coercion-ok: an unreadable body is reported as such, not as an empty error.\n return `Agent chat proxy returned ${response.status} with an unreadable body.`;\n }\n try {\n const parsed = JSON.parse(body) as { error?: unknown };\n if (typeof parsed.error === \"string\" && parsed.error) return parsed.error;\n } catch {\n // coercion-ok: a non-JSON body is still reportable as the status line.\n }\n return body.trim() || `Agent chat proxy returned ${response.status}.`;\n}\n\n/**\n * Point the app pane's chat rail at the app's OWN agent through the Dispatch\n * proxy, and prove the proxy answers before claiming it works. A rail that\n * quietly fell back to Dispatch's agent would look identical while running the\n * wrong tools, instructions, and app resources, so a failed probe is a visible\n * error state instead.\n */\nfunction useWorkspaceAppChatApi(appId: string) {\n const apiUrl = useMemo(\n () => agentNativePath(workspaceAppChatProxyPath(appId)),\n [appId],\n );\n const [attempt, setAttempt] = useState(0);\n const [unavailable, setUnavailable] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n setUnavailable(false);\n // `/mode` is the app's own dev-mode surface: reaching it proves the proxy\n // minted an app session and the app's agent-chat routes answer.\n void fetch(`${apiUrl}/mode`, { credentials: \"include\" })\n .then(async (response) => {\n if (response.ok) return;\n throw new Error(await readWorkspaceAppChatProxyError(response));\n })\n .catch((cause: unknown) => {\n if (cancelled) return;\n console.warn(\n `[dispatch] app chat proxy unavailable for ${appId}`,\n cause,\n );\n setUnavailable(true);\n });\n return () => {\n cancelled = true;\n };\n }, [apiUrl, appId, attempt]);\n\n return {\n apiUrl,\n unavailable,\n retry: useCallback(() => setAttempt((value) => value + 1), []),\n };\n}\n\nexport interface WorkspaceAppChatRailProps {\n appId: string;\n appName: string;\n children: ReactNode;\n copy?: ChatFirstCopy;\n agentPageHref?: string;\n onFullscreenRequest?: () => void;\n}\n\n/**\n * The chat beside an open workspace app. Every surface that hosts an app pane\n * must go through here so the rail is always the app's own agent — same tools,\n * AGENTS.md, skills, app-scoped resources, and dev-mode surface as the app's\n * native chat — and so an unreachable app is one visible error state rather\n * than a per-surface silent handoff back to Dispatch's agent.\n */\nexport function WorkspaceAppChatRail({\n appId,\n appName,\n children,\n copy = defaultChatFirstCopy,\n agentPageHref,\n onFullscreenRequest,\n}: WorkspaceAppChatRailProps) {\n const t = useT();\n const appChat = useWorkspaceAppChatApi(appId);\n\n if (appChat.unavailable) {\n return (\n <div className=\"flex h-full min-h-0\">\n <div\n data-dispatch-app-chat-unavailable\n className=\"w-88 shrink-0 overflow-auto border-r p-4\"\n >\n <Alert variant=\"destructive\">\n <IconAlertTriangle className=\"size-4\" />\n <AlertDescription className=\"flex flex-wrap items-center justify-between gap-3\">\n <span>\n {t(\"dispatch.pages.appChatUnavailable\", {\n defaultValue:\n \"Dispatch could not connect to {{name}}'s agent, so its chat is unavailable here.\",\n name: appName,\n })}\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={appChat.retry}\n >\n {copy(\"retry\")}\n </Button>\n </AlertDescription>\n </Alert>\n </div>\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n );\n }\n\n return (\n <AgentSidebar\n position=\"left\"\n defaultOpen\n openStorageKey=\"dispatch-app-chat\"\n storageKey={`dispatch-app-chat:${appId}`}\n scope={{\n type: \"workspace-app\",\n id: appId,\n label: appName,\n contextKey: `workspace-app:${appId}`,\n }}\n // The app's own server answers this chat, so its tools, AGENTS.md,\n // skills, app-scoped resources, and dev-mode surface are the real ones\n // rather than a copy maintained inside Dispatch.\n apiUrl={appChat.apiUrl}\n agentChatSurface=\"app\"\n showTabBar\n suppressInlineOpenApp\n dynamicSuggestions={false}\n suggestions={[]}\n emptyStateText={`Ask about ${appName}`}\n {...(agentPageHref ? { agentPageHref } : {})}\n {...(onFullscreenRequest ? { onFullscreenRequest } : {})}\n >\n {children}\n </AgentSidebar>\n );\n}\n\nexport interface WorkspaceAppFrameApp {\n id: string;\n name: string;\n path?: string | null;\n url?: string | null;\n}\n\ninterface WorkspaceAppFrameProps {\n app: WorkspaceAppFrameApp;\n navigateToTopWindow?: (href: string) => boolean | void;\n /** Chat-first app tabs use their own route while standalone hosts use app metadata. */\n embedPath?: string;\n /** Chat-first app surfaces own the parent chat rail around the iframe. */\n chatSidebar?: boolean;\n copy?: ChatFirstCopy;\n}\n\nexport function WorkspaceAppFrame({\n app,\n navigateToTopWindow = navigateToWorkspaceApp,\n embedPath,\n chatSidebar = false,\n copy = defaultChatFirstCopy,\n}: WorkspaceAppFrameProps) {\n const { resolvedTheme } = useTheme();\n const theme: WorkspaceAppTheme =\n resolvedTheme === \"dark\" || resolvedTheme === \"light\"\n ? resolvedTheme\n : typeof document !== \"undefined\" &&\n document.documentElement.classList.contains(\"dark\")\n ? \"dark\"\n : \"light\";\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [embedError, setEmbedError] = useState<Error | null>(null);\n const [isDirectFallback, setIsDirectFallback] = useState(false);\n const [embedAttempt, setEmbedAttempt] = useState(0);\n const [topWindowNavigationFailed, setTopWindowNavigationFailed] =\n useState(false);\n const embedFrameRef = useRef<HTMLIFrameElement>(null);\n const postThemeToFrame = useCallback(() => {\n embedFrameRef.current?.contentWindow?.postMessage(\n buildWorkspaceAppThemeUpdate(theme),\n \"*\",\n );\n }, [theme]);\n const handleFrameLoad = useCallback(() => {\n postThemeToFrame();\n if (isDirectFallback) setEmbedError(null);\n }, [isDirectFallback, postThemeToFrame]);\n const workspaceSsoEnabled = useFeatureFlag(DISPATCH_WORKSPACE_SSO_FLAG.key);\n const createEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create_embed_session\", {\n skipActionQueryInvalidation: true,\n });\n const createWorkspaceSsoEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create-workspace-app-embed-session\", {\n skipActionQueryInvalidation: true,\n });\n const appHref = workspaceAppHref({\n id: app.id,\n name: app.name,\n path: app.path ?? \"\",\n url: app.url,\n });\n const topWindowHref = useMemo(() => {\n if (embedPath !== undefined) {\n return workspaceAppDirectHref(\n { path: app.path, url: app.url },\n embedPath,\n );\n }\n\n const target = workspaceAppEmbedTarget({\n path: app.path ?? \"\",\n url: app.url,\n });\n return target.url ?? target.path ?? null;\n }, [app.path, app.url, embedPath]);\n const openInTopWindow = shouldOpenWorkspaceAppInTopWindow();\n const embedInput = useMemo<EmbedSessionInput | null>(() => {\n if (embedPath !== undefined) {\n return buildChatFirstEmbedSessionInput(app.id, embedPath);\n }\n if (!appHref) return null;\n return {\n app: app.id,\n ...workspaceAppEmbedTarget({ path: app.path ?? \"\", url: app.url }),\n chrome: \"minimal\",\n };\n }, [app.id, app.path, app.url, appHref, embedPath]);\n\n useEffect(() => {\n if (!openInTopWindow) {\n setTopWindowNavigationFailed(false);\n return;\n }\n if (!topWindowHref) {\n setTopWindowNavigationFailed(true);\n return;\n }\n\n let didNavigate = false;\n try {\n didNavigate = navigateToTopWindow(topWindowHref) !== false;\n } catch {\n didNavigate = false;\n }\n setTopWindowNavigationFailed(!didNavigate);\n }, [navigateToTopWindow, openInTopWindow, topWindowHref]);\n\n useEffect(() => {\n if (!embedInput || (openInTopWindow && !topWindowNavigationFailed)) return;\n let cancelled = false;\n setEmbedUrl(null);\n setEmbedError(null);\n setIsDirectFallback(false);\n const createSession = workspaceSsoEnabled\n ? createWorkspaceSsoEmbedSession\n : createEmbedSession;\n void createSession\n .mutateAsync(embedInput)\n .then((result) => {\n if (!cancelled) setEmbedUrl(result.startUrl);\n })\n .catch((cause: unknown) => {\n if (cancelled) return;\n const error = cause instanceof Error ? cause : new Error(String(cause));\n if (workspaceSsoEnabled) {\n // An SSO-enabled pane must never fall back to the child app's\n // unauthenticated shell. Keep the parent-owned retry surface in\n // place so a transient exchange failure cannot expose another\n // login form.\n setIsDirectFallback(false);\n setEmbedUrl(null);\n setEmbedError(error);\n return;\n }\n setIsDirectFallback(true);\n setEmbedUrl(\n workspaceAppDirectHref(\n { path: app.path ?? \"\", url: app.url },\n embedPath ?? \"/\",\n ),\n );\n setEmbedError(error);\n });\n return () => {\n cancelled = true;\n };\n }, [\n app.id,\n app.path,\n app.url,\n createEmbedSession.mutateAsync,\n createWorkspaceSsoEmbedSession.mutateAsync,\n embedInput,\n embedPath,\n embedAttempt,\n openInTopWindow,\n topWindowNavigationFailed,\n workspaceSsoEnabled,\n ]);\n\n useEffect(() => {\n const handleEmbedSessionExpired = (event: MessageEvent) => {\n if (\n !isEmbedSessionExpiredMessage(event, embedFrameRef.current, embedUrl)\n ) {\n return;\n }\n setEmbedAttempt((attempt) => attempt + 1);\n };\n\n window.addEventListener(\"message\", handleEmbedSessionExpired);\n return () =>\n window.removeEventListener(\"message\", handleEmbedSessionExpired);\n }, [embedUrl]);\n\n useEffect(() => {\n postThemeToFrame();\n }, [embedUrl, postThemeToFrame]);\n\n const appPane = (\n <ChatFirstAppPane\n app={app}\n status={\n embedUrl\n ? \"ready\"\n : embedError\n ? \"error\"\n : embedInput\n ? \"loading\"\n : \"unresolved\"\n }\n embedUrl={embedUrl}\n errorMessage={embedError?.message}\n onRetry={\n embedInput ? () => setEmbedAttempt((attempt) => attempt + 1) : undefined\n }\n renderEmbed={({ url, title }) => (\n <iframe\n key={url + \":\" + embedAttempt}\n data-dispatch-workspace-app-frame\n src={url}\n title={title ?? app.name}\n ref={embedFrameRef}\n onLoad={handleFrameLoad}\n referrerPolicy=\"no-referrer\"\n allow=\"clipboard-read; clipboard-write\"\n className=\"h-full w-full border-0 bg-background\"\n />\n )}\n copy={copy}\n />\n );\n\n if (!chatSidebar) return appPane;\n\n return (\n <WorkspaceAppChatRail appId={app.id} appName={app.name} copy={copy}>\n {appPane}\n </WorkspaceAppChatRail>\n );\n}\n\nexport function WorkspaceAppHost({\n appId,\n navigateToTopWindow = navigateToWorkspaceApp,\n}: {\n appId?: string;\n navigateToTopWindow?: (href: string) => boolean | void;\n}) {\n const t = useT();\n const workspaceAppsQuery = useActionQuery<WorkspaceAppSummary[]>(\n \"list-workspace-apps\",\n { includeAgentCards: false },\n );\n const grantedAppsQuery = useActionQuery<GrantedWorkspaceAppsResult>(\n \"list_apps\",\n {},\n );\n const apps = useMemo(() => {\n const merged = new Map<string, WorkspaceAppSummary>();\n\n for (const app of mergeChatFirstWorkspaceApps(workspaceAppsQuery.data)) {\n merged.set(app.id.trim().toLowerCase(), app);\n }\n for (const app of grantedAppsQuery.data?.apps ?? []) {\n const id = app.id.trim();\n if (!id || merged.has(id.toLowerCase())) continue;\n merged.set(id.toLowerCase(), {\n id,\n name: app.name.trim() || id,\n path: \"\",\n url: app.url?.trim() || null,\n status: \"ready\",\n });\n }\n\n return [...merged.values()];\n }, [grantedAppsQuery.data?.apps, workspaceAppsQuery.data]);\n const app = useMemo(\n () =>\n apps.find(\n (item) => item.id.trim().toLowerCase() === appId?.trim().toLowerCase(),\n ) ?? null,\n [appId, apps],\n );\n const isLoading = workspaceAppsQuery.isLoading || grantedAppsQuery.isLoading;\n const queryError = workspaceAppsQuery.isError\n ? workspaceAppsQuery.error\n : grantedAppsQuery.isError\n ? grantedAppsQuery.error\n : null;\n\n if (queryError && !app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl\">\n <ActionQueryError\n error={queryError}\n onRetry={() => {\n void workspaceAppsQuery.refetch();\n void grantedAppsQuery.refetch();\n }}\n />\n </div>\n </div>\n );\n }\n\n if (isLoading && !app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl space-y-3 rounded-xl border bg-card p-6\">\n <Skeleton className=\"h-5 w-48\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-2/3\" />\n </div>\n </div>\n );\n }\n\n if (!app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl rounded-xl border bg-card p-6\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.appNotFound\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.pageNotFoundDescription\")}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (app.status === \"pending\") {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl rounded-xl border bg-card p-6\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n {t(\"dispatch.pages.building\")}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.appBuildingPrefix\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n {t(\"dispatch.pages.appBuildingSuffix\")}\n </p>\n {app.builderUrl ? (\n <Button asChild>\n <a\n href={withBuilderUtmTrackingParams(app.builderUrl, {\n campaign: \"product\",\n content: \"dispatch_branch\",\n })}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {t(\"dispatch.pages.openBuilderBranch\", {\n defaultValue: \"Open in Builder\",\n })}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-dispatch-workspace-app-host\n className=\"flex h-full min-h-0 flex-col bg-background\"\n >\n <div className=\"min-h-0 flex-1 bg-muted/20\">\n <WorkspaceAppFrame\n app={app}\n navigateToTopWindow={navigateToTopWindow}\n />\n </div>\n </div>\n );\n}\n\nconst MAX_KEEP_ALIVE_APPS = 3;\n\nexport function WorkspaceAppKeepAlive({\n activeAppId,\n}: {\n activeAppId: string | null;\n}) {\n const [visitedAppIds, setVisitedAppIds] = useState<string[]>(() =>\n activeAppId ? [activeAppId] : [],\n );\n\n useEffect(() => {\n if (!activeAppId) return;\n setVisitedAppIds((current) =>\n [activeAppId, ...current.filter((appId) => appId !== activeAppId)].slice(\n 0,\n MAX_KEEP_ALIVE_APPS,\n ),\n );\n }, [activeAppId]);\n\n const renderedAppIds =\n activeAppId && !visitedAppIds.includes(activeAppId)\n ? [activeAppId, ...visitedAppIds]\n : visitedAppIds;\n\n return (\n <div\n data-dispatch-workspace-app-cache\n className={activeAppId ? \"absolute inset-0 overflow-hidden\" : \"hidden\"}\n >\n {renderedAppIds.map((appId) => {\n const active = appId === activeAppId;\n return (\n <div\n key={appId}\n data-dispatch-workspace-app-cache-entry={appId}\n aria-hidden={!active}\n className={active ? \"h-full min-h-0\" : \"hidden\"}\n >\n <WorkspaceAppHost appId={appId} />\n </div>\n );\n })}\n </div>\n );\n}\n"]}
|
|
@@ -43,6 +43,8 @@ export declare function workspaceAppEmbedTarget(app: Pick<WorkspaceAppSummary, "
|
|
|
43
43
|
*/
|
|
44
44
|
export declare function workspaceAppDirectHref(app: WorkspaceAppHrefSource, targetPath: string): string | null;
|
|
45
45
|
export declare function isPendingBuilderHref(app: WorkspaceAppSummary): boolean;
|
|
46
|
+
export declare function shouldOpenWorkspaceAppInTopWindow(): boolean;
|
|
47
|
+
export declare function navigateToWorkspaceApp(href: string): boolean;
|
|
46
48
|
/**
|
|
47
49
|
* Keep the chat-first rail useful before a workspace manifest is populated.
|
|
48
50
|
* Mounted workspace rows still win, so custom names and routes remain the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-apps.d.ts","sourceRoot":"","sources":["../../src/lib/workspace-apps.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workspace-apps.d.ts","sourceRoot":"","sources":["../../src/lib/workspace-apps.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,UAAU,sBAAsB;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGpE;AAED,wBAAgB,uCAAuC,CACrD,GAAG,EAAE,IAAI,CAAC,mBAAmB,EAAE,IAAI,GAAG,YAAY,CAAC,GAClD,OAAO,CAET;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUvE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAUxE;AAED,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,GAAG,KAAK,CAAC,GAC7C;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAMjC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,sBAAsB,EAC3B,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CA0Df;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAEtE;AAED,wBAAgB,iCAAiC,IAAI,OAAO,CAG3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAgB5D;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,SAAS,mBAAmB,EAAE,GAAG,SAAS,GAC/C,mBAAmB,EAAE,CAiBvB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CHAT_FIRST_DEFAULT_APP_IDS } from "@agent-native/core/client/chat-first";
|
|
2
|
+
import { isInBuilderFrame } from "@agent-native/core/client/host";
|
|
2
3
|
import { withBuilderUtmTrackingParams } from "@agent-native/core/shared/builder-link-tracking";
|
|
3
4
|
import { CANONICAL_WORKSPACE_SSO_APP_ORIGINS } from "../shared/workspace-sso.js";
|
|
4
5
|
export function isDispatchWorkspaceAppId(appId) {
|
|
@@ -107,6 +108,28 @@ export function workspaceAppDirectHref(app, targetPath) {
|
|
|
107
108
|
export function isPendingBuilderHref(app) {
|
|
108
109
|
return app.status === "pending" && !!app.builderUrl;
|
|
109
110
|
}
|
|
111
|
+
export function shouldOpenWorkspaceAppInTopWindow() {
|
|
112
|
+
if (typeof window === "undefined")
|
|
113
|
+
return false;
|
|
114
|
+
return isInBuilderFrame() || window.parent !== window;
|
|
115
|
+
}
|
|
116
|
+
export function navigateToWorkspaceApp(href) {
|
|
117
|
+
if (typeof window === "undefined")
|
|
118
|
+
return false;
|
|
119
|
+
try {
|
|
120
|
+
const targetUrl = new URL(href, window.location.href);
|
|
121
|
+
if (targetUrl.protocol !== "http:" && targetUrl.protocol !== "https:") {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const targetWindow = shouldOpenWorkspaceAppInTopWindow() && window.top ? window.top : window;
|
|
125
|
+
targetWindow.location.href = targetUrl.href;
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// coercion-ok: a blocked top-window assignment is an expected fallback signal.
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
110
133
|
/**
|
|
111
134
|
* Keep the chat-first rail useful before a workspace manifest is populated.
|
|
112
135
|
* Mounted workspace rows still win, so custom names and routes remain the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-apps.js","sourceRoot":"","sources":["../../src/lib/workspace-apps.ts"],"names":[],"mappings":"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","sourcesContent":["import { CHAT_FIRST_DEFAULT_APP_IDS } from \"@agent-native/core/client/chat-first\";\nimport { withBuilderUtmTrackingParams } from \"@agent-native/core/shared/builder-link-tracking\";\n\nimport { CANONICAL_WORKSPACE_SSO_APP_ORIGINS } from \"../shared/workspace-sso\";\n\nexport interface WorkspaceAppSummary {\n id: string;\n name: string;\n description?: string;\n path: string;\n url?: string | null;\n isDispatch?: boolean;\n audience?: \"internal\" | \"public\";\n publicPaths?: string[];\n protectedPaths?: string[];\n status?: \"ready\" | \"pending\";\n statusLabel?: string;\n builderUrl?: string | null;\n branchName?: string | null;\n createdAt?: string | null;\n createdBy?: string | null;\n owner?: string | null;\n teams?: string[];\n agentCardUrl?: string | null;\n agentCardReachable?: boolean;\n a2aEndpointUrl?: string | null;\n agentName?: string | null;\n agentSkillsCount?: number | null;\n archived?: boolean;\n}\n\ninterface WorkspaceAppHrefSource {\n path?: string | null;\n url?: string | null;\n}\n\nexport function isDispatchWorkspaceAppId(appId: string): boolean {\n return appId.trim().toLowerCase() === \"dispatch\";\n}\n\nexport function isDefaultWorkspaceAppHiddenId(appId: string): boolean {\n const normalized = appId.trim().toLowerCase();\n return normalized === \"chat\" || isDispatchWorkspaceAppId(normalized);\n}\n\nexport function isWorkspaceAppVisibleInDefaultLaunchers(\n app: Pick<WorkspaceAppSummary, \"id\" | \"isDispatch\">,\n): boolean {\n return !app.isDispatch && !isDefaultWorkspaceAppHiddenId(app.id);\n}\n\nexport function workspaceAppRoute(appId: string): string {\n return `/apps/${encodeURIComponent(appId)}`;\n}\n\nexport function workspaceAppIdFromRoute(pathname: string): string | null {\n const match = pathname.match(/^\\/apps\\/([^/]+)(?:\\/|$)/);\n if (!match) return null;\n try {\n const appId = decodeURIComponent(match[1]).trim();\n return appId || null;\n } catch {\n // coercion-ok: malformed app routes are inactive, not app ids.\n return null;\n }\n}\n\nexport function workspaceAppHref(app: WorkspaceAppSummary): string | null {\n if (app.status === \"pending\") {\n return app.builderUrl\n ? withBuilderUtmTrackingParams(app.builderUrl, {\n campaign: \"product\",\n content: \"dispatch_branch\",\n })\n : null;\n }\n return app.path || app.url || null;\n}\n\nexport function workspaceAppEmbedTarget(\n app: Pick<WorkspaceAppSummary, \"path\" | \"url\">,\n): { path?: string; url?: string } {\n const url = app.url?.trim();\n if (url) return { url };\n\n const path = app.path.trim();\n return path.startsWith(\"/\") ? { path } : path ? { url: path } : {};\n}\n\n/**\n * Resolve an app route without an embed ticket so the target can render its\n * own error document when session setup fails.\n */\nexport function workspaceAppDirectHref(\n app: WorkspaceAppHrefSource,\n targetPath: string,\n): string | null {\n const target = targetPath.trim();\n if (!target || !target.startsWith(\"/\") || target.startsWith(\"//\")) {\n return null;\n }\n\n let targetUrl: URL;\n try {\n targetUrl = new URL(target, \"https://agent-native.invalid\");\n } catch {\n // coercion-ok: invalid relative target input has no safe href.\n return null;\n }\n const targetPathname = targetUrl.pathname || \"/\";\n\n let absoluteBase: URL | null = null;\n const rawUrl = app.url?.trim();\n if (rawUrl) {\n try {\n const parsedBase = new URL(rawUrl);\n if (parsedBase.protocol === \"http:\" || parsedBase.protocol === \"https:\") {\n absoluteBase = parsedBase;\n }\n // coercion-ok: invalid app URLs use the mounted path fallback below.\n } catch {\n absoluteBase = null;\n }\n }\n\n const mountedPath = app.path?.trim();\n const basePath = absoluteBase\n ? absoluteBase.pathname\n : mountedPath\n ? `/${mountedPath.replace(/^[/\\\\]+/, \"\").split(/[?#]/, 1)[0]}`\n : null;\n if (!basePath) return null;\n\n const normalizedBasePath = basePath.replace(/\\/+$/, \"\") || \"/\";\n const targetIsMountedPath =\n normalizedBasePath !== \"/\" &&\n (targetPathname === normalizedBasePath ||\n targetPathname.startsWith(`${normalizedBasePath}/`));\n const resolvedPath = targetIsMountedPath\n ? targetPathname\n : normalizedBasePath === \"/\"\n ? targetPathname\n : targetPathname === \"/\"\n ? normalizedBasePath\n : `${normalizedBasePath}/${targetPathname.replace(/^\\/+/, \"\")}`;\n\n if (absoluteBase) {\n absoluteBase.pathname = resolvedPath;\n absoluteBase.search = targetUrl.search;\n absoluteBase.hash = targetUrl.hash;\n return absoluteBase.toString();\n }\n\n return `${resolvedPath}${targetUrl.search}${targetUrl.hash}`;\n}\n\nexport function isPendingBuilderHref(app: WorkspaceAppSummary): boolean {\n return app.status === \"pending\" && !!app.builderUrl;\n}\n\n/**\n * Keep the chat-first rail useful before a workspace manifest is populated.\n * Mounted workspace rows still win, so custom names and routes remain the\n * source of truth once an app exists in the workspace.\n */\nexport function mergeChatFirstWorkspaceApps(\n apps: readonly WorkspaceAppSummary[] | undefined,\n): WorkspaceAppSummary[] {\n const merged = new Map<string, WorkspaceAppSummary>();\n for (const id of CHAT_FIRST_DEFAULT_APP_IDS) {\n merged.set(id, {\n id,\n name: id.charAt(0).toUpperCase() + id.slice(1),\n // The five default rows are hosted sibling apps, not routes owned by\n // Dispatch. Keep a mounted path for legacy callers, but give embed\n // session resolution the exact canonical origin.\n path: \"/\",\n url: CANONICAL_WORKSPACE_SSO_APP_ORIGINS[id],\n status: \"ready\",\n });\n }\n for (const app of apps ?? []) merged.set(app.id, app);\n\n return [...merged.values()];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workspace-apps.js","sourceRoot":"","sources":["../../src/lib/workspace-apps.ts"],"names":[],"mappings":"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","sourcesContent":["import { CHAT_FIRST_DEFAULT_APP_IDS } from \"@agent-native/core/client/chat-first\";\nimport { isInBuilderFrame } from \"@agent-native/core/client/host\";\nimport { withBuilderUtmTrackingParams } from \"@agent-native/core/shared/builder-link-tracking\";\n\nimport { CANONICAL_WORKSPACE_SSO_APP_ORIGINS } from \"../shared/workspace-sso\";\n\nexport interface WorkspaceAppSummary {\n id: string;\n name: string;\n description?: string;\n path: string;\n url?: string | null;\n isDispatch?: boolean;\n audience?: \"internal\" | \"public\";\n publicPaths?: string[];\n protectedPaths?: string[];\n status?: \"ready\" | \"pending\";\n statusLabel?: string;\n builderUrl?: string | null;\n branchName?: string | null;\n createdAt?: string | null;\n createdBy?: string | null;\n owner?: string | null;\n teams?: string[];\n agentCardUrl?: string | null;\n agentCardReachable?: boolean;\n a2aEndpointUrl?: string | null;\n agentName?: string | null;\n agentSkillsCount?: number | null;\n archived?: boolean;\n}\n\ninterface WorkspaceAppHrefSource {\n path?: string | null;\n url?: string | null;\n}\n\nexport function isDispatchWorkspaceAppId(appId: string): boolean {\n return appId.trim().toLowerCase() === \"dispatch\";\n}\n\nexport function isDefaultWorkspaceAppHiddenId(appId: string): boolean {\n const normalized = appId.trim().toLowerCase();\n return normalized === \"chat\" || isDispatchWorkspaceAppId(normalized);\n}\n\nexport function isWorkspaceAppVisibleInDefaultLaunchers(\n app: Pick<WorkspaceAppSummary, \"id\" | \"isDispatch\">,\n): boolean {\n return !app.isDispatch && !isDefaultWorkspaceAppHiddenId(app.id);\n}\n\nexport function workspaceAppRoute(appId: string): string {\n return `/apps/${encodeURIComponent(appId)}`;\n}\n\nexport function workspaceAppIdFromRoute(pathname: string): string | null {\n const match = pathname.match(/^\\/apps\\/([^/]+)(?:\\/|$)/);\n if (!match) return null;\n try {\n const appId = decodeURIComponent(match[1]).trim();\n return appId || null;\n } catch {\n // coercion-ok: malformed app routes are inactive, not app ids.\n return null;\n }\n}\n\nexport function workspaceAppHref(app: WorkspaceAppSummary): string | null {\n if (app.status === \"pending\") {\n return app.builderUrl\n ? withBuilderUtmTrackingParams(app.builderUrl, {\n campaign: \"product\",\n content: \"dispatch_branch\",\n })\n : null;\n }\n return app.path || app.url || null;\n}\n\nexport function workspaceAppEmbedTarget(\n app: Pick<WorkspaceAppSummary, \"path\" | \"url\">,\n): { path?: string; url?: string } {\n const url = app.url?.trim();\n if (url) return { url };\n\n const path = app.path.trim();\n return path.startsWith(\"/\") ? { path } : path ? { url: path } : {};\n}\n\n/**\n * Resolve an app route without an embed ticket so the target can render its\n * own error document when session setup fails.\n */\nexport function workspaceAppDirectHref(\n app: WorkspaceAppHrefSource,\n targetPath: string,\n): string | null {\n const target = targetPath.trim();\n if (!target || !target.startsWith(\"/\") || target.startsWith(\"//\")) {\n return null;\n }\n\n let targetUrl: URL;\n try {\n targetUrl = new URL(target, \"https://agent-native.invalid\");\n } catch {\n // coercion-ok: invalid relative target input has no safe href.\n return null;\n }\n const targetPathname = targetUrl.pathname || \"/\";\n\n let absoluteBase: URL | null = null;\n const rawUrl = app.url?.trim();\n if (rawUrl) {\n try {\n const parsedBase = new URL(rawUrl);\n if (parsedBase.protocol === \"http:\" || parsedBase.protocol === \"https:\") {\n absoluteBase = parsedBase;\n }\n // coercion-ok: invalid app URLs use the mounted path fallback below.\n } catch {\n absoluteBase = null;\n }\n }\n\n const mountedPath = app.path?.trim();\n const basePath = absoluteBase\n ? absoluteBase.pathname\n : mountedPath\n ? `/${mountedPath.replace(/^[/\\\\]+/, \"\").split(/[?#]/, 1)[0]}`\n : null;\n if (!basePath) return null;\n\n const normalizedBasePath = basePath.replace(/\\/+$/, \"\") || \"/\";\n const targetIsMountedPath =\n normalizedBasePath !== \"/\" &&\n (targetPathname === normalizedBasePath ||\n targetPathname.startsWith(`${normalizedBasePath}/`));\n const resolvedPath = targetIsMountedPath\n ? targetPathname\n : normalizedBasePath === \"/\"\n ? targetPathname\n : targetPathname === \"/\"\n ? normalizedBasePath\n : `${normalizedBasePath}/${targetPathname.replace(/^\\/+/, \"\")}`;\n\n if (absoluteBase) {\n absoluteBase.pathname = resolvedPath;\n absoluteBase.search = targetUrl.search;\n absoluteBase.hash = targetUrl.hash;\n return absoluteBase.toString();\n }\n\n return `${resolvedPath}${targetUrl.search}${targetUrl.hash}`;\n}\n\nexport function isPendingBuilderHref(app: WorkspaceAppSummary): boolean {\n return app.status === \"pending\" && !!app.builderUrl;\n}\n\nexport function shouldOpenWorkspaceAppInTopWindow(): boolean {\n if (typeof window === \"undefined\") return false;\n return isInBuilderFrame() || window.parent !== window;\n}\n\nexport function navigateToWorkspaceApp(href: string): boolean {\n if (typeof window === \"undefined\") return false;\n\n try {\n const targetUrl = new URL(href, window.location.href);\n if (targetUrl.protocol !== \"http:\" && targetUrl.protocol !== \"https:\") {\n return false;\n }\n const targetWindow =\n shouldOpenWorkspaceAppInTopWindow() && window.top ? window.top : window;\n targetWindow.location.href = targetUrl.href;\n return true;\n } catch {\n // coercion-ok: a blocked top-window assignment is an expected fallback signal.\n return false;\n }\n}\n\n/**\n * Keep the chat-first rail useful before a workspace manifest is populated.\n * Mounted workspace rows still win, so custom names and routes remain the\n * source of truth once an app exists in the workspace.\n */\nexport function mergeChatFirstWorkspaceApps(\n apps: readonly WorkspaceAppSummary[] | undefined,\n): WorkspaceAppSummary[] {\n const merged = new Map<string, WorkspaceAppSummary>();\n for (const id of CHAT_FIRST_DEFAULT_APP_IDS) {\n merged.set(id, {\n id,\n name: id.charAt(0).toUpperCase() + id.slice(1),\n // The five default rows are hosted sibling apps, not routes owned by\n // Dispatch. Keep a mounted path for legacy callers, but give embed\n // session resolution the exact canonical origin.\n path: \"/\",\n url: CANONICAL_WORKSPACE_SSO_APP_ORIGINS[id],\n status: \"ready\",\n });\n }\n for (const app of apps ?? []) merged.set(app.id, app);\n\n return [...merged.values()];\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { navigateToWorkspaceApp, workspaceAppDirectHref, workspaceAppEmbedTarget, } from "./workspace-apps.js";
|
|
3
4
|
describe("workspaceAppEmbedTarget", () => {
|
|
4
5
|
it("uses the app URL as the embed root when a mount path is also present", () => {
|
|
5
6
|
expect(workspaceAppEmbedTarget({
|
|
@@ -30,4 +31,61 @@ describe("workspaceAppDirectHref", () => {
|
|
|
30
31
|
expect(workspaceAppDirectHref({ path: "/atlas" }, "//evil.example")).toBe(null);
|
|
31
32
|
});
|
|
32
33
|
});
|
|
34
|
+
describe("navigateToWorkspaceApp", () => {
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
Object.defineProperty(window, "parent", {
|
|
37
|
+
configurable: true,
|
|
38
|
+
value: window,
|
|
39
|
+
});
|
|
40
|
+
Object.defineProperty(window, "top", {
|
|
41
|
+
configurable: true,
|
|
42
|
+
value: window,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
it("uses the top window when Dispatch is embedded", () => {
|
|
46
|
+
const topWindow = { location: { href: "" } };
|
|
47
|
+
Object.defineProperty(window, "parent", {
|
|
48
|
+
configurable: true,
|
|
49
|
+
value: {},
|
|
50
|
+
});
|
|
51
|
+
Object.defineProperty(window, "top", {
|
|
52
|
+
configurable: true,
|
|
53
|
+
value: topWindow,
|
|
54
|
+
});
|
|
55
|
+
navigateToWorkspaceApp("/mail");
|
|
56
|
+
expect(topWindow.location.href).toBe(new URL("/mail", window.location.href).href);
|
|
57
|
+
});
|
|
58
|
+
it("reports when the embedded top window rejects navigation", () => {
|
|
59
|
+
const location = {};
|
|
60
|
+
Object.defineProperty(location, "href", {
|
|
61
|
+
configurable: true,
|
|
62
|
+
set: () => {
|
|
63
|
+
throw new Error("Top navigation is blocked");
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
const topWindow = { location };
|
|
67
|
+
Object.defineProperty(window, "parent", {
|
|
68
|
+
configurable: true,
|
|
69
|
+
value: {},
|
|
70
|
+
});
|
|
71
|
+
Object.defineProperty(window, "top", {
|
|
72
|
+
configurable: true,
|
|
73
|
+
value: topWindow,
|
|
74
|
+
});
|
|
75
|
+
expect(navigateToWorkspaceApp("/mail")).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
it("rejects non-http navigation targets", () => {
|
|
78
|
+
const topWindow = { location: { href: "" } };
|
|
79
|
+
Object.defineProperty(window, "parent", {
|
|
80
|
+
configurable: true,
|
|
81
|
+
value: {},
|
|
82
|
+
});
|
|
83
|
+
Object.defineProperty(window, "top", {
|
|
84
|
+
configurable: true,
|
|
85
|
+
value: topWindow,
|
|
86
|
+
});
|
|
87
|
+
expect(navigateToWorkspaceApp("javascript:alert(1)")).toBe(false);
|
|
88
|
+
expect(topWindow.location.href).toBe("");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
33
91
|
//# sourceMappingURL=workspace-apps.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-apps.test.js","sourceRoot":"","sources":["../../src/lib/workspace-apps.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace-apps.test.js","sourceRoot":"","sources":["../../src/lib/workspace-apps.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment happy-dom\nimport { afterEach, describe, expect, it } from \"vitest\";\n\nimport {\n navigateToWorkspaceApp,\n workspaceAppDirectHref,\n workspaceAppEmbedTarget,\n} from \"./workspace-apps\";\n\ndescribe(\"workspaceAppEmbedTarget\", () => {\n it(\"uses the app URL as the embed root when a mount path is also present\", () => {\n expect(\n workspaceAppEmbedTarget({\n path: \"/content\",\n url: \"https://workspace.example.test/content\",\n }),\n ).toEqual({ url: \"https://workspace.example.test/content\" });\n });\n\n it(\"falls back to the mounted path when no app URL is available\", () => {\n expect(workspaceAppEmbedTarget({ path: \"/content\", url: null })).toEqual({\n path: \"/content\",\n });\n });\n});\n\ndescribe(\"workspaceAppDirectHref\", () => {\n it(\"joins an app-relative route onto an absolute app URL\", () => {\n expect(\n workspaceAppDirectHref(\n { path: \"/atlas\", url: \"https://workspace.example.test/atlas\" },\n \"/emails?status=failed#latest\",\n ),\n ).toBe(\"https://workspace.example.test/atlas/emails?status=failed#latest\");\n });\n\n it(\"does not duplicate a mount path already present in the target\", () => {\n expect(\n workspaceAppDirectHref(\n { path: \"/atlas\", url: \"https://workspace.example.test/atlas\" },\n \"/atlas\",\n ),\n ).toBe(\"https://workspace.example.test/atlas\");\n });\n\n it(\"resolves a mounted relative app path\", () => {\n expect(workspaceAppDirectHref({ path: \"/atlas\" }, \"/emails\")).toBe(\n \"/atlas/emails\",\n );\n });\n\n it(\"falls back to a safe mount path when the app URL is invalid\", () => {\n expect(\n workspaceAppDirectHref(\n { path: \"/atlas\", url: \"javascript:alert(1)\" },\n \"/\",\n ),\n ).toBe(\"/atlas\");\n });\n\n it(\"rejects an unsafe target path\", () => {\n expect(workspaceAppDirectHref({ path: \"/atlas\" }, \"//evil.example\")).toBe(\n null,\n );\n });\n});\n\ndescribe(\"navigateToWorkspaceApp\", () => {\n afterEach(() => {\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: window,\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: window,\n });\n });\n\n it(\"uses the top window when Dispatch is embedded\", () => {\n const topWindow = { location: { href: \"\" } } as unknown as Window;\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: topWindow,\n });\n\n navigateToWorkspaceApp(\"/mail\");\n\n expect(topWindow.location.href).toBe(\n new URL(\"/mail\", window.location.href).href,\n );\n });\n\n it(\"reports when the embedded top window rejects navigation\", () => {\n const location = {} as Location;\n Object.defineProperty(location, \"href\", {\n configurable: true,\n set: () => {\n throw new Error(\"Top navigation is blocked\");\n },\n });\n const topWindow = { location } as unknown as Window;\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: topWindow,\n });\n\n expect(navigateToWorkspaceApp(\"/mail\")).toBe(false);\n });\n\n it(\"rejects non-http navigation targets\", () => {\n const topWindow = { location: { href: \"\" } } as unknown as Window;\n Object.defineProperty(window, \"parent\", {\n configurable: true,\n value: {},\n });\n Object.defineProperty(window, \"top\", {\n configurable: true,\n value: topWindow,\n });\n\n expect(navigateToWorkspaceApp(\"javascript:alert(1)\")).toBe(false);\n expect(topWindow.location.href).toBe(\"\");\n });\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"$appId.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/$appId.tsx"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"$appId.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/$appId.tsx"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AActB,wBAAgB,IAAI;;IAEnB;AA2CD,wBAAsB,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,kBAAkB,iBAQ1D;AAED,wBAAsB,YAAY,CAAC,EACjC,MAAM,EACN,YAAY,GACb,EAAE,wBAAwB,oBAS1B;AAED,MAAM,CAAC,OAAO,UAAU,yBAAyB,oDA0IhD"}
|
|
@@ -4,7 +4,7 @@ import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
|
4
4
|
import { useT } from "@agent-native/core/client/i18n";
|
|
5
5
|
import { withBuilderUtmTrackingParams } from "@agent-native/core/shared/builder-link-tracking";
|
|
6
6
|
import { IconArrowLeft, IconArrowUpRight, IconClockHour4, } from "@tabler/icons-react";
|
|
7
|
-
import { useEffect, useMemo } from "react";
|
|
7
|
+
import { useEffect, useMemo, useState } from "react";
|
|
8
8
|
import { Link, Navigate, redirect, useParams, } from "react-router";
|
|
9
9
|
import { ActionQueryError } from "../../components/action-query-error.js";
|
|
10
10
|
import { DispatchShell } from "../../components/dispatch-shell.js";
|
|
@@ -12,7 +12,7 @@ import { Badge } from "../../components/ui/badge.js";
|
|
|
12
12
|
import { Button } from "../../components/ui/button.js";
|
|
13
13
|
import { Spinner } from "../../components/ui/spinner.js";
|
|
14
14
|
import { resolveServerCatchAllTarget } from "../../lib/catch-all-target.js";
|
|
15
|
-
import { workspaceAppHref, } from "../../lib/workspace-apps.js";
|
|
15
|
+
import { navigateToWorkspaceApp, workspaceAppHref, } from "../../lib/workspace-apps.js";
|
|
16
16
|
export function meta() {
|
|
17
17
|
return [{ title: "Workspace app - Dispatch" }];
|
|
18
18
|
}
|
|
@@ -90,20 +90,29 @@ export default function WorkspaceAppCatchAllRoute() {
|
|
|
90
90
|
const app = useMemo(() => apps.find((item) => item.id === appId) ?? null, [appId, apps]);
|
|
91
91
|
const href = app ? workspaceAppHref(app) : null;
|
|
92
92
|
const isSelfReference = appId === "dispatch";
|
|
93
|
+
const hasApp = app !== null;
|
|
94
|
+
const appIsPending = app?.status === "pending";
|
|
95
|
+
const [navigationFailed, setNavigationFailed] = useState(false);
|
|
93
96
|
useEffect(() => {
|
|
94
97
|
if (isSelfReference)
|
|
95
98
|
return;
|
|
96
|
-
if (!
|
|
99
|
+
if (!hasApp || appIsPending || !href) {
|
|
100
|
+
setNavigationFailed(false);
|
|
97
101
|
return;
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
}
|
|
103
|
+
setNavigationFailed(!navigateToWorkspaceApp(href));
|
|
104
|
+
}, [appIsPending, hasApp, href, isSelfReference]);
|
|
100
105
|
if (isSelfReference) {
|
|
101
106
|
return _jsx(Navigate, { to: appPath("/overview"), replace: true });
|
|
102
107
|
}
|
|
103
108
|
if (appsQuery.isError) {
|
|
104
109
|
return (_jsx(DispatchShell, { title: t("dispatch.pages.dataLoadFailed"), description: t("dispatch.pages.pageNotFoundDescription"), children: _jsx(ActionQueryError, { error: appsQuery.error, onRetry: () => void appsQuery.refetch() }) }));
|
|
105
110
|
}
|
|
106
|
-
if (
|
|
111
|
+
if (navigationFailed && href) {
|
|
112
|
+
return (_jsx(DispatchShell, { title: t("dispatch.pages.dataLoadFailed"), description: t("dispatch.pages.pageNotFoundDescription"), children: _jsx(ActionQueryError, { onRetry: () => setNavigationFailed(!navigateToWorkspaceApp(href)) }) }));
|
|
113
|
+
}
|
|
114
|
+
if ((isLoading && !app) ||
|
|
115
|
+
(app && app.status !== "pending" && href && !navigationFailed)) {
|
|
107
116
|
return (_jsx("div", { className: "flex h-screen w-full items-center justify-center", children: _jsx(Spinner, { className: "size-8" }) }));
|
|
108
117
|
}
|
|
109
118
|
return (_jsx(DispatchShell, { title: app?.name || t("dispatch.pages.pageNotFound"), description: t("dispatch.pages.pageNotFoundDescription"), children: _jsxs("div", { className: "max-w-2xl rounded-lg bg-card p-5", children: [_jsx(Button, { asChild: true, size: "sm", variant: "ghost", className: "-ml-2 mb-4", children: _jsxs(Link, { to: appPath("/overview"), children: [_jsx(IconArrowLeft, { size: 15, className: "mr-1.5" }), t("dispatch.nav.overview")] }) }), app?.status === "pending" ? (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("h2", { className: "text-base font-semibold text-foreground", children: app.name }), _jsxs(Badge, { variant: "outline", className: "gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300", children: [_jsx(IconClockHour4, { size: 12 }), t("dispatch.pages.building")] })] }), _jsxs("p", { className: "text-sm text-muted-foreground", children: [t("dispatch.pages.appBuildingPrefix"), " ", _jsx("span", { className: "font-mono text-foreground", children: app.path }), " ", t("dispatch.pages.appBuildingSuffix")] }), app.builderUrl ? (_jsx(Button, { asChild: true, children: _jsxs("a", { href: withBuilderUtmTrackingParams(app.builderUrl, {
|