@xpert-ai/plugin-analytics-data-xpert 0.0.0-stage → 0.1.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/README.md CHANGED
@@ -1,3 +1,19 @@
1
- # Temporary Holding Version
1
+ # data-xpert analytics provider
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ System plugin `@xpert-ai/plugin-analytics-data-xpert`; provider `data-xpert.ontology`, operation `action`, version 1.
4
+
5
+ Contributes the data-xpert DataSource type through DataSourceType sync. Server configuration: baseUrl (including /api), audience, assistantCode, assistantXpertId, workspaceId. No database runner is required. Platform code has no ontology dependency.
6
+
7
+ ```json
8
+ {"resourceId":"datax-semantic-model-<published-model-id>","actionTypeCode":"sql.query_cube_slice","target":{"entityTypeCode":"semantic_cube","entityRef":"<published-model-id>::Sales"},"params":{"dimensions":["Region"],"measures":["Amount"],"filters":[{"field":"Region","op":"contains","value":"west"}],"limit":100},"parameterPaths":{"region":["filters","0","value"]}}
9
+ ```
10
+
11
+ This is a SQL semantic-model Action example; it is not the contract for other ontology adapters. Replace placeholders and use exact targets/fields returned by published-resource discovery. Each registered Action defines its own params shape. Array path segments use canonical zero-based indices. Bindings cannot supply actor, credentials or URL. Paths replace entire existing scalar values, never SQL/MDX/OData fragments. Unknown/prototype paths fail.
12
+
13
+ The provider obtains the current actor's token via ActorTokenRuntimeFactoryCapability.createScopedApi(context.actor), then POSTs to /agent-workbench/analytics/query. data-xpert validates OIDC, Assistant/resource binding, explicit viewer policy and the existing Live Artifact read-only allowlist, bypassing Action result caches. Typed rows/columns and audit receipts are projected into the canonical Dataset.
14
+
15
+ Run `corepack pnpm exec nx run @xpert-ai/plugin-analytics-data-xpert:test` with the current platform SDK. Real integration requires the corresponding data-xpert endpoint, published resources, Assistant bindings, explicit viewer policy and working xpert-pro OIDC. Mocked identity tests do not establish SSO acceptance.
16
+
17
+ Discovery preserves each adapter's `definition` (identity, version, native metadata and instructions) and each Action's input schema. Semantic Model, OData and future registered read-only adapters share this transport without sharing query syntax.
18
+
19
+ See [architecture and Binding usage](docs/index.mdx) and [compatibility and verification](docs/verification.mdx).
@@ -0,0 +1,3 @@
1
+ import type { XpertPlugin } from '@xpert-ai/plugin-sdk';
2
+ declare const plugin: XpertPlugin;
3
+ export default plugin;
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_fs_1 = require("node:fs");
4
+ const node_path_1 = require("node:path");
5
+ const plugin_1 = require("./lib/plugin");
6
+ const packageJson = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '../package.json'), 'utf8'));
7
+ const plugin = {
8
+ meta: { name: packageJson.name, version: packageJson.version, category: 'database', displayName: 'data-xpert Analytics', description: 'data-xpert Analytics', author: 'XpertAI', level: packageJson.xpert.plugin.level, artifactNamespace: packageJson.xpert.plugin.artifactNamespace },
9
+ register: () => ({ module: plugin_1.AnalyticsDataXpertPlugin })
10
+ };
11
+ exports.default = plugin;
@@ -0,0 +1,61 @@
1
+ import { z } from 'zod/v3';
2
+ import type { AnalyticsDiscoveryResult } from '@xpert-ai/contracts';
3
+ /** Private wire contract for data-xpert discovery; actor and Assistant scope are server-owned. */
4
+ export declare const dataXpertDiscoveryRequestSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
5
+ kind: z.ZodLiteral<"resources">;
6
+ after: z.ZodOptional<z.ZodString>;
7
+ }, "strict", z.ZodTypeAny, {
8
+ kind: "resources";
9
+ after?: string | undefined;
10
+ }, {
11
+ kind: "resources";
12
+ after?: string | undefined;
13
+ }>, z.ZodObject<{
14
+ kind: z.ZodLiteral<"entities">;
15
+ resourceId: z.ZodString;
16
+ entityTypeCode: z.ZodOptional<z.ZodString>;
17
+ query: z.ZodOptional<z.ZodString>;
18
+ }, "strict", z.ZodTypeAny, {
19
+ kind: "entities";
20
+ resourceId: string;
21
+ entityTypeCode?: string | undefined;
22
+ query?: string | undefined;
23
+ }, {
24
+ kind: "entities";
25
+ resourceId: string;
26
+ entityTypeCode?: string | undefined;
27
+ query?: string | undefined;
28
+ }>, z.ZodObject<{
29
+ kind: z.ZodLiteral<"actions">;
30
+ resourceId: z.ZodString;
31
+ target: z.ZodObject<{
32
+ entityTypeCode: z.ZodString;
33
+ entityRef: z.ZodString;
34
+ }, "strict", z.ZodTypeAny, {
35
+ entityTypeCode: string;
36
+ entityRef: string;
37
+ }, {
38
+ entityTypeCode: string;
39
+ entityRef: string;
40
+ }>;
41
+ }, "strict", z.ZodTypeAny, {
42
+ kind: "actions";
43
+ resourceId: string;
44
+ target: {
45
+ entityTypeCode: string;
46
+ entityRef: string;
47
+ };
48
+ }, {
49
+ kind: "actions";
50
+ resourceId: string;
51
+ target: {
52
+ entityTypeCode: string;
53
+ entityRef: string;
54
+ };
55
+ }>]>;
56
+ /**
57
+ * All ontology adapters share this envelope, not a native query language.
58
+ * Preserve definition.adapterId/contractVersion/metadata/instructions and Action inputSchema
59
+ * from data-xpert. New adapters must not require branches in this provider or the renderer.
60
+ */
61
+ export declare const dataXpertQueryContract: AnalyticsDiscoveryResult['queryContract'];
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataXpertQueryContract = exports.dataXpertDiscoveryRequestSchema = void 0;
4
+ const v3_1 = require("zod/v3");
5
+ /** Private wire contract for data-xpert discovery; actor and Assistant scope are server-owned. */
6
+ exports.dataXpertDiscoveryRequestSchema = v3_1.z.discriminatedUnion('kind', [
7
+ v3_1.z.object({ kind: v3_1.z.literal('resources'), after: v3_1.z.string().min(1).max(160).optional() }).strict(),
8
+ v3_1.z.object({
9
+ kind: v3_1.z.literal('entities'), resourceId: v3_1.z.string().min(1).max(160),
10
+ entityTypeCode: v3_1.z.string().min(1).max(160).optional(), query: v3_1.z.string().max(256).optional()
11
+ }).strict(),
12
+ v3_1.z.object({
13
+ kind: v3_1.z.literal('actions'), resourceId: v3_1.z.string().min(1).max(160),
14
+ target: v3_1.z.object({ entityTypeCode: v3_1.z.string().min(1).max(160), entityRef: v3_1.z.string().min(1).max(512) }).strict()
15
+ }).strict()
16
+ ]);
17
+ /**
18
+ * All ontology adapters share this envelope, not a native query language.
19
+ * Preserve definition.adapterId/contractVersion/metadata/instructions and Action inputSchema
20
+ * from data-xpert. New adapters must not require branches in this provider or the renderer.
21
+ */
22
+ exports.dataXpertQueryContract = {
23
+ operation: 'action', contractVersion: 1,
24
+ payloadSchema: {
25
+ type: 'object', additionalProperties: false,
26
+ required: ['resourceId', 'actionTypeCode', 'target', 'params', 'parameterPaths'],
27
+ properties: {
28
+ resourceId: { type: 'string', minLength: 1, maxLength: 160 },
29
+ actionTypeCode: { type: 'string', minLength: 1, maxLength: 160 },
30
+ target: {
31
+ type: 'object', additionalProperties: false,
32
+ properties: {
33
+ entityId: { type: 'string', minLength: 1, maxLength: 256 },
34
+ entityTypeCode: { type: 'string', minLength: 1, maxLength: 256 },
35
+ entityRef: { type: 'string', minLength: 1, maxLength: 512 }
36
+ },
37
+ anyOf: [{ required: ['entityId'] }, { required: ['entityRef'] }]
38
+ },
39
+ params: { type: 'object' },
40
+ parameterPaths: { type: 'object', additionalProperties: {
41
+ type: 'array', minItems: 1, maxItems: 8, items: { type: 'string', minLength: 1 }
42
+ } }
43
+ }
44
+ },
45
+ instructions: [
46
+ 'Follow inspectRequest: resources -> entities -> actions. Continue nextRequest even when the visible resource page is empty. Entity discovery returns top 20 matches; narrow query/entityTypeCode if needed.',
47
+ 'Entity metadata.definition contains adapterId, contractVersion, metadata and instructions. Follow that adapter-specific definition and the discovered Action inputSchema/inputHint. Preserve canonical targets, resource IDs and exact field names. Do not infer adapter types from names or apply one adapter\'s query, filter or time rules to another.',
48
+ 'Use only published metadata and authorized previews to establish business metrics, time axes, supported filters and complete aggregation. Missing metadata does not authorize guessing; obtain sufficient evidence before generating a time filter.',
49
+ 'Use parameterPaths relative to params to replace an existing scalar value, for example {region:["filters","0","value"]}. Paths must be unique. Never interpolate values into query text. Controls supply scalar values; arrays remain static unless the native Action explicitly supports another declared scalar mapping. Null handling and filter operators are defined by the Action schema and adapter instructions.',
50
+ 'Only read-only Actions allowed by the current viewer policy are returned. Map output sourceField to returned column keys (or an explicit semanticRef). Numeric JSON cells require float64; decimal/int64 require exact strings. Preview to verify types, grain and completeness before binding charts. A partial page is not a complete business aggregate.'
51
+ ].join(' ')
52
+ };
@@ -0,0 +1,2 @@
1
+ export declare class AnalyticsDataXpertPlugin {
2
+ }
@@ -0,0 +1,17 @@
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.AnalyticsDataXpertPlugin = void 0;
10
+ const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
11
+ const provider_1 = require("./provider");
12
+ let AnalyticsDataXpertPlugin = class AnalyticsDataXpertPlugin {
13
+ };
14
+ exports.AnalyticsDataXpertPlugin = AnalyticsDataXpertPlugin;
15
+ exports.AnalyticsDataXpertPlugin = AnalyticsDataXpertPlugin = __decorate([
16
+ (0, plugin_sdk_1.XpertServerPlugin)({ providers: [provider_1.DataXpertAnalyticsProvider] })
17
+ ], AnalyticsDataXpertPlugin);
@@ -0,0 +1,61 @@
1
+ import { z } from 'zod/v3';
2
+ import type { AnalyticsBinding, AnalyticsProviderDescriptor, AnalyticsValue, JSONValue, AnalyticsDiscoveryResult } from '@xpert-ai/contracts';
3
+ import { type AnalyticsQueryProvider, type AnalyticsProviderContext, type AnalyticsProviderResult, type RuntimeCapabilityRegistry } from '@xpert-ai/plugin-sdk';
4
+ /** Native Action arguments stay plugin-owned; the host only interprets Dataset/Binding. */
5
+ export declare const dataXpertPayloadSchema: z.ZodObject<{
6
+ resourceId: z.ZodString;
7
+ actionTypeCode: z.ZodString;
8
+ target: z.ZodEffects<z.ZodObject<{
9
+ entityId: z.ZodOptional<z.ZodString>;
10
+ entityTypeCode: z.ZodOptional<z.ZodString>;
11
+ entityRef: z.ZodOptional<z.ZodString>;
12
+ }, "strict", z.ZodTypeAny, {
13
+ entityTypeCode?: string | undefined;
14
+ entityRef?: string | undefined;
15
+ entityId?: string | undefined;
16
+ }, {
17
+ entityTypeCode?: string | undefined;
18
+ entityRef?: string | undefined;
19
+ entityId?: string | undefined;
20
+ }>, {
21
+ entityTypeCode?: string | undefined;
22
+ entityRef?: string | undefined;
23
+ entityId?: string | undefined;
24
+ }, {
25
+ entityTypeCode?: string | undefined;
26
+ entityRef?: string | undefined;
27
+ entityId?: string | undefined;
28
+ }>;
29
+ params: z.ZodRecord<z.ZodString, z.ZodType<JSONValue, z.ZodTypeDef, JSONValue>>;
30
+ parameterPaths: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
31
+ }, "strict", z.ZodTypeAny, {
32
+ params: Record<string, JSONValue>;
33
+ resourceId: string;
34
+ target: {
35
+ entityTypeCode?: string | undefined;
36
+ entityRef?: string | undefined;
37
+ entityId?: string | undefined;
38
+ };
39
+ actionTypeCode: string;
40
+ parameterPaths: Record<string, string[]>;
41
+ }, {
42
+ params: Record<string, JSONValue>;
43
+ resourceId: string;
44
+ target: {
45
+ entityTypeCode?: string | undefined;
46
+ entityRef?: string | undefined;
47
+ entityId?: string | undefined;
48
+ };
49
+ actionTypeCode: string;
50
+ parameterPaths?: Record<string, string[]> | undefined;
51
+ }>;
52
+ export declare class DataXpertAnalyticsProvider implements AnalyticsQueryProvider {
53
+ private readonly runtime;
54
+ readonly descriptor: AnalyticsProviderDescriptor;
55
+ constructor(runtime: RuntimeCapabilityRegistry);
56
+ discover(request: JSONValue, context: AnalyticsProviderContext): Promise<AnalyticsDiscoveryResult>;
57
+ private request;
58
+ execute(binding: AnalyticsBinding, parameters: Record<string, AnalyticsValue>, context: AnalyticsProviderContext): Promise<AnalyticsProviderResult>;
59
+ }
60
+ /** Typed paths replace whole parameter values, never splice text into SQL/MDX/OData. */
61
+ export declare function bindParameters(params: Record<string, JSONValue>, paths: Record<string, string[]>, parameters: Record<string, AnalyticsValue>): Record<string, JSONValue>;
@@ -0,0 +1,164 @@
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.DataXpertAnalyticsProvider = exports.dataXpertPayloadSchema = void 0;
16
+ exports.bindParameters = bindParameters;
17
+ const common_1 = require("@nestjs/common");
18
+ const v3_1 = require("zod/v3");
19
+ const discovery_1 = require("./discovery");
20
+ const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
21
+ const scalar = v3_1.z.union([v3_1.z.string(), v3_1.z.number().finite(), v3_1.z.boolean(), v3_1.z.null()]);
22
+ const json = v3_1.z.lazy(() => v3_1.z.union([scalar, v3_1.z.array(json), v3_1.z.record(json)]));
23
+ /** Native Action arguments stay plugin-owned; the host only interprets Dataset/Binding. */
24
+ exports.dataXpertPayloadSchema = v3_1.z.object({
25
+ resourceId: v3_1.z.string().min(1).max(160), actionTypeCode: v3_1.z.string().min(1).max(160),
26
+ target: v3_1.z.object({
27
+ entityId: v3_1.z.string().trim().min(1).max(256).optional(),
28
+ entityTypeCode: v3_1.z.string().trim().min(1).max(256).optional(),
29
+ entityRef: v3_1.z.string().trim().min(1).max(512).optional()
30
+ }).strict().refine(target => Boolean(target.entityId || target.entityRef), 'Target reference is required'),
31
+ params: v3_1.z.record(json),
32
+ parameterPaths: v3_1.z.record(v3_1.z.array(v3_1.z.string().min(1)).min(1).max(8)).default({})
33
+ }).strict();
34
+ /** Trusted DataSource configuration; never merge it with payloads, controls or discovery requests. */
35
+ const optionsSchema = v3_1.z.object({
36
+ baseUrl: v3_1.z.string().url(), audience: v3_1.z.string().min(1),
37
+ assistantCode: v3_1.z.string().min(1), assistantXpertId: v3_1.z.string().uuid(), workspaceId: v3_1.z.string().uuid()
38
+ }).strict();
39
+ const responseSchema = v3_1.z.object({
40
+ effect: v3_1.z.object({ version: v3_1.z.literal(2), kind: v3_1.z.enum(['rows', 'series', 'entity']),
41
+ columns: v3_1.z.array(v3_1.z.object({ key: v3_1.z.string(), semanticRef: v3_1.z.string().optional() })),
42
+ rows: v3_1.z.array(v3_1.z.record(scalar)),
43
+ summary: v3_1.z.object({ rowCount: v3_1.z.number().int().nonnegative(), returnedRowCount: v3_1.z.number().int().nonnegative(), truncated: v3_1.z.boolean() }) }),
44
+ fetchedAt: v3_1.z.string().datetime(), executionId: v3_1.z.string(), auditRef: v3_1.z.string(), fromCache: v3_1.z.literal(false)
45
+ });
46
+ let DataXpertAnalyticsProvider = class DataXpertAnalyticsProvider {
47
+ constructor(runtime) {
48
+ this.runtime = runtime;
49
+ this.descriptor = {
50
+ id: 'data-xpert.ontology', name: 'data-xpert Ontology', dataSourceTypes: ['data-xpert'],
51
+ operations: [{ name: 'action', contractVersion: 1, readOnly: true }],
52
+ connectionType: { type: 'data-xpert', name: 'data-xpert', configuration: {
53
+ type: 'object', properties: { baseUrl: { type: 'string', title: 'API URL' }, audience: { type: 'string', title: 'OIDC audience' },
54
+ assistantCode: { type: 'string', title: 'Assistant code' }, assistantXpertId: { type: 'string', title: 'Assistant Xpert ID' },
55
+ workspaceId: { type: 'string', title: 'Workspace ID' } },
56
+ required: ['baseUrl', 'audience', 'assistantCode', 'assistantXpertId', 'workspaceId'], additionalProperties: false
57
+ } }
58
+ };
59
+ }
60
+ async discover(request, context) {
61
+ const empty = request && typeof request === 'object' && !Array.isArray(request) && Object.keys(request).length === 0;
62
+ const parsed = discovery_1.dataXpertDiscoveryRequestSchema.parse(empty ? { kind: 'resources' } : request);
63
+ const response = await this.request('discover', context, { request: parsed });
64
+ const result = v3_1.z.object({ items: v3_1.z.array(v3_1.z.unknown()),
65
+ nextRequest: json.optional() }).strict().parse(await boundedJson(response));
66
+ return plugin_sdk_1.analyticsDiscoveryResultSchema.parse({ ...result, schemaVersion: 1, queryContract: discovery_1.dataXpertQueryContract });
67
+ }
68
+ async request(endpoint, context, body) {
69
+ context.signal.throwIfAborted();
70
+ const options = optionsSchema.parse(context.options);
71
+ const base = new URL(options.baseUrl);
72
+ if (!['http:', 'https:'].includes(base.protocol) || base.username || base.password || base.search || base.hash)
73
+ throw new Error('DATAX_URL_INVALID');
74
+ const token = await this.runtime.require(plugin_sdk_1.ActorTokenRuntimeFactoryCapability).createScopedApi(context.actor).getToken({ audience: options.audience });
75
+ context.signal.throwIfAborted();
76
+ const response = await fetch(`${base.href.replace(/\/$/, '')}/agent-workbench/analytics/${endpoint}`, {
77
+ method: 'POST', redirect: 'error', signal: context.signal,
78
+ headers: { Authorization: `Bearer ${token.token}`, 'Content-Type': 'application/json', 'organization-id': context.actor.organizationId },
79
+ body: JSON.stringify({ assistant: { code: options.assistantCode, xpertId: options.assistantXpertId, workspaceId: options.workspaceId }, ...body })
80
+ });
81
+ if (!response.ok) {
82
+ // Do not retain an unconsumed upstream error stream or expose its potentially sensitive body.
83
+ await response.body?.cancel().catch(() => undefined);
84
+ throw new Error(`DATAX_QUERY_HTTP_${response.status}`);
85
+ }
86
+ return response;
87
+ }
88
+ async execute(binding, parameters, context) {
89
+ const payload = exports.dataXpertPayloadSchema.parse(binding.payload);
90
+ const response = await this.request('query', context, {
91
+ binding: { id: binding.id, resourceId: payload.resourceId, actionTypeCode: payload.actionTypeCode, target: payload.target,
92
+ params: bindParameters(payload.params, payload.parameterPaths, parameters) }, maxRows: context.maxRows
93
+ });
94
+ const result = responseSchema.parse(await boundedJson(response));
95
+ return { columns: result.effect.columns, rows: result.effect.rows,
96
+ completeness: { returnedRows: result.effect.summary.returnedRowCount, totalRows: result.effect.summary.rowCount,
97
+ totalAccuracy: result.effect.summary.truncated ? 'unknown' : 'exact', scope: result.effect.summary.truncated ? 'page' : 'full_query_result',
98
+ truncated: result.effect.summary.truncated }, sourceFetchedAt: result.fetchedAt,
99
+ upstreamExecutionId: result.executionId, auditRef: result.auditRef };
100
+ }
101
+ };
102
+ exports.DataXpertAnalyticsProvider = DataXpertAnalyticsProvider;
103
+ exports.DataXpertAnalyticsProvider = DataXpertAnalyticsProvider = __decorate([
104
+ (0, plugin_sdk_1.AnalyticsProvider)('data-xpert.ontology'),
105
+ __param(0, (0, common_1.Inject)(plugin_sdk_1.XPERT_RUNTIME_CAPABILITIES_TOKEN)),
106
+ __metadata("design:paramtypes", [Object])
107
+ ], DataXpertAnalyticsProvider);
108
+ /** Typed paths replace whole parameter values, never splice text into SQL/MDX/OData. */
109
+ function bindParameters(params, paths, parameters) {
110
+ const result = structuredClone(params);
111
+ if (Object.keys(parameters).some((name) => !Object.hasOwn(paths, name)) || Object.keys(paths).some((name) => !Object.hasOwn(parameters, name)))
112
+ throw new Error('DATAX_PARAMETER_MAPPING_INVALID');
113
+ if (new Set(Object.values(paths).map((path) => JSON.stringify(path))).size !== Object.keys(paths).length)
114
+ throw new Error('DATAX_PARAMETER_MAPPING_INVALID');
115
+ for (const [name, path] of Object.entries(paths)) {
116
+ let current = result;
117
+ for (const [index, key] of path.entries()) {
118
+ if (['__proto__', 'prototype', 'constructor'].includes(key) || !current || typeof current !== 'object' || !Object.hasOwn(current, key))
119
+ throw new common_1.BadRequestException('DATAX_PARAMETER_PATH_INVALID: parameterPaths are relative to params. Omit the leading params segment and target an existing scalar, for example ["slicers","1","value"].');
120
+ if (Array.isArray(current)) {
121
+ if (!/^(0|[1-9]\d*)$/.test(key) || Number(key) >= current.length)
122
+ throw new Error('DATAX_PARAMETER_PATH_INVALID');
123
+ const position = Number(key);
124
+ if (index === path.length - 1) {
125
+ if (current[position] !== null && typeof current[position] === 'object')
126
+ throw new Error('DATAX_PARAMETER_PATH_NOT_SCALAR');
127
+ current[position] = parameters[name];
128
+ }
129
+ else
130
+ current = current[position];
131
+ }
132
+ else if (index === path.length - 1) {
133
+ if (current[key] !== null && typeof current[key] === 'object')
134
+ throw new Error('DATAX_PARAMETER_PATH_NOT_SCALAR');
135
+ current[key] = parameters[name];
136
+ }
137
+ else
138
+ current = current[key];
139
+ }
140
+ }
141
+ return result;
142
+ }
143
+ async function boundedJson(response) {
144
+ if (!response.body)
145
+ throw new Error('DATAX_EMPTY_RESPONSE');
146
+ const reader = response.body.getReader();
147
+ const chunks = [];
148
+ let size = 0;
149
+ try {
150
+ for (;;) {
151
+ const chunk = await reader.read();
152
+ if (chunk.done)
153
+ break;
154
+ size += chunk.value.byteLength;
155
+ if (size > 2_500_000)
156
+ throw new Error('DATAX_RESPONSE_TOO_LARGE');
157
+ chunks.push(chunk.value);
158
+ }
159
+ return JSON.parse(Buffer.concat(chunks).toString('utf8'));
160
+ }
161
+ finally {
162
+ await reader.cancel();
163
+ }
164
+ }
package/docs/AGENTS.md ADDED
@@ -0,0 +1,11 @@
1
+ # data-xpert analytics documentation
2
+
3
+ This Mintlify site documents the analytics provider, not ontology authoring or deployment automation.
4
+ Use MDX with YAML frontmatter and keep `docs.json` navigation synchronized.
5
+
6
+ - Distinguish Xpert Dataset/Binding, this provider's transport, and data-xpert's ontology adapters.
7
+ - Keep native query semantics in adapter-owned `definition` and Action `inputSchema`.
8
+ - Explain current-viewer Actor Token and Assistant/resource authorization without exposing credentials.
9
+ - Do not claim that a registered ontology adapter automatically permits a read-only Action.
10
+ - Distinguish HTTP fixtures and lifecycle mocks from real SSO and dashboard acceptance.
11
+ - Keep machine paths, private receipts and tokens out of the package.
package/docs/docs.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://mintlify.com/docs.json",
3
+ "theme": "maple",
4
+ "name": "data-xpert Analytics",
5
+ "colors": {
6
+ "primary": "#0f766e",
7
+ "light": "#14b8a6",
8
+ "dark": "#115e59"
9
+ },
10
+ "navigation": {
11
+ "tabs": [
12
+ {
13
+ "tab": "Documentation",
14
+ "groups": [
15
+ {
16
+ "group": "Provider",
17
+ "pages": [
18
+ "index",
19
+ "verification"
20
+ ]
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+ }
26
+ }
package/docs/index.mdx ADDED
@@ -0,0 +1,100 @@
1
+ ---
2
+ title: "data-xpert analytics provider"
3
+ description: "Adapt governed ontology Actions to Xpert Dataset/Binding without coupling the platform to native query languages."
4
+ ---
5
+
6
+ ## Responsibilities
7
+
8
+ Xpert owns the shared Dataset/Binding protocol, current actor, source configuration,
9
+ parameter validation, execution budget and Artifact persistence. This system plugin registers
10
+ `data-xpert.ontology`, operation `action`, contract version `1`, and the `data-xpert`
11
+ DataSource type. It converts declared scalar parameters into native Action arguments and
12
+ maps canonical rows, completeness and audit receipts back to the shared Dataset path.
13
+
14
+ Data-xpert owns published resource discovery, ontology adapter metadata, Action input
15
+ schemas, Assistant/resource access, explicit viewer policy and registered read-only execution.
16
+ No database runner or ontology-specific branch is added to the Xpert platform.
17
+
18
+ The provider ID is a stable Binding reference; package ownership uses the
19
+ `analytics_data_xpert` artifact namespace. Changing the ID requires saved-Binding migration.
20
+
21
+ ## Server configuration and identity
22
+
23
+ Configure `baseUrl` (including `/api`), `audience`, `assistantCode`, `assistantXpertId`
24
+ and `workspaceId` on the platform DataSource. The Assistant and workspace must already
25
+ have the intended published resources and viewer policy in data-xpert.
26
+
27
+ The provider gets a short-lived token through
28
+ `ActorTokenRuntimeFactoryCapability.createScopedApi(context.actor)`. Every request sends
29
+ that token and the current actor's `organization-id` header. Neither the Agent's Binding nor
30
+ the HTML iframe supplies a token, actor, API URL or Assistant scope.
31
+
32
+ Only HTTP(S) base URLs without embedded credentials, query or fragment are accepted.
33
+ Redirects are rejected. The host abort signal propagates through fetch and response streaming;
34
+ a cancellation before or during token acquisition prevents a subsequent HTTP call.
35
+
36
+ ## Discovery across ontology adapters
37
+
38
+ Start with `{}` or `{ "kind": "resources" }`. Follow each `inspectRequest` from resources
39
+ to entities and then Actions. Follow `nextRequest` even if authorization filtered the entire
40
+ page. Entity results are limited to 20 matches; narrow `query` or `entityTypeCode` as needed.
41
+
42
+ Entity metadata carries this adapter-owned envelope:
43
+
44
+ ```json
45
+ {
46
+ "definition": {
47
+ "adapterId": "sap_odata_api",
48
+ "contractVersion": 1,
49
+ "metadata": { "properties": [], "keys": [], "queryCapabilities": {} },
50
+ "instructions": ["Use the published property definitions and Action inputSchema."]
51
+ }
52
+ }
53
+ ```
54
+
55
+ The provider preserves this envelope, canonical targets, snapshot/version/partition fields,
56
+ and each Action's `inputSchema` and `inputHint`. Semantic Model metadata may contain an
57
+ `analysisContract`; OData metadata has different fields. The plugin does not translate either
58
+ into the other's language or infer an adapter from labels.
59
+
60
+ A new ontology adapter can use this path when data-xpert registers its discovery metadata
61
+ and permits its read-only Actions with canonical effects. Registration, published resource
62
+ access and explicit viewer policy are all required. A resource type alone is insufficient.
63
+
64
+ ## Binding and filters
65
+
66
+ The payload selects exact discovered `resourceId`, `actionTypeCode`, canonical `target`,
67
+ native `params` and `parameterPaths`. The target must contain `entityId` or `entityRef`.
68
+ Use discovered `entityTypeCode` with external references when supplied.
69
+
70
+ For an Action whose input schema defines the following filter, a mapping looks like this:
71
+
72
+ ```json
73
+ {
74
+ "params": { "filters": [{ "field": "Region", "op": "eq", "value": null }] },
75
+ "parameterPaths": { "region": ["filters", "0", "value"] }
76
+ }
77
+ ```
78
+
79
+ This is a parameter-mapping example, not a universal ontology filter format. Use the selected
80
+ Action's actual input schema. Paths are relative to `params`, unique, and must identify an
81
+ existing scalar. Array segments use canonical zero-based indices. Missing/extra mappings,
82
+ prototype keys and replacing an entire object/array are rejected. Parameters are cloned and
83
+ replaced as whole values; the provider never interpolates query text.
84
+
85
+ Each adapter defines its own null semantics, operators, supported time fields and aggregation
86
+ limits. Verify business meaning and time axes with published metadata and authorized previews.
87
+ Map returned column keys or explicit semantic references to Dataset field IDs.
88
+
89
+ ## Rendering and freshness
90
+
91
+ Data-xpert's query endpoint supplies canonical rows for `rows`, `series` and `entity` effects.
92
+ The provider carries those rows, source-fetch time, upstream execution ID and audit reference
93
+ into the platform normalizer. Truncated results use page scope and unknown total accuracy.
94
+ The reported count must not be interpreted as a verified full business aggregate.
95
+
96
+ Structured Data Analytics views and free HTML/JS Artifacts consume the same Dataset through
97
+ the host's declared Binding bridge. They do not call data-xpert directly. Filtering resolves
98
+ new declared parameters; refreshing performs another request. Responses must declare
99
+ `fromCache: false`, meaning the data-xpert Action result cache was bypassed. Upstream source
100
+ caches and business-data freshness are separate concerns.
@@ -0,0 +1,58 @@
1
+ ---
2
+ title: "Build and verification"
3
+ description: "Separate transport-contract tests from real SSO and end-to-end dashboard acceptance."
4
+ ---
5
+
6
+ ## Compatibility
7
+
8
+ Development dependencies pin published SDK/contracts 3.25.0, which export AnalyticsProvider,
9
+ AnalyticsQueryProvider, ActorTokenRuntimeFactoryCapability and the shared Dataset/Binding
10
+ contracts. The host must provide the corresponding runtime capabilities.
11
+
12
+ Data-xpert must expose `POST /agent-workbench/analytics/discover` and
13
+ `POST /agent-workbench/analytics/query` under the configured API base. Discovery returns
14
+ adapter-owned `definition` metadata. Query returns canonical rows, execution/audit references,
15
+ fetch time and `fromCache: false` after the normal authorization and read-only Action pipeline.
16
+
17
+ ## Local verification
18
+
19
+ From the xpertai workspace:
20
+
21
+ ```sh
22
+ corepack pnpm exec nx run @xpert-ai/plugin-analytics-data-xpert:test
23
+ corepack pnpm exec nx run @xpert-ai/plugin-analytics-data-xpert:typecheck
24
+ ```
25
+
26
+ Tests start a disposable local HTTP fixture and use synthetic Actor Tokens. They verify:
27
+
28
+ - current actor, organization, trusted Assistant scope and token audience;
29
+ - Semantic Model/OData metadata, canonical targets, pagination and Action schemas;
30
+ - scalar filter mapping and rejection of unknown/prototype/duplicate paths;
31
+ - rows/series/entity projection, partial totals, audit receipts and fresh requests;
32
+ - unknown inputs, missing targets, cached responses and invalid configured URLs;
33
+ - refused redirects, bounded response streaming and cancellation.
34
+
35
+ Build and package before running `plugin-dev-harness`. Its lifecycle mocks verify package
36
+ loading and Nest injection, not a real identity provider or data-xpert authorization decision.
37
+ Validate `docs.json` navigation and run `mint validate` when updating documentation.
38
+
39
+ ## Separate live acceptance
40
+
41
+ Install through the authenticated system-plugin workflow with tenant scope in the Default
42
+ tenant. Configure a DataSource pointing to the intended data-xpert API and its OIDC audience.
43
+ Use xpert-pro SSO, a published Assistant/resource binding and explicit viewer policy.
44
+
45
+ Verify an allowed viewer can discover and query, while a denied viewer cannot. Preview both
46
+ Semantic Model and OData (or another registered adapter) using their actual Action schemas.
47
+ Then verify one-sentence generation, filters, changed-source refresh, save/reopen and rejection
48
+ of undeclared HTML frontend queries. Local HTTP fixtures do not establish those outcomes.
49
+
50
+ ## Errors
51
+
52
+ - `DATAX_PARAMETER_MAPPING_INVALID`: align parameter names with unique declared paths.
53
+ - `DATAX_PARAMETER_PATH_INVALID`: use an existing path relative to params with canonical indices.
54
+ - `DATAX_PARAMETER_PATH_NOT_SCALAR`: target a scalar value, not an object or array.
55
+ - `DATAX_URL_INVALID`: correct trusted source configuration.
56
+ - `DATAX_QUERY_HTTP_<status>`: investigate authorization or request validation in data-xpert;
57
+ upstream error bodies and secrets are not forwarded.
58
+ - `DATAX_RESPONSE_TOO_LARGE`: reduce result size; response buffering is bounded.
package/index.cjs ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./dist/index.js').default
package/package.json CHANGED
@@ -1,6 +1,59 @@
1
1
  {
2
2
  "name": "@xpert-ai/plugin-analytics-data-xpert",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Governed data-xpert ontology analytics provider",
5
+ "main": "./index.cjs",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "require": "./index.cjs",
11
+ "default": "./index.cjs"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "index.cjs",
18
+ "README.md",
19
+ "docs"
20
+ ],
21
+ "license": "AGPL-3.0",
22
+ "private": false,
23
+ "xpert": {
24
+ "plugin": {
25
+ "level": "system",
26
+ "artifactNamespace": "analytics_data_xpert"
27
+ }
28
+ },
29
+ "peerDependencies": {
30
+ "@xpert-ai/plugin-sdk": "^3.25.0",
31
+ "@xpert-ai/contracts": "^3.25.0",
32
+ "@nestjs/common": "^11.1.6",
33
+ "@nestjs/core": "^11.1.6",
34
+ "zod": "3.25.67"
35
+ },
36
+ "devDependencies": {
37
+ "@xpert-ai/plugin-sdk": "3.25.0",
38
+ "@xpert-ai/contracts": "3.25.0",
39
+ "@nestjs/common": "^11.1.6",
40
+ "@nestjs/core": "^11.1.6",
41
+ "zod": "3.25.67",
42
+ "typescript": "^5.9.2",
43
+ "@types/node": "^20.19.9",
44
+ "reflect-metadata": "^0.2.2"
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/xpert-ai/xpert-plugins.git",
49
+ "directory": "xpertai/tools/analytics-data-xpert"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "scripts": {
55
+ "build": "tsc -p tsconfig.json",
56
+ "test": "pnpm run build && node --test tests/*.test.cjs",
57
+ "typecheck": "tsc -p tsconfig.json --noEmit"
58
+ }
6
59
  }