@agent-native/core 0.87.0 → 0.88.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/docs/content/agent-native-toolkit.mdx +162 -0
- package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +73 -0
- package/corpus/core/docs/content/locales/ar-SA/sharing.mdx +15 -7
- package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +77 -0
- package/corpus/core/docs/content/locales/de-DE/sharing.mdx +15 -7
- package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +77 -0
- package/corpus/core/docs/content/locales/es-ES/sharing.mdx +15 -7
- package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +79 -0
- package/corpus/core/docs/content/locales/fr-FR/sharing.mdx +15 -7
- package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +74 -0
- package/corpus/core/docs/content/locales/hi-IN/sharing.mdx +15 -7
- package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +67 -0
- package/corpus/core/docs/content/locales/ja-JP/sharing.mdx +15 -7
- package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +63 -0
- package/corpus/core/docs/content/locales/ko-KR/sharing.mdx +15 -7
- package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +77 -0
- package/corpus/core/docs/content/locales/pt-BR/sharing.mdx +15 -7
- package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +61 -0
- package/corpus/core/docs/content/locales/zh-CN/sharing.mdx +15 -7
- package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +61 -0
- package/corpus/core/docs/content/locales/zh-TW/sharing.mdx +15 -7
- package/corpus/core/docs/content/sharing.mdx +39 -7
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/action-routes.ts +8 -1
- package/corpus/core/src/server/index.ts +9 -0
- package/corpus/core/src/shared/agent-readable-resource.ts +111 -0
- package/corpus/core/src/shared/index.ts +9 -0
- package/corpus/core/src/sharing/actions/create-agent-resource-link.ts +91 -0
- package/corpus/core/src/sharing/registry.ts +19 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +92 -0
- package/corpus/core/src/vite/client.ts +1 -0
- package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -113
- package/corpus/templates/analytics/server/db/index.ts +14 -0
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +156 -0
- package/corpus/templates/analytics/server/routes/[...page].get.ts +92 -25
- package/corpus/templates/analytics/server/routes/api/analysis-agent-context.json.get.ts +94 -0
- package/corpus/templates/analytics/server/routes/api/dashboard-agent-context.json.get.ts +109 -0
- package/corpus/templates/analytics/shared/resource-agent-access.ts +6 -0
- package/corpus/templates/content/app/routes/p.$id.tsx +114 -9
- package/corpus/templates/content/server/db/index.ts +9 -0
- package/corpus/templates/content/server/routes/api/document-agent-context.json.get.ts +90 -0
- package/corpus/templates/content/shared/agent-readable.ts +56 -0
- package/corpus/templates/design/server/db/index.ts +8 -0
- package/corpus/templates/design/server/routes/[...page].get.ts +94 -1
- package/corpus/templates/design/server/routes/api/design-agent-context.json.get.ts +84 -0
- package/corpus/templates/design/shared/agent-readable.ts +2 -0
- package/corpus/templates/plan/server/db/index.ts +12 -0
- package/corpus/templates/plan/server/plans.ts +25 -1
- package/corpus/templates/plan/server/routes/[...page].get.ts +107 -1
- package/corpus/templates/plan/server/routes/api/plan-agent-context.json.get.ts +65 -0
- package/corpus/templates/plan/shared/agent-readable.ts +2 -0
- package/corpus/templates/slides/app/routes/p.$id.tsx +126 -12
- package/corpus/templates/slides/app/routes/share.$token.tsx +55 -8
- package/corpus/templates/slides/server/db/index.ts +9 -0
- package/corpus/templates/slides/server/routes/api/deck-agent-context.json.get.ts +97 -0
- package/corpus/templates/slides/shared/agent-readable.ts +2 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +4 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +8 -2
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/shared/agent-readable-resource.d.ts +29 -0
- package/dist/shared/agent-readable-resource.d.ts.map +1 -0
- package/dist/shared/agent-readable-resource.js +60 -0
- package/dist/shared/agent-readable-resource.js.map +1 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +1 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/sharing/actions/create-agent-resource-link.d.ts +13 -0
- package/dist/sharing/actions/create-agent-resource-link.d.ts.map +1 -0
- package/dist/sharing/actions/create-agent-resource-link.js +72 -0
- package/dist/sharing/actions/create-agent-resource-link.js.map +1 -0
- package/dist/sharing/registry.d.ts +17 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +92 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +1 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/agent-native-toolkit.mdx +162 -0
- package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +73 -0
- package/docs/content/locales/ar-SA/sharing.mdx +15 -7
- package/docs/content/locales/de-DE/agent-native-toolkit.mdx +77 -0
- package/docs/content/locales/de-DE/sharing.mdx +15 -7
- package/docs/content/locales/es-ES/agent-native-toolkit.mdx +77 -0
- package/docs/content/locales/es-ES/sharing.mdx +15 -7
- package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +79 -0
- package/docs/content/locales/fr-FR/sharing.mdx +15 -7
- package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +74 -0
- package/docs/content/locales/hi-IN/sharing.mdx +15 -7
- package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +67 -0
- package/docs/content/locales/ja-JP/sharing.mdx +15 -7
- package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +63 -0
- package/docs/content/locales/ko-KR/sharing.mdx +15 -7
- package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +77 -0
- package/docs/content/locales/pt-BR/sharing.mdx +15 -7
- package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +61 -0
- package/docs/content/locales/zh-CN/sharing.mdx +15 -7
- package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +61 -0
- package/docs/content/locales/zh-TW/sharing.mdx +15 -7
- package/docs/content/sharing.mdx +39 -7
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +92 -0
|
@@ -1,5 +1,111 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_ACCESS_PARAM,
|
|
3
|
+
getConfiguredAppBasePath,
|
|
4
|
+
} from "@agent-native/core/server";
|
|
1
5
|
import { createH3SSRHandler } from "@agent-native/core/server/ssr-handler";
|
|
6
|
+
import {
|
|
7
|
+
buildAgentReadableResourceDiscovery,
|
|
8
|
+
renderAgentReadableResourceDiscoveryScript,
|
|
9
|
+
} from "@agent-native/core/shared";
|
|
10
|
+
import {
|
|
11
|
+
defineEventHandler,
|
|
12
|
+
getQuery,
|
|
13
|
+
getRequestURL,
|
|
14
|
+
setResponseHeader,
|
|
15
|
+
} from "h3";
|
|
2
16
|
|
|
3
|
-
|
|
17
|
+
import { PLAN_AGENT_CONTEXT_ENDPOINT } from "../../shared/agent-readable.js";
|
|
18
|
+
|
|
19
|
+
const ssrHandler = createH3SSRHandler(
|
|
4
20
|
() => import("virtual:react-router/server-build"),
|
|
5
21
|
);
|
|
22
|
+
|
|
23
|
+
function stripBasePath(pathname: string): string {
|
|
24
|
+
const basePath = getConfiguredAppBasePath();
|
|
25
|
+
if (!basePath) return pathname;
|
|
26
|
+
if (pathname === basePath) return "/";
|
|
27
|
+
if (pathname.startsWith(`${basePath}/`)) {
|
|
28
|
+
return pathname.slice(basePath.length) || "/";
|
|
29
|
+
}
|
|
30
|
+
return pathname;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function planFromPath(pathname: string): {
|
|
34
|
+
id: string;
|
|
35
|
+
kind: "plan" | "recap";
|
|
36
|
+
} | null {
|
|
37
|
+
const stripped = stripBasePath(pathname);
|
|
38
|
+
const match = stripped.match(/^\/(plans|recaps)\/([^/]+)\/?$/);
|
|
39
|
+
if (!match?.[1] || !match[2]) return null;
|
|
40
|
+
try {
|
|
41
|
+
return {
|
|
42
|
+
kind: match[1] === "recaps" ? "recap" : "plan",
|
|
43
|
+
id: decodeURIComponent(match[2]),
|
|
44
|
+
};
|
|
45
|
+
} catch {
|
|
46
|
+
return {
|
|
47
|
+
kind: match[1] === "recaps" ? "recap" : "plan",
|
|
48
|
+
id: match[2],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function queryString(value: unknown): string {
|
|
54
|
+
if (typeof value === "string") return value;
|
|
55
|
+
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function injectScript(html: string, script: string): string {
|
|
60
|
+
if (html.includes("agent-native-plan-agent-context")) return html;
|
|
61
|
+
if (html.includes("</head>"))
|
|
62
|
+
return html.replace("</head>", `${script}</head>`);
|
|
63
|
+
if (html.includes("</body>"))
|
|
64
|
+
return html.replace("</body>", `${script}</body>`);
|
|
65
|
+
return `${html}${script}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default defineEventHandler(async (event) => {
|
|
69
|
+
const response = (await ssrHandler(event)) as Response;
|
|
70
|
+
const requestUrl = getRequestURL(event);
|
|
71
|
+
const resource = planFromPath(requestUrl.pathname);
|
|
72
|
+
if (!resource) return response;
|
|
73
|
+
|
|
74
|
+
const token = queryString(getQuery(event)[AGENT_ACCESS_PARAM]);
|
|
75
|
+
const script = renderAgentReadableResourceDiscoveryScript(
|
|
76
|
+
buildAgentReadableResourceDiscovery({
|
|
77
|
+
resourceType: "plan",
|
|
78
|
+
resourceId: resource.id,
|
|
79
|
+
path:
|
|
80
|
+
resource.kind === "recap"
|
|
81
|
+
? `/recaps/${resource.id}`
|
|
82
|
+
: `/plans/${resource.id}`,
|
|
83
|
+
contextEndpoint: PLAN_AGENT_CONTEXT_ENDPOINT,
|
|
84
|
+
origin: requestUrl.origin,
|
|
85
|
+
basePath: getConfiguredAppBasePath(),
|
|
86
|
+
token,
|
|
87
|
+
instructions:
|
|
88
|
+
"Use contextUrl to read the visual plan bundle as structured JSON. Token links are read-only.",
|
|
89
|
+
}),
|
|
90
|
+
{ id: "agent-native-plan-agent-context" },
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
94
|
+
if (!contentType.includes("text/html")) return response;
|
|
95
|
+
|
|
96
|
+
const html = await response.text();
|
|
97
|
+
const headers = new Headers(response.headers);
|
|
98
|
+
headers.delete("content-length");
|
|
99
|
+
if (token) {
|
|
100
|
+
headers.set("Cache-Control", "private, max-age=0, no-store");
|
|
101
|
+
headers.set("Referrer-Policy", "no-referrer");
|
|
102
|
+
setResponseHeader(event, "Cache-Control", "private, max-age=0, no-store");
|
|
103
|
+
setResponseHeader(event, "Referrer-Policy", "no-referrer");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return new Response(injectScript(html, script), {
|
|
107
|
+
status: response.status,
|
|
108
|
+
statusText: response.statusText,
|
|
109
|
+
headers,
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_ACCESS_PARAM,
|
|
3
|
+
verifyScopedAgentAccessToken,
|
|
4
|
+
} from "@agent-native/core/server";
|
|
5
|
+
import {
|
|
6
|
+
defineEventHandler,
|
|
7
|
+
getQuery,
|
|
8
|
+
setResponseHeader,
|
|
9
|
+
setResponseStatus,
|
|
10
|
+
} from "h3";
|
|
11
|
+
|
|
12
|
+
import { PLAN_AGENT_RESOURCE_KIND } from "../../../shared/agent-readable.js";
|
|
13
|
+
import { loadPlanBundle, loadPlanBundleForAgentAccess } from "../../plans.js";
|
|
14
|
+
|
|
15
|
+
function queryString(value: unknown): string {
|
|
16
|
+
if (typeof value === "string") return value;
|
|
17
|
+
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default defineEventHandler(async (event) => {
|
|
22
|
+
setResponseHeader(event, "Cache-Control", "no-store");
|
|
23
|
+
setResponseHeader(event, "Referrer-Policy", "no-referrer");
|
|
24
|
+
setResponseHeader(event, "X-Content-Type-Options", "nosniff");
|
|
25
|
+
|
|
26
|
+
const query = getQuery(event);
|
|
27
|
+
const id = queryString(query.id);
|
|
28
|
+
if (!id) {
|
|
29
|
+
setResponseStatus(event, 400);
|
|
30
|
+
return { error: "Plan id is required" };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const token = queryString(query[AGENT_ACCESS_PARAM]);
|
|
34
|
+
try {
|
|
35
|
+
const tokenAccess = token
|
|
36
|
+
? verifyScopedAgentAccessToken(token, {
|
|
37
|
+
resourceKind: PLAN_AGENT_RESOURCE_KIND,
|
|
38
|
+
resourceId: id,
|
|
39
|
+
}).ok
|
|
40
|
+
: false;
|
|
41
|
+
const bundle = tokenAccess
|
|
42
|
+
? await loadPlanBundleForAgentAccess(id)
|
|
43
|
+
: await loadPlanBundle(id);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
resourceType: "plan",
|
|
47
|
+
id: bundle.plan.id,
|
|
48
|
+
title: bundle.plan.title,
|
|
49
|
+
kind: bundle.plan.kind,
|
|
50
|
+
access: bundle.access,
|
|
51
|
+
plan: bundle.plan,
|
|
52
|
+
sections: bundle.sections,
|
|
53
|
+
comments: bundle.comments,
|
|
54
|
+
events: bundle.events,
|
|
55
|
+
summary: bundle.summary,
|
|
56
|
+
url:
|
|
57
|
+
bundle.plan.kind === "recap"
|
|
58
|
+
? `/recaps/${bundle.plan.id}`
|
|
59
|
+
: `/plans/${bundle.plan.id}`,
|
|
60
|
+
};
|
|
61
|
+
} catch (error: any) {
|
|
62
|
+
setResponseStatus(event, error?.statusCode === 403 ? 403 : 404);
|
|
63
|
+
return { error: "Plan not found" };
|
|
64
|
+
}
|
|
65
|
+
});
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AGENT_ACCESS_PARAM,
|
|
3
|
+
getConfiguredAppBasePath,
|
|
4
|
+
verifyScopedAgentAccessToken,
|
|
5
|
+
} from "@agent-native/core/server";
|
|
6
|
+
import {
|
|
7
|
+
AGENT_READABLE_RESOURCE_SCRIPT_TYPE,
|
|
8
|
+
buildAgentReadableResourceDiscovery,
|
|
9
|
+
safeJsonForHtml,
|
|
10
|
+
} from "@agent-native/core/shared";
|
|
2
11
|
import {
|
|
3
12
|
toSharedDeckSlide,
|
|
4
13
|
type SharedDeckResponse,
|
|
@@ -6,15 +15,29 @@ import {
|
|
|
6
15
|
} from "@shared/api";
|
|
7
16
|
import { eq } from "drizzle-orm";
|
|
8
17
|
import { useEffect } from "react";
|
|
9
|
-
import type {
|
|
10
|
-
|
|
18
|
+
import type {
|
|
19
|
+
HeadersArgs,
|
|
20
|
+
LoaderFunctionArgs,
|
|
21
|
+
MetaFunction,
|
|
22
|
+
} from "react-router";
|
|
23
|
+
import { data, useLoaderData } from "react-router";
|
|
11
24
|
|
|
12
25
|
import SharedPresentation from "@/pages/SharedPresentation";
|
|
13
26
|
|
|
14
27
|
import { getDb, schema } from "../../server/db";
|
|
28
|
+
import {
|
|
29
|
+
DECK_AGENT_CONTEXT_ENDPOINT,
|
|
30
|
+
DECK_AGENT_RESOURCE_KIND,
|
|
31
|
+
} from "../../shared/agent-readable";
|
|
15
32
|
|
|
16
33
|
type LoaderData =
|
|
17
|
-
| {
|
|
34
|
+
| {
|
|
35
|
+
deck: SharedDeckResponse;
|
|
36
|
+
error?: undefined;
|
|
37
|
+
id: string;
|
|
38
|
+
basePath: string;
|
|
39
|
+
agentAccessToken?: string | null;
|
|
40
|
+
}
|
|
18
41
|
| {
|
|
19
42
|
deck: null;
|
|
20
43
|
error: string;
|
|
@@ -34,6 +57,22 @@ type DeckData = {
|
|
|
34
57
|
aspectRatio?: SharedDeckResponse["aspectRatio"];
|
|
35
58
|
};
|
|
36
59
|
|
|
60
|
+
const PRIVATE_AGENT_DECK_HEADERS = {
|
|
61
|
+
"Cache-Control": "private, max-age=0, no-store",
|
|
62
|
+
"Referrer-Policy": "no-referrer",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function publicDeckLoaderData(payload: LoaderData, privateAgentAccess = false) {
|
|
66
|
+
if (!privateAgentAccess) return payload;
|
|
67
|
+
return data(payload, {
|
|
68
|
+
headers: PRIVATE_AGENT_DECK_HEADERS,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function headers({ loaderHeaders }: HeadersArgs) {
|
|
73
|
+
return loaderHeaders;
|
|
74
|
+
}
|
|
75
|
+
|
|
37
76
|
function toSharedDeck(row: {
|
|
38
77
|
title: string | null;
|
|
39
78
|
data: string;
|
|
@@ -48,11 +87,13 @@ function toSharedDeck(row: {
|
|
|
48
87
|
};
|
|
49
88
|
}
|
|
50
89
|
|
|
51
|
-
export async function loader({
|
|
52
|
-
params,
|
|
53
|
-
}: LoaderFunctionArgs): Promise<LoaderData> {
|
|
90
|
+
export async function loader({ params, request }: LoaderFunctionArgs) {
|
|
54
91
|
const id = params.id;
|
|
55
92
|
if (!id) throw new Response("Not found", { status: 404 });
|
|
93
|
+
const agentAccessToken = new URL(request.url).searchParams.get(
|
|
94
|
+
AGENT_ACCESS_PARAM,
|
|
95
|
+
);
|
|
96
|
+
const basePath = getConfiguredAppBasePath();
|
|
56
97
|
|
|
57
98
|
// Access is checked on the deck, not the URL shape: `/p/<id>` (presentation)
|
|
58
99
|
// and `/deck/<id>` (editor) share the same rules. SSR renders impersonally (no
|
|
@@ -76,12 +117,28 @@ export async function loader({
|
|
|
76
117
|
.limit(1);
|
|
77
118
|
|
|
78
119
|
if (!deck) throw new Response("Not found", { status: 404 });
|
|
79
|
-
|
|
80
|
-
|
|
120
|
+
const tokenAccess = agentAccessToken
|
|
121
|
+
? verifyScopedAgentAccessToken(agentAccessToken, {
|
|
122
|
+
resourceKind: DECK_AGENT_RESOURCE_KIND,
|
|
123
|
+
resourceId: id,
|
|
124
|
+
}).ok
|
|
125
|
+
: false;
|
|
126
|
+
if (deck.visibility === "public" || tokenAccess) {
|
|
127
|
+
return publicDeckLoaderData(
|
|
128
|
+
{
|
|
129
|
+
deck: toSharedDeck(deck),
|
|
130
|
+
id,
|
|
131
|
+
basePath,
|
|
132
|
+
agentAccessToken: tokenAccess ? agentAccessToken : null,
|
|
133
|
+
},
|
|
134
|
+
tokenAccess,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return publicDeckLoaderData({
|
|
81
138
|
deck: null,
|
|
82
139
|
error: "restricted",
|
|
83
|
-
restricted: { id, basePath
|
|
84
|
-
};
|
|
140
|
+
restricted: { id, basePath },
|
|
141
|
+
});
|
|
85
142
|
}
|
|
86
143
|
|
|
87
144
|
export const meta: MetaFunction<typeof loader> = ({ loaderData }) => {
|
|
@@ -101,8 +158,65 @@ export default function PublicDeckRoute() {
|
|
|
101
158
|
|
|
102
159
|
// Redirecting to the guarded editor to resolve per-user access client-side.
|
|
103
160
|
if (restricted) return null;
|
|
161
|
+
if (data.deck === null) {
|
|
162
|
+
return (
|
|
163
|
+
<SharedPresentation initialDeck={data.deck} initialError={data.error} />
|
|
164
|
+
);
|
|
165
|
+
}
|
|
104
166
|
|
|
105
167
|
return (
|
|
106
|
-
|
|
168
|
+
<>
|
|
169
|
+
<AgentReadableDeckDiscovery
|
|
170
|
+
id={data.id}
|
|
171
|
+
title={data.deck.title}
|
|
172
|
+
basePath={data.basePath}
|
|
173
|
+
token={data.agentAccessToken}
|
|
174
|
+
/>
|
|
175
|
+
<SharedPresentation initialDeck={data.deck} initialError={data.error} />
|
|
176
|
+
</>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function buildDeckDiscovery({
|
|
181
|
+
id,
|
|
182
|
+
title,
|
|
183
|
+
basePath,
|
|
184
|
+
token,
|
|
185
|
+
}: {
|
|
186
|
+
id: string;
|
|
187
|
+
title?: string;
|
|
188
|
+
basePath?: string;
|
|
189
|
+
token?: string | null;
|
|
190
|
+
}) {
|
|
191
|
+
return buildAgentReadableResourceDiscovery({
|
|
192
|
+
resourceType: "deck",
|
|
193
|
+
resourceId: id,
|
|
194
|
+
title,
|
|
195
|
+
path: `/p/${id}`,
|
|
196
|
+
contextEndpoint: DECK_AGENT_CONTEXT_ENDPOINT,
|
|
197
|
+
basePath,
|
|
198
|
+
token,
|
|
199
|
+
instructions:
|
|
200
|
+
"Use contextUrl to read this shared Slides deck as JSON. Slide numbers are 1-based for users.",
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function AgentReadableDeckDiscovery({
|
|
205
|
+
id,
|
|
206
|
+
title,
|
|
207
|
+
basePath,
|
|
208
|
+
token,
|
|
209
|
+
}: {
|
|
210
|
+
id: string;
|
|
211
|
+
title?: string;
|
|
212
|
+
basePath?: string;
|
|
213
|
+
token?: string | null;
|
|
214
|
+
}) {
|
|
215
|
+
const discovery = buildDeckDiscovery({ id, title, basePath, token });
|
|
216
|
+
return (
|
|
217
|
+
<script
|
|
218
|
+
type={AGENT_READABLE_RESOURCE_SCRIPT_TYPE}
|
|
219
|
+
dangerouslySetInnerHTML={{ __html: safeJsonForHtml(discovery) }}
|
|
220
|
+
/>
|
|
107
221
|
);
|
|
108
222
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_READABLE_RESOURCE_PAYLOAD_TYPE,
|
|
3
|
+
AGENT_READABLE_RESOURCE_SCRIPT_TYPE,
|
|
4
|
+
safeJsonForHtml,
|
|
5
|
+
} from "@agent-native/core/shared";
|
|
1
6
|
import type { SharedDeckResponse } from "@shared/api";
|
|
2
7
|
import type { LoaderFunctionArgs } from "react-router";
|
|
3
|
-
import { useLoaderData } from "react-router";
|
|
8
|
+
import { useLoaderData, useParams } from "react-router";
|
|
4
9
|
|
|
5
10
|
import messages from "@/i18n/en-US";
|
|
6
11
|
import SharedPresentation from "@/pages/SharedPresentation";
|
|
7
12
|
|
|
8
13
|
type LoaderData =
|
|
9
|
-
| { deck: SharedDeckResponse; error?: undefined }
|
|
14
|
+
| { deck: SharedDeckResponse; error?: undefined; basePath: string }
|
|
10
15
|
| { deck: null; error: string };
|
|
11
16
|
|
|
12
17
|
function normalizeBasePath(value: string | undefined): string {
|
|
@@ -30,10 +35,8 @@ export async function loader({
|
|
|
30
35
|
return { deck: null, error: "Token is required" };
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
requestUrl,
|
|
36
|
-
);
|
|
38
|
+
const basePath = appBasePathForRequest();
|
|
39
|
+
const url = new URL(`${basePath}/api/share/${params.token}`, requestUrl);
|
|
37
40
|
const res = await fetch(url, { headers: { accept: "application/json" } });
|
|
38
41
|
const data = await res.json().catch(() => null);
|
|
39
42
|
|
|
@@ -44,7 +47,7 @@ export async function loader({
|
|
|
44
47
|
};
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
return { deck: data as SharedDeckResponse };
|
|
50
|
+
return { deck: data as SharedDeckResponse, basePath };
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
export function meta() {
|
|
@@ -54,6 +57,50 @@ export function meta() {
|
|
|
54
57
|
export default function SharedPresentationRoute() {
|
|
55
58
|
const data = useLoaderData<typeof loader>();
|
|
56
59
|
return (
|
|
57
|
-
|
|
60
|
+
<>
|
|
61
|
+
{data.deck ? (
|
|
62
|
+
<SnapshotDiscovery deck={data.deck} basePath={data.basePath} />
|
|
63
|
+
) : null}
|
|
64
|
+
<SharedPresentation initialDeck={data.deck} initialError={data.error} />
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function buildSnapshotDiscovery({
|
|
70
|
+
deck,
|
|
71
|
+
token,
|
|
72
|
+
basePath,
|
|
73
|
+
}: {
|
|
74
|
+
deck: SharedDeckResponse;
|
|
75
|
+
token: string;
|
|
76
|
+
basePath?: string;
|
|
77
|
+
}) {
|
|
78
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
79
|
+
return {
|
|
80
|
+
type: AGENT_READABLE_RESOURCE_PAYLOAD_TYPE,
|
|
81
|
+
resourceType: "deck-snapshot",
|
|
82
|
+
resourceId: token,
|
|
83
|
+
title: deck.title,
|
|
84
|
+
contextUrl: `${normalizedBasePath}/api/share/${encodeURIComponent(token)}`,
|
|
85
|
+
instructions:
|
|
86
|
+
"Use contextUrl to read this shared Slides snapshot as JSON. This snapshot link is independent of live deck visibility.",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function SnapshotDiscovery({
|
|
91
|
+
deck,
|
|
92
|
+
basePath,
|
|
93
|
+
}: {
|
|
94
|
+
deck: SharedDeckResponse;
|
|
95
|
+
basePath: string;
|
|
96
|
+
}) {
|
|
97
|
+
const { token } = useParams<{ token: string }>();
|
|
98
|
+
if (!token) return null;
|
|
99
|
+
const discovery = buildSnapshotDiscovery({ deck, token, basePath });
|
|
100
|
+
return (
|
|
101
|
+
<script
|
|
102
|
+
type={AGENT_READABLE_RESOURCE_SCRIPT_TYPE}
|
|
103
|
+
dangerouslySetInnerHTML={{ __html: safeJsonForHtml(discovery) }}
|
|
104
|
+
/>
|
|
58
105
|
);
|
|
59
106
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createGetDb } from "@agent-native/core/db";
|
|
2
2
|
import { registerShareableResource } from "@agent-native/core/sharing";
|
|
3
3
|
|
|
4
|
+
import {
|
|
5
|
+
DECK_AGENT_CONTEXT_ENDPOINT,
|
|
6
|
+
DECK_AGENT_RESOURCE_KIND,
|
|
7
|
+
} from "../../shared/agent-readable.js";
|
|
4
8
|
import * as schema from "./schema.js";
|
|
5
9
|
|
|
6
10
|
export const getDb = createGetDb(schema);
|
|
@@ -13,6 +17,11 @@ registerShareableResource({
|
|
|
13
17
|
displayName: "Deck",
|
|
14
18
|
titleColumn: "title",
|
|
15
19
|
getResourcePath: (deck) => `/deck/${deck.id}`,
|
|
20
|
+
agentReadable: {
|
|
21
|
+
resourceKind: DECK_AGENT_RESOURCE_KIND,
|
|
22
|
+
getContextPath: () => DECK_AGENT_CONTEXT_ENDPOINT,
|
|
23
|
+
getPagePath: (deck) => `/p/${deck.id}`,
|
|
24
|
+
},
|
|
16
25
|
getDb,
|
|
17
26
|
});
|
|
18
27
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_ACCESS_PARAM,
|
|
3
|
+
verifyScopedAgentAccessToken,
|
|
4
|
+
} from "@agent-native/core/server";
|
|
5
|
+
import { toSharedDeckSlide } from "@shared/api";
|
|
6
|
+
import { eq } from "drizzle-orm";
|
|
7
|
+
import {
|
|
8
|
+
defineEventHandler,
|
|
9
|
+
getQuery,
|
|
10
|
+
setResponseHeader,
|
|
11
|
+
setResponseStatus,
|
|
12
|
+
} from "h3";
|
|
13
|
+
|
|
14
|
+
import { DECK_AGENT_RESOURCE_KIND } from "../../../shared/agent-readable.js";
|
|
15
|
+
import { getDb, schema } from "../../db/index.js";
|
|
16
|
+
|
|
17
|
+
function queryString(value: unknown): string {
|
|
18
|
+
if (typeof value === "string") return value;
|
|
19
|
+
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseDeckData(value: string): Record<string, any> {
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(value);
|
|
26
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
27
|
+
} catch {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default defineEventHandler(async (event) => {
|
|
33
|
+
setResponseHeader(event, "Cache-Control", "no-store");
|
|
34
|
+
setResponseHeader(event, "Referrer-Policy", "no-referrer");
|
|
35
|
+
setResponseHeader(event, "X-Content-Type-Options", "nosniff");
|
|
36
|
+
|
|
37
|
+
const query = getQuery(event);
|
|
38
|
+
const id = queryString(query.id);
|
|
39
|
+
if (!id) {
|
|
40
|
+
setResponseStatus(event, 400);
|
|
41
|
+
return { error: "Deck id is required" };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const token = queryString(query[AGENT_ACCESS_PARAM]);
|
|
45
|
+
const db = getDb();
|
|
46
|
+
// guard:allow-unscoped -- this endpoint returns a deck only when it is public or a deck-scoped agent_access token verifies for this id.
|
|
47
|
+
const [deck] = await db
|
|
48
|
+
.select({
|
|
49
|
+
id: schema.decks.id,
|
|
50
|
+
title: schema.decks.title,
|
|
51
|
+
data: schema.decks.data,
|
|
52
|
+
visibility: schema.decks.visibility,
|
|
53
|
+
designSystemId: schema.decks.designSystemId,
|
|
54
|
+
createdAt: schema.decks.createdAt,
|
|
55
|
+
updatedAt: schema.decks.updatedAt,
|
|
56
|
+
})
|
|
57
|
+
.from(schema.decks)
|
|
58
|
+
.where(eq(schema.decks.id, id))
|
|
59
|
+
.limit(1);
|
|
60
|
+
|
|
61
|
+
if (!deck) {
|
|
62
|
+
setResponseStatus(event, 404);
|
|
63
|
+
return { error: "Deck not found" };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const tokenAccess = token
|
|
67
|
+
? verifyScopedAgentAccessToken(token, {
|
|
68
|
+
resourceKind: DECK_AGENT_RESOURCE_KIND,
|
|
69
|
+
resourceId: id,
|
|
70
|
+
}).ok
|
|
71
|
+
: false;
|
|
72
|
+
if (deck.visibility !== "public" && !tokenAccess) {
|
|
73
|
+
setResponseStatus(event, 403);
|
|
74
|
+
return { error: "Invalid or expired agent access token" };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const data = parseDeckData(deck.data);
|
|
78
|
+
const slides = Array.isArray(data.slides)
|
|
79
|
+
? data.slides.map((slide: unknown, index: number) =>
|
|
80
|
+
toSharedDeckSlide(slide, index),
|
|
81
|
+
)
|
|
82
|
+
: [];
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
resourceType: "deck",
|
|
86
|
+
id: deck.id,
|
|
87
|
+
title: deck.title || data.title || "Untitled",
|
|
88
|
+
visibility: deck.visibility,
|
|
89
|
+
designSystemId: deck.designSystemId ?? null,
|
|
90
|
+
slideCount: slides.length,
|
|
91
|
+
aspectRatio: data.aspectRatio ?? null,
|
|
92
|
+
slides,
|
|
93
|
+
createdAt: deck.createdAt,
|
|
94
|
+
updatedAt: deck.updatedAt,
|
|
95
|
+
url: `/p/${deck.id}`,
|
|
96
|
+
};
|
|
97
|
+
});
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -41,9 +41,9 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
|
|
|
41
41
|
* Body: { text: string, fieldName?: string, requestSource?: string }
|
|
42
42
|
*/
|
|
43
43
|
export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
44
|
-
text?: undefined;
|
|
45
44
|
ok?: undefined;
|
|
46
45
|
error: string;
|
|
46
|
+
text?: undefined;
|
|
47
47
|
} | {
|
|
48
48
|
error?: undefined;
|
|
49
49
|
ok: boolean;
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
ok?: undefined;
|
|
17
16
|
error: string;
|
|
17
|
+
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
@@ -41,28 +41,28 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
error?: undefined;
|
|
45
|
-
ok?: undefined;
|
|
46
44
|
summary: import("./types.js").TraceSummary;
|
|
47
45
|
spans: import("./types.js").TraceSpan[];
|
|
48
46
|
id?: undefined;
|
|
49
|
-
} | {
|
|
50
47
|
error?: undefined;
|
|
51
48
|
ok?: undefined;
|
|
49
|
+
} | {
|
|
52
50
|
summary?: undefined;
|
|
53
51
|
spans?: undefined;
|
|
54
52
|
id: string;
|
|
55
|
-
|
|
53
|
+
error?: undefined;
|
|
56
54
|
ok?: undefined;
|
|
55
|
+
} | {
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
59
58
|
id?: undefined;
|
|
60
59
|
error: any;
|
|
60
|
+
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
64
|
id?: undefined;
|
|
65
|
+
error?: undefined;
|
|
66
66
|
ok: boolean;
|
|
67
67
|
}>>;
|
|
68
68
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -73,7 +73,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
73
73
|
offset?: unknown;
|
|
74
74
|
limit?: unknown;
|
|
75
75
|
}, {
|
|
76
|
-
deleted?: undefined;
|
|
77
76
|
jobs: {
|
|
78
77
|
id: string;
|
|
79
78
|
name: string;
|
|
@@ -84,6 +83,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
84
83
|
updatedAt: string;
|
|
85
84
|
}[];
|
|
86
85
|
total: number;
|
|
86
|
+
deleted?: undefined;
|
|
87
87
|
jobId?: undefined;
|
|
88
88
|
} | {
|
|
89
89
|
jobs?: undefined;
|
|
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
91
91
|
deleted: boolean;
|
|
92
92
|
jobId: string;
|
|
93
93
|
} | {
|
|
94
|
-
deleted?: undefined;
|
|
95
94
|
jobs?: undefined;
|
|
96
95
|
total?: undefined;
|
|
96
|
+
deleted?: undefined;
|
|
97
97
|
jobId?: undefined;
|
|
98
98
|
hits: Record<string, unknown>[];
|
|
99
99
|
offset: number;
|