@xpert-ai/plugin-live-dashboard 0.1.3 → 0.2.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 +1 -1
- package/README.md +5 -5
- package/dist/lib/agent.schema.d.ts +11 -11
- package/dist/lib/agent.schema.js +3 -3
- package/dist/lib/analytics-source.d.ts +3 -0
- package/dist/lib/analytics-source.js +11 -0
- package/dist/lib/content-capability.d.ts +22 -0
- package/dist/lib/content-capability.js +33 -0
- package/dist/lib/content.provider.d.ts +23 -0
- package/dist/lib/content.provider.js +163 -0
- package/dist/lib/dashboard-icon.d.ts +2 -1
- package/dist/lib/dashboard-icon.js +10 -2
- package/dist/lib/dashboard-navigation.d.ts +24 -0
- package/dist/lib/dashboard-navigation.js +1 -1
- package/dist/lib/dashboard-queries.d.ts +11 -0
- package/dist/lib/dashboard-queries.js +20 -0
- package/dist/lib/dashboard-search.d.ts +5 -0
- package/dist/lib/dashboard-search.js +12 -0
- package/dist/lib/dashboard-sharing.js +2 -1
- package/dist/lib/dashboard-view.schema.d.ts +1249 -0
- package/dist/lib/dashboard-view.schema.js +19 -0
- package/dist/lib/dashboard.schema.d.ts +12 -12
- package/dist/lib/dashboard.schema.js +5 -3
- package/dist/lib/dashboard.service.d.ts +11 -3
- package/dist/lib/dashboard.service.js +55 -21
- package/dist/lib/definition-tools.d.ts +56 -0
- package/dist/lib/definition-tools.js +42 -0
- package/dist/lib/document-host.d.ts +4 -0
- package/dist/lib/document-host.js +13 -0
- package/dist/lib/document-network.d.ts +3 -0
- package/dist/lib/document-network.js +9 -0
- package/dist/lib/document-package.d.ts +1 -1
- package/dist/lib/document-package.js +2 -2
- package/dist/lib/html-guidance.d.ts +1 -1
- package/dist/lib/html-guidance.js +1 -1
- package/dist/lib/middleware.js +54 -18
- package/dist/lib/plugin.js +3 -1
- package/dist/lib/presentation.provider.d.ts +6 -1
- package/dist/lib/presentation.provider.js +2 -2
- package/dist/lib/preview-retry.d.ts +7 -0
- package/dist/lib/preview-retry.js +52 -0
- package/dist/lib/renderer.js +5 -0
- package/dist/lib/resource-card.js +1 -1
- package/dist/lib/save-evidence.d.ts +1 -1
- package/dist/lib/sharing.service.js +2 -0
- package/dist/lib/view.provider.d.ts +7 -2
- package/dist/lib/view.provider.js +5 -4
- package/dist/remote/app.css +1 -1
- package/dist/remote/app.js +36 -36
- package/dist/remote/build-manifest.json +42 -30
- package/dist/sandbox-actions/action.json +1 -1
- package/dist/sandbox-actions/bundle/host.js +2 -2
- package/dist/sandbox-actions/bundle/validate.mjs +24 -3
- package/dist/validation/host-entry.d.ts +3 -1
- package/dist/validation/host-entry.js +5 -2
- package/docs/drafts.mdx +1 -1
- package/docs/frontend-libraries.mdx +3 -3
- package/docs/html-mode.md +5 -5
- package/docs/index.mdx +14 -0
- package/docs/team-sharing.mdx +1 -1
- package/docs/verification.mdx +3 -1
- package/examples/data-xpert-sales.json +6 -3
- package/examples/postgres-sales.json +5 -3
- package/package.json +6 -6
- package/skills/live-dashboard/SKILL.md +1 -1
- package/skills/live-dashboard/references/data-validation.md +4 -0
- package/skills/live-dashboard/references/html-runtime.md +1 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dashboardViewDataSchema = exports.dashboardSavedSchema = exports.dashboardSummarySchema = void 0;
|
|
4
|
+
const v3_1 = require("zod/v3");
|
|
5
|
+
const analytics_1 = require("@xpert-ai/plugin-sdk/analytics");
|
|
6
|
+
const artifact_document_1 = require("@xpert-ai/plugin-sdk/artifact-document");
|
|
7
|
+
const dashboard_schema_1 = require("./dashboard.schema");
|
|
8
|
+
const dashboard_navigation_1 = require("./dashboard-navigation");
|
|
9
|
+
exports.dashboardSummarySchema = v3_1.z.object({ artifactId: v3_1.z.string(), versionId: v3_1.z.string().nullish(), versionNumber: v3_1.z.number().optional(), title: v3_1.z.string().nullish(), updatedAt: v3_1.z.string().optional(), draftRevision: v3_1.z.number().optional(), isDraft: v3_1.z.boolean().optional(), baseVersionId: v3_1.z.string().nullish() });
|
|
10
|
+
exports.dashboardSavedSchema = exports.dashboardSummarySchema.extend({ manifest: dashboard_schema_1.dashboardSchema, dependencies: artifact_document_1.artifactDocumentDependencyAssets.optional() });
|
|
11
|
+
// Source identities belong to Analytics, including immutable Definitions and Artifacts.
|
|
12
|
+
// The viewer consumes only the reference and label; DataSource fields are not universal.
|
|
13
|
+
exports.dashboardViewDataSchema = v3_1.z.object({ meta: v3_1.z.object({
|
|
14
|
+
navigationTarget: dashboard_navigation_1.dashboardNavigationTargetSchema.optional(),
|
|
15
|
+
dashboards: v3_1.z.array(exports.dashboardSummarySchema),
|
|
16
|
+
sources: v3_1.z.array(v3_1.z.object({ source: analytics_1.analyticsSourceRefSchema, name: v3_1.z.string() })),
|
|
17
|
+
saved: exports.dashboardSavedSchema.optional(),
|
|
18
|
+
versions: v3_1.z.array(exports.dashboardSummarySchema).optional()
|
|
19
|
+
}) });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod/v3';
|
|
2
2
|
export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
3
|
-
schema: z.ZodLiteral<"xpert.live-dashboard
|
|
3
|
+
schema: z.ZodLiteral<"xpert.live-dashboard">;
|
|
4
4
|
id: z.ZodString;
|
|
5
5
|
title: z.ZodString;
|
|
6
6
|
bindings: z.ZodArray<z.ZodType<import("@xpert-ai/contracts").AnalyticsBinding, z.ZodTypeDef, import("@xpert-ai/contracts").AnalyticsBinding>, "many">;
|
|
@@ -114,7 +114,7 @@ export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
|
114
114
|
fields: string[];
|
|
115
115
|
}>, "many">;
|
|
116
116
|
}, "strict", z.ZodTypeAny, {
|
|
117
|
-
schema: "xpert.live-dashboard
|
|
117
|
+
schema: "xpert.live-dashboard";
|
|
118
118
|
id: string;
|
|
119
119
|
title: string;
|
|
120
120
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -146,7 +146,7 @@ export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
|
146
146
|
dependencies?: import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentDependencyLock[] | undefined;
|
|
147
147
|
} | undefined;
|
|
148
148
|
}, {
|
|
149
|
-
schema: "xpert.live-dashboard
|
|
149
|
+
schema: "xpert.live-dashboard";
|
|
150
150
|
id: string;
|
|
151
151
|
title: string;
|
|
152
152
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -184,7 +184,7 @@ export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
|
184
184
|
}[] | undefined;
|
|
185
185
|
} | undefined;
|
|
186
186
|
}>, {
|
|
187
|
-
schema: "xpert.live-dashboard
|
|
187
|
+
schema: "xpert.live-dashboard";
|
|
188
188
|
id: string;
|
|
189
189
|
title: string;
|
|
190
190
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -216,7 +216,7 @@ export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
|
216
216
|
dependencies?: import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentDependencyLock[] | undefined;
|
|
217
217
|
} | undefined;
|
|
218
218
|
}, {
|
|
219
|
-
schema: "xpert.live-dashboard
|
|
219
|
+
schema: "xpert.live-dashboard";
|
|
220
220
|
id: string;
|
|
221
221
|
title: string;
|
|
222
222
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -442,7 +442,7 @@ export type DashboardChanges = z.output<typeof dashboardChangesSchema>;
|
|
|
442
442
|
export declare const controlsSchema: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
443
443
|
export declare const saveSchema: z.ZodObject<{
|
|
444
444
|
manifest: z.ZodEffects<z.ZodObject<{
|
|
445
|
-
schema: z.ZodLiteral<"xpert.live-dashboard
|
|
445
|
+
schema: z.ZodLiteral<"xpert.live-dashboard">;
|
|
446
446
|
id: z.ZodString;
|
|
447
447
|
title: z.ZodString;
|
|
448
448
|
bindings: z.ZodArray<z.ZodType<import("@xpert-ai/contracts").AnalyticsBinding, z.ZodTypeDef, import("@xpert-ai/contracts").AnalyticsBinding>, "many">;
|
|
@@ -556,7 +556,7 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
556
556
|
fields: string[];
|
|
557
557
|
}>, "many">;
|
|
558
558
|
}, "strict", z.ZodTypeAny, {
|
|
559
|
-
schema: "xpert.live-dashboard
|
|
559
|
+
schema: "xpert.live-dashboard";
|
|
560
560
|
id: string;
|
|
561
561
|
title: string;
|
|
562
562
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -588,7 +588,7 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
588
588
|
dependencies?: import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentDependencyLock[] | undefined;
|
|
589
589
|
} | undefined;
|
|
590
590
|
}, {
|
|
591
|
-
schema: "xpert.live-dashboard
|
|
591
|
+
schema: "xpert.live-dashboard";
|
|
592
592
|
id: string;
|
|
593
593
|
title: string;
|
|
594
594
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -626,7 +626,7 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
626
626
|
}[] | undefined;
|
|
627
627
|
} | undefined;
|
|
628
628
|
}>, {
|
|
629
|
-
schema: "xpert.live-dashboard
|
|
629
|
+
schema: "xpert.live-dashboard";
|
|
630
630
|
id: string;
|
|
631
631
|
title: string;
|
|
632
632
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -658,7 +658,7 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
658
658
|
dependencies?: import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentDependencyLock[] | undefined;
|
|
659
659
|
} | undefined;
|
|
660
660
|
}, {
|
|
661
|
-
schema: "xpert.live-dashboard
|
|
661
|
+
schema: "xpert.live-dashboard";
|
|
662
662
|
id: string;
|
|
663
663
|
title: string;
|
|
664
664
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -700,7 +700,7 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
700
700
|
expectedRevision: z.ZodDefault<z.ZodNumber>;
|
|
701
701
|
}, "strict", z.ZodTypeAny, {
|
|
702
702
|
manifest: {
|
|
703
|
-
schema: "xpert.live-dashboard
|
|
703
|
+
schema: "xpert.live-dashboard";
|
|
704
704
|
id: string;
|
|
705
705
|
title: string;
|
|
706
706
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -736,7 +736,7 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
736
736
|
expectedRevision: number;
|
|
737
737
|
}, {
|
|
738
738
|
manifest: {
|
|
739
|
-
schema: "xpert.live-dashboard
|
|
739
|
+
schema: "xpert.live-dashboard";
|
|
740
740
|
id: string;
|
|
741
741
|
title: string;
|
|
742
742
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -6,8 +6,8 @@ const analytics_1 = require("@xpert-ai/plugin-sdk/analytics");
|
|
|
6
6
|
const artifact_document_1 = require("@xpert-ai/plugin-sdk/artifact-document");
|
|
7
7
|
const id = v3_1.z.string().regex(/^[A-Za-z][A-Za-z0-9_.-]{0,79}$/);
|
|
8
8
|
exports.dashboardSchema = v3_1.z.object({
|
|
9
|
-
schema: v3_1.z.literal('xpert.live-dashboard
|
|
10
|
-
bindings: v3_1.z.array(analytics_1.analyticsBindingSchema).
|
|
9
|
+
schema: v3_1.z.literal('xpert.live-dashboard'), id: v3_1.z.string().uuid(), title: v3_1.z.string().min(1).max(160),
|
|
10
|
+
bindings: v3_1.z.array(analytics_1.analyticsBindingSchema).max(8),
|
|
11
11
|
presentation: v3_1.z.object({ type: v3_1.z.literal('html'), html: artifact_document_1.artifactDocumentHtml, dependencies: artifact_document_1.artifactDocumentDependencyLocks.optional() }).strict().optional(),
|
|
12
12
|
controls: v3_1.z.array(v3_1.z.object({ controlId: id, type: v3_1.z.enum(['string', 'number', 'boolean', 'date']),
|
|
13
13
|
defaultValue: v3_1.z.union([v3_1.z.string(), v3_1.z.number(), v3_1.z.boolean(), v3_1.z.null()]).optional(),
|
|
@@ -21,11 +21,13 @@ exports.dashboardSchema = v3_1.z.object({
|
|
|
21
21
|
if (new Set(values).size !== values.length)
|
|
22
22
|
ctx.addIssue({ code: 'custom', message: `Duplicate ${name}` });
|
|
23
23
|
};
|
|
24
|
+
if (!manifest.bindings.length && (manifest.presentation || manifest.controls.length || manifest.charts.length || manifest.tables.length))
|
|
25
|
+
ctx.addIssue({ code: 'custom', message: 'An unconfigured draft must contain no views or controls' });
|
|
24
26
|
unique(manifest.bindings.map((binding) => binding.id), 'binding');
|
|
25
27
|
unique(manifest.bindings.map((binding) => binding.output.datasetId), 'dataset');
|
|
26
28
|
unique(manifest.controls.map((control) => control.controlId), 'control');
|
|
27
29
|
unique([...manifest.charts, ...manifest.tables].map((view) => view.id), 'view');
|
|
28
|
-
if (!manifest.presentation && !manifest.charts.length && !manifest.tables.length)
|
|
30
|
+
if (manifest.bindings.length && !manifest.presentation && !manifest.charts.length && !manifest.tables.length)
|
|
29
31
|
ctx.addIssue({ code: 'custom', message: 'At least one chart or table is required' });
|
|
30
32
|
if (manifest.presentation && (manifest.charts.length || manifest.tables.length))
|
|
31
33
|
ctx.addIssue({ code: 'custom', message: 'HTML presentation owns its charts; use empty charts/tables arrays' });
|
|
@@ -27,7 +27,7 @@ export declare class DashboardService {
|
|
|
27
27
|
}>;
|
|
28
28
|
open(context: XpertResolvedViewHostContext, artifactId: string, versionId?: string, draftRevision?: number): Promise<{
|
|
29
29
|
manifest: {
|
|
30
|
-
schema: "xpert.live-dashboard
|
|
30
|
+
schema: "xpert.live-dashboard";
|
|
31
31
|
id: string;
|
|
32
32
|
title: string;
|
|
33
33
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -72,7 +72,7 @@ export declare class DashboardService {
|
|
|
72
72
|
/** Trusted View transport only. Agent read tools receive locks, never megabytes of library code. */
|
|
73
73
|
openDocument(context: XpertResolvedViewHostContext, artifactId: string, versionId?: string, draftRevision?: number): Promise<{
|
|
74
74
|
manifest: {
|
|
75
|
-
schema: "xpert.live-dashboard
|
|
75
|
+
schema: "xpert.live-dashboard";
|
|
76
76
|
id: string;
|
|
77
77
|
title: string;
|
|
78
78
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -194,8 +194,13 @@ export declare class DashboardService {
|
|
|
194
194
|
};
|
|
195
195
|
} | undefined;
|
|
196
196
|
datasets: import("@xpert-ai/contracts").AnalyticalDataset[];
|
|
197
|
+
failures: {
|
|
198
|
+
bindingId: string;
|
|
199
|
+
datasetId: string;
|
|
200
|
+
reason: "unavailable";
|
|
201
|
+
}[];
|
|
197
202
|
manifest: {
|
|
198
|
-
schema: "xpert.live-dashboard
|
|
203
|
+
schema: "xpert.live-dashboard";
|
|
199
204
|
id: string;
|
|
200
205
|
title: string;
|
|
201
206
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -238,6 +243,9 @@ export declare class DashboardService {
|
|
|
238
243
|
versionNumber: number | undefined;
|
|
239
244
|
}>;
|
|
240
245
|
queryBinding(context: XpertResolvedViewHostContext, artifactId: string, versionId: string | undefined, bindingId: string, input: Record<string, AnalyticsValue>, draftRevision?: number): Promise<import("@xpert-ai/contracts").AnalyticalDataset>;
|
|
246
|
+
/** Every operation uses the current human's Project role; data queries still use actor(context). */
|
|
247
|
+
authorizeProject(context: XpertResolvedViewHostContext, operation?: 'read' | 'write' | 'manage'): Promise<void>;
|
|
241
248
|
private execute;
|
|
242
249
|
private probeControls;
|
|
243
250
|
}
|
|
251
|
+
export declare function projectId(context: XpertResolvedViewHostContext): string | null | undefined;
|
|
@@ -13,6 +13,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.DashboardService = exports.PLUGIN_NAME = void 0;
|
|
16
|
+
exports.projectId = projectId;
|
|
16
17
|
const common_1 = require("@nestjs/common");
|
|
17
18
|
const node_crypto_1 = require("node:crypto");
|
|
18
19
|
const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
|
|
@@ -39,20 +40,24 @@ let DashboardService = class DashboardService {
|
|
|
39
40
|
return dataset;
|
|
40
41
|
}
|
|
41
42
|
async list(context) {
|
|
42
|
-
|
|
43
|
+
await this.authorizeProject(context);
|
|
43
44
|
const result = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).listArtifacts({ pluginName: exports.PLUGIN_NAME, resourceType: RESOURCE_TYPE, resourceIdPrefix: hostPrefix(context), status: 'active', pageSize: 100, includeCurrentVersion: true });
|
|
44
|
-
const items = result.items.filter((item) => item.resourceId.startsWith(hostPrefix(context)) && item.userId === context.userId);
|
|
45
|
+
const items = result.items.filter((item) => item.resourceId.startsWith(hostPrefix(context)) && (item.userId === context.userId || !item.userId && item.projectId === projectId(context)));
|
|
45
46
|
const drafts = new Map((await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).list(items.map(item => item.id))).map(draft => [draft.artifactId, draft]));
|
|
46
47
|
return items.map(item => ({ ...summary(item), ...(drafts.has(item.id) ? { title: drafts.get(item.id).title ?? item.title } : {}) }));
|
|
47
48
|
}
|
|
48
49
|
async save(context, manifest, expectedVersionId, expectedRevision = 0) {
|
|
50
|
+
await this.authorizeProject(context, 'write');
|
|
49
51
|
const scopedActor = actor(context);
|
|
50
52
|
const parsed = dashboard_schema_1.dashboardSchema.parse(manifest);
|
|
51
53
|
const artifacts = this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability);
|
|
52
54
|
const source = { pluginName: exports.PLUGIN_NAME, resourceType: RESOURCE_TYPE, resourceId: `${hostPrefix(context)}${parsed.id}` };
|
|
53
55
|
const existing = await artifacts.findArtifactBySource(source);
|
|
54
|
-
if (existing
|
|
55
|
-
|
|
56
|
+
if (existing) {
|
|
57
|
+
assertOwned(context, existing);
|
|
58
|
+
if (existing.status !== 'active')
|
|
59
|
+
throw new common_1.ForbiddenException('DASHBOARD_ARCHIVED');
|
|
60
|
+
}
|
|
56
61
|
// A failed first upload can leave an Artifact with no saved content; revision 0 initializes it.
|
|
57
62
|
const previous = existing && (existing.currentVersionId || expectedRevision > 0) ? await this.openDocument(context, existing.id) : null;
|
|
58
63
|
const dependencies = await (0, document_libraries_1.resolveLibraryAssets)(parsed.presentation?.dependencies ?? [], previous?.dependencies);
|
|
@@ -70,14 +75,22 @@ let DashboardService = class DashboardService {
|
|
|
70
75
|
await this.probeControls(context, binding, Object.fromEntries(parsed.controls.filter((control) => control.defaultValue !== undefined).map((control) => [control.controlId, control.defaultValue ?? null])));
|
|
71
76
|
const htmlValidation = await this.htmlValidation.validate(context, parsed, preview, dependencies);
|
|
72
77
|
const artifact = existing ?? await artifacts.createArtifact({ source, kind: 'file', title: parsed.title,
|
|
73
|
-
scope: { ...scopedActor, ...(context
|
|
78
|
+
scope: { ...scopedActor, ...(projectId(context) ? { projectId: projectId(context), userId: null } : { xpertId: context.hostId }) } });
|
|
74
79
|
const files = this.runtime.require(plugin_sdk_1.WorkspaceFilesRuntimeCapability);
|
|
75
|
-
const
|
|
76
|
-
|
|
80
|
+
const project = projectId(context);
|
|
81
|
+
// Shared definitions live in the Project volume; private legacy dashboards retain their user volume.
|
|
82
|
+
const privateScope = { tenantId: scopedActor.tenantId, userId: scopedActor.userId, catalog: 'users' };
|
|
83
|
+
const reference = project
|
|
84
|
+
? (await files.writeRuntimeBuffer({ tenantId: scopedActor.tenantId, userId: scopedActor.userId,
|
|
85
|
+
catalog: 'projects', scopeId: project, projectId: project, buffer, originalName: 'dashboard.json',
|
|
86
|
+
fileName: `${digest}.json`, folder: `.xpert/live-dashboards/${parsed.id}`, mimeType: MIME_TYPE })).reference
|
|
87
|
+
: await files.uploadBuffer({ ...privateScope, buffer, originalName: 'dashboard.json', fileName: `${digest}.json`,
|
|
88
|
+
folder: `live-dashboards/${parsed.id}`, mimeType: MIME_TYPE }).then(file => ({
|
|
89
|
+
source: 'platform.workspace.files', ...privateScope, filePath: file.filePath, workspacePath: file.workspacePath
|
|
90
|
+
}));
|
|
77
91
|
const result = await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).save({ artifactId: artifact.id, expectedRevision,
|
|
78
92
|
expectedCurrentVersionId: expectedVersionId, mimeType: MIME_TYPE, title: parsed.title, fileName: 'dashboard.json', sha256: digest,
|
|
79
|
-
...(htmlValidation ? { metadata: { htmlValidation } } : {}),
|
|
80
|
-
workspaceFileRef: { source: 'platform.workspace.files', ...scope, filePath: file.filePath, workspacePath: file.workspacePath } });
|
|
93
|
+
...(htmlValidation ? { metadata: { htmlValidation } } : {}), workspaceFileRef: reference });
|
|
81
94
|
return { artifactId: artifact.id, draftId: result.id, draftRevision: result.revision, baseVersionId: result.baseVersionId, status: 'draft', title: parsed.title };
|
|
82
95
|
}
|
|
83
96
|
async open(context, artifactId, versionId, draftRevision) {
|
|
@@ -86,10 +99,12 @@ let DashboardService = class DashboardService {
|
|
|
86
99
|
}
|
|
87
100
|
/** Trusted View transport only. Agent read tools receive locks, never megabytes of library code. */
|
|
88
101
|
async openDocument(context, artifactId, versionId, draftRevision) {
|
|
89
|
-
|
|
102
|
+
await this.authorizeProject(context);
|
|
90
103
|
const artifacts = this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability);
|
|
91
104
|
const artifact = await artifacts.getArtifact(artifactId);
|
|
92
105
|
assertOwned(context, artifact);
|
|
106
|
+
if (artifact.status !== 'active')
|
|
107
|
+
throw new common_1.ForbiddenException('DASHBOARD_ARCHIVED');
|
|
93
108
|
const draft = !versionId || draftRevision !== undefined ? await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).get(artifactId) : null;
|
|
94
109
|
if (draftRevision !== undefined && draft?.revision !== draftRevision)
|
|
95
110
|
throw new common_1.ConflictException('DASHBOARD_DRAFT_CONFLICT');
|
|
@@ -109,10 +124,12 @@ let DashboardService = class DashboardService {
|
|
|
109
124
|
isDraft: draft.revision !== draft.publishedRevision, title: draft.title, updatedAt: draft.updatedAt } : {}), manifest, dependencies };
|
|
110
125
|
}
|
|
111
126
|
async history(context, artifactId) {
|
|
112
|
-
|
|
127
|
+
await this.authorizeProject(context);
|
|
113
128
|
const artifacts = this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability);
|
|
114
129
|
const artifact = await artifacts.getArtifact(artifactId);
|
|
115
130
|
assertOwned(context, artifact);
|
|
131
|
+
if (artifact.status !== 'active')
|
|
132
|
+
throw new common_1.ForbiddenException('DASHBOARD_ARCHIVED');
|
|
116
133
|
const versions = await artifacts.listArtifactVersions({ artifactId, status: 'active' });
|
|
117
134
|
return versions.map(version => ({ ...summary(artifact, version), current: version.id === artifact.currentVersionId }))
|
|
118
135
|
.sort((a, b) => (b.versionNumber ?? 0) - (a.versionNumber ?? 0));
|
|
@@ -124,7 +141,10 @@ let DashboardService = class DashboardService {
|
|
|
124
141
|
return this.save(context, dashboard_schema_1.dashboardSchema.parse({ ...saved.manifest, ...dashboard_schema_1.dashboardChangesSchema.parse(changes) }), saved.baseVersionId ?? saved.versionId ?? null, expectedRevision);
|
|
125
142
|
}
|
|
126
143
|
async publish(context, artifactId, expectedRevision, expectedCurrentVersionId) {
|
|
144
|
+
await this.authorizeProject(context, 'manage');
|
|
127
145
|
const saved = await this.open(context, artifactId, undefined, expectedRevision);
|
|
146
|
+
if (!saved.manifest.bindings.length)
|
|
147
|
+
throw new common_1.BadRequestException('DASHBOARD_CONFIGURE_BEFORE_PUBLISHING');
|
|
128
148
|
const share = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).getArtifactShare({ artifactId, shareKey: dashboard_sharing_1.DASHBOARD_SHARE_KEY });
|
|
129
149
|
if (share?.status === 'active' && share.versionMode === 'latest' && (!share.expiresAt || new Date(share.expiresAt).getTime() > Date.now())) {
|
|
130
150
|
const issues = (0, dashboard_sharing_1.dashboardSharingIssues)(saved.manifest, await this.sources(context));
|
|
@@ -138,13 +158,29 @@ let DashboardService = class DashboardService {
|
|
|
138
158
|
}
|
|
139
159
|
async query(context, artifactId, versionId, controls, draftRevision) {
|
|
140
160
|
const saved = await this.open(context, artifactId, versionId, draftRevision);
|
|
141
|
-
const datasets = await this.
|
|
142
|
-
return { ...saved, datasets, ...(saved.manifest.presentation ? {} : { payload: (0, renderer_1.toDataAnalyticsPayload)(saved.manifest, datasets) }) };
|
|
161
|
+
const { datasets, failures } = await (0, dashboard_queries_1.queryDashboardPartial)(this.runtime, actor(context), saved.manifest, controls);
|
|
162
|
+
return { ...saved, datasets, failures, ...(saved.manifest.presentation ? {} : { payload: (0, renderer_1.toDataAnalyticsPayload)(saved.manifest, datasets) }) };
|
|
143
163
|
}
|
|
144
164
|
async queryBinding(context, artifactId, versionId, bindingId, input, draftRevision) {
|
|
145
165
|
const saved = await this.open(context, artifactId, versionId, draftRevision);
|
|
146
166
|
return (0, dashboard_queries_1.queryDashboardBinding)(this.runtime, actor(context), saved.manifest, bindingId, input);
|
|
147
167
|
}
|
|
168
|
+
/** Every operation uses the current human's Project role; data queries still use actor(context). */
|
|
169
|
+
async authorizeProject(context, operation = 'read') {
|
|
170
|
+
const scopedActor = actor(context), project = projectId(context);
|
|
171
|
+
if (!project)
|
|
172
|
+
return;
|
|
173
|
+
const access = this.runtime.require(plugin_sdk_1.ProjectAccessRuntimeCapability);
|
|
174
|
+
const input = { actor: scopedActor, projectId: project };
|
|
175
|
+
if (context.hostType === 'agent')
|
|
176
|
+
await access.assertAssistant({ ...input, assistantId: context.hostId });
|
|
177
|
+
if (operation === 'write')
|
|
178
|
+
await access.assertEdit(input);
|
|
179
|
+
else if (operation === 'manage')
|
|
180
|
+
await access.assertManage(input);
|
|
181
|
+
else if (!(await access.listReadable({ actor: scopedActor, projectIds: [project] })).length)
|
|
182
|
+
throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
|
|
183
|
+
}
|
|
148
184
|
execute(context, manifest, input) {
|
|
149
185
|
return (0, dashboard_queries_1.queryDashboard)(this.runtime, actor(context), manifest, input);
|
|
150
186
|
}
|
|
@@ -159,12 +195,9 @@ let DashboardService = class DashboardService {
|
|
|
159
195
|
const samples = [...(alternative !== undefined ? [alternative] : []),
|
|
160
196
|
...(current !== null && parameter.allowedValues?.includes(null) ? [null] : [])];
|
|
161
197
|
for (const value of samples) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
throw new common_1.BadRequestException(`DASHBOARD_FILTER_PREVIEW_FAILED: ${parameter.controlId}; a declared non-default filter failed. Inspect the native parameter type and operator before retrying.`);
|
|
167
|
-
}
|
|
198
|
+
// Analytics already redacts provider failures. Preserve its actionable validation
|
|
199
|
+
// errors: a duplicate grain is not a malformed filter and needs a different repair.
|
|
200
|
+
await this.runtime.require(plugin_sdk_1.AnalyticsRuntimeCapability).query(actor(context), { binding, controls: { ...controls, [parameter.controlId]: value } });
|
|
168
201
|
}
|
|
169
202
|
}
|
|
170
203
|
}
|
|
@@ -180,10 +213,11 @@ function actor(context) {
|
|
|
180
213
|
throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
|
|
181
214
|
return { tenantId: context.tenantId, organizationId: context.organizationId, userId: context.userId };
|
|
182
215
|
}
|
|
183
|
-
function
|
|
216
|
+
function projectId(context) { return context.hostType === 'project' ? context.hostId : context.runtimeScope?.projectId; }
|
|
217
|
+
function hostPrefix(context) { return projectId(context) ? `project/${projectId(context)}/` : `${context.hostType}/${context.hostId}/`; }
|
|
184
218
|
function assertOwned(context, artifact) {
|
|
185
219
|
if (artifact.pluginName !== exports.PLUGIN_NAME || artifact.resourceType !== RESOURCE_TYPE || !artifact.resourceId.startsWith(hostPrefix(context)) ||
|
|
186
|
-
artifact.tenantId !== context.tenantId || artifact.organizationId !== context.organizationId || artifact.userId !== context.userId)
|
|
220
|
+
artifact.tenantId !== context.tenantId || artifact.organizationId !== context.organizationId || (artifact.userId ? artifact.userId !== context.userId : !projectId(context) || artifact.projectId !== projectId(context)))
|
|
187
221
|
throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
|
|
188
222
|
}
|
|
189
223
|
function summary(artifact, version = artifact.currentVersion) {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod/v3';
|
|
2
|
+
import { type IAgentMiddlewareContext } from '@xpert-ai/plugin-sdk';
|
|
3
|
+
export declare const DEFINITION_GUIDANCE = "If dashboard_list_sources lacks the requested source, call analytics_definition_connections, then analytics_definition_inspect with an exact returned selection and inputJson=\"{}\". Follow its instructions, inspect real source data and generate the business definition yourself. Never ask users to fill internal IDs/configuration. Register using analytics_definition_register and a stable requestId, refresh dashboard_list_sources, discover the returned source and preview before saving. Settings \u2192 Analytics is for viewing, correction and version management. Do not create redundant definitions for already governed sources.";
|
|
4
|
+
export declare function createDefinitionTools(context: IAgentMiddlewareContext, registered: () => void): (import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>, {}, {}, string> | import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
|
|
5
|
+
providerId: z.ZodString;
|
|
6
|
+
connectionKey: z.ZodString;
|
|
7
|
+
} & {
|
|
8
|
+
inputJson: z.ZodString;
|
|
9
|
+
}, "strict", z.ZodTypeAny, {
|
|
10
|
+
providerId: string;
|
|
11
|
+
connectionKey: string;
|
|
12
|
+
inputJson: string;
|
|
13
|
+
}, {
|
|
14
|
+
providerId: string;
|
|
15
|
+
connectionKey: string;
|
|
16
|
+
inputJson: string;
|
|
17
|
+
}>, {
|
|
18
|
+
providerId: string;
|
|
19
|
+
connectionKey: string;
|
|
20
|
+
inputJson: string;
|
|
21
|
+
}, {
|
|
22
|
+
providerId: string;
|
|
23
|
+
connectionKey: string;
|
|
24
|
+
inputJson: string;
|
|
25
|
+
}, string> | import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
|
|
26
|
+
providerId: z.ZodString;
|
|
27
|
+
connectionKey: z.ZodString;
|
|
28
|
+
} & {
|
|
29
|
+
requestId: z.ZodString;
|
|
30
|
+
name: z.ZodString;
|
|
31
|
+
definitionJson: z.ZodString;
|
|
32
|
+
}, "strict", z.ZodTypeAny, {
|
|
33
|
+
name: string;
|
|
34
|
+
providerId: string;
|
|
35
|
+
connectionKey: string;
|
|
36
|
+
requestId: string;
|
|
37
|
+
definitionJson: string;
|
|
38
|
+
}, {
|
|
39
|
+
name: string;
|
|
40
|
+
providerId: string;
|
|
41
|
+
connectionKey: string;
|
|
42
|
+
requestId: string;
|
|
43
|
+
definitionJson: string;
|
|
44
|
+
}>, {
|
|
45
|
+
name: string;
|
|
46
|
+
providerId: string;
|
|
47
|
+
connectionKey: string;
|
|
48
|
+
requestId: string;
|
|
49
|
+
definitionJson: string;
|
|
50
|
+
}, {
|
|
51
|
+
name: string;
|
|
52
|
+
providerId: string;
|
|
53
|
+
connectionKey: string;
|
|
54
|
+
requestId: string;
|
|
55
|
+
definitionJson: string;
|
|
56
|
+
}, string>)[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFINITION_GUIDANCE = void 0;
|
|
4
|
+
exports.createDefinitionTools = createDefinitionTools;
|
|
5
|
+
const tools_1 = require("@langchain/core/tools");
|
|
6
|
+
const v3_1 = require("zod/v3");
|
|
7
|
+
const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
|
|
8
|
+
const selection = v3_1.z.object({ providerId: v3_1.z.string().min(1).max(160), connectionKey: v3_1.z.string().min(1).max(400) }).strict();
|
|
9
|
+
exports.DEFINITION_GUIDANCE = 'If dashboard_list_sources lacks the requested source, call analytics_definition_connections, then analytics_definition_inspect with an exact returned selection and inputJson="{}". Follow its instructions, inspect real source data and generate the business definition yourself. Never ask users to fill internal IDs/configuration. Register using analytics_definition_register and a stable requestId, refresh dashboard_list_sources, discover the returned source and preview before saving. Settings → Analytics is for viewing, correction and version management. Do not create redundant definitions for already governed sources.';
|
|
10
|
+
function createDefinitionTools(context, registered) {
|
|
11
|
+
const api = () => {
|
|
12
|
+
if (!context.workspaceId || !context.xpertId || !context.organizationId || !context.runtime?.capabilities)
|
|
13
|
+
throw new Error('ANALYTICS_ASSISTANT_SCOPE_REQUIRED');
|
|
14
|
+
return { runtime: context.runtime.capabilities.require(plugin_sdk_1.AnalyticsDefinitionAuthoringCapability), scope: {
|
|
15
|
+
actor: { tenantId: context.tenantId, organizationId: context.organizationId, userId: context.userId },
|
|
16
|
+
workspaceId: context.workspaceId, xpertId: context.xpertId,
|
|
17
|
+
files: context.runtime.capabilities.require(plugin_sdk_1.WorkspaceFilesRuntimeCapability)
|
|
18
|
+
} };
|
|
19
|
+
};
|
|
20
|
+
return [
|
|
21
|
+
(0, tools_1.tool)(async () => { const { runtime, scope } = api(); return JSON.stringify(await runtime.catalog(scope)); }, {
|
|
22
|
+
name: 'analytics_definition_connections', description: 'List authorized connections for generating a missing analytics source. Returns exact providerId/connectionKey selections; no credentials.',
|
|
23
|
+
schema: v3_1.z.object({}).strict(), metadata: { toolName: { en_US: 'Find analytics inputs', zh_Hans: '发现分析输入' } }
|
|
24
|
+
}),
|
|
25
|
+
(0, tools_1.tool)(async ({ inputJson, ...selection }) => {
|
|
26
|
+
const { runtime, scope } = api();
|
|
27
|
+
return JSON.stringify(await runtime.inspect(scope, selection, plugin_sdk_1.analyticsOptionsSchema.parse(JSON.parse(inputJson))));
|
|
28
|
+
}, { name: 'analytics_definition_inspect', description: 'Inspect approved input metadata and provider authoring schemas. Start inputJson="{}". Follow inspectSchema for operation samples/file inspection. Input cannot override host scope.',
|
|
29
|
+
schema: selection.extend({ inputJson: v3_1.z.string().min(2).max(100000) }).strict(), verboseParsingErrors: true,
|
|
30
|
+
metadata: { toolName: { en_US: 'Inspect analytics input', zh_Hans: '检查分析输入' } }
|
|
31
|
+
}),
|
|
32
|
+
(0, tools_1.tool)(async ({ definitionJson, ...input }) => {
|
|
33
|
+
const { runtime, scope } = api();
|
|
34
|
+
const receipt = await runtime.register(scope, { ...input, definition: plugin_sdk_1.analyticsOptionsSchema.parse(JSON.parse(definitionJson)) });
|
|
35
|
+
registered();
|
|
36
|
+
return JSON.stringify(receipt);
|
|
37
|
+
}, { name: 'analytics_definition_register', description: 'Register an Agent-generated definition matching definitionSchema. System supplies identities and validates real input. Keep requestId stable on retries; changed intent needs another key. Returns an immutable source and discoveryRequest. Refresh sources, discover and preview it before saving a dashboard.',
|
|
38
|
+
schema: selection.extend({ requestId: v3_1.z.string().min(1).max(128), name: v3_1.z.string().min(1).max(200), definitionJson: v3_1.z.string().min(2).max(100000) }).strict(), verboseParsingErrors: true,
|
|
39
|
+
metadata: { toolName: { en_US: 'Register analytics definition', zh_Hans: '生成分析定义' } }
|
|
40
|
+
})
|
|
41
|
+
];
|
|
42
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type ArtifactDocumentHostOptions } from '@xpert-ai/plugin-sdk/artifact-document';
|
|
2
|
+
/** The HTML never supplies or overrides its network permissions. */
|
|
3
|
+
export declare function mountDashboardDocument<T>(frame: HTMLIFrameElement, options: ArtifactDocumentHostOptions<T>): import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentHost;
|
|
4
|
+
export declare function isDashboardResourceAllowed(resourceType: string, url: string): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mountDashboardDocument = mountDashboardDocument;
|
|
4
|
+
exports.isDashboardResourceAllowed = isDashboardResourceAllowed;
|
|
5
|
+
const artifact_document_1 = require("@xpert-ai/plugin-sdk/artifact-document");
|
|
6
|
+
const document_network_1 = require("./document-network");
|
|
7
|
+
/** The HTML never supplies or overrides its network permissions. */
|
|
8
|
+
function mountDashboardDocument(frame, options) {
|
|
9
|
+
return (0, artifact_document_1.mountArtifactDocument)(frame, { ...options, networkPolicy: document_network_1.DASHBOARD_NETWORK_POLICY });
|
|
10
|
+
}
|
|
11
|
+
function isDashboardResourceAllowed(resourceType, url) {
|
|
12
|
+
return (0, artifact_document_1.isArtifactDocumentResourceAllowed)(document_network_1.DASHBOARD_NETWORK_POLICY, resourceType, url);
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DASHBOARD_NETWORK_POLICY = void 0;
|
|
4
|
+
/** Trusted plugin policy, shared by the displayed document and browser preflight. */
|
|
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
|
+
};
|
|
@@ -5,7 +5,7 @@ export declare const MAX_DASHBOARD_PACKAGE_BYTES = 9000000;
|
|
|
5
5
|
export declare function encodeDashboardPackage(manifest: DashboardManifest, dependencies: ArtifactDocumentDependencyAsset[]): Buffer<ArrayBuffer>;
|
|
6
6
|
export declare function decodeDashboardPackage(bytes: Buffer): {
|
|
7
7
|
manifest: {
|
|
8
|
-
schema: "xpert.live-dashboard
|
|
8
|
+
schema: "xpert.live-dashboard";
|
|
9
9
|
id: string;
|
|
10
10
|
title: string;
|
|
11
11
|
bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
|
|
@@ -8,13 +8,13 @@ const artifact_document_1 = require("@xpert-ai/plugin-sdk/artifact-document");
|
|
|
8
8
|
const dashboard_schema_1 = require("./dashboard.schema");
|
|
9
9
|
exports.MAX_DASHBOARD_PACKAGE_BYTES = 9_000_000;
|
|
10
10
|
const storedSchema = v3_1.z.union([
|
|
11
|
-
v3_1.z.object({ schema: v3_1.z.literal('xpert.live-dashboard.package
|
|
11
|
+
v3_1.z.object({ schema: v3_1.z.literal('xpert.live-dashboard.package'), manifest: dashboard_schema_1.dashboardSchema, dependencies: artifact_document_1.artifactDocumentDependencyAssets }).strict(),
|
|
12
12
|
dashboard_schema_1.dashboardSchema.transform(manifest => ({ manifest, dependencies: [] }))
|
|
13
13
|
]);
|
|
14
14
|
/** Definition and exact library bytes share one immutable Artifact Version and content checksum. */
|
|
15
15
|
function encodeDashboardPackage(manifest, dependencies) {
|
|
16
16
|
(0, artifact_document_1.verifyArtifactDocumentDependencies)(dependencies, manifest.presentation?.dependencies ?? []);
|
|
17
|
-
return Buffer.from(JSON.stringify(dependencies.length ? { schema: 'xpert.live-dashboard.package
|
|
17
|
+
return Buffer.from(JSON.stringify(dependencies.length ? { schema: 'xpert.live-dashboard.package', manifest, dependencies } : manifest));
|
|
18
18
|
}
|
|
19
19
|
function decodeDashboardPackage(bytes) {
|
|
20
20
|
if (bytes.byteLength > exports.MAX_DASHBOARD_PACKAGE_BYTES)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const HTML_DASHBOARD_GUIDANCE = "For free-layout HTML dashboards, read the installed live-dashboard skill's references/html-runtime.md through read_skill_file, relative to its SKILL.md path in the skill catalog. Create/edit the HTML with SandboxFile; then dashboard_create_html or dashboard_update validates and saves it. Do not use generic present_files to save or preview a live dashboard. Its preview belongs to the Live dashboards Workbench. ECharts/Three.js
|
|
1
|
+
export declare const HTML_DASHBOARD_GUIDANCE = "For free-layout HTML dashboards, read the installed live-dashboard skill's references/html-runtime.md through read_skill_file, relative to its SKILL.md path in the skill catalog. Create/edit the HTML with SandboxFile; then dashboard_create_html or dashboard_update validates and saves it. Do not use generic present_files to save or preview a live dashboard. Its preview belongs to the Live dashboards Workbench. Call dashboard_libraries for packaged ECharts/Three.js and the allowed HTTPS origins for online scripts, stylesheets and fonts. Tailwind CDN is supported through script src; pin a version when possible. Online resource failures must be repaired before saving. Do not add online URLs to the packaged libraries array; all data access remains declarative Binding queries through window.xpertLiveArtifact. The page MUST declare const live = window.xpertLiveArtifact in its script (live is not a host global), register live.subscribe(bindingId, render), await live.ready(), and invoke live.query(bindingId, typedControls) for initial data and filter changes. window.__datasets and window.__refresh are not platform APIs and are never populated by the host. Read the runtime reference and its html-lifecycle.md example before writing HTML; preserve that lifecycle while freely changing the layout. Use one complete Dataset for KPI, trend and details when they share the same metric and filters; do not create a redundant unfiltered total Binding. Never let filtered and unfiltered subscriptions both write the same KPI: a refresh would race and overwrite the selected-region total. Distinct global totals need distinct, clearly labeled elements. Native data selects require data-dashboard-control=\"<controlId>\"; purely visual selects use data-dashboard-presentation. A data filter must query, not only filter cached browser rows. bindSelect controls ONE binding only. For a shared dimension filter across multiple bindings, follow the skill custom-select example and query every affected binding with the same typed controls; matching control IDs do not broadcast automatically. bindSelect derives options from Dataset fields and is only for dimension filters. Fixed query enums (days, page size, granularity) need options from parameter.allowedValues and a typed change listener calling live.query; never use date rows as numeric day-count options.";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HTML_DASHBOARD_GUIDANCE = void 0;
|
|
4
|
-
exports.HTML_DASHBOARD_GUIDANCE = `For free-layout HTML dashboards, read the installed live-dashboard skill's references/html-runtime.md through read_skill_file, relative to its SKILL.md path in the skill catalog. Create/edit the HTML with SandboxFile; then dashboard_create_html or dashboard_update validates and saves it. Do not use generic present_files to save or preview a live dashboard. Its preview belongs to the Live dashboards Workbench. ECharts/Three.js
|
|
4
|
+
exports.HTML_DASHBOARD_GUIDANCE = `For free-layout HTML dashboards, read the installed live-dashboard skill's references/html-runtime.md through read_skill_file, relative to its SKILL.md path in the skill catalog. Create/edit the HTML with SandboxFile; then dashboard_create_html or dashboard_update validates and saves it. Do not use generic present_files to save or preview a live dashboard. Its preview belongs to the Live dashboards Workbench. Call dashboard_libraries for packaged ECharts/Three.js and the allowed HTTPS origins for online scripts, stylesheets and fonts. Tailwind CDN is supported through script src; pin a version when possible. Online resource failures must be repaired before saving. Do not add online URLs to the packaged libraries array; all data access remains declarative Binding queries through window.xpertLiveArtifact. The page MUST declare const live = window.xpertLiveArtifact in its script (live is not a host global), register live.subscribe(bindingId, render), await live.ready(), and invoke live.query(bindingId, typedControls) for initial data and filter changes. window.__datasets and window.__refresh are not platform APIs and are never populated by the host. Read the runtime reference and its html-lifecycle.md example before writing HTML; preserve that lifecycle while freely changing the layout. Use one complete Dataset for KPI, trend and details when they share the same metric and filters; do not create a redundant unfiltered total Binding. Never let filtered and unfiltered subscriptions both write the same KPI: a refresh would race and overwrite the selected-region total. Distinct global totals need distinct, clearly labeled elements. Native data selects require data-dashboard-control="<controlId>"; purely visual selects use data-dashboard-presentation. A data filter must query, not only filter cached browser rows. bindSelect controls ONE binding only. For a shared dimension filter across multiple bindings, follow the skill custom-select example and query every affected binding with the same typed controls; matching control IDs do not broadcast automatically. bindSelect derives options from Dataset fields and is only for dimension filters. Fixed query enums (days, page size, granularity) need options from parameter.allowedValues and a typed change listener calling live.query; never use date rows as numeric day-count options.`;
|