@xpert-ai/plugin-live-dashboard 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.xpertai-plugin/plugin.json +10 -2
- package/README.md +70 -0
- package/assets/branding/cover-v1.prompt.txt +27 -0
- package/assets/branding/cover-v1.webp +0 -0
- package/assets/previews/customer-brief.png +0 -0
- package/assets/previews/profit-atlas.png +0 -0
- package/assets/previews/trend-workspace.png +0 -0
- package/dist/index.js +14 -1
- package/dist/lib/agent.schema.d.ts +43 -37
- package/dist/lib/agent.schema.js +2 -1
- package/dist/lib/app-config.d.ts +62 -0
- package/dist/lib/app-config.js +38 -0
- package/dist/lib/artifact-type.provider.d.ts +15 -0
- package/dist/lib/artifact-type.provider.js +78 -0
- package/dist/lib/assistant-template.d.ts +2 -0
- package/dist/lib/assistant-template.js +7 -0
- package/dist/lib/content.provider.js +13 -5
- package/dist/lib/dashboard-navigation.d.ts +58 -14
- package/dist/lib/dashboard-navigation.js +11 -10
- package/dist/lib/dashboard-view.schema.d.ts +197 -46
- package/dist/lib/dashboard-view.schema.js +8 -1
- package/dist/lib/dashboard.service.d.ts +39 -2
- package/dist/lib/dashboard.service.js +52 -28
- package/dist/lib/document-libraries.d.ts +1 -1
- package/dist/lib/document-libraries.js +3 -2
- package/dist/lib/document-network.d.ts +1 -1
- package/dist/lib/document-network.js +2 -6
- package/dist/lib/editing-feedback.d.ts +5 -0
- package/dist/lib/editing-feedback.js +19 -0
- package/dist/lib/editing-patch.d.ts +22 -0
- package/dist/lib/editing-patch.js +46 -0
- package/dist/lib/editing-session.d.ts +167 -0
- package/dist/lib/editing-session.js +53 -0
- package/dist/lib/editing.d.ts +92 -60
- package/dist/lib/editing.js +36 -24
- package/dist/lib/html-guidance.d.ts +1 -1
- package/dist/lib/html-guidance.js +1 -1
- package/dist/lib/middleware.js +48 -20
- package/dist/lib/monitor.d.ts +70 -0
- package/dist/lib/monitor.js +101 -0
- package/dist/lib/plugin.js +5 -1
- package/dist/lib/project.d.ts +106 -0
- package/dist/lib/project.js +17 -0
- package/dist/lib/projects.schema.d.ts +49 -0
- package/dist/lib/projects.schema.js +7 -0
- package/dist/lib/projects.service.d.ts +23 -0
- package/dist/lib/projects.service.js +65 -0
- package/dist/lib/prompt-workflows.d.ts +3 -2
- package/dist/lib/prompt-workflows.js +17 -3
- package/dist/lib/provider-options.controller.d.ts +10 -0
- package/dist/lib/provider-options.controller.js +49 -0
- package/dist/lib/resource-card.js +1 -1
- package/dist/lib/runtime-context.d.ts +3 -0
- package/dist/lib/runtime-context.js +13 -0
- package/dist/lib/save-evidence.d.ts +1 -1
- package/dist/lib/templates.d.ts +2 -0
- package/dist/lib/templates.js +55 -0
- package/dist/lib/view.provider.d.ts +97 -7
- package/dist/lib/view.provider.js +39 -10
- package/dist/libraries/TREND-WORKSPACE-ICONS-LICENSE.txt +43 -0
- package/dist/libraries/catalog.json +1 -1
- package/dist/remote/app.css +1 -1
- package/dist/remote/app.js +33 -33
- package/dist/remote/build-manifest.json +118 -32
- package/dist/sandbox-actions/action.json +1 -1
- package/dist/sandbox-actions/bundle/host.js +2 -2
- package/dist/sandbox-actions/bundle/resource-cache.mjs +2 -1
- package/dist/sandbox-actions/bundle/validate.mjs +1 -1
- package/docs/docs.json +7 -0
- package/docs/frontend-libraries.mdx +12 -4
- package/docs/html-mode.md +3 -3
- package/docs/quick-start-acceptance.mdx +50 -0
- package/docs/quick-start.mdx +64 -0
- package/docs/superstore.mdx +38 -0
- package/docs/team-sharing.mdx +6 -2
- package/docs/trend-workspace-acceptance.mdx +43 -0
- package/docs/trend-workspace.mdx +30 -0
- package/docs/verification.mdx +4 -2
- package/docs/visual-styles-acceptance.mdx +40 -0
- package/docs/visual-styles.mdx +46 -0
- package/package.json +25 -18
- package/scripts/import-prompts.mjs +108 -0
- package/skills/live-dashboard/SKILL.md +27 -3
- package/skills/live-dashboard/examples/README.md +25 -0
- package/skills/live-dashboard/examples/catalog.json +79 -0
- package/skills/live-dashboard/examples/production-quality/README.md +25 -0
- package/skills/live-dashboard/examples/production-quality/dashboard.html +129 -0
- package/skills/live-dashboard/examples/production-quality/definition.json +67 -0
- package/skills/live-dashboard/examples/production-quality/demo.csv +7 -0
- package/skills/live-dashboard/examples/production-quality/demo.json +44 -0
- package/skills/live-dashboard/examples/production-quality/expected.json +26 -0
- package/skills/live-dashboard/examples/production-quality/preview.png +0 -0
- package/skills/live-dashboard/examples/production-quality/query.json +53 -0
- package/skills/live-dashboard/examples/regional-sales/README.md +25 -0
- package/skills/live-dashboard/examples/regional-sales/dashboard-create.json +35 -0
- package/skills/live-dashboard/examples/regional-sales/definition.json +35 -0
- package/skills/live-dashboard/examples/regional-sales/demo.csv +5 -0
- package/skills/live-dashboard/examples/regional-sales/demo.json +18 -0
- package/skills/live-dashboard/examples/regional-sales/expected.json +19 -0
- package/skills/live-dashboard/examples/regional-sales/preview.png +0 -0
- package/skills/live-dashboard/examples/regional-sales/query.json +45 -0
- package/skills/live-dashboard/examples/sales-overview/README.md +25 -0
- package/skills/live-dashboard/examples/sales-overview/dashboard.html +129 -0
- package/skills/live-dashboard/examples/sales-overview/definition.json +57 -0
- package/skills/live-dashboard/examples/sales-overview/demo.csv +7 -0
- package/skills/live-dashboard/examples/sales-overview/demo.json +38 -0
- package/skills/live-dashboard/examples/sales-overview/expected.json +22 -0
- package/skills/live-dashboard/examples/sales-overview/preview.png +0 -0
- package/skills/live-dashboard/examples/sales-overview/query.json +52 -0
- package/skills/live-dashboard/examples/superstore/README.md +64 -0
- package/skills/live-dashboard/examples/superstore/THIRD_PARTY_LICENSE.txt +207 -0
- package/skills/live-dashboard/examples/superstore/build_data.py +155 -0
- package/skills/live-dashboard/examples/superstore/build_pages.py +129 -0
- package/skills/live-dashboard/examples/superstore/checks.json +104 -0
- package/skills/live-dashboard/examples/superstore/customers-mobile-dark.png +0 -0
- package/skills/live-dashboard/examples/superstore/customers-select.png +0 -0
- package/skills/live-dashboard/examples/superstore/customers.html +316 -0
- package/skills/live-dashboard/examples/superstore/customers.png +0 -0
- package/skills/live-dashboard/examples/superstore/customers.queries.json +512 -0
- package/skills/live-dashboard/examples/superstore/expected.json +4069 -0
- package/skills/live-dashboard/examples/superstore/import.json +200 -0
- package/skills/live-dashboard/examples/superstore/orders.csv +10195 -0
- package/skills/live-dashboard/examples/superstore/overview-mobile-dark.png +0 -0
- package/skills/live-dashboard/examples/superstore/overview-select.png +0 -0
- package/skills/live-dashboard/examples/superstore/overview.html +310 -0
- package/skills/live-dashboard/examples/superstore/overview.png +0 -0
- package/skills/live-dashboard/examples/superstore/overview.queries.json +636 -0
- package/skills/live-dashboard/examples/superstore/page.js +109 -0
- package/skills/live-dashboard/examples/superstore/profitability-mobile-dark.png +0 -0
- package/skills/live-dashboard/examples/superstore/profitability-select.png +0 -0
- package/skills/live-dashboard/examples/superstore/profitability.html +307 -0
- package/skills/live-dashboard/examples/superstore/profitability.png +0 -0
- package/skills/live-dashboard/examples/superstore/profitability.queries.json +334 -0
- package/skills/live-dashboard/examples/superstore/provenance.json +24 -0
- package/skills/live-dashboard/examples/superstore/semantic-model.json +220 -0
- package/skills/live-dashboard/examples/superstore/test_data.py +62 -0
- package/skills/live-dashboard/references/html-runtime.md +1 -1
- package/skills/live-dashboard/themes/README.md +50 -0
- package/skills/live-dashboard/themes/customer-brief/README.md +47 -0
- package/skills/live-dashboard/themes/customer-brief/charts.js +17 -0
- package/skills/live-dashboard/themes/customer-brief/layout.html +22 -0
- package/skills/live-dashboard/themes/customer-brief/theme.css +108 -0
- package/skills/live-dashboard/themes/profit-atlas/README.md +43 -0
- package/skills/live-dashboard/themes/profit-atlas/charts.js +30 -0
- package/skills/live-dashboard/themes/profit-atlas/layout.html +19 -0
- package/skills/live-dashboard/themes/profit-atlas/theme.css +75 -0
- package/skills/live-dashboard/themes/trend-workspace/ICON_LICENSE.txt +43 -0
- package/skills/live-dashboard/themes/trend-workspace/README.md +144 -0
- package/skills/live-dashboard/themes/trend-workspace/charts.js +60 -0
- package/skills/live-dashboard/themes/trend-workspace/icons.js +11 -0
- package/skills/live-dashboard/themes/trend-workspace/layout.html +45 -0
- package/skills/live-dashboard/themes/trend-workspace/select.js +127 -0
- package/skills/live-dashboard/themes/trend-workspace/theme.css +137 -0
|
@@ -2,6 +2,7 @@ import type { AnalyticsValue, AnalyticsDiscoveryRequest, AnalyticsQueryRequest,
|
|
|
2
2
|
import { type RuntimeCapabilityRegistry } from '@xpert-ai/plugin-sdk';
|
|
3
3
|
import { type DashboardManifest, type DashboardChanges } from './dashboard.schema';
|
|
4
4
|
import { HtmlDashboardValidation } from './html-validation';
|
|
5
|
+
import type { DashboardAccess, DashboardMode } from './project';
|
|
5
6
|
export declare const PLUGIN_NAME = "@xpert-ai/plugin-live-dashboard";
|
|
6
7
|
export declare class DashboardService {
|
|
7
8
|
private readonly runtime;
|
|
@@ -19,6 +20,37 @@ export declare class DashboardService {
|
|
|
19
20
|
versionNumber: number | undefined;
|
|
20
21
|
updatedAt: string | Date | undefined;
|
|
21
22
|
}[]>;
|
|
23
|
+
access(context: XpertResolvedViewHostContext): Promise<DashboardAccess>;
|
|
24
|
+
listPage(context: XpertResolvedViewHostContext, page?: number, pageSize?: number): Promise<{
|
|
25
|
+
page: number;
|
|
26
|
+
pageSize: number;
|
|
27
|
+
hasMore: boolean;
|
|
28
|
+
items: {
|
|
29
|
+
title: string | null | undefined;
|
|
30
|
+
draftRevision?: number | undefined;
|
|
31
|
+
canDelete: boolean;
|
|
32
|
+
artifactId: string;
|
|
33
|
+
versionId: string | null | undefined;
|
|
34
|
+
versionNumber: number | undefined;
|
|
35
|
+
updatedAt: string | Date | undefined;
|
|
36
|
+
}[];
|
|
37
|
+
}>;
|
|
38
|
+
searchPage(context: XpertResolvedViewHostContext, input: {
|
|
39
|
+
search: string;
|
|
40
|
+
page: number;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
items: {
|
|
43
|
+
title: string | null | undefined;
|
|
44
|
+
draftRevision?: number | undefined;
|
|
45
|
+
canDelete: boolean;
|
|
46
|
+
artifactId: string;
|
|
47
|
+
versionId: string | null | undefined;
|
|
48
|
+
versionNumber: number | undefined;
|
|
49
|
+
updatedAt: string | Date | undefined;
|
|
50
|
+
}[];
|
|
51
|
+
page: number;
|
|
52
|
+
hasMore: boolean;
|
|
53
|
+
}>;
|
|
22
54
|
trash(context: XpertResolvedViewHostContext, artifactId: string, expectedRevision: number): Promise<{
|
|
23
55
|
artifactId: string;
|
|
24
56
|
}>;
|
|
@@ -30,7 +62,7 @@ export declare class DashboardService {
|
|
|
30
62
|
status: "draft";
|
|
31
63
|
title: string;
|
|
32
64
|
}>;
|
|
33
|
-
open(context: XpertResolvedViewHostContext, artifactId: string, versionId?: string, draftRevision?: number): Promise<{
|
|
65
|
+
open(context: XpertResolvedViewHostContext, artifactId: string, versionId?: string, draftRevision?: number, mode?: DashboardMode): Promise<{
|
|
34
66
|
manifest: {
|
|
35
67
|
schema: "xpert.live-dashboard";
|
|
36
68
|
id: string;
|
|
@@ -64,6 +96,7 @@ export declare class DashboardService {
|
|
|
64
96
|
dependencies?: import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentDependencyLock[] | undefined;
|
|
65
97
|
} | undefined;
|
|
66
98
|
};
|
|
99
|
+
contentHash: string;
|
|
67
100
|
draftId?: string | undefined;
|
|
68
101
|
draftRevision?: number | undefined;
|
|
69
102
|
baseVersionId?: string | null | undefined;
|
|
@@ -75,7 +108,7 @@ export declare class DashboardService {
|
|
|
75
108
|
versionNumber: number | undefined;
|
|
76
109
|
}>;
|
|
77
110
|
/** Trusted View transport only. Agent read tools receive locks, never megabytes of library code. */
|
|
78
|
-
openDocument(context: XpertResolvedViewHostContext, artifactId: string, versionId?: string, draftRevision?: number): Promise<{
|
|
111
|
+
openDocument(context: XpertResolvedViewHostContext, artifactId: string, versionId?: string, draftRevision?: number, mode?: DashboardMode): Promise<{
|
|
79
112
|
manifest: {
|
|
80
113
|
schema: "xpert.live-dashboard";
|
|
81
114
|
id: string;
|
|
@@ -110,6 +143,7 @@ export declare class DashboardService {
|
|
|
110
143
|
} | undefined;
|
|
111
144
|
};
|
|
112
145
|
dependencies: import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentDependencyAsset[];
|
|
146
|
+
contentHash: string;
|
|
113
147
|
draftId?: string | undefined;
|
|
114
148
|
draftRevision?: number | undefined;
|
|
115
149
|
baseVersionId?: string | null | undefined;
|
|
@@ -142,6 +176,8 @@ export declare class DashboardService {
|
|
|
142
176
|
versionNumber: number;
|
|
143
177
|
title: string | null | undefined;
|
|
144
178
|
}>;
|
|
179
|
+
validatePublication(context: XpertResolvedViewHostContext, artifactId: string, expectedRevision: number, expectedCurrentVersionId: string | null): Promise<void>;
|
|
180
|
+
private publishDraft;
|
|
145
181
|
query(context: XpertResolvedViewHostContext, artifactId: string, versionId: string | undefined, controls: Record<string, AnalyticsValue>, draftRevision?: number): Promise<{
|
|
146
182
|
payload?: {
|
|
147
183
|
ok: boolean;
|
|
@@ -237,6 +273,7 @@ export declare class DashboardService {
|
|
|
237
273
|
dependencies?: import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentDependencyLock[] | undefined;
|
|
238
274
|
} | undefined;
|
|
239
275
|
};
|
|
276
|
+
contentHash: string;
|
|
240
277
|
draftId?: string | undefined;
|
|
241
278
|
draftRevision?: number | undefined;
|
|
242
279
|
baseVersionId?: string | null | undefined;
|
|
@@ -32,23 +32,49 @@ let DashboardService = class DashboardService {
|
|
|
32
32
|
this.runtime = runtime;
|
|
33
33
|
this.htmlValidation = htmlValidation;
|
|
34
34
|
}
|
|
35
|
-
sources(context) { return this.runtime.require(plugin_sdk_1.AnalyticsRuntimeCapability).list(actor(context)); }
|
|
36
|
-
discover(context, input) { return this.runtime.require(plugin_sdk_1.AnalyticsRuntimeCapability).discover(actor(context), input); }
|
|
35
|
+
async sources(context) { await this.authorizeProject(context); return this.runtime.require(plugin_sdk_1.AnalyticsRuntimeCapability).list(actor(context)); }
|
|
36
|
+
async discover(context, input) { await this.authorizeProject(context); return this.runtime.require(plugin_sdk_1.AnalyticsRuntimeCapability).discover(actor(context), input); }
|
|
37
37
|
async preview(context, input) {
|
|
38
|
+
await this.authorizeProject(context);
|
|
38
39
|
const dataset = await this.runtime.require(plugin_sdk_1.AnalyticsRuntimeCapability).query(actor(context), input);
|
|
39
40
|
await this.probeControls(context, input.binding, input.controls ?? {});
|
|
40
41
|
return dataset;
|
|
41
42
|
}
|
|
42
43
|
async list(context) {
|
|
44
|
+
return (await this.listPage(context)).items;
|
|
45
|
+
}
|
|
46
|
+
async access(context) {
|
|
43
47
|
const access = await this.authorizeProject(context);
|
|
44
|
-
|
|
48
|
+
return { project: !!projectId(context), archived: access?.archived ?? false,
|
|
49
|
+
canReadDraft: !access || access.role !== 'member', canEdit: !access || !access.archived && access.role !== 'member',
|
|
50
|
+
canManage: !access || !access.archived && access.canManage };
|
|
51
|
+
}
|
|
52
|
+
async listPage(context, page = 1, pageSize = 50) {
|
|
53
|
+
const access = await this.access(context);
|
|
54
|
+
if (!Number.isSafeInteger(page) || page < 1 || !Number.isSafeInteger(pageSize) || pageSize < 1 || pageSize > 100)
|
|
55
|
+
throw new common_1.BadRequestException('DASHBOARD_INVALID_CURSOR');
|
|
56
|
+
const result = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).listArtifacts({ pluginName: exports.PLUGIN_NAME, resourceType: RESOURCE_TYPE, resourceIdPrefix: hostPrefix(context), status: 'active', page, pageSize, includeCurrentVersion: true });
|
|
45
57
|
const items = result.items.filter((item) => item.resourceId.startsWith(hostPrefix(context)) && (item.userId === context.userId || !item.userId && item.projectId === projectId(context)));
|
|
46
|
-
const drafts = new Map((await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).list(items.map(item => item.id))).map(draft => [draft.artifactId, draft]));
|
|
47
|
-
return items.map(item => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
const drafts = new Map((access.canReadDraft ? await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).list(items.map(item => item.id)) : []).map(draft => [draft.artifactId, draft]));
|
|
59
|
+
return { page, pageSize, hasMore: page * pageSize < result.total, items: items.filter(item => access.canReadDraft || !!item.currentVersionId).map(item => {
|
|
60
|
+
const draft = drafts.get(item.id);
|
|
61
|
+
return { ...summary(item), canDelete: !!draft && access.canManage,
|
|
62
|
+
...(draft ? { title: draft.title ?? item.title, draftRevision: draft.revision } : {}) };
|
|
63
|
+
}) };
|
|
64
|
+
}
|
|
65
|
+
async searchPage(context, input) {
|
|
66
|
+
// Search the authorized display titles, including editor drafts. Filtering a stored
|
|
67
|
+
// Artifact title in the host would miss renamed drafts or expose them to members.
|
|
68
|
+
const pageSize = 20, offset = (input.page - 1) * pageSize;
|
|
69
|
+
const search = input.search.trim().toLocaleLowerCase();
|
|
70
|
+
const matches = [];
|
|
71
|
+
for (let page = 1;; page++) {
|
|
72
|
+
const batch = await this.listPage(context, page, 100);
|
|
73
|
+
matches.push(...batch.items.filter(item => `${item.title ?? ''} ${item.artifactId}`.toLocaleLowerCase().includes(search)));
|
|
74
|
+
if (!batch.hasMore || matches.length > offset + pageSize)
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
return { items: matches.slice(offset, offset + pageSize), page: input.page, hasMore: matches.length > offset + pageSize };
|
|
52
78
|
}
|
|
53
79
|
async trash(context, artifactId, expectedRevision) {
|
|
54
80
|
await this.authorizeProject(context, 'manage');
|
|
@@ -88,38 +114,30 @@ let DashboardService = class DashboardService {
|
|
|
88
114
|
for (const binding of parsed.bindings)
|
|
89
115
|
await this.probeControls(context, binding, Object.fromEntries(parsed.controls.filter((control) => control.defaultValue !== undefined).map((control) => [control.controlId, control.defaultValue ?? null])));
|
|
90
116
|
const htmlValidation = await this.htmlValidation.validate(context, parsed, preview, dependencies);
|
|
91
|
-
const artifact = existing ?? await artifacts.createArtifact({ source, kind: 'file', title: parsed.title,
|
|
117
|
+
const artifact = existing ?? await artifacts.createArtifact({ source, type: { pluginName: exports.PLUGIN_NAME, resourceType: RESOURCE_TYPE }, kind: 'file', title: parsed.title,
|
|
92
118
|
scope: { ...scopedActor, ...(projectId(context) ? { projectId: projectId(context), userId: null } : { xpertId: context.hostId }) } });
|
|
93
|
-
const
|
|
94
|
-
const project = projectId(context);
|
|
95
|
-
// Shared definitions live in the Project volume; private legacy dashboards retain their user volume.
|
|
96
|
-
const privateScope = { tenantId: scopedActor.tenantId, userId: scopedActor.userId, catalog: 'users' };
|
|
97
|
-
const reference = project
|
|
98
|
-
? (await files.writeRuntimeBuffer({ tenantId: scopedActor.tenantId, userId: scopedActor.userId,
|
|
99
|
-
catalog: 'projects', scopeId: project, projectId: project, buffer, originalName: 'dashboard.json',
|
|
100
|
-
fileName: `${digest}.json`, folder: `.xpert/live-dashboards/${parsed.id}`, mimeType: MIME_TYPE })).reference
|
|
101
|
-
: await files.uploadBuffer({ ...privateScope, buffer, originalName: 'dashboard.json', fileName: `${digest}.json`,
|
|
102
|
-
folder: `live-dashboards/${parsed.id}`, mimeType: MIME_TYPE }).then(file => ({
|
|
103
|
-
source: 'platform.workspace.files', ...privateScope, filePath: file.filePath, workspacePath: file.workspacePath
|
|
104
|
-
}));
|
|
119
|
+
const reference = await this.runtime.require(plugin_sdk_1.ArtifactContentRuntimeCapability).write({ artifactId: artifact.id, buffer });
|
|
105
120
|
const result = await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).save({ artifactId: artifact.id, expectedRevision,
|
|
106
121
|
expectedCurrentVersionId: expectedVersionId, mimeType: MIME_TYPE, title: parsed.title, fileName: 'dashboard.json', sha256: digest,
|
|
107
122
|
...(htmlValidation ? { metadata: { htmlValidation } } : {}), workspaceFileRef: reference });
|
|
108
123
|
return { artifactId: artifact.id, draftId: result.id, draftRevision: result.revision, baseVersionId: result.baseVersionId, status: 'draft', title: parsed.title };
|
|
109
124
|
}
|
|
110
|
-
async open(context, artifactId, versionId, draftRevision) {
|
|
111
|
-
const { dependencies, ...saved } = await this.openDocument(context, artifactId, versionId, draftRevision);
|
|
125
|
+
async open(context, artifactId, versionId, draftRevision, mode = 'draft') {
|
|
126
|
+
const { dependencies, ...saved } = await this.openDocument(context, artifactId, versionId, draftRevision, mode);
|
|
112
127
|
return saved;
|
|
113
128
|
}
|
|
114
129
|
/** Trusted View transport only. Agent read tools receive locks, never megabytes of library code. */
|
|
115
|
-
async openDocument(context, artifactId, versionId, draftRevision) {
|
|
116
|
-
await this.
|
|
130
|
+
async openDocument(context, artifactId, versionId, draftRevision, mode = 'draft') {
|
|
131
|
+
const access = await this.access(context);
|
|
132
|
+
const readDraft = draftRevision !== undefined || !versionId && mode === 'draft';
|
|
133
|
+
if (readDraft && !access.canReadDraft)
|
|
134
|
+
throw new common_1.ForbiddenException('DASHBOARD_DRAFT_ACCESS_DENIED');
|
|
117
135
|
const artifacts = this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability);
|
|
118
136
|
const artifact = await artifacts.getArtifact(artifactId);
|
|
119
137
|
assertOwned(context, artifact);
|
|
120
138
|
if (artifact.status !== 'active')
|
|
121
139
|
throw new common_1.ForbiddenException('DASHBOARD_ARCHIVED');
|
|
122
|
-
const draft =
|
|
140
|
+
const draft = readDraft ? await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).get(artifactId) : null;
|
|
123
141
|
if (draftRevision !== undefined && draft?.revision !== draftRevision)
|
|
124
142
|
throw new common_1.ConflictException('DASHBOARD_DRAFT_CONFLICT');
|
|
125
143
|
const version = draft ?? (versionId ? (await artifacts.listArtifactVersions({ artifactId, status: 'active' })).find((item) => item.id === versionId) : artifact.currentVersion);
|
|
@@ -135,7 +153,7 @@ let DashboardService = class DashboardService {
|
|
|
135
153
|
throw new common_1.BadRequestException('DASHBOARD_IDENTITY_MISMATCH');
|
|
136
154
|
return { ...summary(artifact, 'versionNumber' in version ? version : undefined),
|
|
137
155
|
...(draft ? { draftId: draft.id, draftRevision: draft.revision, baseVersionId: draft.baseVersionId,
|
|
138
|
-
isDraft: draft.revision !== draft.publishedRevision, title: draft.title, updatedAt: draft.updatedAt } : {}), manifest, dependencies };
|
|
156
|
+
isDraft: draft.revision !== draft.publishedRevision, title: draft.title, updatedAt: draft.updatedAt } : {}), manifest, dependencies, contentHash: version.sha256 };
|
|
139
157
|
}
|
|
140
158
|
async history(context, artifactId) {
|
|
141
159
|
await this.authorizeProject(context);
|
|
@@ -156,6 +174,9 @@ let DashboardService = class DashboardService {
|
|
|
156
174
|
}
|
|
157
175
|
async publish(context, artifactId, expectedRevision, expectedCurrentVersionId) {
|
|
158
176
|
await this.authorizeProject(context, 'manage');
|
|
177
|
+
return this.publishDraft(context, artifactId, expectedRevision, expectedCurrentVersionId);
|
|
178
|
+
}
|
|
179
|
+
async validatePublication(context, artifactId, expectedRevision, expectedCurrentVersionId) {
|
|
159
180
|
const saved = await this.open(context, artifactId, undefined, expectedRevision);
|
|
160
181
|
if (!saved.manifest.bindings.length)
|
|
161
182
|
throw new common_1.BadRequestException('DASHBOARD_CONFIGURE_BEFORE_PUBLISHING');
|
|
@@ -167,6 +188,9 @@ let DashboardService = class DashboardService {
|
|
|
167
188
|
}
|
|
168
189
|
// Revalidate current source access and bindings before publishing the exact reviewed bytes.
|
|
169
190
|
await this.execute(context, saved.manifest, {});
|
|
191
|
+
}
|
|
192
|
+
async publishDraft(context, artifactId, expectedRevision, expectedCurrentVersionId) {
|
|
193
|
+
await this.validatePublication(context, artifactId, expectedRevision, expectedCurrentVersionId);
|
|
170
194
|
const version = await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).publish({ artifactId, expectedRevision, expectedCurrentVersionId });
|
|
171
195
|
return { artifactId, versionId: version.id, versionNumber: version.versionNumber, title: version.title };
|
|
172
196
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod/v3';
|
|
2
2
|
import { type ArtifactDocumentDependencyAsset, type ArtifactDocumentDependencyLock } from '@xpert-ai/plugin-sdk/artifact-document';
|
|
3
3
|
/** The plugin owns its catalog. The platform owns verification and isolated execution. */
|
|
4
|
-
export declare const libraryRequestsSchema: z.ZodEffects<z.ZodArray<z.ZodEnum<["echarts", "three"]>, "many">, ("echarts" | "three")[], ("echarts" | "three")[]>;
|
|
4
|
+
export declare const libraryRequestsSchema: z.ZodEffects<z.ZodEffects<z.ZodArray<z.ZodEnum<["echarts", "three", "trend-workspace", "profit-atlas", "customer-brief"]>, "many">, ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[], ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[]>, ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[], ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[]>;
|
|
5
5
|
export type LibraryRequests = z.output<typeof libraryRequestsSchema>;
|
|
6
6
|
export declare function bundledLibraries(): Promise<ArtifactDocumentDependencyAsset[]>;
|
|
7
7
|
export declare function libraryLocks(ids: LibraryRequests): Promise<ArtifactDocumentDependencyLock[]>;
|
|
@@ -9,8 +9,9 @@ const node_path_1 = require("node:path");
|
|
|
9
9
|
const v3_1 = require("zod/v3");
|
|
10
10
|
const artifact_document_1 = require("@xpert-ai/plugin-sdk/artifact-document");
|
|
11
11
|
/** The plugin owns its catalog. The platform owns verification and isolated execution. */
|
|
12
|
-
exports.libraryRequestsSchema = v3_1.z.array(v3_1.z.enum(['echarts', 'three'])).max(
|
|
13
|
-
.refine(ids => new Set(ids).size === ids.length, 'Duplicate library')
|
|
12
|
+
exports.libraryRequestsSchema = v3_1.z.array(v3_1.z.enum(['echarts', 'three', 'trend-workspace', 'profit-atlas', 'customer-brief'])).max(3)
|
|
13
|
+
.refine(ids => new Set(ids).size === ids.length, 'Duplicate library')
|
|
14
|
+
.refine(ids => ids.filter(id => ['trend-workspace', 'profit-atlas', 'customer-brief'].includes(id)).length <= 1, 'Choose one dashboard visual theme');
|
|
14
15
|
async function bundledLibraries() {
|
|
15
16
|
return (0, artifact_document_1.verifyArtifactDocumentDependencies)(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(__dirname, '../libraries/catalog.json'), 'utf8')));
|
|
16
17
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ArtifactDocumentNetworkPolicy } from '@xpert-ai/plugin-sdk/artifact-document';
|
|
2
|
-
/**
|
|
2
|
+
/** Temporary functional acceptance policy, shared by preview and viewing. */
|
|
3
3
|
export declare const DASHBOARD_NETWORK_POLICY: ArtifactDocumentNetworkPolicy;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DASHBOARD_NETWORK_POLICY = void 0;
|
|
4
|
-
/**
|
|
5
|
-
exports.DASHBOARD_NETWORK_POLICY = {
|
|
6
|
-
scripts: ['https://cdn.tailwindcss.com', 'https://cdn.jsdelivr.net', 'https://cdnjs.cloudflare.com', 'https://unpkg.com'],
|
|
7
|
-
styles: ['https://cdn.jsdelivr.net', 'https://cdnjs.cloudflare.com', 'https://unpkg.com', 'https://fonts.googleapis.com'],
|
|
8
|
-
fonts: ['https://cdn.jsdelivr.net', 'https://cdnjs.cloudflare.com', 'https://unpkg.com', 'https://fonts.gstatic.com']
|
|
9
|
-
};
|
|
4
|
+
/** Temporary functional acceptance policy, shared by preview and viewing. */
|
|
5
|
+
exports.DASHBOARD_NETWORK_POLICY = { mode: 'unrestricted' };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Missing selection is a normal interaction outcome; no Artifact operation is attempted. */
|
|
2
|
+
export declare function dashboardSelectionRequired(config: unknown): {
|
|
3
|
+
status: "selection_required";
|
|
4
|
+
message: "Please select a dashboard to edit first." | "请先选择要编辑的看板" | "請先選擇要編輯的看板";
|
|
5
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dashboardSelectionRequired = dashboardSelectionRequired;
|
|
4
|
+
const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
|
|
5
|
+
const v3_1 = require("zod/v3");
|
|
6
|
+
const messages = {
|
|
7
|
+
en: 'Please select a dashboard to edit first.',
|
|
8
|
+
'zh-Hans': '请先选择要编辑的看板',
|
|
9
|
+
'zh-Hant': '請先選擇要編輯的看板'
|
|
10
|
+
};
|
|
11
|
+
const languageConfig = v3_1.z.object({ configurable: v3_1.z.object({ language: v3_1.z.string().optional() }).optional() });
|
|
12
|
+
/** Missing selection is a normal interaction outcome; no Artifact operation is attempted. */
|
|
13
|
+
function dashboardSelectionRequired(config) {
|
|
14
|
+
const parsed = languageConfig.safeParse(config);
|
|
15
|
+
const language = (parsed.success ? parsed.data.configurable?.language : undefined) ?? plugin_sdk_1.RequestContext.getLanguageCode();
|
|
16
|
+
const locale = language.toLowerCase().replaceAll('_', '-');
|
|
17
|
+
const key = ['zh-hant', 'zh-tw', 'zh-hk', 'zh-mo'].includes(locale) ? 'zh-Hant' : locale === 'zh' || locale.startsWith('zh-') ? 'zh-Hans' : 'en';
|
|
18
|
+
return { status: 'selection_required', message: messages[key] };
|
|
19
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod/v3';
|
|
2
|
+
import type { DashboardManifest } from './dashboard.schema';
|
|
3
|
+
export declare const removalsSchema: z.ZodOptional<z.ZodObject<{
|
|
4
|
+
bindingIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
5
|
+
controlIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
6
|
+
viewIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
7
|
+
htmlElementIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
8
|
+
}, "strict", z.ZodTypeAny, {
|
|
9
|
+
bindingIds: string[];
|
|
10
|
+
controlIds: string[];
|
|
11
|
+
viewIds: string[];
|
|
12
|
+
htmlElementIds: string[];
|
|
13
|
+
}, {
|
|
14
|
+
bindingIds?: string[] | undefined;
|
|
15
|
+
controlIds?: string[] | undefined;
|
|
16
|
+
viewIds?: string[] | undefined;
|
|
17
|
+
htmlElementIds?: string[] | undefined;
|
|
18
|
+
}>>;
|
|
19
|
+
export declare function upsertComponents<T>(existing: T[], changes: T[] | undefined, id: (item: T) => string, removed?: string[]): T[];
|
|
20
|
+
export declare function htmlElementIds(html: string): Set<string>;
|
|
21
|
+
/** IDs are an explicit preservation contract, not a semantic comparison of arbitrary HTML. */
|
|
22
|
+
export declare function assertHtmlPreserved(previous: DashboardManifest['presentation'], next: DashboardManifest['presentation'], removed?: string[]): void;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removalsSchema = void 0;
|
|
4
|
+
exports.upsertComponents = upsertComponents;
|
|
5
|
+
exports.htmlElementIds = htmlElementIds;
|
|
6
|
+
exports.assertHtmlPreserved = assertHtmlPreserved;
|
|
7
|
+
const parse5_1 = require("parse5");
|
|
8
|
+
const v3_1 = require("zod/v3");
|
|
9
|
+
exports.removalsSchema = v3_1.z.object({
|
|
10
|
+
bindingIds: v3_1.z.array(v3_1.z.string()).max(8).default([]), controlIds: v3_1.z.array(v3_1.z.string()).max(24).default([]),
|
|
11
|
+
viewIds: v3_1.z.array(v3_1.z.string()).max(40).default([]), htmlElementIds: v3_1.z.array(v3_1.z.string()).max(500).default([])
|
|
12
|
+
}).strict().optional().describe('Only explicitly requested removals. Arrays of changed components otherwise upsert by stable ID and preserve omitted components.');
|
|
13
|
+
function upsertComponents(existing, changes, id, removed = []) {
|
|
14
|
+
if (new Set(changes?.map(id)).size !== (changes?.length ?? 0))
|
|
15
|
+
throw new Error('DASHBOARD_DUPLICATE_COMPONENT');
|
|
16
|
+
if (removed.some(key => !existing.some(item => id(item) === key) || changes?.some(item => id(item) === key)))
|
|
17
|
+
throw new Error('DASHBOARD_REMOVAL_INVALID: copy existing IDs and do not also update a removed component');
|
|
18
|
+
const next = new Map(existing.filter(item => !removed.includes(id(item))).map(item => [id(item), item]));
|
|
19
|
+
for (const item of changes ?? [])
|
|
20
|
+
next.set(id(item), item);
|
|
21
|
+
return [...next.values()];
|
|
22
|
+
}
|
|
23
|
+
function htmlElementIds(html) {
|
|
24
|
+
const ids = new Set();
|
|
25
|
+
const visit = (node) => {
|
|
26
|
+
if ('attrs' in node)
|
|
27
|
+
for (const attr of node.attrs)
|
|
28
|
+
if (attr.name === 'id' && attr.value)
|
|
29
|
+
ids.add(attr.value);
|
|
30
|
+
if ('childNodes' in node)
|
|
31
|
+
node.childNodes.forEach(visit);
|
|
32
|
+
if ('content' in node)
|
|
33
|
+
visit(node.content);
|
|
34
|
+
};
|
|
35
|
+
visit((0, parse5_1.parse)(html));
|
|
36
|
+
return ids;
|
|
37
|
+
}
|
|
38
|
+
/** IDs are an explicit preservation contract, not a semantic comparison of arbitrary HTML. */
|
|
39
|
+
function assertHtmlPreserved(previous, next, removed = []) {
|
|
40
|
+
if (!previous || !next || previous.html === next.html)
|
|
41
|
+
return;
|
|
42
|
+
const before = htmlElementIds(previous.html), after = htmlElementIds(next.html);
|
|
43
|
+
const missing = [...before].filter(id => !after.has(id) && !removed.includes(id));
|
|
44
|
+
if (missing.length)
|
|
45
|
+
throw new Error(`DASHBOARD_COMPONENT_REMOVAL_REQUIRED: preserve existing HTML IDs or explicitly declare user-requested removals: ${JSON.stringify(missing)}. Your editing file is retained.`);
|
|
46
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { z } from 'zod/v3';
|
|
2
|
+
import type { IAgentMiddlewareContext } from '@xpert-ai/plugin-sdk';
|
|
3
|
+
export declare const dashboardSelectionSchema: z.ZodObject<{
|
|
4
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
5
|
+
artifactId: z.ZodString;
|
|
6
|
+
versionId: z.ZodOptional<z.ZodString>;
|
|
7
|
+
draftRevision: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
}, "strict", z.ZodTypeAny, {
|
|
9
|
+
artifactId: string;
|
|
10
|
+
schemaVersion: 1;
|
|
11
|
+
versionId?: string | undefined;
|
|
12
|
+
draftRevision?: number | undefined;
|
|
13
|
+
}, {
|
|
14
|
+
artifactId: string;
|
|
15
|
+
schemaVersion: 1;
|
|
16
|
+
versionId?: string | undefined;
|
|
17
|
+
draftRevision?: number | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export declare const dashboardContextSchema: z.ZodObject<{
|
|
20
|
+
live_dashboard: z.ZodOptional<z.ZodNullable<z.ZodUnion<[z.ZodObject<{
|
|
21
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
22
|
+
artifactId: z.ZodString;
|
|
23
|
+
versionId: z.ZodOptional<z.ZodString>;
|
|
24
|
+
draftRevision: z.ZodOptional<z.ZodNumber>;
|
|
25
|
+
}, "strict", z.ZodTypeAny, {
|
|
26
|
+
artifactId: string;
|
|
27
|
+
schemaVersion: 1;
|
|
28
|
+
versionId?: string | undefined;
|
|
29
|
+
draftRevision?: number | undefined;
|
|
30
|
+
}, {
|
|
31
|
+
artifactId: string;
|
|
32
|
+
schemaVersion: 1;
|
|
33
|
+
versionId?: string | undefined;
|
|
34
|
+
draftRevision?: number | undefined;
|
|
35
|
+
}>, z.ZodObject<{
|
|
36
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
37
|
+
mode: z.ZodLiteral<"new">;
|
|
38
|
+
}, "strict", z.ZodTypeAny, {
|
|
39
|
+
mode: "new";
|
|
40
|
+
schemaVersion: 1;
|
|
41
|
+
}, {
|
|
42
|
+
mode: "new";
|
|
43
|
+
schemaVersion: 1;
|
|
44
|
+
}>]>>>;
|
|
45
|
+
}, "strip", z.ZodTypeAny, {
|
|
46
|
+
live_dashboard?: {
|
|
47
|
+
artifactId: string;
|
|
48
|
+
schemaVersion: 1;
|
|
49
|
+
versionId?: string | undefined;
|
|
50
|
+
draftRevision?: number | undefined;
|
|
51
|
+
} | {
|
|
52
|
+
mode: "new";
|
|
53
|
+
schemaVersion: 1;
|
|
54
|
+
} | null | undefined;
|
|
55
|
+
}, {
|
|
56
|
+
live_dashboard?: {
|
|
57
|
+
artifactId: string;
|
|
58
|
+
schemaVersion: 1;
|
|
59
|
+
versionId?: string | undefined;
|
|
60
|
+
draftRevision?: number | undefined;
|
|
61
|
+
} | {
|
|
62
|
+
mode: "new";
|
|
63
|
+
schemaVersion: 1;
|
|
64
|
+
} | null | undefined;
|
|
65
|
+
}>;
|
|
66
|
+
export type DashboardSelection = z.infer<typeof dashboardSelectionSchema>;
|
|
67
|
+
export declare const dashboardStateSchema: z.ZodObject<{
|
|
68
|
+
dashboardEditing: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
69
|
+
scope: z.ZodString;
|
|
70
|
+
target: z.ZodNullable<z.ZodObject<{
|
|
71
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
72
|
+
artifactId: z.ZodString;
|
|
73
|
+
versionId: z.ZodOptional<z.ZodString>;
|
|
74
|
+
draftRevision: z.ZodOptional<z.ZodNumber>;
|
|
75
|
+
}, "strict", z.ZodTypeAny, {
|
|
76
|
+
artifactId: string;
|
|
77
|
+
schemaVersion: 1;
|
|
78
|
+
versionId?: string | undefined;
|
|
79
|
+
draftRevision?: number | undefined;
|
|
80
|
+
}, {
|
|
81
|
+
artifactId: string;
|
|
82
|
+
schemaVersion: 1;
|
|
83
|
+
versionId?: string | undefined;
|
|
84
|
+
draftRevision?: number | undefined;
|
|
85
|
+
}>>;
|
|
86
|
+
}, "strip", z.ZodTypeAny, {
|
|
87
|
+
target: {
|
|
88
|
+
artifactId: string;
|
|
89
|
+
schemaVersion: 1;
|
|
90
|
+
versionId?: string | undefined;
|
|
91
|
+
draftRevision?: number | undefined;
|
|
92
|
+
} | null;
|
|
93
|
+
scope: string;
|
|
94
|
+
}, {
|
|
95
|
+
target: {
|
|
96
|
+
artifactId: string;
|
|
97
|
+
schemaVersion: 1;
|
|
98
|
+
versionId?: string | undefined;
|
|
99
|
+
draftRevision?: number | undefined;
|
|
100
|
+
} | null;
|
|
101
|
+
scope: string;
|
|
102
|
+
}>>>;
|
|
103
|
+
}, "strip", z.ZodTypeAny, {
|
|
104
|
+
dashboardEditing?: {
|
|
105
|
+
target: {
|
|
106
|
+
artifactId: string;
|
|
107
|
+
schemaVersion: 1;
|
|
108
|
+
versionId?: string | undefined;
|
|
109
|
+
draftRevision?: number | undefined;
|
|
110
|
+
} | null;
|
|
111
|
+
scope: string;
|
|
112
|
+
} | null | undefined;
|
|
113
|
+
}, {
|
|
114
|
+
dashboardEditing?: {
|
|
115
|
+
target: {
|
|
116
|
+
artifactId: string;
|
|
117
|
+
schemaVersion: 1;
|
|
118
|
+
versionId?: string | undefined;
|
|
119
|
+
draftRevision?: number | undefined;
|
|
120
|
+
} | null;
|
|
121
|
+
scope: string;
|
|
122
|
+
} | null | undefined;
|
|
123
|
+
}>;
|
|
124
|
+
/** Checkpoints belong to an actor and conversation. Copied state must never choose another user's target. */
|
|
125
|
+
export declare function editingSession(context: IAgentMiddlewareContext): {
|
|
126
|
+
current: () => {
|
|
127
|
+
artifactId: string;
|
|
128
|
+
schemaVersion: 1;
|
|
129
|
+
versionId?: string | undefined;
|
|
130
|
+
draftRevision?: number | undefined;
|
|
131
|
+
} | null;
|
|
132
|
+
resolve(config: unknown): {
|
|
133
|
+
artifactId: string;
|
|
134
|
+
schemaVersion: 1;
|
|
135
|
+
versionId?: string | undefined;
|
|
136
|
+
draftRevision?: number | undefined;
|
|
137
|
+
} | null;
|
|
138
|
+
restore(state: unknown, requestContext: unknown): {
|
|
139
|
+
dashboardEditing: {
|
|
140
|
+
scope: string;
|
|
141
|
+
target: {
|
|
142
|
+
artifactId: string;
|
|
143
|
+
schemaVersion: 1;
|
|
144
|
+
versionId?: string | undefined;
|
|
145
|
+
draftRevision?: number | undefined;
|
|
146
|
+
} | null;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
select(target: DashboardSelection | null): {
|
|
150
|
+
dashboardEditing: {
|
|
151
|
+
scope: string;
|
|
152
|
+
target: {
|
|
153
|
+
artifactId: string;
|
|
154
|
+
schemaVersion: 1;
|
|
155
|
+
versionId?: string | undefined;
|
|
156
|
+
draftRevision?: number | undefined;
|
|
157
|
+
} | null;
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
export declare function selectionFromContext(value: unknown): DashboardSelection | null | undefined;
|
|
162
|
+
export declare function selectionFromConfig(value: unknown): {
|
|
163
|
+
artifactId: string;
|
|
164
|
+
schemaVersion: 1;
|
|
165
|
+
versionId?: string | undefined;
|
|
166
|
+
draftRevision?: number | undefined;
|
|
167
|
+
} | null | undefined;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dashboardStateSchema = exports.dashboardContextSchema = exports.dashboardSelectionSchema = void 0;
|
|
4
|
+
exports.editingSession = editingSession;
|
|
5
|
+
exports.selectionFromContext = selectionFromContext;
|
|
6
|
+
exports.selectionFromConfig = selectionFromConfig;
|
|
7
|
+
const v3_1 = require("zod/v3");
|
|
8
|
+
exports.dashboardSelectionSchema = v3_1.z.object({ schemaVersion: v3_1.z.literal(1), artifactId: v3_1.z.string().uuid(),
|
|
9
|
+
versionId: v3_1.z.string().uuid().optional(), draftRevision: v3_1.z.number().int().positive().optional() }).strict();
|
|
10
|
+
const newSelection = v3_1.z.object({ schemaVersion: v3_1.z.literal(1), mode: v3_1.z.literal('new') }).strict();
|
|
11
|
+
exports.dashboardContextSchema = v3_1.z.object({ live_dashboard: v3_1.z.union([exports.dashboardSelectionSchema, newSelection]).nullable().optional() });
|
|
12
|
+
exports.dashboardStateSchema = v3_1.z.object({ dashboardEditing: v3_1.z.object({
|
|
13
|
+
scope: v3_1.z.string(), target: exports.dashboardSelectionSchema.nullable()
|
|
14
|
+
}).nullable().optional() });
|
|
15
|
+
/** Checkpoints belong to an actor and conversation. Copied state must never choose another user's target. */
|
|
16
|
+
function editingSession(context) {
|
|
17
|
+
const scope = JSON.stringify([context.tenantId, context.organizationId, context.userId, context.xpertId,
|
|
18
|
+
context.projectId, context.conversationId, context.threadId, context.agentKey]);
|
|
19
|
+
let active = null;
|
|
20
|
+
let initialized = false;
|
|
21
|
+
return {
|
|
22
|
+
current: () => active,
|
|
23
|
+
resolve(config) { return initialized ? active : selectionFromConfig(config) ?? active; },
|
|
24
|
+
restore(state, requestContext) {
|
|
25
|
+
const persisted = exports.dashboardStateSchema.safeParse(state);
|
|
26
|
+
const selected = selectionFromContext(requestContext);
|
|
27
|
+
active = selected !== undefined ? selected : persisted.success && persisted.data.dashboardEditing?.scope === scope
|
|
28
|
+
? persisted.data.dashboardEditing.target : null;
|
|
29
|
+
initialized = true;
|
|
30
|
+
return { dashboardEditing: { scope, target: active } };
|
|
31
|
+
},
|
|
32
|
+
select(target) { active = target; initialized = true; return { dashboardEditing: { scope, target } }; }
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function selectionFromContext(value) {
|
|
36
|
+
if (value === undefined || value === null)
|
|
37
|
+
return undefined;
|
|
38
|
+
const parsed = exports.dashboardContextSchema.safeParse(value);
|
|
39
|
+
if (!parsed.success)
|
|
40
|
+
throw new Error('DASHBOARD_SELECTION_INVALID: select the dashboard again before editing');
|
|
41
|
+
if (parsed.data.live_dashboard === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
const selected = parsed.data.live_dashboard;
|
|
44
|
+
return selected && 'artifactId' in selected ? selected : null;
|
|
45
|
+
}
|
|
46
|
+
function selectionFromConfig(value) {
|
|
47
|
+
const config = v3_1.z.object({ context: v3_1.z.unknown(),
|
|
48
|
+
configurable: v3_1.z.object({ context: v3_1.z.unknown() }).optional() }).safeParse(value);
|
|
49
|
+
if (!config.success)
|
|
50
|
+
return undefined;
|
|
51
|
+
const primary = selectionFromContext(config.data.context);
|
|
52
|
+
return primary !== undefined ? primary : selectionFromContext(config.data.configurable?.context);
|
|
53
|
+
}
|