@xpert-ai/plugin-live-dashboard 0.1.1 → 0.1.3
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/dist/lib/dashboard-navigation.d.ts +26 -0
- package/dist/lib/dashboard-navigation.js +15 -1
- package/dist/lib/dashboard-sharing.d.ts +8 -0
- package/dist/lib/dashboard-sharing.js +17 -0
- package/dist/lib/dashboard.service.js +7 -0
- package/dist/lib/middleware.d.ts +1 -1
- package/dist/lib/middleware.js +1 -1
- package/dist/lib/resource-card.js +1 -2
- package/dist/lib/sharing.service.js +5 -11
- package/dist/lib/view.provider.d.ts +8 -0
- package/dist/lib/view.provider.js +1 -1
- package/dist/remote/app.js +21 -21
- package/dist/remote/build-manifest.json +13 -12
- package/package.json +1 -1
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
import { z } from 'zod/v3';
|
|
2
|
+
import type { XpertResolvedViewHostContext } from '@xpert-ai/contracts';
|
|
2
3
|
export declare const DASHBOARD_VIEW_KEY = "live-dashboard__live-dashboard";
|
|
4
|
+
export declare const dashboardNavigationTargetSchema: z.ZodDiscriminatedUnion<"target", [z.ZodObject<{
|
|
5
|
+
target: z.ZodLiteral<"workbench.view">;
|
|
6
|
+
viewKey: z.ZodLiteral<"live-dashboard__live-dashboard">;
|
|
7
|
+
}, "strict", z.ZodTypeAny, {
|
|
8
|
+
target: "workbench.view";
|
|
9
|
+
viewKey: "live-dashboard__live-dashboard";
|
|
10
|
+
}, {
|
|
11
|
+
target: "workbench.view";
|
|
12
|
+
viewKey: "live-dashboard__live-dashboard";
|
|
13
|
+
}>, z.ZodObject<{
|
|
14
|
+
target: z.ZodLiteral<"assistant.project">;
|
|
15
|
+
projectId: z.ZodString;
|
|
16
|
+
viewKey: z.ZodLiteral<"live-dashboard__live-dashboard">;
|
|
17
|
+
}, "strict", z.ZodTypeAny, {
|
|
18
|
+
projectId: string;
|
|
19
|
+
target: "assistant.project";
|
|
20
|
+
viewKey: "live-dashboard__live-dashboard";
|
|
21
|
+
}, {
|
|
22
|
+
projectId: string;
|
|
23
|
+
target: "assistant.project";
|
|
24
|
+
viewKey: "live-dashboard__live-dashboard";
|
|
25
|
+
}>]>;
|
|
26
|
+
export type DashboardNavigationTarget = z.infer<typeof dashboardNavigationTargetSchema>;
|
|
27
|
+
/** Resolve from the authorized host context, never from the dashboard's business identity. */
|
|
28
|
+
export declare function dashboardNavigationTarget(context: XpertResolvedViewHostContext): DashboardNavigationTarget;
|
|
3
29
|
export declare const dashboardNavigationSchema: z.ZodEffects<z.ZodObject<{
|
|
4
30
|
selectionId: z.ZodOptional<z.ZodString>;
|
|
5
31
|
parameters: z.ZodOptional<z.ZodObject<{
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dashboardNavigationKey = exports.dashboardNavigationSchema = exports.DASHBOARD_VIEW_KEY = void 0;
|
|
3
|
+
exports.dashboardNavigationKey = exports.dashboardNavigationSchema = exports.dashboardNavigationTargetSchema = exports.DASHBOARD_VIEW_KEY = void 0;
|
|
4
|
+
exports.dashboardNavigationTarget = dashboardNavigationTarget;
|
|
4
5
|
const v3_1 = require("zod/v3");
|
|
5
6
|
// The host namespaces provider + local view key; cards use the resolved public key.
|
|
6
7
|
exports.DASHBOARD_VIEW_KEY = 'live-dashboard__live-dashboard';
|
|
8
|
+
exports.dashboardNavigationTargetSchema = v3_1.z.discriminatedUnion('target', [
|
|
9
|
+
v3_1.z.object({ target: v3_1.z.literal('workbench.view'), viewKey: v3_1.z.literal(exports.DASHBOARD_VIEW_KEY) }).strict(),
|
|
10
|
+
v3_1.z.object({ target: v3_1.z.literal('assistant.project'), projectId: v3_1.z.string().uuid(), viewKey: v3_1.z.literal(exports.DASHBOARD_VIEW_KEY) }).strict()
|
|
11
|
+
]);
|
|
12
|
+
/** Resolve from the authorized host context, never from the dashboard's business identity. */
|
|
13
|
+
function dashboardNavigationTarget(context) {
|
|
14
|
+
if (context.hostType !== 'agent' && context.hostType !== 'project')
|
|
15
|
+
throw new Error('DASHBOARD_HOST_UNSUPPORTED');
|
|
16
|
+
return {
|
|
17
|
+
...(context.hostType === 'project' ? { target: 'assistant.project', projectId: context.hostId } : { target: 'workbench.view' }),
|
|
18
|
+
viewKey: exports.DASHBOARD_VIEW_KEY
|
|
19
|
+
};
|
|
20
|
+
}
|
|
7
21
|
exports.dashboardNavigationSchema = v3_1.z.object({
|
|
8
22
|
selectionId: v3_1.z.string().uuid().optional(),
|
|
9
23
|
parameters: v3_1.z.object({ artifactId: v3_1.z.string().uuid().optional(), versionId: v3_1.z.string().uuid().optional() }).strict().optional()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AnalyticsSourceSummary } from '@xpert-ai/contracts';
|
|
2
|
+
import type { DashboardManifest } from './dashboard.schema';
|
|
3
|
+
export declare const DASHBOARD_SHARE_KEY = "team-readonly";
|
|
4
|
+
/** Apply the same provider policy when enabling a share and advancing a latest share. */
|
|
5
|
+
export declare function dashboardSharingIssues(manifest: DashboardManifest, sources: AnalyticsSourceSummary[]): {
|
|
6
|
+
code: string;
|
|
7
|
+
bindingId: string;
|
|
8
|
+
}[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DASHBOARD_SHARE_KEY = void 0;
|
|
4
|
+
exports.dashboardSharingIssues = dashboardSharingIssues;
|
|
5
|
+
exports.DASHBOARD_SHARE_KEY = 'team-readonly';
|
|
6
|
+
/** Apply the same provider policy when enabling a share and advancing a latest share. */
|
|
7
|
+
function dashboardSharingIssues(manifest, sources) {
|
|
8
|
+
const issues = [];
|
|
9
|
+
for (const binding of manifest.bindings) {
|
|
10
|
+
const provider = sources.find(source => source.id === binding.dataSourceId)?.providers.find(provider => provider.id === binding.providerId);
|
|
11
|
+
if (!provider)
|
|
12
|
+
issues.push({ code: 'DASHBOARD_SOURCE_UNAVAILABLE', bindingId: binding.id });
|
|
13
|
+
else if (provider.sharing?.mode === 'owner_only')
|
|
14
|
+
issues.push({ code: provider.sharing.reasonCode ?? 'DASHBOARD_PRIVATE_DEPENDENCY', bindingId: binding.id });
|
|
15
|
+
}
|
|
16
|
+
return issues;
|
|
17
|
+
}
|
|
@@ -22,6 +22,7 @@ const dashboard_queries_1 = require("./dashboard-queries");
|
|
|
22
22
|
const html_validation_1 = require("./html-validation");
|
|
23
23
|
const document_libraries_1 = require("./document-libraries");
|
|
24
24
|
const document_package_1 = require("./document-package");
|
|
25
|
+
const dashboard_sharing_1 = require("./dashboard-sharing");
|
|
25
26
|
exports.PLUGIN_NAME = '@xpert-ai/plugin-live-dashboard';
|
|
26
27
|
const RESOURCE_TYPE = 'live-dashboard';
|
|
27
28
|
const MIME_TYPE = 'application/json';
|
|
@@ -124,6 +125,12 @@ let DashboardService = class DashboardService {
|
|
|
124
125
|
}
|
|
125
126
|
async publish(context, artifactId, expectedRevision, expectedCurrentVersionId) {
|
|
126
127
|
const saved = await this.open(context, artifactId, undefined, expectedRevision);
|
|
128
|
+
const share = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).getArtifactShare({ artifactId, shareKey: dashboard_sharing_1.DASHBOARD_SHARE_KEY });
|
|
129
|
+
if (share?.status === 'active' && share.versionMode === 'latest' && (!share.expiresAt || new Date(share.expiresAt).getTime() > Date.now())) {
|
|
130
|
+
const issues = (0, dashboard_sharing_1.dashboardSharingIssues)(saved.manifest, await this.sources(context));
|
|
131
|
+
if (issues.length)
|
|
132
|
+
throw new common_1.BadRequestException({ message: 'DASHBOARD_PUBLISH_SHARE_BLOCKED', code: 'DASHBOARD_PUBLISH_SHARE_BLOCKED', issues });
|
|
133
|
+
}
|
|
127
134
|
// Revalidate current source access and bindings before publishing the exact reviewed bytes.
|
|
128
135
|
await this.execute(context, saved.manifest, {});
|
|
129
136
|
const version = await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).publish({ artifactId, expectedRevision, expectedCurrentVersionId });
|
package/dist/lib/middleware.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export declare class DashboardMiddleware implements IAgentMiddlewareStrategy {
|
|
|
6
6
|
private readonly service;
|
|
7
7
|
readonly meta: TAgentMiddlewareMeta;
|
|
8
8
|
constructor(service: DashboardService);
|
|
9
|
-
createMiddleware(_options:
|
|
9
|
+
createMiddleware(_options: unknown, context: IAgentMiddlewareContext): AgentMiddleware;
|
|
10
10
|
}
|
package/dist/lib/middleware.js
CHANGED
|
@@ -28,7 +28,7 @@ const filter_evidence_1 = require("./filter-evidence");
|
|
|
28
28
|
const save_evidence_1 = require("./save-evidence");
|
|
29
29
|
const resource_card_1 = require("./resource-card");
|
|
30
30
|
const dashboard_icon_1 = require("./dashboard-icon");
|
|
31
|
-
const middlewareOptionsSchema = v3_1.z.object({ allowedFinalProviders: v3_1.z.array(v3_1.z.string().min(1)).min(1).max(20).optional() }).strict();
|
|
31
|
+
const middlewareOptionsSchema = v3_1.z.object({ allowedFinalProviders: v3_1.z.array(v3_1.z.string().min(1)).min(1).max(20).optional() }).strict().default({});
|
|
32
32
|
exports.DASHBOARD_GUIDANCE = `For a new independent dashboard, use a new unused HTML file path. An existing filename or matching title is not authorization to edit another Artifact; do not switch from creation to dashboard_open/update because a file already exists. For a new dashboard, call dashboard_list_sources before searching raw files, reading old scripts or creating transformations. Named governed sources may already provide the required query; inspect their provider contracts first. File-backed sources do not imply that a script must be written. For creating or editing live dashboards, first read the installed live-dashboard skill using read_skill_file and follow only the references relevant to the task. Use SandboxFile tools for all source-file authoring; HTML alone is not a saved dashboard. Follow preview.validation.checks immediately before authoring HTML; each suggestedCall must actually succeed. Business definitions, real data previews and nullable-filter tests are required before saving new or changed Bindings. Do not invent business data or substitute a different metric without agreement. Report completion only from a successful dashboard_create/dashboard_create_html/dashboard_update draft receipt. Human publication is separate. Successful create, update and open tools emit a chat resource card automatically; resourceCard.status reports emission. Its Open button selects the exact dashboard in Workbench. Never fabricate a link, call present_files for dashboard HTML, or output card JSON as text. If card delivery is unavailable, report the saved receipt and use the Workbench picker.`;
|
|
33
33
|
let DashboardMiddleware = class DashboardMiddleware {
|
|
34
34
|
constructor(service) {
|
|
@@ -15,8 +15,7 @@ function dashboardResourceCard(scope, saved) {
|
|
|
15
15
|
description: saved.state === 'draft' ? `Dashboard · Draft r${saved.revision}` : `Dashboard · v${saved.versionNumber}`,
|
|
16
16
|
icon: dashboard_icon_1.dashboardIcon,
|
|
17
17
|
open: {
|
|
18
|
-
...(
|
|
19
|
-
viewKey: dashboard_navigation_1.DASHBOARD_VIEW_KEY, selectionId: saved.artifactId,
|
|
18
|
+
...(0, dashboard_navigation_1.dashboardNavigationTarget)(scope), selectionId: saved.artifactId,
|
|
20
19
|
...(saved.state === 'published' && saved.versionId ? { parameters: { versionId: saved.versionId } } : {})
|
|
21
20
|
}
|
|
22
21
|
};
|
|
@@ -17,9 +17,9 @@ const common_1 = require("@nestjs/common");
|
|
|
17
17
|
const v3_1 = require("zod/v3");
|
|
18
18
|
const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
|
|
19
19
|
const dashboard_service_1 = require("./dashboard.service");
|
|
20
|
+
const dashboard_sharing_1 = require("./dashboard-sharing");
|
|
20
21
|
exports.shareInput = v3_1.z.object({ artifactId: v3_1.z.string().uuid(), versionMode: v3_1.z.enum(['latest', 'version']), versionId: v3_1.z.string().uuid().optional() }).strict()
|
|
21
22
|
.refine(value => value.versionMode !== 'version' || !!value.versionId, 'A fixed share requires a published version');
|
|
22
|
-
const SHARE_KEY = 'team-readonly';
|
|
23
23
|
let DashboardSharingService = class DashboardSharingService {
|
|
24
24
|
constructor(dashboards, runtime) {
|
|
25
25
|
this.dashboards = dashboards;
|
|
@@ -28,20 +28,14 @@ let DashboardSharingService = class DashboardSharingService {
|
|
|
28
28
|
async inspect(context, artifactId, versionId) {
|
|
29
29
|
const versions = await this.dashboards.history(context, artifactId);
|
|
30
30
|
const selected = versionId ? versions.find(version => version.versionId === versionId) : versions.find(version => version.current);
|
|
31
|
-
const link = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).getArtifactShare({ artifactId, shareKey:
|
|
31
|
+
const link = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).getArtifactShare({ artifactId, shareKey: dashboard_sharing_1.DASHBOARD_SHARE_KEY });
|
|
32
32
|
const issues = [];
|
|
33
33
|
if (!selected?.versionId)
|
|
34
34
|
issues.push({ code: 'DASHBOARD_PUBLISH_BEFORE_SHARING' });
|
|
35
35
|
else {
|
|
36
36
|
const saved = await this.dashboards.open(context, artifactId, selected.versionId);
|
|
37
37
|
const sources = await this.dashboards.sources(context);
|
|
38
|
-
|
|
39
|
-
const provider = sources.find(source => source.id === binding.dataSourceId)?.providers.find(provider => provider.id === binding.providerId);
|
|
40
|
-
if (!provider)
|
|
41
|
-
issues.push({ code: 'DASHBOARD_SOURCE_UNAVAILABLE', bindingId: binding.id });
|
|
42
|
-
else if (provider.sharing?.mode === 'owner_only')
|
|
43
|
-
issues.push({ code: provider.sharing.reasonCode ?? 'DASHBOARD_PRIVATE_DEPENDENCY', bindingId: binding.id });
|
|
44
|
-
}
|
|
38
|
+
issues.push(...(0, dashboard_sharing_1.dashboardSharingIssues)(saved.manifest, sources));
|
|
45
39
|
}
|
|
46
40
|
return { published: selected ?? null, issues, link: link ? { status: link.status, versionMode: link.versionMode,
|
|
47
41
|
versionId: link.artifactVersionId, url: link.presentationUrl, expiresAt: link.expiresAt } : null };
|
|
@@ -50,7 +44,7 @@ let DashboardSharingService = class DashboardSharingService {
|
|
|
50
44
|
const state = await this.inspect(context, input.artifactId, input.versionMode === 'version' ? input.versionId : undefined);
|
|
51
45
|
if (state.issues.length || !state.published?.versionId)
|
|
52
46
|
throw new common_1.BadRequestException({ code: 'DASHBOARD_SHARE_BLOCKED', issues: state.issues });
|
|
53
|
-
const result = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).ensureArtifactShare({ artifactId: input.artifactId, shareKey:
|
|
47
|
+
const result = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).ensureArtifactShare({ artifactId: input.artifactId, shareKey: dashboard_sharing_1.DASHBOARD_SHARE_KEY,
|
|
54
48
|
versionMode: input.versionMode, ...(input.versionMode === 'version' ? { artifactVersionId: state.published.versionId } : {}),
|
|
55
49
|
access: { mode: 'organization_all' }, presentation: { allowDownload: false, disposition: 'inline', safeHtmlProfile: 'strict' } });
|
|
56
50
|
if (!result.link.presentationUrl)
|
|
@@ -59,7 +53,7 @@ let DashboardSharingService = class DashboardSharingService {
|
|
|
59
53
|
}
|
|
60
54
|
async revoke(context, artifactId) {
|
|
61
55
|
await this.dashboards.history(context, artifactId);
|
|
62
|
-
await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).revokeArtifactShare({ artifactId, shareKey:
|
|
56
|
+
await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).revokeArtifactShare({ artifactId, shareKey: dashboard_sharing_1.DASHBOARD_SHARE_KEY });
|
|
63
57
|
return this.inspect(context, artifactId);
|
|
64
58
|
}
|
|
65
59
|
};
|
|
@@ -70,6 +70,14 @@ export declare class DashboardViewProvider implements IXpertViewExtensionProvide
|
|
|
70
70
|
title: string | null | undefined;
|
|
71
71
|
updatedAt: string | Date | undefined;
|
|
72
72
|
}[] | undefined;
|
|
73
|
+
navigationTarget: {
|
|
74
|
+
target: "workbench.view";
|
|
75
|
+
viewKey: "live-dashboard__live-dashboard";
|
|
76
|
+
} | {
|
|
77
|
+
projectId: string;
|
|
78
|
+
target: "assistant.project";
|
|
79
|
+
viewKey: "live-dashboard__live-dashboard";
|
|
80
|
+
};
|
|
73
81
|
dashboards: {
|
|
74
82
|
title: string | null | undefined;
|
|
75
83
|
artifactId: string;
|
|
@@ -56,7 +56,7 @@ let DashboardViewProvider = class DashboardViewProvider {
|
|
|
56
56
|
async getViewData(context, viewKey, query) {
|
|
57
57
|
assertView(viewKey);
|
|
58
58
|
const params = dashboard_navigation_1.dashboardNavigationSchema.parse(query);
|
|
59
|
-
return { meta: { dashboards: await this.service.list(context), sources: await this.service.sources(context),
|
|
59
|
+
return { meta: { navigationTarget: (0, dashboard_navigation_1.dashboardNavigationTarget)(context), dashboards: await this.service.list(context), sources: await this.service.sources(context),
|
|
60
60
|
...(params.artifactId ? { saved: await this.service.openDocument(context, params.artifactId, params.versionId),
|
|
61
61
|
versions: await this.service.history(context, params.artifactId) } : {}) } };
|
|
62
62
|
}
|