@xpert-ai/plugin-live-dashboard 0.1.1 → 0.1.2

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpert-ai/plugin-live-dashboard",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "level": "system",
5
5
  "artifactNamespace": "live_dashboard",
6
6
  "sandboxActions": "./dist/sandbox-actions/action.json",
@@ -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 });
@@ -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
- ...(scope.hostType === 'project' ? { target: 'assistant.project', projectId: scope.hostId } : { target: 'workbench.view' }),
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: SHARE_KEY });
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
- for (const binding of saved.manifest.bindings) {
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: SHARE_KEY,
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: SHARE_KEY });
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
  }