@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.
Files changed (67) hide show
  1. package/.xpertai-plugin/plugin.json +1 -1
  2. package/README.md +5 -5
  3. package/dist/lib/agent.schema.d.ts +11 -11
  4. package/dist/lib/agent.schema.js +3 -3
  5. package/dist/lib/analytics-source.d.ts +3 -0
  6. package/dist/lib/analytics-source.js +11 -0
  7. package/dist/lib/content-capability.d.ts +22 -0
  8. package/dist/lib/content-capability.js +33 -0
  9. package/dist/lib/content.provider.d.ts +23 -0
  10. package/dist/lib/content.provider.js +163 -0
  11. package/dist/lib/dashboard-icon.d.ts +2 -1
  12. package/dist/lib/dashboard-icon.js +10 -2
  13. package/dist/lib/dashboard-navigation.d.ts +24 -0
  14. package/dist/lib/dashboard-navigation.js +1 -1
  15. package/dist/lib/dashboard-queries.d.ts +11 -0
  16. package/dist/lib/dashboard-queries.js +20 -0
  17. package/dist/lib/dashboard-search.d.ts +5 -0
  18. package/dist/lib/dashboard-search.js +12 -0
  19. package/dist/lib/dashboard-sharing.js +2 -1
  20. package/dist/lib/dashboard-view.schema.d.ts +1249 -0
  21. package/dist/lib/dashboard-view.schema.js +19 -0
  22. package/dist/lib/dashboard.schema.d.ts +12 -12
  23. package/dist/lib/dashboard.schema.js +5 -3
  24. package/dist/lib/dashboard.service.d.ts +11 -3
  25. package/dist/lib/dashboard.service.js +55 -21
  26. package/dist/lib/definition-tools.d.ts +56 -0
  27. package/dist/lib/definition-tools.js +42 -0
  28. package/dist/lib/document-host.d.ts +4 -0
  29. package/dist/lib/document-host.js +13 -0
  30. package/dist/lib/document-network.d.ts +3 -0
  31. package/dist/lib/document-network.js +9 -0
  32. package/dist/lib/document-package.d.ts +1 -1
  33. package/dist/lib/document-package.js +2 -2
  34. package/dist/lib/html-guidance.d.ts +1 -1
  35. package/dist/lib/html-guidance.js +1 -1
  36. package/dist/lib/middleware.js +54 -18
  37. package/dist/lib/plugin.js +3 -1
  38. package/dist/lib/presentation.provider.d.ts +6 -1
  39. package/dist/lib/presentation.provider.js +2 -2
  40. package/dist/lib/preview-retry.d.ts +7 -0
  41. package/dist/lib/preview-retry.js +52 -0
  42. package/dist/lib/renderer.js +5 -0
  43. package/dist/lib/resource-card.js +1 -1
  44. package/dist/lib/save-evidence.d.ts +1 -1
  45. package/dist/lib/sharing.service.js +2 -0
  46. package/dist/lib/view.provider.d.ts +7 -2
  47. package/dist/lib/view.provider.js +5 -4
  48. package/dist/remote/app.css +1 -1
  49. package/dist/remote/app.js +36 -36
  50. package/dist/remote/build-manifest.json +42 -30
  51. package/dist/sandbox-actions/action.json +1 -1
  52. package/dist/sandbox-actions/bundle/host.js +2 -2
  53. package/dist/sandbox-actions/bundle/validate.mjs +24 -3
  54. package/dist/validation/host-entry.d.ts +3 -1
  55. package/dist/validation/host-entry.js +5 -2
  56. package/docs/drafts.mdx +1 -1
  57. package/docs/frontend-libraries.mdx +3 -3
  58. package/docs/html-mode.md +5 -5
  59. package/docs/index.mdx +14 -0
  60. package/docs/team-sharing.mdx +1 -1
  61. package/docs/verification.mdx +3 -1
  62. package/examples/data-xpert-sales.json +6 -3
  63. package/examples/postgres-sales.json +5 -3
  64. package/package.json +6 -6
  65. package/skills/live-dashboard/SKILL.md +1 -1
  66. package/skills/live-dashboard/references/data-validation.md +4 -0
  67. package/skills/live-dashboard/references/html-runtime.md +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpert-ai/plugin-live-dashboard",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "level": "system",
5
5
  "artifactNamespace": "live_dashboard",
6
6
  "sandboxActions": "./dist/sandbox-actions/action.json",
package/README.md CHANGED
@@ -31,7 +31,7 @@ The Workbench supplies its selected Artifact/version through the platform contex
31
31
  update preserves identity and omitted fields, requires fresh preview receipts for replacement bindings, and uses the existing validation/CAS path.
32
32
  Successful update receipts open the saved working draft; only human publication creates a formal version. See [continuous editing](docs/editing.mdx).
33
33
 
34
- Tools are `dashboard_list_sources`, `dashboard_discover`, `dashboard_preview`, `dashboard_preview_binding`, `dashboard_create`, `dashboard_open`, `dashboard_update` and `dashboard_create_html`. File authoring is supplied by the platform SandboxFile middleware. The canonical preview tool accepts complete `bindingJson` returned by an adapter registration tool. Both preview tools use the same discovery and receipt checks. The middleware adds business-semantic guidance while preserving the host system message. It requires discovery and a successful preview of the exact binding/defaults before creation via run-local `previewId` receipts; save revalidates current source access. Evidence is local to the run and actor, and repeated creation of the same manifest rechecks authorization before reusing its Artifact.
34
+ Tools are `dashboard_list_sources`, `dashboard_discover`, `dashboard_preview`, `dashboard_preview_binding`, `dashboard_create`, `dashboard_open`, `dashboard_update` and `dashboard_create_html`. File authoring is supplied by the platform SandboxFile middleware. The canonical preview tool accepts complete `bindingJson` returned by an adapter registration tool. Both preview tools automatically discover the query contract with an empty request when it is absent in this run. Contracts are cached by exact source (including version) and provider; concurrent previews share discovery, and discovery failures propagate without executing the query and can be retried. Explicit discovery remains available for business metadata inspection. Source selection, payload validation and runtime authorization still apply. The middleware adds business-semantic guidance while preserving the host system message. Creation requires a successful preview of the exact binding/defaults via run-local `previewId` receipts; save revalidates current source access. Evidence is local to the run and actor, and repeated creation of the same manifest rechecks authorization before reusing its Artifact.
35
35
 
36
36
  Specialized Assistants may configure `allowedFinalProviders` on the middleware, for example `["workspace_script.query"]`. Upstream discovery and preview still work with other providers, but Agent save rejects final bindings outside this list. Omit it for the existing unrestricted provider workflow. This is an Agent workflow policy, not a substitute for the server's source/Artifact authorization.
37
37
 
@@ -39,7 +39,7 @@ For a dedicated Dashboard Assistant, mark its connected `LiveDashboard` middlewa
39
39
 
40
40
  Only the LLM boundary uses `requestJson` / `payloadJson` strings, because recursive JSON unions are unreliable in model function schemas. The plugin parses these into the unchanged canonical JSON protocol before calling the platform. Protocol version and operation come from the discovered provider contract; parameter name also supplies the Agent-created control ID. Saved manifests retain the full generic Binding protocol. Preview uses Binding parameter defaults, avoiding a second control mapping for the model to repeat. Provider syntax is learned from discovery, not embedded in platform prompts.
41
41
 
42
- The model gets at most 20 preview rows/32 KB, with original completeness and explicit omitted-row counts. It has 24 model tool rounds; after that it must report saved receipts or a blocker. Authored definitions, units, date range and aggregation grain still require business verification: structural validation alone does not prove metric correctness.
42
+ The model gets at most 20 preview rows/32 KB, with original completeness and explicit omitted-row counts. Failed preview/filter-test inputs execute at most twice per run; repeated identical failures are blocked before querying. Six consecutive preview failures stop tool use and request a truthful blocker summary; a model that still emits tool calls receives a terminal diagnostic instead. Schema failures count too. Successful previews reset the consecutive-failure count, and corrected inputs can be tried before the limit. The model also has 24 model tool rounds; after that it must report saved receipts or a blocker. Authored definitions, units, date range and aggregation grain still require business verification: structural validation alone does not prove metric correctness.
43
43
 
44
44
  Categorical charts must encode every Dataset grain dimension in `x`/`color`. The renderer does not aggregate: totals need a separate previewed binding. Unsupported business metrics require clarification, not a proxy dashboard without agreement.
45
45
 
@@ -49,7 +49,7 @@ The creation tool returns `{ dashboard: { artifactId, versionId, versionNumber,
49
49
 
50
50
  ## Save, filter, refresh
51
51
 
52
- `xpert.live-dashboard/v1` contains id/title/bindings/controls/charts/tables and optional `presentation: { type: "html", html }`. Charts reference stable Dataset field IDs. Controls map to typed binding parameters. Filter and refresh execute the saved server-side definition for the current viewer.
52
+ `xpert.live-dashboard` contains id/title/bindings/controls/charts/tables and optional `presentation: { type: "html", html }`. Charts reference stable Dataset field IDs. Controls map to typed binding parameters. Filter and refresh execute the saved server-side definition for the current viewer.
53
53
 
54
54
  Save validates and queries all bindings before writing immutable JSON. `expectedVersionId` (null for new) and platform Artifact row-lock compare-and-set prevent lost updates. Content hashes support retries. Saved files contain definitions, not result rows.
55
55
 
@@ -63,7 +63,7 @@ See [HTML mode and acceptance](docs/html-mode.md). Agent tools write a complete
63
63
 
64
64
  The platform browser entry `@xpert-ai/plugin-sdk/artifact-document` owns the sandbox, CSP, MessageChannel, request validation and latest-response policy. The plugin owns lifecycle and supplies a narrow callback to `query-binding`. Its server loads the exact saved Artifact version, finds the declared Binding, validates controls and rechecks source access. The endpoint rejects full Binding, Provider, SQL/payload and alternate source fields.
65
65
 
66
- Pages call `window.xpertLiveArtifact.ready()`, `subscribe(bindingId, render)`, `query(bindingId, controls)` and `refresh(bindingId)`. Register subscriptions before the first query so the toolbar can refresh without resetting the document. Use inline DOM/SVG/Canvas; network/CDN imports and eval are unavailable. Script processing remains in the separate Workspace Script provider.
66
+ Pages call `window.xpertLiveArtifact.ready()`, `subscribe(bindingId, render)`, `query(bindingId, controls)` and `refresh(bindingId)`. Register subscriptions before the first query so the toolbar can refresh without resetting the document. Use inline DOM/SVG/Canvas or online scripts/styles/fonts from the HTTPS origins returned by `dashboard_libraries.online`. Tailwind CDN, jsDelivr, cdnjs, unpkg and Google Fonts are supported according to their resource-type allowlist. Preflight and the displayed page use the same policy; failed loads prevent saving. Pin online versions where possible. Unlike packaged libraries, online bytes are not part of the immutable Artifact snapshot. Direct data requests, external images, eval, workers and frames remain unavailable. Script processing remains in the separate Workspace Script provider.
67
67
 
68
68
  HTML is capped at 300 KB; the whole saved definition remains capped at 600 KB. HTML pages own their chart layout, so their charts/tables arrays are empty. The standard renderer remains an independent option. Sharing, data permissions and source limits are unchanged.
69
69
 
@@ -75,7 +75,7 @@ The renderer runs in a script-only sandbox with no-network CSP. A small in-memor
75
75
 
76
76
  ## Build and validation
77
77
 
78
- Use the xpertai workspace's declared Corepack/pnpm. Development dependencies pin published SDK/contracts 3.25.0 and the Data Analytics renderer 0.1.35; no local platform or community links are required.
78
+ Use the xpertai workspace's declared Corepack/pnpm. Development dependencies pin published SDK/contracts 3.27.0 and the Data Analytics renderer 0.1.35; no local platform or community links are required. SDK 3.27.0 includes the host-owned online resource policy, so no SDK patch is required. Rebuild both the viewer and HTML preflight assets after changing the SDK; `verify:dist` checks their source fingerprints.
79
79
 
80
80
  ```sh
81
81
  corepack pnpm install --frozen-lockfile
@@ -1,20 +1,20 @@
1
1
  import { z } from 'zod/v3';
2
2
  import type { AnalyticsDiscoveryResult } from '@xpert-ai/contracts';
3
3
  export declare const discoverToolSchema: z.ZodObject<{
4
- dataSourceId: z.ZodString;
4
+ source: z.ZodType<import("@xpert-ai/contracts").AnalyticsSourceRef, z.ZodTypeDef, import("@xpert-ai/contracts").AnalyticsSourceRef>;
5
5
  providerId: z.ZodString;
6
6
  requestJson: z.ZodDefault<z.ZodString>;
7
7
  }, "strict", z.ZodTypeAny, {
8
- dataSourceId: string;
8
+ source: import("@xpert-ai/contracts").AnalyticsSourceRef;
9
9
  providerId: string;
10
10
  requestJson: string;
11
11
  }, {
12
- dataSourceId: string;
12
+ source: import("@xpert-ai/contracts").AnalyticsSourceRef;
13
13
  providerId: string;
14
14
  requestJson?: string | undefined;
15
15
  }>;
16
16
  export declare const agentBindingSchema: z.ZodObject<{
17
- dataSourceId: z.ZodString;
17
+ source: z.ZodType<import("@xpert-ai/contracts").AnalyticsSourceRef, z.ZodTypeDef, import("@xpert-ai/contracts").AnalyticsSourceRef>;
18
18
  providerId: z.ZodString;
19
19
  output: z.ZodObject<{
20
20
  datasetId: z.ZodString;
@@ -97,6 +97,7 @@ export declare const agentBindingSchema: z.ZodObject<{
97
97
  }>, "many">;
98
98
  payloadJson: z.ZodString;
99
99
  }, "strict", z.ZodTypeAny, {
100
+ source: import("@xpert-ai/contracts").AnalyticsSourceRef;
100
101
  parameters: {
101
102
  type: "string" | "number" | "boolean" | "date";
102
103
  name: string;
@@ -104,7 +105,6 @@ export declare const agentBindingSchema: z.ZodObject<{
104
105
  defaultValue?: string | number | boolean | null | undefined;
105
106
  allowedValues?: (string | number | boolean | null)[] | undefined;
106
107
  }[];
107
- dataSourceId: string;
108
108
  providerId: string;
109
109
  output: {
110
110
  datasetId?: string;
@@ -122,6 +122,7 @@ export declare const agentBindingSchema: z.ZodObject<{
122
122
  };
123
123
  payloadJson: string;
124
124
  }, {
125
+ source: import("@xpert-ai/contracts").AnalyticsSourceRef;
125
126
  parameters: {
126
127
  type: "string" | "number" | "boolean" | "date";
127
128
  name: string;
@@ -129,7 +130,6 @@ export declare const agentBindingSchema: z.ZodObject<{
129
130
  defaultValue?: string | number | boolean | null | undefined;
130
131
  allowedValues?: (string | number | boolean | null)[] | undefined;
131
132
  }[];
132
- dataSourceId: string;
133
133
  providerId: string;
134
134
  output: {
135
135
  datasetId?: string;
@@ -149,7 +149,7 @@ export declare const agentBindingSchema: z.ZodObject<{
149
149
  }>;
150
150
  export declare const previewToolSchema: z.ZodObject<{
151
151
  binding: z.ZodObject<{
152
- dataSourceId: z.ZodString;
152
+ source: z.ZodType<import("@xpert-ai/contracts").AnalyticsSourceRef, z.ZodTypeDef, import("@xpert-ai/contracts").AnalyticsSourceRef>;
153
153
  providerId: z.ZodString;
154
154
  output: z.ZodObject<{
155
155
  datasetId: z.ZodString;
@@ -232,6 +232,7 @@ export declare const previewToolSchema: z.ZodObject<{
232
232
  }>, "many">;
233
233
  payloadJson: z.ZodString;
234
234
  }, "strict", z.ZodTypeAny, {
235
+ source: import("@xpert-ai/contracts").AnalyticsSourceRef;
235
236
  parameters: {
236
237
  type: "string" | "number" | "boolean" | "date";
237
238
  name: string;
@@ -239,7 +240,6 @@ export declare const previewToolSchema: z.ZodObject<{
239
240
  defaultValue?: string | number | boolean | null | undefined;
240
241
  allowedValues?: (string | number | boolean | null)[] | undefined;
241
242
  }[];
242
- dataSourceId: string;
243
243
  providerId: string;
244
244
  output: {
245
245
  datasetId?: string;
@@ -257,6 +257,7 @@ export declare const previewToolSchema: z.ZodObject<{
257
257
  };
258
258
  payloadJson: string;
259
259
  }, {
260
+ source: import("@xpert-ai/contracts").AnalyticsSourceRef;
260
261
  parameters: {
261
262
  type: "string" | "number" | "boolean" | "date";
262
263
  name: string;
@@ -264,7 +265,6 @@ export declare const previewToolSchema: z.ZodObject<{
264
265
  defaultValue?: string | number | boolean | null | undefined;
265
266
  allowedValues?: (string | number | boolean | null)[] | undefined;
266
267
  }[];
267
- dataSourceId: string;
268
268
  providerId: string;
269
269
  output: {
270
270
  datasetId?: string;
@@ -284,6 +284,7 @@ export declare const previewToolSchema: z.ZodObject<{
284
284
  }>;
285
285
  }, "strict", z.ZodTypeAny, {
286
286
  binding: {
287
+ source: import("@xpert-ai/contracts").AnalyticsSourceRef;
287
288
  parameters: {
288
289
  type: "string" | "number" | "boolean" | "date";
289
290
  name: string;
@@ -291,7 +292,6 @@ export declare const previewToolSchema: z.ZodObject<{
291
292
  defaultValue?: string | number | boolean | null | undefined;
292
293
  allowedValues?: (string | number | boolean | null)[] | undefined;
293
294
  }[];
294
- dataSourceId: string;
295
295
  providerId: string;
296
296
  output: {
297
297
  datasetId?: string;
@@ -311,6 +311,7 @@ export declare const previewToolSchema: z.ZodObject<{
311
311
  };
312
312
  }, {
313
313
  binding: {
314
+ source: import("@xpert-ai/contracts").AnalyticsSourceRef;
314
315
  parameters: {
315
316
  type: "string" | "number" | "boolean" | "date";
316
317
  name: string;
@@ -318,7 +319,6 @@ export declare const previewToolSchema: z.ZodObject<{
318
319
  defaultValue?: string | number | boolean | null | undefined;
319
320
  allowedValues?: (string | number | boolean | null)[] | undefined;
320
321
  }[];
321
- dataSourceId: string;
322
322
  providerId: string;
323
323
  output: {
324
324
  datasetId?: string;
@@ -14,9 +14,9 @@ const agentControlsSchema = v3_1.z.array(dashboard_schema_1.dashboardSchema.inne
14
14
  defaultValue: dashboard_schema_1.dashboardSchema.innerType().shape.controls.element.shape.defaultValue.unwrap()
15
15
  .describe('Required: copy the verified Binding parameter defaultValue, including explicit null for an all-values filter.')
16
16
  })).max(24);
17
- exports.discoverToolSchema = v3_1.z.object({ dataSourceId: v3_1.z.string().uuid(), providerId: v3_1.z.string().min(1).max(160),
17
+ exports.discoverToolSchema = v3_1.z.object({ source: analytics_1.analyticsSourceRefSchema, providerId: v3_1.z.string().min(1).max(160),
18
18
  requestJson: objectJson.default('{}').describe('JSON text. Start with "{}"; then JSON.stringify the returned inspectRequest/nextRequest directly. Do not wrap it in inspectRequest.') }).strict();
19
- exports.agentBindingSchema = analytics_1.analyticsBindingObjectSchema.omit({ payload: true, schemaVersion: true, id: true, operation: true, contractVersion: true }).extend({
19
+ exports.agentBindingSchema = analytics_1.analyticsBindingObjectSchema.omit({ payload: true, id: true, operation: true, contractVersion: true }).extend({
20
20
  parameters: v3_1.z.array(analytics_1.analyticsBindingObjectSchema.shape.parameters.element.omit({ controlId: true })).max(24),
21
21
  payloadJson: objectJson
22
22
  }).strict();
@@ -46,7 +46,7 @@ function parseObjectJson(text) {
46
46
  }
47
47
  function fromAgentBinding(input, contract) {
48
48
  const { payloadJson, ...binding } = input;
49
- return analytics_1.analyticsBindingSchema.parse({ ...binding, schemaVersion: 1, id: input.output.datasetId,
49
+ return analytics_1.analyticsBindingSchema.parse({ ...binding, id: input.output.datasetId,
50
50
  operation: contract.operation, contractVersion: contract.contractVersion,
51
51
  parameters: input.parameters.map(parameter => ({ ...parameter, controlId: parameter.name })), payload: parseObjectJson(payloadJson) });
52
52
  }
@@ -0,0 +1,3 @@
1
+ import type { AnalyticsSourceRef } from '@xpert-ai/contracts';
2
+ /** Catalogs show the latest version; historical pinned queries still resolve through the host ACL. */
3
+ export declare function sameAnalyticsResource(left: AnalyticsSourceRef, right: AnalyticsSourceRef): boolean;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sameAnalyticsResource = sameAnalyticsResource;
4
+ /** Catalogs show the latest version; historical pinned queries still resolve through the host ACL. */
5
+ function sameAnalyticsResource(left, right) {
6
+ if (left.kind === 'data-source')
7
+ return right.kind === 'data-source' && left.dataSourceId === right.dataSourceId;
8
+ if (left.kind === 'definition')
9
+ return right.kind === 'definition' && left.definitionId === right.definitionId;
10
+ return right.kind === 'artifact' && left.artifactId === right.artifactId;
11
+ }
@@ -0,0 +1,22 @@
1
+ import { ModelFeature, type TXpertTeamDraft } from '@xpert-ai/contracts';
2
+ import { type IAssistantCapabilityProvider, type AssistantCapabilityApplyContext } from '@xpert-ai/plugin-sdk';
3
+ /** Optional tool composition; installing a View never silently enables these tools. */
4
+ export declare class DashboardCapability implements IAssistantCapabilityProvider {
5
+ readonly key = "live-dashboard";
6
+ readonly availableForBlankAssistant = true;
7
+ readonly supportsQuickCreation = true;
8
+ readonly requiredModelFeatures: ModelFeature[];
9
+ readonly label: {
10
+ en_US: string;
11
+ zh_Hans: string;
12
+ };
13
+ readonly description: {
14
+ en_US: string;
15
+ zh_Hans: string;
16
+ };
17
+ isEnabled(draft: TXpertTeamDraft): boolean;
18
+ check(): Promise<{
19
+ available: boolean;
20
+ }>;
21
+ apply({ draft }: AssistantCapabilityApplyContext): Promise<void>;
22
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.DashboardCapability = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const contracts_1 = require("@xpert-ai/contracts");
12
+ const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
13
+ /** Optional tool composition; installing a View never silently enables these tools. */
14
+ let DashboardCapability = class DashboardCapability {
15
+ constructor() {
16
+ this.key = 'live-dashboard';
17
+ this.availableForBlankAssistant = true;
18
+ this.supportsQuickCreation = true;
19
+ this.requiredModelFeatures = [contracts_1.ModelFeature.TOOL_CALL];
20
+ this.label = { en_US: 'Live dashboards', zh_Hans: '实时看板' };
21
+ this.description = { en_US: 'Create and maintain live dashboards with this Assistant in a Project.', zh_Hans: '为 Assistant 增加实时看板的创建与维护工具,沿用项目权限。' };
22
+ }
23
+ isEnabled(draft) { return (0, plugin_sdk_1.hasAssistantMiddleware)(draft, 'LiveDashboard'); }
24
+ async check() { return { available: true }; }
25
+ async apply({ draft }) {
26
+ (0, plugin_sdk_1.applyAssistantMiddleware)(draft, { provider: 'LiveDashboard', key: 'Capability_DashboardCapability', title: 'Live dashboards', instructions: 'Maintain live dashboard definitions in the current Project using Live Dashboard tools and registered Analytics providers. Discover and validate current data bindings before saving. Read the current draft revision and handle conflicts without overwriting another editor. Query with the current actor permissions; never copy credentials or query rows into the shared definition. Publishing a definition is separate from saving and refreshing data.' });
27
+ }
28
+ };
29
+ exports.DashboardCapability = DashboardCapability;
30
+ exports.DashboardCapability = DashboardCapability = __decorate([
31
+ (0, common_1.Injectable)(),
32
+ (0, plugin_sdk_1.AssistantCapabilityProvider)('live-dashboard')
33
+ ], DashboardCapability);
@@ -0,0 +1,23 @@
1
+ import type { ResourceContentEditorRegistration } from '@xpert-ai/contracts';
2
+ import { type RuntimeCapabilityRegistry, type ContentCatalogProvider, type ContentResourceRequest, type IContentResourceProvider } from '@xpert-ai/plugin-sdk';
3
+ import { DashboardService } from './dashboard.service';
4
+ export declare const DASHBOARD_CONTENT_PROVIDER = "live-dashboard.content";
5
+ export declare class DashboardContentEditor {
6
+ readonly descriptor: ResourceContentEditorRegistration;
7
+ }
8
+ export declare class DashboardContentProvider implements IContentResourceProvider {
9
+ private readonly service;
10
+ private readonly runtime;
11
+ readonly title = "Live Dashboard";
12
+ constructor(service: DashboardService, runtime: RuntimeCapabilityRegistry);
13
+ readonly catalog: ContentCatalogProvider;
14
+ authorize(input: ContentResourceRequest): Promise<{
15
+ providerKey: string;
16
+ resourceId: string;
17
+ title: string;
18
+ role: "owner" | "manager" | "editor" | "member";
19
+ canWrite: boolean;
20
+ canManage: boolean;
21
+ }>;
22
+ private describe;
23
+ }
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.DashboardContentProvider = exports.DashboardContentEditor = exports.DASHBOARD_CONTENT_PROVIDER = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
18
+ const dashboard_service_1 = require("./dashboard.service");
19
+ const dashboard_search_1 = require("./dashboard-search");
20
+ exports.DASHBOARD_CONTENT_PROVIDER = 'live-dashboard.content';
21
+ const MODEL = { typeId: 'xpert.live-dashboard', modelId: 'live-dashboard.manifest', schemaVersion: '1', editProtocol: { id: 'artifact-draft.cas', version: '1' } };
22
+ const EDITOR = 'live-dashboard.editor';
23
+ const context = (scope) => ({ ...scope.actor, hostType: 'project', hostId: scope.projectId, slots: [] });
24
+ let DashboardContentEditor = class DashboardContentEditor {
25
+ constructor() {
26
+ this.descriptor = { kind: 'resource', protocolVersion: 2, key: EDITOR, title: 'Live Dashboard',
27
+ view: { providerKey: 'live-dashboard', viewKey: 'live-dashboard', entry: 'live-dashboard' }, supports: [{ ...MODEL, access: ['view', 'edit'] }] };
28
+ }
29
+ };
30
+ exports.DashboardContentEditor = DashboardContentEditor;
31
+ exports.DashboardContentEditor = DashboardContentEditor = __decorate([
32
+ (0, common_1.Injectable)(),
33
+ (0, plugin_sdk_1.ContentEditorProvider)(EDITOR)
34
+ ], DashboardContentEditor);
35
+ let DashboardContentProvider = class DashboardContentProvider {
36
+ constructor(service, runtime) {
37
+ this.service = service;
38
+ this.runtime = runtime;
39
+ this.title = 'Live Dashboard';
40
+ this.catalog = {
41
+ types: [{ ...MODEL, title: { en_US: 'Live dashboards', zh_Hans: '实时看板' }, implementationKey: 'live-dashboard',
42
+ implementationTitle: { en_US: 'Live Dashboard', zh_Hans: '实时看板' }, defaultEditorKey: EDITOR }],
43
+ discover: async (scope) => {
44
+ await this.service.authorizeProject(context(scope));
45
+ const page = Number(scope.cursor ?? 1);
46
+ if (!Number.isSafeInteger(page) || page < 1)
47
+ throw new common_1.BadRequestException('DASHBOARD_INVALID_CURSOR');
48
+ const result = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).listArtifacts({ pluginName: dashboard_service_1.PLUGIN_NAME,
49
+ resourceType: 'live-dashboard', resourceIdPrefix: `project/${scope.projectId}/`, page, pageSize: 100, status: 'all' });
50
+ return { resourceIds: result.items.filter(item => !item.userId && item.projectId === scope.projectId && item.status !== 'deleted').map(item => item.id),
51
+ ...(result.items.length === 100 ? { nextCursor: String(page + 1) } : {}) };
52
+ },
53
+ describe: input => this.describe(input),
54
+ extract: async (input) => {
55
+ const saved = await this.service.open(context(input), input.sourceResourceId);
56
+ return (0, dashboard_search_1.dashboardSearchProjection)(saved.manifest, String(saved.draftRevision ?? 0));
57
+ },
58
+ create: async (input) => {
59
+ if (input.templateId)
60
+ throw new common_1.BadRequestException('DASHBOARD_TEMPLATE_UNAVAILABLE');
61
+ const ctx = context(input);
62
+ await this.service.authorizeProject(ctx, 'write');
63
+ const source = { pluginName: dashboard_service_1.PLUGIN_NAME, resourceType: 'live-dashboard', resourceId: `project/${input.projectId}/${input.requestId}` };
64
+ const existing = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).findArtifactBySource(source);
65
+ if (existing && await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).get(existing.id))
66
+ return { providerKey: exports.DASHBOARD_CONTENT_PROVIDER, resourceId: existing.id };
67
+ try {
68
+ const created = await this.service.save(ctx, { schema: 'xpert.live-dashboard', id: input.requestId, title: input.title,
69
+ bindings: [], controls: [], charts: [], tables: [] }, null, 0);
70
+ return { providerKey: exports.DASHBOARD_CONTENT_PROVIDER, resourceId: created.artifactId };
71
+ }
72
+ catch (error) {
73
+ // Another request with this create-only identity may have saved the first
74
+ // draft. Reuse it; never retry a create by overwriting the current draft.
75
+ if (!(error instanceof common_1.ConflictException))
76
+ throw error;
77
+ const winner = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).findArtifactBySource(source);
78
+ if (!winner || !await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).get(winner.id))
79
+ throw error;
80
+ await this.service.open(ctx, winner.id);
81
+ return { providerKey: exports.DASHBOARD_CONTENT_PROVIDER, resourceId: winner.id };
82
+ }
83
+ },
84
+ mutate: async (input) => {
85
+ const ctx = context(input), drafts = this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability);
86
+ if (input.change.action === 'trash' || input.change.action === 'restore') {
87
+ await this.service.authorizeProject(ctx, 'manage');
88
+ await drafts.setRecycled({ artifactId: input.sourceResourceId, expectedRevision: Number(input.expectedRevision), recycled: input.change.action === 'trash' });
89
+ }
90
+ else {
91
+ const saved = await this.service.open(ctx, input.sourceResourceId);
92
+ if (String(saved.draftRevision ?? 0) !== input.expectedRevision)
93
+ throw new common_1.ConflictException('DASHBOARD_DRAFT_CONFLICT');
94
+ if (input.change.action === 'duplicate') {
95
+ await this.service.authorizeProject(ctx, 'write');
96
+ const artifacts = this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability);
97
+ const source = { pluginName: dashboard_service_1.PLUGIN_NAME, resourceType: 'live-dashboard', resourceId: `project/${input.projectId}/${input.change.requestId}` };
98
+ const existing = await artifacts.findArtifactBySource(source);
99
+ if (existing && await drafts.get(existing.id))
100
+ return { providerKey: exports.DASHBOARD_CONTENT_PROVIDER, resourceId: existing.id };
101
+ try {
102
+ // Only the definition is copied. Data is queried again as the acting user;
103
+ // published versions, share tokens and query results are never cloned.
104
+ const copy = await this.service.save(ctx, { ...saved.manifest, id: input.change.requestId, title: input.change.title }, null, 0);
105
+ return { providerKey: exports.DASHBOARD_CONTENT_PROVIDER, resourceId: copy.artifactId };
106
+ }
107
+ catch (error) {
108
+ if (!(error instanceof common_1.ConflictException))
109
+ throw error;
110
+ const winner = await artifacts.findArtifactBySource(source);
111
+ if (!winner || !await drafts.get(winner.id))
112
+ throw error;
113
+ return { providerKey: exports.DASHBOARD_CONTENT_PROVIDER, resourceId: winner.id };
114
+ }
115
+ }
116
+ if (input.change.action !== 'rename')
117
+ throw new common_1.BadRequestException('DASHBOARD_ACTION_UNAVAILABLE');
118
+ await this.service.update(ctx, input.sourceResourceId, Number(input.expectedRevision), { title: input.change.title }, saved.baseVersionId ?? saved.versionId ?? null);
119
+ }
120
+ return { providerKey: exports.DASHBOARD_CONTENT_PROVIDER, resourceId: input.sourceResourceId };
121
+ }
122
+ };
123
+ }
124
+ async authorize(input) {
125
+ const artifact = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).getArtifact(input.resourceId);
126
+ if (!artifact.projectId || artifact.userId || artifact.pluginName !== dashboard_service_1.PLUGIN_NAME || artifact.resourceType !== 'live-dashboard')
127
+ throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
128
+ const ctx = context({ actor: input.actor, projectId: artifact.projectId });
129
+ await this.service.authorizeProject(ctx, input.operation === 'read' ? 'read' : input.operation === 'manage' ? 'manage' : 'write');
130
+ const access = (await this.runtime.require(plugin_sdk_1.ProjectAccessRuntimeCapability).listReadable({ actor: input.actor, projectIds: [artifact.projectId] }))[0];
131
+ if (!access)
132
+ throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
133
+ return { providerKey: exports.DASHBOARD_CONTENT_PROVIDER, resourceId: input.resourceId, title: artifact.title ?? '', role: access.role,
134
+ canWrite: !access.archived && ['owner', 'manager', 'editor'].includes(access.role), canManage: access.canManage };
135
+ }
136
+ async describe(input) {
137
+ await this.service.authorizeProject(context(input));
138
+ const access = (await this.runtime.require(plugin_sdk_1.ProjectAccessRuntimeCapability).listReadable({ actor: input.actor, projectIds: [input.projectId] }))[0];
139
+ if (!access)
140
+ throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
141
+ const artifact = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).getArtifact(input.sourceResourceId);
142
+ if (artifact.projectId !== input.projectId || artifact.status === 'deleted')
143
+ throw new common_1.ForbiddenException('DASHBOARD_ACCESS_DENIED');
144
+ const draft = await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).get(input.sourceResourceId, { includeArchived: true });
145
+ if (!draft)
146
+ throw new common_1.BadRequestException('DASHBOARD_VERSION_UNAVAILABLE');
147
+ const deleted = artifact.status === 'archived';
148
+ const saved = !deleted ? await this.service.open(context(input), input.sourceResourceId) : null;
149
+ return { ...MODEL, offlineAccess: 'project', projectId: input.projectId, title: draft.title ?? artifact.title ?? '', revision: String(draft.revision),
150
+ updatedAt: new Date(draft.updatedAt ?? artifact.updatedAt ?? 0).toISOString(), deleted, defaultEditorKey: EDITOR,
151
+ allowedActions: [...(!deleted ? ['rename', 'duplicate'] : []), ...(access.canManage ? [deleted ? 'restore' : 'trash'] : [])],
152
+ ...(saved ? { preview: { kind: 'outline', revision: String(draft.revision), lines: [saved.manifest.title, ...saved.manifest.charts.map(chart => chart.title), ...saved.manifest.tables.map(table => table.title)].slice(0, 6).map(line => line.slice(0, 160)) } } : {}),
153
+ sourceSync: { status: 'synced', revision: String(draft.revision) },
154
+ source: draft.workspaceFileRef?.workspacePath ? { kind: 'file', path: draft.workspaceFileRef.workspacePath } : { kind: 'managed' } };
155
+ }
156
+ };
157
+ exports.DashboardContentProvider = DashboardContentProvider;
158
+ exports.DashboardContentProvider = DashboardContentProvider = __decorate([
159
+ (0, common_1.Injectable)(),
160
+ (0, plugin_sdk_1.ContentResourceProvider)(exports.DASHBOARD_CONTENT_PROVIDER),
161
+ __param(1, (0, common_1.Inject)(plugin_sdk_1.XPERT_RUNTIME_CAPABILITIES_TOKEN)),
162
+ __metadata("design:paramtypes", [dashboard_service_1.DashboardService, Object])
163
+ ], DashboardContentProvider);
@@ -1,5 +1,6 @@
1
- /** Self-contained for hosts that do not load the plugin's icon font. */
1
+ /** Rising data columns with a detached live signal, legible without host fonts or CSS. */
2
2
  export declare const dashboardIcon: {
3
3
  type: "svg";
4
+ alt: string;
4
5
  value: string;
5
6
  };
@@ -1,8 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.dashboardIcon = void 0;
4
- /** Self-contained for hosts that do not load the plugin's icon font. */
4
+ const palette = { tile: '#EAF0FA', chart: '#3659A2', live: '#178577' };
5
+ /** Rising data columns with a detached live signal, legible without host fonts or CSS. */
5
6
  exports.dashboardIcon = {
6
7
  type: 'svg',
7
- value: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="3"/><path d="M7 16v-4m5 4V7m5 9v-6"/></svg>'
8
+ alt: 'Live dashboards',
9
+ value: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
10
+ <rect x="24" y="24" width="208" height="208" rx="40" fill="${palette.tile}"/>
11
+ <rect x="66" y="144" width="26" height="44" rx="7" fill="${palette.chart}"/>
12
+ <rect x="114" y="112" width="26" height="76" rx="7" fill="${palette.chart}"/>
13
+ <rect x="162" y="96" width="26" height="92" rx="7" fill="${palette.live}"/>
14
+ <circle cx="175" cy="72" r="13" fill="${palette.live}"/>
15
+ </svg>`
8
16
  };
@@ -29,26 +29,46 @@ export declare function dashboardNavigationTarget(context: XpertResolvedViewHost
29
29
  export declare const dashboardNavigationSchema: z.ZodEffects<z.ZodObject<{
30
30
  selectionId: z.ZodOptional<z.ZodString>;
31
31
  parameters: z.ZodOptional<z.ZodObject<{
32
+ contentProvider: z.ZodOptional<z.ZodString>;
33
+ contentId: z.ZodOptional<z.ZodString>;
34
+ contentTitle: z.ZodOptional<z.ZodString>;
35
+ contentEditor: z.ZodOptional<z.ZodString>;
32
36
  artifactId: z.ZodOptional<z.ZodString>;
33
37
  versionId: z.ZodOptional<z.ZodString>;
34
38
  }, "strict", z.ZodTypeAny, {
35
39
  artifactId?: string | undefined;
36
40
  versionId?: string | undefined;
41
+ contentProvider?: string | undefined;
42
+ contentId?: string | undefined;
43
+ contentTitle?: string | undefined;
44
+ contentEditor?: string | undefined;
37
45
  }, {
38
46
  artifactId?: string | undefined;
39
47
  versionId?: string | undefined;
48
+ contentProvider?: string | undefined;
49
+ contentId?: string | undefined;
50
+ contentTitle?: string | undefined;
51
+ contentEditor?: string | undefined;
40
52
  }>>;
41
53
  }, "strip", z.ZodTypeAny, {
42
54
  selectionId?: string | undefined;
43
55
  parameters?: {
44
56
  artifactId?: string | undefined;
45
57
  versionId?: string | undefined;
58
+ contentProvider?: string | undefined;
59
+ contentId?: string | undefined;
60
+ contentTitle?: string | undefined;
61
+ contentEditor?: string | undefined;
46
62
  } | undefined;
47
63
  }, {
48
64
  selectionId?: string | undefined;
49
65
  parameters?: {
50
66
  artifactId?: string | undefined;
51
67
  versionId?: string | undefined;
68
+ contentProvider?: string | undefined;
69
+ contentId?: string | undefined;
70
+ contentTitle?: string | undefined;
71
+ contentEditor?: string | undefined;
52
72
  } | undefined;
53
73
  }>, {
54
74
  artifactId: string | undefined;
@@ -58,6 +78,10 @@ export declare const dashboardNavigationSchema: z.ZodEffects<z.ZodObject<{
58
78
  parameters?: {
59
79
  artifactId?: string | undefined;
60
80
  versionId?: string | undefined;
81
+ contentProvider?: string | undefined;
82
+ contentId?: string | undefined;
83
+ contentTitle?: string | undefined;
84
+ contentEditor?: string | undefined;
61
85
  } | undefined;
62
86
  }>;
63
87
  export type DashboardNavigation = z.infer<typeof dashboardNavigationSchema>;
@@ -20,7 +20,7 @@ function dashboardNavigationTarget(context) {
20
20
  }
21
21
  exports.dashboardNavigationSchema = v3_1.z.object({
22
22
  selectionId: v3_1.z.string().uuid().optional(),
23
- parameters: v3_1.z.object({ artifactId: v3_1.z.string().uuid().optional(), versionId: v3_1.z.string().uuid().optional() }).strict().optional()
23
+ parameters: v3_1.z.object({ contentProvider: v3_1.z.string().optional(), contentId: v3_1.z.string().uuid().optional(), contentTitle: v3_1.z.string().optional(), contentEditor: v3_1.z.string().optional(), artifactId: v3_1.z.string().uuid().optional(), versionId: v3_1.z.string().uuid().optional() }).strict().optional()
24
24
  }).transform((query, ctx) => {
25
25
  const artifactId = query.selectionId ?? query.parameters?.artifactId;
26
26
  const versionId = query.parameters?.versionId;
@@ -6,3 +6,14 @@ export declare function validateControls(manifest: DashboardManifest, input: Rec
6
6
  };
7
7
  export declare function queryDashboardBinding(runtime: RuntimeCapabilityRegistry, actor: DataSourceActor, manifest: DashboardManifest, bindingId: string, input: Record<string, AnalyticsValue>): Promise<AnalyticalDataset>;
8
8
  export declare function queryDashboard(runtime: RuntimeCapabilityRegistry, actor: DataSourceActor, manifest: DashboardManifest, input: Record<string, AnalyticsValue>): Promise<AnalyticalDataset[]>;
9
+ /** Read-only rendering can show independent successful bindings. Saving/publishing still
10
+ * uses queryDashboard and requires every binding to validate. Never return native error
11
+ * details, which may contain private source configuration or query text. */
12
+ export declare function queryDashboardPartial(runtime: RuntimeCapabilityRegistry, actor: DataSourceActor, manifest: DashboardManifest, input: Record<string, AnalyticsValue>): Promise<{
13
+ datasets: AnalyticalDataset[];
14
+ failures: {
15
+ bindingId: string;
16
+ datasetId: string;
17
+ reason: "unavailable";
18
+ }[];
19
+ }>;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateControls = validateControls;
4
4
  exports.queryDashboardBinding = queryDashboardBinding;
5
5
  exports.queryDashboard = queryDashboard;
6
+ exports.queryDashboardPartial = queryDashboardPartial;
6
7
  const common_1 = require("@nestjs/common");
7
8
  const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
8
9
  function validateControls(manifest, input) {
@@ -37,3 +38,22 @@ async function queryDashboard(runtime, actor, manifest, input) {
37
38
  throw new common_1.BadRequestException('DASHBOARD_RESULT_TOO_LARGE');
38
39
  return datasets;
39
40
  }
41
+ /** Read-only rendering can show independent successful bindings. Saving/publishing still
42
+ * uses queryDashboard and requires every binding to validate. Never return native error
43
+ * details, which may contain private source configuration or query text. */
44
+ async function queryDashboardPartial(runtime, actor, manifest, input) {
45
+ validateControls(manifest, input);
46
+ const datasets = [];
47
+ const failures = [];
48
+ for (const binding of manifest.bindings) {
49
+ try {
50
+ datasets.push(await queryDashboardBinding(runtime, actor, manifest, binding.id, input));
51
+ }
52
+ catch {
53
+ failures.push({ bindingId: binding.id, datasetId: binding.output.datasetId, reason: 'unavailable' });
54
+ }
55
+ }
56
+ if (Buffer.byteLength(JSON.stringify(datasets)) > 3_000_000)
57
+ throw new common_1.BadRequestException('DASHBOARD_RESULT_TOO_LARGE');
58
+ return { datasets, failures };
59
+ }
@@ -0,0 +1,5 @@
1
+ import type { ContentSearchProjection } from '@xpert-ai/contracts';
2
+ import type { DashboardManifest } from './dashboard.schema';
3
+ /** Explicit allowlist: no Binding source, parameter values, HTML/script, credentials
4
+ * or queried rows cross the shared knowledge boundary. Data access stays viewer-specific. */
5
+ export declare function dashboardSearchProjection(manifest: DashboardManifest, revision: string): ContentSearchProjection;