@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
|
@@ -6,97 +6,12 @@ import {
|
|
|
6
6
|
} from "@agent-native/core/server";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
|
|
9
|
-
import { buildDashboardPanelGroups } from "../app/pages/adhoc/sql-dashboard/dashboard-layout";
|
|
10
9
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "../
|
|
10
|
+
buildDashboardAgentContext,
|
|
11
|
+
buildDashboardSeedAgentContext,
|
|
12
|
+
} from "../server/lib/agent-readable-resource-context";
|
|
14
13
|
import { loadDashboardSeed } from "../server/lib/dashboard-seeds";
|
|
15
14
|
import { getDashboard } from "../server/lib/dashboards-store";
|
|
16
|
-
import { getPanelOrder } from "./dashboard-panel-order";
|
|
17
|
-
|
|
18
|
-
function dashboardLayoutSummary(config: Record<string, unknown>) {
|
|
19
|
-
const panels = Array.isArray(config.panels)
|
|
20
|
-
? (config.panels as SqlPanel[])
|
|
21
|
-
: [];
|
|
22
|
-
const columns = clampDashboardColumns(config.columns);
|
|
23
|
-
const groups = buildDashboardPanelGroups(panels, columns);
|
|
24
|
-
const panelOrder = getPanelOrder(config);
|
|
25
|
-
let visibleRowNumber = 1;
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
panelCount: panelOrder.length,
|
|
29
|
-
panelOrder,
|
|
30
|
-
firstPanelIds: panelOrder.slice(0, 10),
|
|
31
|
-
groups: groups.map((group) => ({
|
|
32
|
-
key: group.key,
|
|
33
|
-
sectionId: group.section?.id ?? null,
|
|
34
|
-
sectionTitle: group.section?.title ?? null,
|
|
35
|
-
columns: group.columns,
|
|
36
|
-
rows: group.rows.map((row, rowIndex) => {
|
|
37
|
-
const rowNumber = visibleRowNumber++;
|
|
38
|
-
return {
|
|
39
|
-
rowNumber,
|
|
40
|
-
rowIndex,
|
|
41
|
-
panelIds: row.panels.map((panel) => panel.id),
|
|
42
|
-
};
|
|
43
|
-
}),
|
|
44
|
-
})),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function panelSummaries(config: Record<string, unknown>) {
|
|
49
|
-
const panels = Array.isArray(config.panels)
|
|
50
|
-
? (config.panels as Array<Record<string, unknown>>)
|
|
51
|
-
: [];
|
|
52
|
-
return panels.map((panel, index) => {
|
|
53
|
-
const panelConfig =
|
|
54
|
-
panel.config &&
|
|
55
|
-
typeof panel.config === "object" &&
|
|
56
|
-
!Array.isArray(panel.config)
|
|
57
|
-
? (panel.config as Record<string, unknown>)
|
|
58
|
-
: {};
|
|
59
|
-
return {
|
|
60
|
-
index,
|
|
61
|
-
id: typeof panel.id === "string" ? panel.id : "",
|
|
62
|
-
title: typeof panel.title === "string" ? panel.title : "",
|
|
63
|
-
chartType: typeof panel.chartType === "string" ? panel.chartType : "",
|
|
64
|
-
source: typeof panel.source === "string" ? panel.source : undefined,
|
|
65
|
-
width: typeof panel.width === "number" ? panel.width : undefined,
|
|
66
|
-
columns: typeof panel.columns === "number" ? panel.columns : undefined,
|
|
67
|
-
tab: typeof panel.tab === "string" ? panel.tab : undefined,
|
|
68
|
-
description:
|
|
69
|
-
typeof panelConfig.description === "string"
|
|
70
|
-
? panelConfig.description
|
|
71
|
-
: undefined,
|
|
72
|
-
};
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function seededResponse(
|
|
77
|
-
id: string,
|
|
78
|
-
seed: Record<string, unknown>,
|
|
79
|
-
includeConfig: boolean,
|
|
80
|
-
): Record<string, unknown> {
|
|
81
|
-
const base = {
|
|
82
|
-
id,
|
|
83
|
-
name: typeof seed.name === "string" ? seed.name : id,
|
|
84
|
-
description:
|
|
85
|
-
typeof seed.description === "string" ? seed.description : undefined,
|
|
86
|
-
filters: seed.filters,
|
|
87
|
-
variables: seed.variables,
|
|
88
|
-
columns: seed.columns,
|
|
89
|
-
panels: panelSummaries(seed),
|
|
90
|
-
layout: dashboardLayoutSummary(seed),
|
|
91
|
-
ownerEmail: null,
|
|
92
|
-
orgId: null,
|
|
93
|
-
visibility: "org",
|
|
94
|
-
archivedAt: null,
|
|
95
|
-
hiddenAt: null,
|
|
96
|
-
hiddenBy: null,
|
|
97
|
-
};
|
|
98
|
-
return includeConfig ? { ...base, ...seed } : base;
|
|
99
|
-
}
|
|
100
15
|
|
|
101
16
|
export default defineAction({
|
|
102
17
|
description:
|
|
@@ -149,34 +64,15 @@ export default defineAction({
|
|
|
149
64
|
if (!dash || dash.kind !== "sql") {
|
|
150
65
|
const seed = loadDashboardSeed(args.id);
|
|
151
66
|
if (seed)
|
|
152
|
-
return
|
|
67
|
+
return buildDashboardSeedAgentContext(args.id, seed, {
|
|
68
|
+
includeConfig: args.includeConfig === true,
|
|
69
|
+
});
|
|
153
70
|
throw Object.assign(new Error("Dashboard not found"), {
|
|
154
71
|
statusCode: 404,
|
|
155
72
|
});
|
|
156
73
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
name: typeof config.name === "string" ? config.name : args.id,
|
|
161
|
-
description:
|
|
162
|
-
typeof config.description === "string" ? config.description : undefined,
|
|
163
|
-
filters: config.filters,
|
|
164
|
-
variables: config.variables,
|
|
165
|
-
columns: config.columns,
|
|
166
|
-
panels: panelSummaries(config),
|
|
167
|
-
layout: dashboardLayoutSummary(config),
|
|
168
|
-
ownerEmail: dash.ownerEmail,
|
|
169
|
-
orgId: dash.orgId,
|
|
170
|
-
visibility: dash.visibility,
|
|
171
|
-
role: dash.role,
|
|
172
|
-
canEdit: dash.canEdit,
|
|
173
|
-
canManage: dash.canManage,
|
|
174
|
-
archivedAt: dash.archivedAt,
|
|
175
|
-
hiddenAt: dash.hiddenAt,
|
|
176
|
-
hiddenBy: dash.hiddenBy,
|
|
177
|
-
createdAt: dash.createdAt,
|
|
178
|
-
updatedAt: dash.updatedAt,
|
|
179
|
-
};
|
|
180
|
-
return args.includeConfig === true ? { ...base, ...config } : base;
|
|
74
|
+
return buildDashboardAgentContext(dash, {
|
|
75
|
+
includeConfig: args.includeConfig === true,
|
|
76
|
+
});
|
|
181
77
|
},
|
|
182
78
|
});
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { createGetDb } from "@agent-native/core/db";
|
|
2
2
|
import { registerShareableResource } from "@agent-native/core/sharing";
|
|
3
3
|
|
|
4
|
+
import {
|
|
5
|
+
ANALYTICS_ANALYSIS_AGENT_CONTEXT_ENDPOINT,
|
|
6
|
+
ANALYTICS_ANALYSIS_AGENT_RESOURCE_KIND,
|
|
7
|
+
ANALYTICS_DASHBOARD_AGENT_CONTEXT_ENDPOINT,
|
|
8
|
+
ANALYTICS_DASHBOARD_AGENT_RESOURCE_KIND,
|
|
9
|
+
} from "../../shared/resource-agent-access.js";
|
|
4
10
|
import * as schema from "./schema.js";
|
|
5
11
|
|
|
6
12
|
export const getDb = createGetDb(schema);
|
|
@@ -13,6 +19,10 @@ registerShareableResource({
|
|
|
13
19
|
displayName: "Dashboard",
|
|
14
20
|
titleColumn: "title",
|
|
15
21
|
getResourcePath: (dashboard) => `/dashboards/${dashboard.id}`,
|
|
22
|
+
agentReadable: {
|
|
23
|
+
resourceKind: ANALYTICS_DASHBOARD_AGENT_RESOURCE_KIND,
|
|
24
|
+
getContextPath: () => ANALYTICS_DASHBOARD_AGENT_CONTEXT_ENDPOINT,
|
|
25
|
+
},
|
|
16
26
|
getDb,
|
|
17
27
|
});
|
|
18
28
|
|
|
@@ -23,6 +33,10 @@ registerShareableResource({
|
|
|
23
33
|
displayName: "Analysis",
|
|
24
34
|
titleColumn: "name",
|
|
25
35
|
getResourcePath: (analysis) => `/analyses/${analysis.id}`,
|
|
36
|
+
agentReadable: {
|
|
37
|
+
resourceKind: ANALYTICS_ANALYSIS_AGENT_RESOURCE_KIND,
|
|
38
|
+
getContextPath: () => ANALYTICS_ANALYSIS_AGENT_CONTEXT_ENDPOINT,
|
|
39
|
+
},
|
|
26
40
|
getDb,
|
|
27
41
|
});
|
|
28
42
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { getPanelOrder } from "../../actions/dashboard-panel-order.js";
|
|
2
|
+
import { buildDashboardPanelGroups } from "../../app/pages/adhoc/sql-dashboard/dashboard-layout";
|
|
3
|
+
import {
|
|
4
|
+
clampDashboardColumns,
|
|
5
|
+
type SqlPanel,
|
|
6
|
+
} from "../../app/pages/adhoc/sql-dashboard/types";
|
|
7
|
+
import type { AnalysisRecord, DashboardRecord } from "./dashboards-store.js";
|
|
8
|
+
|
|
9
|
+
function dashboardLayoutSummary(config: Record<string, unknown>) {
|
|
10
|
+
const panels = Array.isArray(config.panels)
|
|
11
|
+
? (config.panels as SqlPanel[])
|
|
12
|
+
: [];
|
|
13
|
+
const columns = clampDashboardColumns(config.columns);
|
|
14
|
+
const groups = buildDashboardPanelGroups(panels, columns);
|
|
15
|
+
const panelOrder = getPanelOrder(config);
|
|
16
|
+
let visibleRowNumber = 1;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
panelCount: panelOrder.length,
|
|
20
|
+
panelOrder,
|
|
21
|
+
firstPanelIds: panelOrder.slice(0, 10),
|
|
22
|
+
groups: groups.map((group) => ({
|
|
23
|
+
key: group.key,
|
|
24
|
+
sectionId: group.section?.id ?? null,
|
|
25
|
+
sectionTitle: group.section?.title ?? null,
|
|
26
|
+
columns: group.columns,
|
|
27
|
+
rows: group.rows.map((row, rowIndex) => {
|
|
28
|
+
const rowNumber = visibleRowNumber++;
|
|
29
|
+
return {
|
|
30
|
+
rowNumber,
|
|
31
|
+
rowIndex,
|
|
32
|
+
panelIds: row.panels.map((panel) => panel.id),
|
|
33
|
+
};
|
|
34
|
+
}),
|
|
35
|
+
})),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function panelSummaries(config: Record<string, unknown>) {
|
|
40
|
+
const panels = Array.isArray(config.panels)
|
|
41
|
+
? (config.panels as Array<Record<string, unknown>>)
|
|
42
|
+
: [];
|
|
43
|
+
return panels.map((panel, index) => {
|
|
44
|
+
const panelConfig =
|
|
45
|
+
panel.config &&
|
|
46
|
+
typeof panel.config === "object" &&
|
|
47
|
+
!Array.isArray(panel.config)
|
|
48
|
+
? (panel.config as Record<string, unknown>)
|
|
49
|
+
: {};
|
|
50
|
+
return {
|
|
51
|
+
index,
|
|
52
|
+
id: typeof panel.id === "string" ? panel.id : "",
|
|
53
|
+
title: typeof panel.title === "string" ? panel.title : "",
|
|
54
|
+
chartType: typeof panel.chartType === "string" ? panel.chartType : "",
|
|
55
|
+
source: typeof panel.source === "string" ? panel.source : undefined,
|
|
56
|
+
width: typeof panel.width === "number" ? panel.width : undefined,
|
|
57
|
+
columns: typeof panel.columns === "number" ? panel.columns : undefined,
|
|
58
|
+
tab: typeof panel.tab === "string" ? panel.tab : undefined,
|
|
59
|
+
description:
|
|
60
|
+
typeof panelConfig.description === "string"
|
|
61
|
+
? panelConfig.description
|
|
62
|
+
: undefined,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function buildDashboardAgentContext(
|
|
68
|
+
dashboard: DashboardRecord,
|
|
69
|
+
options: { includeConfig?: boolean } = {},
|
|
70
|
+
): Record<string, unknown> {
|
|
71
|
+
const config = dashboard.config as Record<string, unknown>;
|
|
72
|
+
const base = {
|
|
73
|
+
resourceType: "analytics-dashboard",
|
|
74
|
+
id: dashboard.id,
|
|
75
|
+
kind: dashboard.kind,
|
|
76
|
+
name: typeof config.name === "string" ? config.name : dashboard.title,
|
|
77
|
+
title: dashboard.title,
|
|
78
|
+
description:
|
|
79
|
+
typeof config.description === "string" ? config.description : undefined,
|
|
80
|
+
filters: config.filters,
|
|
81
|
+
variables: config.variables,
|
|
82
|
+
columns: config.columns,
|
|
83
|
+
panels: panelSummaries(config),
|
|
84
|
+
layout: dashboardLayoutSummary(config),
|
|
85
|
+
ownerEmail: dashboard.ownerEmail,
|
|
86
|
+
orgId: dashboard.orgId,
|
|
87
|
+
visibility: dashboard.visibility,
|
|
88
|
+
role: dashboard.role,
|
|
89
|
+
canEdit: dashboard.canEdit,
|
|
90
|
+
canManage: dashboard.canManage,
|
|
91
|
+
archivedAt: dashboard.archivedAt,
|
|
92
|
+
hiddenAt: dashboard.hiddenAt,
|
|
93
|
+
hiddenBy: dashboard.hiddenBy,
|
|
94
|
+
createdAt: dashboard.createdAt,
|
|
95
|
+
updatedAt: dashboard.updatedAt,
|
|
96
|
+
url: `/dashboards/${dashboard.id}`,
|
|
97
|
+
};
|
|
98
|
+
return options.includeConfig === false ? base : { ...base, ...config };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function buildDashboardSeedAgentContext(
|
|
102
|
+
id: string,
|
|
103
|
+
seed: Record<string, unknown>,
|
|
104
|
+
options: { includeConfig?: boolean } = {},
|
|
105
|
+
): Record<string, unknown> {
|
|
106
|
+
const base = {
|
|
107
|
+
resourceType: "analytics-dashboard",
|
|
108
|
+
id,
|
|
109
|
+
kind: "sql",
|
|
110
|
+
name: typeof seed.name === "string" ? seed.name : id,
|
|
111
|
+
title: typeof seed.name === "string" ? seed.name : id,
|
|
112
|
+
description:
|
|
113
|
+
typeof seed.description === "string" ? seed.description : undefined,
|
|
114
|
+
filters: seed.filters,
|
|
115
|
+
variables: seed.variables,
|
|
116
|
+
columns: seed.columns,
|
|
117
|
+
panels: panelSummaries(seed),
|
|
118
|
+
layout: dashboardLayoutSummary(seed),
|
|
119
|
+
ownerEmail: null,
|
|
120
|
+
orgId: null,
|
|
121
|
+
visibility: "org",
|
|
122
|
+
archivedAt: null,
|
|
123
|
+
hiddenAt: null,
|
|
124
|
+
hiddenBy: null,
|
|
125
|
+
url: `/dashboards/${id}`,
|
|
126
|
+
};
|
|
127
|
+
return options.includeConfig === false ? base : { ...base, ...seed };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function buildAnalysisAgentContext(
|
|
131
|
+
analysis: AnalysisRecord,
|
|
132
|
+
): Record<string, unknown> {
|
|
133
|
+
return {
|
|
134
|
+
resourceType: "analytics-analysis",
|
|
135
|
+
id: analysis.id,
|
|
136
|
+
name: analysis.name,
|
|
137
|
+
description: analysis.description,
|
|
138
|
+
question: analysis.question,
|
|
139
|
+
instructions: analysis.instructions,
|
|
140
|
+
dataSources: analysis.dataSources,
|
|
141
|
+
resultMarkdown: analysis.resultMarkdown,
|
|
142
|
+
resultData: analysis.resultData,
|
|
143
|
+
author: analysis.author,
|
|
144
|
+
createdAt: analysis.createdAt,
|
|
145
|
+
updatedAt: analysis.updatedAt,
|
|
146
|
+
ownerEmail: analysis.ownerEmail,
|
|
147
|
+
orgId: analysis.orgId,
|
|
148
|
+
visibility: analysis.visibility,
|
|
149
|
+
role: analysis.role,
|
|
150
|
+
canEdit: analysis.canEdit,
|
|
151
|
+
canManage: analysis.canManage,
|
|
152
|
+
hiddenAt: analysis.hiddenAt,
|
|
153
|
+
hiddenBy: analysis.hiddenBy,
|
|
154
|
+
url: `/analyses/${analysis.id}`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { AGENT_ACCESS_PARAM } from "@agent-native/core/server";
|
|
1
2
|
import { createH3SSRHandler } from "@agent-native/core/server/ssr-handler";
|
|
3
|
+
import {
|
|
4
|
+
buildAgentReadableResourceDiscovery,
|
|
5
|
+
renderAgentReadableResourceDiscoveryScript,
|
|
6
|
+
} from "@agent-native/core/shared";
|
|
2
7
|
import {
|
|
3
8
|
defineEventHandler,
|
|
4
9
|
getQuery,
|
|
@@ -6,6 +11,10 @@ import {
|
|
|
6
11
|
setResponseHeader,
|
|
7
12
|
} from "h3";
|
|
8
13
|
|
|
14
|
+
import {
|
|
15
|
+
ANALYTICS_ANALYSIS_AGENT_CONTEXT_ENDPOINT,
|
|
16
|
+
ANALYTICS_DASHBOARD_AGENT_CONTEXT_ENDPOINT,
|
|
17
|
+
} from "../../shared/resource-agent-access.js";
|
|
9
18
|
import {
|
|
10
19
|
buildSessionReplayAgentContext,
|
|
11
20
|
safeJsonForHtml,
|
|
@@ -43,36 +52,92 @@ function sessionRecordingIdFromPath(pathname: string): string | null {
|
|
|
43
52
|
}
|
|
44
53
|
}
|
|
45
54
|
|
|
55
|
+
function analyticsResourceFromPath(
|
|
56
|
+
pathname: string,
|
|
57
|
+
): { type: "dashboard" | "analysis"; id: string } | null {
|
|
58
|
+
const stripped = stripAppBasePath(pathname);
|
|
59
|
+
const dashboard = stripped.match(/^\/dashboards\/([^/]+)\/?$/);
|
|
60
|
+
const analysis = stripped.match(/^\/analyses\/([^/]+)\/?$/);
|
|
61
|
+
const type = dashboard ? "dashboard" : analysis ? "analysis" : null;
|
|
62
|
+
const rawId = dashboard?.[1] ?? analysis?.[1];
|
|
63
|
+
if (!type || !rawId) return null;
|
|
64
|
+
try {
|
|
65
|
+
return { type, id: decodeURIComponent(rawId) };
|
|
66
|
+
} catch {
|
|
67
|
+
return { type, id: rawId };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
46
71
|
function queryString(value: unknown): string {
|
|
47
72
|
if (typeof value === "string") return value;
|
|
48
73
|
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
49
74
|
return "";
|
|
50
75
|
}
|
|
51
76
|
|
|
52
|
-
|
|
77
|
+
interface AgentDiscoveryInjection {
|
|
78
|
+
script: string;
|
|
79
|
+
privateResponse: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function buildAgentDiscoveryScript(
|
|
83
|
+
event: any,
|
|
84
|
+
): Promise<AgentDiscoveryInjection | null> {
|
|
53
85
|
const requestUrl = getRequestURL(event);
|
|
54
86
|
const recordingId = sessionRecordingIdFromPath(requestUrl.pathname);
|
|
55
|
-
if (!recordingId) return null;
|
|
56
|
-
|
|
57
87
|
const query = getQuery(event);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
88
|
+
if (recordingId) {
|
|
89
|
+
const token = queryString(query[SESSION_REPLAY_AGENT_ACCESS_PARAM]);
|
|
90
|
+
if (!token) return null;
|
|
91
|
+
|
|
92
|
+
const context = await buildSessionReplayAgentContext({
|
|
93
|
+
recordingId,
|
|
94
|
+
token,
|
|
95
|
+
origin: requestUrl.origin,
|
|
96
|
+
includeTimeline: false,
|
|
97
|
+
}).catch(() => null);
|
|
98
|
+
if (!context) return null;
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
script: `<script type="application/agent-native+json" id="analytics-session-replay-agent-context">${safeJsonForHtml(
|
|
102
|
+
context,
|
|
103
|
+
)}</script>`,
|
|
104
|
+
privateResponse: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const resource = analyticsResourceFromPath(requestUrl.pathname);
|
|
109
|
+
if (!resource) return null;
|
|
110
|
+
|
|
111
|
+
const token = queryString(query[AGENT_ACCESS_PARAM]);
|
|
112
|
+
const isDashboard = resource.type === "dashboard";
|
|
113
|
+
return {
|
|
114
|
+
script: renderAgentReadableResourceDiscoveryScript(
|
|
115
|
+
buildAgentReadableResourceDiscovery({
|
|
116
|
+
resourceType: isDashboard ? "dashboard" : "analysis",
|
|
117
|
+
resourceId: resource.id,
|
|
118
|
+
path: `/${isDashboard ? "dashboards" : "analyses"}/${encodeURIComponent(resource.id)}`,
|
|
119
|
+
contextEndpoint: isDashboard
|
|
120
|
+
? ANALYTICS_DASHBOARD_AGENT_CONTEXT_ENDPOINT
|
|
121
|
+
: ANALYTICS_ANALYSIS_AGENT_CONTEXT_ENDPOINT,
|
|
122
|
+
origin: requestUrl.origin,
|
|
123
|
+
basePath: configuredAppBasePath(),
|
|
124
|
+
token,
|
|
125
|
+
instructions:
|
|
126
|
+
"Use contextUrl to read this Analytics artifact as structured JSON. Token links are read-only and do not grant edit access.",
|
|
127
|
+
}),
|
|
128
|
+
{
|
|
129
|
+
id: isDashboard
|
|
130
|
+
? "analytics-dashboard-agent-context"
|
|
131
|
+
: "analytics-analysis-agent-context",
|
|
132
|
+
},
|
|
133
|
+
),
|
|
134
|
+
privateResponse: Boolean(token),
|
|
135
|
+
};
|
|
72
136
|
}
|
|
73
137
|
|
|
74
138
|
function injectAgentDiscovery(html: string, script: string): string {
|
|
75
|
-
|
|
139
|
+
const scriptId = script.match(/\sid="([^"]+)"/)?.[1];
|
|
140
|
+
if (scriptId && html.includes(`id="${scriptId}"`)) {
|
|
76
141
|
return html;
|
|
77
142
|
}
|
|
78
143
|
if (html.includes("</head>")) {
|
|
@@ -86,8 +151,8 @@ function injectAgentDiscovery(html: string, script: string): string {
|
|
|
86
151
|
|
|
87
152
|
export default defineEventHandler(async (event) => {
|
|
88
153
|
const response = (await ssrHandler(event)) as Response;
|
|
89
|
-
const
|
|
90
|
-
if (!
|
|
154
|
+
const discovery = await buildAgentDiscoveryScript(event);
|
|
155
|
+
if (!discovery) return response;
|
|
91
156
|
|
|
92
157
|
const contentType = response.headers.get("content-type") ?? "";
|
|
93
158
|
if (!contentType.includes("text/html")) return response;
|
|
@@ -95,12 +160,14 @@ export default defineEventHandler(async (event) => {
|
|
|
95
160
|
const html = await response.text();
|
|
96
161
|
const headers = new Headers(response.headers);
|
|
97
162
|
headers.delete("content-length");
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
163
|
+
if (discovery.privateResponse) {
|
|
164
|
+
headers.set("Cache-Control", "private, max-age=0, no-store");
|
|
165
|
+
headers.set("Referrer-Policy", "no-referrer");
|
|
166
|
+
setResponseHeader(event, "Cache-Control", "private, max-age=0, no-store");
|
|
167
|
+
setResponseHeader(event, "Referrer-Policy", "no-referrer");
|
|
168
|
+
}
|
|
102
169
|
|
|
103
|
-
return new Response(injectAgentDiscovery(html, script), {
|
|
170
|
+
return new Response(injectAgentDiscovery(html, discovery.script), {
|
|
104
171
|
status: response.status,
|
|
105
172
|
statusText: response.statusText,
|
|
106
173
|
headers,
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_ACCESS_PARAM,
|
|
3
|
+
verifyScopedAgentAccessToken,
|
|
4
|
+
} from "@agent-native/core/server";
|
|
5
|
+
import { eq } from "drizzle-orm";
|
|
6
|
+
import {
|
|
7
|
+
defineEventHandler,
|
|
8
|
+
getQuery,
|
|
9
|
+
setResponseHeader,
|
|
10
|
+
setResponseStatus,
|
|
11
|
+
} from "h3";
|
|
12
|
+
|
|
13
|
+
import { ANALYTICS_ANALYSIS_AGENT_RESOURCE_KIND } from "../../../shared/resource-agent-access.js";
|
|
14
|
+
import { getDb, schema } from "../../db/index.js";
|
|
15
|
+
import { buildAnalysisAgentContext } from "../../lib/agent-readable-resource-context.js";
|
|
16
|
+
import type { AnalysisRecord } from "../../lib/dashboards-store.js";
|
|
17
|
+
|
|
18
|
+
function queryString(value: unknown): string {
|
|
19
|
+
if (typeof value === "string") return value;
|
|
20
|
+
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseJson<T>(value: unknown, fallback: T): T {
|
|
25
|
+
if (typeof value !== "string") return fallback;
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(value) as T;
|
|
28
|
+
} catch {
|
|
29
|
+
return fallback;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function rowToAnalysis(row: any): AnalysisRecord {
|
|
34
|
+
return {
|
|
35
|
+
id: row.id,
|
|
36
|
+
name: row.name,
|
|
37
|
+
description: row.description,
|
|
38
|
+
question: row.question,
|
|
39
|
+
instructions: row.instructions,
|
|
40
|
+
dataSources: parseJson<string[]>(row.dataSources, []),
|
|
41
|
+
resultMarkdown: row.resultMarkdown,
|
|
42
|
+
resultData: row.resultData
|
|
43
|
+
? parseJson<Record<string, unknown> | null>(row.resultData, null)
|
|
44
|
+
: null,
|
|
45
|
+
author: row.author ?? null,
|
|
46
|
+
ownerEmail: row.ownerEmail,
|
|
47
|
+
orgId: row.orgId ?? null,
|
|
48
|
+
visibility: row.visibility,
|
|
49
|
+
createdAt: row.createdAt,
|
|
50
|
+
updatedAt: row.updatedAt,
|
|
51
|
+
hiddenAt: row.hiddenAt ?? null,
|
|
52
|
+
hiddenBy: row.hiddenBy ?? null,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default defineEventHandler(async (event) => {
|
|
57
|
+
setResponseHeader(event, "Cache-Control", "no-store");
|
|
58
|
+
setResponseHeader(event, "Referrer-Policy", "no-referrer");
|
|
59
|
+
setResponseHeader(event, "X-Content-Type-Options", "nosniff");
|
|
60
|
+
|
|
61
|
+
const query = getQuery(event);
|
|
62
|
+
const id = queryString(query.id);
|
|
63
|
+
if (!id) {
|
|
64
|
+
setResponseStatus(event, 400);
|
|
65
|
+
return { error: "Analysis id is required" };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const token = queryString(query[AGENT_ACCESS_PARAM]);
|
|
69
|
+
const db = getDb() as any;
|
|
70
|
+
// guard:allow-unscoped -- this endpoint returns analysis context only when the analysis is public or an analysis-scoped agent_access token verifies for this id.
|
|
71
|
+
const [row] = await db
|
|
72
|
+
.select()
|
|
73
|
+
.from(schema.analyses)
|
|
74
|
+
.where(eq(schema.analyses.id, id))
|
|
75
|
+
.limit(1);
|
|
76
|
+
|
|
77
|
+
if (!row) {
|
|
78
|
+
setResponseStatus(event, 404);
|
|
79
|
+
return { error: "Analysis not found" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const tokenAccess = token
|
|
83
|
+
? verifyScopedAgentAccessToken(token, {
|
|
84
|
+
resourceKind: ANALYTICS_ANALYSIS_AGENT_RESOURCE_KIND,
|
|
85
|
+
resourceId: id,
|
|
86
|
+
}).ok
|
|
87
|
+
: false;
|
|
88
|
+
if (row.visibility !== "public" && !tokenAccess) {
|
|
89
|
+
setResponseStatus(event, 403);
|
|
90
|
+
return { error: "Invalid or expired agent access token" };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return buildAnalysisAgentContext(rowToAnalysis(row));
|
|
94
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_ACCESS_PARAM,
|
|
3
|
+
verifyScopedAgentAccessToken,
|
|
4
|
+
} from "@agent-native/core/server";
|
|
5
|
+
import { eq } from "drizzle-orm";
|
|
6
|
+
import {
|
|
7
|
+
defineEventHandler,
|
|
8
|
+
getQuery,
|
|
9
|
+
setResponseHeader,
|
|
10
|
+
setResponseStatus,
|
|
11
|
+
} from "h3";
|
|
12
|
+
|
|
13
|
+
import { ANALYTICS_DASHBOARD_AGENT_RESOURCE_KIND } from "../../../shared/resource-agent-access.js";
|
|
14
|
+
import { getDb, schema } from "../../db/index.js";
|
|
15
|
+
import {
|
|
16
|
+
buildDashboardAgentContext,
|
|
17
|
+
buildDashboardSeedAgentContext,
|
|
18
|
+
} from "../../lib/agent-readable-resource-context.js";
|
|
19
|
+
import { loadDashboardSeed } from "../../lib/dashboard-seeds.js";
|
|
20
|
+
import type { DashboardRecord } from "../../lib/dashboards-store.js";
|
|
21
|
+
|
|
22
|
+
function queryString(value: unknown): string {
|
|
23
|
+
if (typeof value === "string") return value;
|
|
24
|
+
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseJsonObject(value: unknown): Record<string, unknown> {
|
|
29
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
30
|
+
return value as Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
if (typeof value !== "string") return {};
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(value);
|
|
35
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
36
|
+
? (parsed as Record<string, unknown>)
|
|
37
|
+
: {};
|
|
38
|
+
} catch {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function rowToDashboard(row: any): DashboardRecord {
|
|
44
|
+
return {
|
|
45
|
+
id: row.id,
|
|
46
|
+
kind: row.kind,
|
|
47
|
+
title: row.title,
|
|
48
|
+
config: parseJsonObject(row.config),
|
|
49
|
+
ownerEmail: row.ownerEmail,
|
|
50
|
+
orgId: row.orgId ?? null,
|
|
51
|
+
visibility: row.visibility,
|
|
52
|
+
createdAt: row.createdAt,
|
|
53
|
+
updatedAt: row.updatedAt,
|
|
54
|
+
archivedAt: row.archivedAt ?? null,
|
|
55
|
+
hiddenAt: row.hiddenAt ?? null,
|
|
56
|
+
hiddenBy: row.hiddenBy ?? null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default defineEventHandler(async (event) => {
|
|
61
|
+
setResponseHeader(event, "Cache-Control", "no-store");
|
|
62
|
+
setResponseHeader(event, "Referrer-Policy", "no-referrer");
|
|
63
|
+
setResponseHeader(event, "X-Content-Type-Options", "nosniff");
|
|
64
|
+
|
|
65
|
+
const query = getQuery(event);
|
|
66
|
+
const id = queryString(query.id);
|
|
67
|
+
if (!id) {
|
|
68
|
+
setResponseStatus(event, 400);
|
|
69
|
+
return { error: "Dashboard id is required" };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const token = queryString(query[AGENT_ACCESS_PARAM]);
|
|
73
|
+
const tokenAccess = token
|
|
74
|
+
? verifyScopedAgentAccessToken(token, {
|
|
75
|
+
resourceKind: ANALYTICS_DASHBOARD_AGENT_RESOURCE_KIND,
|
|
76
|
+
resourceId: id,
|
|
77
|
+
}).ok
|
|
78
|
+
: false;
|
|
79
|
+
const db = getDb() as any;
|
|
80
|
+
// guard:allow-unscoped -- this endpoint returns dashboard context only when the dashboard is public or a dashboard-scoped agent_access token verifies for this id.
|
|
81
|
+
const [row] = await db
|
|
82
|
+
.select()
|
|
83
|
+
.from(schema.dashboards)
|
|
84
|
+
.where(eq(schema.dashboards.id, id))
|
|
85
|
+
.limit(1);
|
|
86
|
+
|
|
87
|
+
const seed = row ? null : loadDashboardSeed(id);
|
|
88
|
+
if (!row && seed) {
|
|
89
|
+
if (!tokenAccess) {
|
|
90
|
+
setResponseStatus(event, 403);
|
|
91
|
+
return { error: "Invalid or expired agent access token" };
|
|
92
|
+
}
|
|
93
|
+
return buildDashboardSeedAgentContext(id, seed, { includeConfig: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!row) {
|
|
97
|
+
setResponseStatus(event, 404);
|
|
98
|
+
return { error: "Dashboard not found" };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (row.visibility !== "public" && !tokenAccess) {
|
|
102
|
+
setResponseStatus(event, 403);
|
|
103
|
+
return { error: "Invalid or expired agent access token" };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return buildDashboardAgentContext(rowToDashboard(row), {
|
|
107
|
+
includeConfig: true,
|
|
108
|
+
});
|
|
109
|
+
});
|