@xpert-ai/plugin-live-dashboard 0.1.4 → 0.3.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 +77 -4
- 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-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-view.schema.d.ts +27 -0
- package/dist/lib/dashboard-view.schema.js +1 -1
- package/dist/lib/dashboard.schema.js +4 -2
- package/dist/lib/dashboard.service.d.ts +13 -0
- package/dist/lib/dashboard.service.js +69 -16
- 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/html-guidance.d.ts +1 -1
- package/dist/lib/html-guidance.js +1 -1
- package/dist/lib/middleware.js +49 -15
- package/dist/lib/plugin.js +3 -1
- package/dist/lib/presentation.provider.d.ts +5 -0
- 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/prompt-workflows.d.ts +3 -0
- package/dist/lib/prompt-workflows.js +118 -0
- package/dist/lib/renderer.js +5 -0
- package/dist/lib/save-evidence.d.ts +1 -1
- package/dist/lib/sharing.service.js +2 -0
- package/dist/lib/view.provider.d.ts +2 -117
- package/dist/lib/view.provider.js +12 -4
- package/dist/remote/app.css +1 -1
- package/dist/remote/app.js +36 -36
- package/dist/remote/build-manifest.json +38 -28
- 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 +39 -0
- package/dist/sandbox-actions/bundle/validate.mjs +106 -17
- 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 +2 -2
- package/docs/html-mode.md +3 -3
- package/docs/team-sharing.mdx +1 -1
- package/docs/verification.mdx +3 -1
- 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
|
@@ -6,6 +6,7 @@ export declare const dashboardSummarySchema: z.ZodObject<{
|
|
|
6
6
|
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
7
7
|
updatedAt: z.ZodOptional<z.ZodString>;
|
|
8
8
|
draftRevision: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
canDelete: z.ZodOptional<z.ZodBoolean>;
|
|
9
10
|
isDraft: z.ZodOptional<z.ZodBoolean>;
|
|
10
11
|
baseVersionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
11
12
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -17,6 +18,7 @@ export declare const dashboardSummarySchema: z.ZodObject<{
|
|
|
17
18
|
baseVersionId?: string | null | undefined;
|
|
18
19
|
isDraft?: boolean | undefined;
|
|
19
20
|
updatedAt?: string | undefined;
|
|
21
|
+
canDelete?: boolean | undefined;
|
|
20
22
|
}, {
|
|
21
23
|
artifactId: string;
|
|
22
24
|
title?: string | null | undefined;
|
|
@@ -26,6 +28,7 @@ export declare const dashboardSummarySchema: z.ZodObject<{
|
|
|
26
28
|
baseVersionId?: string | null | undefined;
|
|
27
29
|
isDraft?: boolean | undefined;
|
|
28
30
|
updatedAt?: string | undefined;
|
|
31
|
+
canDelete?: boolean | undefined;
|
|
29
32
|
}>;
|
|
30
33
|
export declare const dashboardSavedSchema: z.ZodObject<{
|
|
31
34
|
artifactId: z.ZodString;
|
|
@@ -34,6 +37,7 @@ export declare const dashboardSavedSchema: z.ZodObject<{
|
|
|
34
37
|
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
35
38
|
updatedAt: z.ZodOptional<z.ZodString>;
|
|
36
39
|
draftRevision: z.ZodOptional<z.ZodNumber>;
|
|
40
|
+
canDelete: z.ZodOptional<z.ZodBoolean>;
|
|
37
41
|
isDraft: z.ZodOptional<z.ZodBoolean>;
|
|
38
42
|
baseVersionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
39
43
|
} & {
|
|
@@ -365,6 +369,7 @@ export declare const dashboardSavedSchema: z.ZodObject<{
|
|
|
365
369
|
baseVersionId?: string | null | undefined;
|
|
366
370
|
isDraft?: boolean | undefined;
|
|
367
371
|
updatedAt?: string | undefined;
|
|
372
|
+
canDelete?: boolean | undefined;
|
|
368
373
|
}, {
|
|
369
374
|
manifest: {
|
|
370
375
|
schema: "xpert.live-dashboard";
|
|
@@ -421,6 +426,7 @@ export declare const dashboardSavedSchema: z.ZodObject<{
|
|
|
421
426
|
baseVersionId?: string | null | undefined;
|
|
422
427
|
isDraft?: boolean | undefined;
|
|
423
428
|
updatedAt?: string | undefined;
|
|
429
|
+
canDelete?: boolean | undefined;
|
|
424
430
|
}>;
|
|
425
431
|
export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
426
432
|
meta: z.ZodObject<{
|
|
@@ -453,6 +459,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
453
459
|
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
454
460
|
updatedAt: z.ZodOptional<z.ZodString>;
|
|
455
461
|
draftRevision: z.ZodOptional<z.ZodNumber>;
|
|
462
|
+
canDelete: z.ZodOptional<z.ZodBoolean>;
|
|
456
463
|
isDraft: z.ZodOptional<z.ZodBoolean>;
|
|
457
464
|
baseVersionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
458
465
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -464,6 +471,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
464
471
|
baseVersionId?: string | null | undefined;
|
|
465
472
|
isDraft?: boolean | undefined;
|
|
466
473
|
updatedAt?: string | undefined;
|
|
474
|
+
canDelete?: boolean | undefined;
|
|
467
475
|
}, {
|
|
468
476
|
artifactId: string;
|
|
469
477
|
title?: string | null | undefined;
|
|
@@ -473,6 +481,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
473
481
|
baseVersionId?: string | null | undefined;
|
|
474
482
|
isDraft?: boolean | undefined;
|
|
475
483
|
updatedAt?: string | undefined;
|
|
484
|
+
canDelete?: boolean | undefined;
|
|
476
485
|
}>, "many">;
|
|
477
486
|
sources: z.ZodArray<z.ZodObject<{
|
|
478
487
|
source: z.ZodType<import("@xpert-ai/contracts").AnalyticsSourceRef, z.ZodTypeDef, import("@xpert-ai/contracts").AnalyticsSourceRef>;
|
|
@@ -491,6 +500,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
491
500
|
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
492
501
|
updatedAt: z.ZodOptional<z.ZodString>;
|
|
493
502
|
draftRevision: z.ZodOptional<z.ZodNumber>;
|
|
503
|
+
canDelete: z.ZodOptional<z.ZodBoolean>;
|
|
494
504
|
isDraft: z.ZodOptional<z.ZodBoolean>;
|
|
495
505
|
baseVersionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
496
506
|
} & {
|
|
@@ -822,6 +832,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
822
832
|
baseVersionId?: string | null | undefined;
|
|
823
833
|
isDraft?: boolean | undefined;
|
|
824
834
|
updatedAt?: string | undefined;
|
|
835
|
+
canDelete?: boolean | undefined;
|
|
825
836
|
}, {
|
|
826
837
|
manifest: {
|
|
827
838
|
schema: "xpert.live-dashboard";
|
|
@@ -878,6 +889,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
878
889
|
baseVersionId?: string | null | undefined;
|
|
879
890
|
isDraft?: boolean | undefined;
|
|
880
891
|
updatedAt?: string | undefined;
|
|
892
|
+
canDelete?: boolean | undefined;
|
|
881
893
|
}>>;
|
|
882
894
|
versions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
883
895
|
artifactId: z.ZodString;
|
|
@@ -886,6 +898,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
886
898
|
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
887
899
|
updatedAt: z.ZodOptional<z.ZodString>;
|
|
888
900
|
draftRevision: z.ZodOptional<z.ZodNumber>;
|
|
901
|
+
canDelete: z.ZodOptional<z.ZodBoolean>;
|
|
889
902
|
isDraft: z.ZodOptional<z.ZodBoolean>;
|
|
890
903
|
baseVersionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
891
904
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -897,6 +910,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
897
910
|
baseVersionId?: string | null | undefined;
|
|
898
911
|
isDraft?: boolean | undefined;
|
|
899
912
|
updatedAt?: string | undefined;
|
|
913
|
+
canDelete?: boolean | undefined;
|
|
900
914
|
}, {
|
|
901
915
|
artifactId: string;
|
|
902
916
|
title?: string | null | undefined;
|
|
@@ -906,6 +920,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
906
920
|
baseVersionId?: string | null | undefined;
|
|
907
921
|
isDraft?: boolean | undefined;
|
|
908
922
|
updatedAt?: string | undefined;
|
|
923
|
+
canDelete?: boolean | undefined;
|
|
909
924
|
}>, "many">>;
|
|
910
925
|
}, "strip", z.ZodTypeAny, {
|
|
911
926
|
sources: {
|
|
@@ -921,6 +936,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
921
936
|
baseVersionId?: string | null | undefined;
|
|
922
937
|
isDraft?: boolean | undefined;
|
|
923
938
|
updatedAt?: string | undefined;
|
|
939
|
+
canDelete?: boolean | undefined;
|
|
924
940
|
}[];
|
|
925
941
|
saved?: {
|
|
926
942
|
manifest: {
|
|
@@ -965,6 +981,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
965
981
|
baseVersionId?: string | null | undefined;
|
|
966
982
|
isDraft?: boolean | undefined;
|
|
967
983
|
updatedAt?: string | undefined;
|
|
984
|
+
canDelete?: boolean | undefined;
|
|
968
985
|
} | undefined;
|
|
969
986
|
navigationTarget?: {
|
|
970
987
|
target: "workbench.view";
|
|
@@ -983,6 +1000,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
983
1000
|
baseVersionId?: string | null | undefined;
|
|
984
1001
|
isDraft?: boolean | undefined;
|
|
985
1002
|
updatedAt?: string | undefined;
|
|
1003
|
+
canDelete?: boolean | undefined;
|
|
986
1004
|
}[] | undefined;
|
|
987
1005
|
}, {
|
|
988
1006
|
sources: {
|
|
@@ -998,6 +1016,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
998
1016
|
baseVersionId?: string | null | undefined;
|
|
999
1017
|
isDraft?: boolean | undefined;
|
|
1000
1018
|
updatedAt?: string | undefined;
|
|
1019
|
+
canDelete?: boolean | undefined;
|
|
1001
1020
|
}[];
|
|
1002
1021
|
saved?: {
|
|
1003
1022
|
manifest: {
|
|
@@ -1055,6 +1074,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
1055
1074
|
baseVersionId?: string | null | undefined;
|
|
1056
1075
|
isDraft?: boolean | undefined;
|
|
1057
1076
|
updatedAt?: string | undefined;
|
|
1077
|
+
canDelete?: boolean | undefined;
|
|
1058
1078
|
} | undefined;
|
|
1059
1079
|
navigationTarget?: {
|
|
1060
1080
|
target: "workbench.view";
|
|
@@ -1073,6 +1093,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
1073
1093
|
baseVersionId?: string | null | undefined;
|
|
1074
1094
|
isDraft?: boolean | undefined;
|
|
1075
1095
|
updatedAt?: string | undefined;
|
|
1096
|
+
canDelete?: boolean | undefined;
|
|
1076
1097
|
}[] | undefined;
|
|
1077
1098
|
}>;
|
|
1078
1099
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -1090,6 +1111,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
1090
1111
|
baseVersionId?: string | null | undefined;
|
|
1091
1112
|
isDraft?: boolean | undefined;
|
|
1092
1113
|
updatedAt?: string | undefined;
|
|
1114
|
+
canDelete?: boolean | undefined;
|
|
1093
1115
|
}[];
|
|
1094
1116
|
saved?: {
|
|
1095
1117
|
manifest: {
|
|
@@ -1134,6 +1156,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
1134
1156
|
baseVersionId?: string | null | undefined;
|
|
1135
1157
|
isDraft?: boolean | undefined;
|
|
1136
1158
|
updatedAt?: string | undefined;
|
|
1159
|
+
canDelete?: boolean | undefined;
|
|
1137
1160
|
} | undefined;
|
|
1138
1161
|
navigationTarget?: {
|
|
1139
1162
|
target: "workbench.view";
|
|
@@ -1152,6 +1175,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
1152
1175
|
baseVersionId?: string | null | undefined;
|
|
1153
1176
|
isDraft?: boolean | undefined;
|
|
1154
1177
|
updatedAt?: string | undefined;
|
|
1178
|
+
canDelete?: boolean | undefined;
|
|
1155
1179
|
}[] | undefined;
|
|
1156
1180
|
};
|
|
1157
1181
|
}, {
|
|
@@ -1169,6 +1193,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
1169
1193
|
baseVersionId?: string | null | undefined;
|
|
1170
1194
|
isDraft?: boolean | undefined;
|
|
1171
1195
|
updatedAt?: string | undefined;
|
|
1196
|
+
canDelete?: boolean | undefined;
|
|
1172
1197
|
}[];
|
|
1173
1198
|
saved?: {
|
|
1174
1199
|
manifest: {
|
|
@@ -1226,6 +1251,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
1226
1251
|
baseVersionId?: string | null | undefined;
|
|
1227
1252
|
isDraft?: boolean | undefined;
|
|
1228
1253
|
updatedAt?: string | undefined;
|
|
1254
|
+
canDelete?: boolean | undefined;
|
|
1229
1255
|
} | undefined;
|
|
1230
1256
|
navigationTarget?: {
|
|
1231
1257
|
target: "workbench.view";
|
|
@@ -1244,6 +1270,7 @@ export declare const dashboardViewDataSchema: z.ZodObject<{
|
|
|
1244
1270
|
baseVersionId?: string | null | undefined;
|
|
1245
1271
|
isDraft?: boolean | undefined;
|
|
1246
1272
|
updatedAt?: string | undefined;
|
|
1273
|
+
canDelete?: boolean | undefined;
|
|
1247
1274
|
}[] | undefined;
|
|
1248
1275
|
};
|
|
1249
1276
|
}>;
|
|
@@ -6,7 +6,7 @@ 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 dashboard_schema_1 = require("./dashboard.schema");
|
|
8
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() });
|
|
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(), canDelete: v3_1.z.boolean().optional(), isDraft: v3_1.z.boolean().optional(), baseVersionId: v3_1.z.string().nullish() });
|
|
10
10
|
exports.dashboardSavedSchema = exports.dashboardSummarySchema.extend({ manifest: dashboard_schema_1.dashboardSchema, dependencies: artifact_document_1.artifactDocumentDependencyAssets.optional() });
|
|
11
11
|
// Source identities belong to Analytics, including immutable Definitions and Artifacts.
|
|
12
12
|
// The viewer consumes only the reference and label; DataSource fields are not universal.
|
|
@@ -7,7 +7,7 @@ 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
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).
|
|
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' });
|
|
@@ -12,11 +12,16 @@ export declare class DashboardService {
|
|
|
12
12
|
preview(context: XpertResolvedViewHostContext, input: AnalyticsQueryRequest): Promise<import("@xpert-ai/contracts").AnalyticalDataset>;
|
|
13
13
|
list(context: XpertResolvedViewHostContext): Promise<{
|
|
14
14
|
title: string | null | undefined;
|
|
15
|
+
draftRevision?: number | undefined;
|
|
16
|
+
canDelete: boolean;
|
|
15
17
|
artifactId: string;
|
|
16
18
|
versionId: string | null | undefined;
|
|
17
19
|
versionNumber: number | undefined;
|
|
18
20
|
updatedAt: string | Date | undefined;
|
|
19
21
|
}[]>;
|
|
22
|
+
trash(context: XpertResolvedViewHostContext, artifactId: string, expectedRevision: number): Promise<{
|
|
23
|
+
artifactId: string;
|
|
24
|
+
}>;
|
|
20
25
|
save(context: XpertResolvedViewHostContext, manifest: DashboardManifest, expectedVersionId: string | null, expectedRevision?: number): Promise<{
|
|
21
26
|
artifactId: string;
|
|
22
27
|
draftId: string;
|
|
@@ -194,6 +199,11 @@ export declare class DashboardService {
|
|
|
194
199
|
};
|
|
195
200
|
} | undefined;
|
|
196
201
|
datasets: import("@xpert-ai/contracts").AnalyticalDataset[];
|
|
202
|
+
failures: {
|
|
203
|
+
bindingId: string;
|
|
204
|
+
datasetId: string;
|
|
205
|
+
reason: "unavailable";
|
|
206
|
+
}[];
|
|
197
207
|
manifest: {
|
|
198
208
|
schema: "xpert.live-dashboard";
|
|
199
209
|
id: string;
|
|
@@ -238,6 +248,9 @@ export declare class DashboardService {
|
|
|
238
248
|
versionNumber: number | undefined;
|
|
239
249
|
}>;
|
|
240
250
|
queryBinding(context: XpertResolvedViewHostContext, artifactId: string, versionId: string | undefined, bindingId: string, input: Record<string, AnalyticsValue>, draftRevision?: number): Promise<import("@xpert-ai/contracts").AnalyticalDataset>;
|
|
251
|
+
/** Every operation uses the current human's Project role; data queries still use actor(context). */
|
|
252
|
+
authorizeProject(context: XpertResolvedViewHostContext, operation?: 'read' | 'write' | 'manage'): Promise<import("@xpert-ai/plugin-sdk").ProjectHumanAccess | undefined>;
|
|
241
253
|
private execute;
|
|
242
254
|
private probeControls;
|
|
243
255
|
}
|
|
256
|
+
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,38 @@ let DashboardService = class DashboardService {
|
|
|
39
40
|
return dataset;
|
|
40
41
|
}
|
|
41
42
|
async list(context) {
|
|
42
|
-
|
|
43
|
+
const access = 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
|
-
return items.map(item =>
|
|
47
|
+
return items.map(item => {
|
|
48
|
+
const draft = drafts.get(item.id);
|
|
49
|
+
return { ...summary(item), canDelete: !!draft && (!projectId(context) || access?.canManage === true),
|
|
50
|
+
...(draft ? { title: draft.title ?? item.title, draftRevision: draft.revision } : {}) };
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async trash(context, artifactId, expectedRevision) {
|
|
54
|
+
await this.authorizeProject(context, 'manage');
|
|
55
|
+
const artifact = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).getArtifact(artifactId);
|
|
56
|
+
assertOwned(context, artifact);
|
|
57
|
+
if (artifact.status !== 'active')
|
|
58
|
+
throw new common_1.ForbiddenException('DASHBOARD_ARCHIVED');
|
|
59
|
+
// Reuse the platform's atomic revision check, recycle state and share revocation.
|
|
60
|
+
await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).setRecycled({ artifactId, expectedRevision, recycled: true });
|
|
61
|
+
return { artifactId };
|
|
47
62
|
}
|
|
48
63
|
async save(context, manifest, expectedVersionId, expectedRevision = 0) {
|
|
64
|
+
await this.authorizeProject(context, 'write');
|
|
49
65
|
const scopedActor = actor(context);
|
|
50
66
|
const parsed = dashboard_schema_1.dashboardSchema.parse(manifest);
|
|
51
67
|
const artifacts = this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability);
|
|
52
68
|
const source = { pluginName: exports.PLUGIN_NAME, resourceType: RESOURCE_TYPE, resourceId: `${hostPrefix(context)}${parsed.id}` };
|
|
53
69
|
const existing = await artifacts.findArtifactBySource(source);
|
|
54
|
-
if (existing
|
|
55
|
-
|
|
70
|
+
if (existing) {
|
|
71
|
+
assertOwned(context, existing);
|
|
72
|
+
if (existing.status !== 'active')
|
|
73
|
+
throw new common_1.ForbiddenException('DASHBOARD_ARCHIVED');
|
|
74
|
+
}
|
|
56
75
|
// A failed first upload can leave an Artifact with no saved content; revision 0 initializes it.
|
|
57
76
|
const previous = existing && (existing.currentVersionId || expectedRevision > 0) ? await this.openDocument(context, existing.id) : null;
|
|
58
77
|
const dependencies = await (0, document_libraries_1.resolveLibraryAssets)(parsed.presentation?.dependencies ?? [], previous?.dependencies);
|
|
@@ -70,14 +89,22 @@ let DashboardService = class DashboardService {
|
|
|
70
89
|
await this.probeControls(context, binding, Object.fromEntries(parsed.controls.filter((control) => control.defaultValue !== undefined).map((control) => [control.controlId, control.defaultValue ?? null])));
|
|
71
90
|
const htmlValidation = await this.htmlValidation.validate(context, parsed, preview, dependencies);
|
|
72
91
|
const artifact = existing ?? await artifacts.createArtifact({ source, kind: 'file', title: parsed.title,
|
|
73
|
-
scope: { ...scopedActor, ...(context
|
|
92
|
+
scope: { ...scopedActor, ...(projectId(context) ? { projectId: projectId(context), userId: null } : { xpertId: context.hostId }) } });
|
|
74
93
|
const files = this.runtime.require(plugin_sdk_1.WorkspaceFilesRuntimeCapability);
|
|
75
|
-
const
|
|
76
|
-
|
|
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
|
+
}));
|
|
77
105
|
const result = await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).save({ artifactId: artifact.id, expectedRevision,
|
|
78
106
|
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 } });
|
|
107
|
+
...(htmlValidation ? { metadata: { htmlValidation } } : {}), workspaceFileRef: reference });
|
|
81
108
|
return { artifactId: artifact.id, draftId: result.id, draftRevision: result.revision, baseVersionId: result.baseVersionId, status: 'draft', title: parsed.title };
|
|
82
109
|
}
|
|
83
110
|
async open(context, artifactId, versionId, draftRevision) {
|
|
@@ -86,10 +113,12 @@ let DashboardService = class DashboardService {
|
|
|
86
113
|
}
|
|
87
114
|
/** Trusted View transport only. Agent read tools receive locks, never megabytes of library code. */
|
|
88
115
|
async openDocument(context, artifactId, versionId, draftRevision) {
|
|
89
|
-
|
|
116
|
+
await this.authorizeProject(context);
|
|
90
117
|
const artifacts = this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability);
|
|
91
118
|
const artifact = await artifacts.getArtifact(artifactId);
|
|
92
119
|
assertOwned(context, artifact);
|
|
120
|
+
if (artifact.status !== 'active')
|
|
121
|
+
throw new common_1.ForbiddenException('DASHBOARD_ARCHIVED');
|
|
93
122
|
const draft = !versionId || draftRevision !== undefined ? await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).get(artifactId) : null;
|
|
94
123
|
if (draftRevision !== undefined && draft?.revision !== draftRevision)
|
|
95
124
|
throw new common_1.ConflictException('DASHBOARD_DRAFT_CONFLICT');
|
|
@@ -109,10 +138,12 @@ let DashboardService = class DashboardService {
|
|
|
109
138
|
isDraft: draft.revision !== draft.publishedRevision, title: draft.title, updatedAt: draft.updatedAt } : {}), manifest, dependencies };
|
|
110
139
|
}
|
|
111
140
|
async history(context, artifactId) {
|
|
112
|
-
|
|
141
|
+
await this.authorizeProject(context);
|
|
113
142
|
const artifacts = this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability);
|
|
114
143
|
const artifact = await artifacts.getArtifact(artifactId);
|
|
115
144
|
assertOwned(context, artifact);
|
|
145
|
+
if (artifact.status !== 'active')
|
|
146
|
+
throw new common_1.ForbiddenException('DASHBOARD_ARCHIVED');
|
|
116
147
|
const versions = await artifacts.listArtifactVersions({ artifactId, status: 'active' });
|
|
117
148
|
return versions.map(version => ({ ...summary(artifact, version), current: version.id === artifact.currentVersionId }))
|
|
118
149
|
.sort((a, b) => (b.versionNumber ?? 0) - (a.versionNumber ?? 0));
|
|
@@ -124,7 +155,10 @@ let DashboardService = class DashboardService {
|
|
|
124
155
|
return this.save(context, dashboard_schema_1.dashboardSchema.parse({ ...saved.manifest, ...dashboard_schema_1.dashboardChangesSchema.parse(changes) }), saved.baseVersionId ?? saved.versionId ?? null, expectedRevision);
|
|
125
156
|
}
|
|
126
157
|
async publish(context, artifactId, expectedRevision, expectedCurrentVersionId) {
|
|
158
|
+
await this.authorizeProject(context, 'manage');
|
|
127
159
|
const saved = await this.open(context, artifactId, undefined, expectedRevision);
|
|
160
|
+
if (!saved.manifest.bindings.length)
|
|
161
|
+
throw new common_1.BadRequestException('DASHBOARD_CONFIGURE_BEFORE_PUBLISHING');
|
|
128
162
|
const share = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).getArtifactShare({ artifactId, shareKey: dashboard_sharing_1.DASHBOARD_SHARE_KEY });
|
|
129
163
|
if (share?.status === 'active' && share.versionMode === 'latest' && (!share.expiresAt || new Date(share.expiresAt).getTime() > Date.now())) {
|
|
130
164
|
const issues = (0, dashboard_sharing_1.dashboardSharingIssues)(saved.manifest, await this.sources(context));
|
|
@@ -138,13 +172,31 @@ let DashboardService = class DashboardService {
|
|
|
138
172
|
}
|
|
139
173
|
async query(context, artifactId, versionId, controls, draftRevision) {
|
|
140
174
|
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) }) };
|
|
175
|
+
const { datasets, failures } = await (0, dashboard_queries_1.queryDashboardPartial)(this.runtime, actor(context), saved.manifest, controls);
|
|
176
|
+
return { ...saved, datasets, failures, ...(saved.manifest.presentation ? {} : { payload: (0, renderer_1.toDataAnalyticsPayload)(saved.manifest, datasets) }) };
|
|
143
177
|
}
|
|
144
178
|
async queryBinding(context, artifactId, versionId, bindingId, input, draftRevision) {
|
|
145
179
|
const saved = await this.open(context, artifactId, versionId, draftRevision);
|
|
146
180
|
return (0, dashboard_queries_1.queryDashboardBinding)(this.runtime, actor(context), saved.manifest, bindingId, input);
|
|
147
181
|
}
|
|
182
|
+
/** Every operation uses the current human's Project role; data queries still use actor(context). */
|
|
183
|
+
async authorizeProject(context, operation = 'read') {
|
|
184
|
+
const scopedActor = actor(context), project = projectId(context);
|
|
185
|
+
if (!project)
|
|
186
|
+
return;
|
|
187
|
+
const access = this.runtime.require(plugin_sdk_1.ProjectAccessRuntimeCapability);
|
|
188
|
+
const input = { actor: scopedActor, projectId: project };
|
|
189
|
+
if (context.hostType === 'agent')
|
|
190
|
+
await access.assertAssistant({ ...input, assistantId: context.hostId });
|
|
191
|
+
if (operation === 'write')
|
|
192
|
+
return access.assertEdit(input);
|
|
193
|
+
if (operation === 'manage')
|
|
194
|
+
return access.assertManage(input);
|
|
195
|
+
const readable = (await access.listReadable({ actor: scopedActor, projectIds: [project] })).find(item => item.projectId === project);
|
|
196
|
+
if (!readable)
|
|
197
|
+
throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
|
|
198
|
+
return readable;
|
|
199
|
+
}
|
|
148
200
|
execute(context, manifest, input) {
|
|
149
201
|
return (0, dashboard_queries_1.queryDashboard)(this.runtime, actor(context), manifest, input);
|
|
150
202
|
}
|
|
@@ -177,10 +229,11 @@ function actor(context) {
|
|
|
177
229
|
throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
|
|
178
230
|
return { tenantId: context.tenantId, organizationId: context.organizationId, userId: context.userId };
|
|
179
231
|
}
|
|
180
|
-
function
|
|
232
|
+
function projectId(context) { return context.hostType === 'project' ? context.hostId : context.runtimeScope?.projectId; }
|
|
233
|
+
function hostPrefix(context) { return projectId(context) ? `project/${projectId(context)}/` : `${context.hostType}/${context.hostId}/`; }
|
|
181
234
|
function assertOwned(context, artifact) {
|
|
182
235
|
if (artifact.pluginName !== exports.PLUGIN_NAME || artifact.resourceType !== RESOURCE_TYPE || !artifact.resourceId.startsWith(hostPrefix(context)) ||
|
|
183
|
-
artifact.tenantId !== context.tenantId || artifact.organizationId !== context.organizationId || artifact.userId !== context.userId)
|
|
236
|
+
artifact.tenantId !== context.tenantId || artifact.organizationId !== context.organizationId || (artifact.userId ? artifact.userId !== context.userId : !projectId(context) || artifact.projectId !== projectId(context)))
|
|
184
237
|
throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
|
|
185
238
|
}
|
|
186
239
|
function summary(artifact, version = artifact.currentVersion) {
|
|
@@ -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
|
+
};
|
|
@@ -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.`;
|