@sparkelf/dsh-dataops-managed 0.3.27 → 0.3.29
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/lib/client.js +230 -171
- package/lib/client.js.map +1 -1
- package/lib/index.js +59 -12
- package/lib/types/client/DataOpsResourcesTab.d.ts +6 -2
- package/package.json +1 -1
- package/skills/dataops-collect/SKILL.md +17 -18
package/lib/index.js
CHANGED
|
@@ -1463,6 +1463,41 @@ function buildAiManagementTools(ctx, reviewApi, readScope, collectHub) {
|
|
|
1463
1463
|
if (!Array.isArray(parsed)) throw new Error(tool + ": " + label + " must be a JSON array");
|
|
1464
1464
|
return parsed;
|
|
1465
1465
|
};
|
|
1466
|
+
/**
|
|
1467
|
+
* Keep only the fields the DataOps execute DTO accepts. The plan tool returns
|
|
1468
|
+
* `createSql`/`targetSchema`/`notes` beside `createTable`; if the model passes
|
|
1469
|
+
* the whole plan (or rebuilds it after a table-name edit), the backend's
|
|
1470
|
+
* `forbidNonWhitelisted` pipe rejects the call with a generic 400.
|
|
1471
|
+
*/
|
|
1472
|
+
const normalizeCreatePlan = (tool, plan) => {
|
|
1473
|
+
if (plan === void 0) return void 0;
|
|
1474
|
+
const tableName = typeof plan.tableName === "string" ? plan.tableName.trim() : "";
|
|
1475
|
+
if (!tableName) throw new Error(tool + ": create_plan_json.tableName is required");
|
|
1476
|
+
const rawColumns = Array.isArray(plan.columns) ? plan.columns : [];
|
|
1477
|
+
if (!rawColumns.length) throw new Error(tool + ": create_plan_json.columns is required");
|
|
1478
|
+
const columns = rawColumns.map((raw, index) => {
|
|
1479
|
+
const column = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
1480
|
+
const sourceKey = typeof column.sourceKey === "string" ? column.sourceKey : "";
|
|
1481
|
+
const name = typeof column.name === "string" ? column.name : "";
|
|
1482
|
+
const chineseName = typeof column.chineseName === "string" ? column.chineseName : "";
|
|
1483
|
+
const dataType = typeof column.dataType === "string" ? column.dataType : "";
|
|
1484
|
+
if (!sourceKey || !name || !chineseName || !dataType) throw new Error(tool + ": create_plan_json.columns[" + index + "] needs sourceKey/name/chineseName/dataType");
|
|
1485
|
+
return {
|
|
1486
|
+
sourceKey,
|
|
1487
|
+
name,
|
|
1488
|
+
chineseName,
|
|
1489
|
+
dataType,
|
|
1490
|
+
...typeof column.nullable === "boolean" ? { nullable: column.nullable } : {},
|
|
1491
|
+
...typeof column.comment === "string" ? { comment: column.comment } : {}
|
|
1492
|
+
};
|
|
1493
|
+
});
|
|
1494
|
+
return {
|
|
1495
|
+
tableName,
|
|
1496
|
+
...typeof plan.tableChineseName === "string" && plan.tableChineseName.trim() ? { tableChineseName: plan.tableChineseName.trim() } : {},
|
|
1497
|
+
...typeof plan.tableComment === "string" && plan.tableComment.trim() ? { tableComment: plan.tableComment.trim() } : {},
|
|
1498
|
+
columns
|
|
1499
|
+
};
|
|
1500
|
+
};
|
|
1466
1501
|
return [
|
|
1467
1502
|
{
|
|
1468
1503
|
name: "skill_list",
|
|
@@ -1949,7 +1984,7 @@ function buildAiManagementTools(ctx, reviewApi, readScope, collectHub) {
|
|
|
1949
1984
|
name: "dataops_collect_execute",
|
|
1950
1985
|
definition: defineTool({
|
|
1951
1986
|
name: "dataops_collect_execute",
|
|
1952
|
-
description: "Execute one confirmed spreadsheet import: create a table (or write to an existing table), insert rows, and register the DataOps resource. This is the ONLY collection tool that mutates data. Call it only after the user explicitly
|
|
1987
|
+
description: "Execute one confirmed spreadsheet import: create a table (or write to an existing table), insert rows, and register the DataOps resource. This is the ONLY collection tool that mutates data. Call it only after the user explicitly confirms the read-only plan in natural language (e.g. 确认/开始吧); never call it while the plan is still being edited.",
|
|
1953
1988
|
parameters: {
|
|
1954
1989
|
session_id: {
|
|
1955
1990
|
type: "string",
|
|
@@ -1976,7 +2011,7 @@ function buildAiManagementTools(ctx, reviewApi, readScope, collectHub) {
|
|
|
1976
2011
|
},
|
|
1977
2012
|
create_plan_json: {
|
|
1978
2013
|
type: "string",
|
|
1979
|
-
description: "
|
|
2014
|
+
description: "Only plan.createTable (tableName/tableChineseName/tableComment/columns). Do not send createSql/targetSchema/notes; if the user edits the table name, call dataops_collect_plan again and pass its createTable."
|
|
1980
2015
|
},
|
|
1981
2016
|
mappings_json: {
|
|
1982
2017
|
type: "string",
|
|
@@ -2010,7 +2045,7 @@ function buildAiManagementTools(ctx, reviewApi, readScope, collectHub) {
|
|
|
2010
2045
|
},
|
|
2011
2046
|
execute: async (args, exec) => {
|
|
2012
2047
|
if (args.mode !== "CREATE_TABLE" && args.mode !== "EXISTING_TABLE") throw new Error("dataops_collect_execute: mode must be CREATE_TABLE or EXISTING_TABLE");
|
|
2013
|
-
const createPlan = parseJsonObject("dataops_collect_execute", "create_plan_json", args.create_plan_json);
|
|
2048
|
+
const createPlan = normalizeCreatePlan("dataops_collect_execute", parseJsonObject("dataops_collect_execute", "create_plan_json", args.create_plan_json));
|
|
2014
2049
|
const mappings = parseJsonArray("dataops_collect_execute", "mappings_json", args.mappings_json);
|
|
2015
2050
|
if (args.mode === "CREATE_TABLE" && createPlan === void 0) throw new Error("dataops_collect_execute: create_plan_json is required for CREATE_TABLE");
|
|
2016
2051
|
if (args.mode === "EXISTING_TABLE" && (!args.target_schema || !args.target_table)) throw new Error("dataops_collect_execute: target_schema and target_table are required for EXISTING_TABLE");
|
|
@@ -2433,10 +2468,10 @@ const CONNECTOR_STATE_PATH = join(process.env.DSH_HOME?.trim() || "/workspace/.d
|
|
|
2433
2468
|
/**
|
|
2434
2469
|
* Capability definitions before permission filtering.
|
|
2435
2470
|
*
|
|
2436
|
-
* The set
|
|
2437
|
-
*
|
|
2438
|
-
* Skill plaza and review queue are governed by
|
|
2439
|
-
* connector's capability switches.
|
|
2471
|
+
* The set follows `dataops/docs/dataops-connector-development-spec.md` §2.3's credential list.
|
|
2472
|
+
* `approval.push` was renamed to `resource.manage` when the capability grew from approval push
|
|
2473
|
+
* into resource approval and authorization. Skill plaza and review queue are governed by
|
|
2474
|
+
* `skill:*` permissions and stay out of the connector's capability switches.
|
|
2440
2475
|
*/
|
|
2441
2476
|
const CAPABILITIES = [
|
|
2442
2477
|
{
|
|
@@ -2444,23 +2479,34 @@ const CAPABILITIES = [
|
|
|
2444
2479
|
name: "数据查询",
|
|
2445
2480
|
description: "通过 DataOps 数据查询能力回答业务问题",
|
|
2446
2481
|
defaultEnabled: true,
|
|
2482
|
+
visible: () => true,
|
|
2447
2483
|
allowed: () => true
|
|
2448
2484
|
},
|
|
2449
2485
|
{
|
|
2450
|
-
id: "
|
|
2451
|
-
name: "
|
|
2452
|
-
description: "
|
|
2486
|
+
id: "resource.manage",
|
|
2487
|
+
name: "资源管理",
|
|
2488
|
+
description: "在对话中批量审批资源申请,给用户或角色授予资源访问权限",
|
|
2453
2489
|
defaultEnabled: true,
|
|
2454
|
-
|
|
2490
|
+
visible: (scope) => isAdmin(scope),
|
|
2491
|
+
allowed: (scope) => isAdmin(scope)
|
|
2455
2492
|
},
|
|
2456
2493
|
{
|
|
2457
2494
|
id: "admin.manage",
|
|
2458
2495
|
name: "万相中台管理",
|
|
2459
2496
|
description: "登录万相中台后台执行管理动作",
|
|
2460
2497
|
defaultEnabled: true,
|
|
2498
|
+
visible: (scope) => isAdmin(scope),
|
|
2461
2499
|
allowed: (scope) => isAdmin(scope)
|
|
2462
2500
|
}
|
|
2463
2501
|
];
|
|
2502
|
+
/**
|
|
2503
|
+
* Persisted ids that moved to a new capability id.
|
|
2504
|
+
*
|
|
2505
|
+
* `approval.push` shipped before the capability grew from approval push into resource
|
|
2506
|
+
* approval and authorization. Its stored toggle carries over to `resource.manage`; when both
|
|
2507
|
+
* keys are present the current id wins.
|
|
2508
|
+
*/
|
|
2509
|
+
const CAPABILITY_ALIASES = { "approval.push": "resource.manage" };
|
|
2464
2510
|
/** The ids the catalogue accepts; any other persisted key is a legacy value. */
|
|
2465
2511
|
const CAPABILITY_IDS = CAPABILITIES.map((capability) => capability.id);
|
|
2466
2512
|
/**
|
|
@@ -2477,6 +2523,7 @@ function migrateCapabilities(value) {
|
|
|
2477
2523
|
const stored = value;
|
|
2478
2524
|
const capabilities = {};
|
|
2479
2525
|
for (const id of CAPABILITY_IDS) if (typeof stored[id] === "boolean") capabilities[id] = stored[id];
|
|
2526
|
+
for (const [legacyId, id] of Object.entries(CAPABILITY_ALIASES)) if (capabilities[id] === void 0 && typeof stored[legacyId] === "boolean") capabilities[id] = stored[legacyId];
|
|
2480
2527
|
return capabilities;
|
|
2481
2528
|
}
|
|
2482
2529
|
/** Read state; a missing or corrupt file is a safe disabled state. */
|
|
@@ -2512,7 +2559,7 @@ async function writeConnectorState(state, path = CONNECTOR_STATE_PATH) {
|
|
|
2512
2559
|
}
|
|
2513
2560
|
/** Build the connector row for one current scope. */
|
|
2514
2561
|
function buildConnectorView(scope, state) {
|
|
2515
|
-
const capabilities = scope === void 0 ? [] : CAPABILITIES.filter((capability) => capability.allowed(scope)).map((capability) => ({
|
|
2562
|
+
const capabilities = scope === void 0 ? [] : CAPABILITIES.filter((capability) => capability.visible(scope) && capability.allowed(scope)).map((capability) => ({
|
|
2516
2563
|
id: capability.id,
|
|
2517
2564
|
name: capability.name,
|
|
2518
2565
|
description: capability.description,
|
|
@@ -21,7 +21,11 @@ export interface DataOpsResourcesTabProps {
|
|
|
21
21
|
};
|
|
22
22
|
tab: ResourceTabRecord;
|
|
23
23
|
visible?: boolean;
|
|
24
|
-
|
|
24
|
+
insertResourceReference?: (resource: {
|
|
25
|
+
id?: unknown;
|
|
26
|
+
name?: unknown;
|
|
27
|
+
table?: unknown;
|
|
28
|
+
}, reference: Record<string, unknown>, sessionId?: string) => void;
|
|
25
29
|
}
|
|
26
30
|
/** Render the embedded DataOps resource page and translate its parent messages. */
|
|
27
|
-
export declare function DataOpsResourcesTab({ tab, scope,
|
|
31
|
+
export declare function DataOpsResourcesTab({ tab, scope, insertResourceReference }: DataOpsResourcesTabProps): import("react").JSX.Element;
|
package/package.json
CHANGED
|
@@ -38,7 +38,7 @@ description: 把表格 / Excel / CSV 建成可问数的数据资源,或把新
|
|
|
38
38
|
|
|
39
39
|
1. **一次会话一份文件、一个目标资源。** 多文件批量不做。
|
|
40
40
|
2. **有引用 = 追加;无引用 = 先问,不默认新建。** 用户话里已经说清“新建”或“追加”的,跳过这一问;没说清又没引用,必须先出「新建资源 / 选择已有资源」二选一,不能直接出新建计划。
|
|
41
|
-
3. **计划确认是唯一硬闸。**
|
|
41
|
+
3. **计划确认是唯一硬闸。** 用户明确回复「确认」「开始吧」「可以」等之前,绝不调用 `dataops_collect_execute`,绝不建表、绝不写数。预览和计划工具本身不建表,可以放心调用。
|
|
42
42
|
4. **执行走原生工具流。** 不要自己造执行卡、进度卡;工具参数、结果、耗时、失败原因都在原生工具卡里。你只在旁边说人话。
|
|
43
43
|
|
|
44
44
|
## 三、主流程
|
|
@@ -86,43 +86,42 @@ description: 把表格 / Excel / CSV 建成可问数的数据资源,或把新
|
|
|
86
86
|
- 新建表:`plan.mode === 'CREATE_TABLE'`,有 `plan.createTable`、`plan.createSql`;
|
|
87
87
|
- 追加:`plan.mode === 'EXISTING_TABLE'`,有 `plan.mappings`,并在计划里多一行映射摘要(匹配 N 个字段、新增 N 个、N 个类型不兼容)。
|
|
88
88
|
|
|
89
|
-
### 5. 出计划 GenUI
|
|
89
|
+
### 5. 出计划 GenUI(只读预览,语言确认)
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
计划是主确认点,但**只读**:卡上不放 `input` / `textarea` / 下拉 / `submit`,也不放「开始导入」「再调整」按钮。用户看完卡后,直接用自然语言回复「确认」「开始吧」才执行。
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
- 目标表(原始层库表名,AI 起草,可改)
|
|
95
|
-
- 导入数据源(只有一个源时自动选中并置灰;多个源时让用户选)
|
|
96
|
-
- 字段总数,以及哪几个类型拿不准(标黄 / 写“需确认”)
|
|
97
|
-
- 去向(新建资源 / 追加到某个已有资源)
|
|
98
|
-
- 描述与标签(AI 起草,可改)
|
|
99
|
-
- 默认权限(仅自己可见)
|
|
93
|
+
必须包含(全部纯展示):
|
|
100
94
|
|
|
101
|
-
|
|
95
|
+
- 资源名(AI 起草)
|
|
96
|
+
- 去向(新建资源 / 追加到「已有资源名」)
|
|
97
|
+
- 目标表(库.表)
|
|
98
|
+
- 导入数据源
|
|
99
|
+
- 行数 / 字段数
|
|
100
|
+
- **表头映射**:原表头 → 目标字段 → 类型;低置信度类型写「需确认」,并用 warning callout 再列一遍(GenUI 表格单元格不支持单独标黄)
|
|
101
|
+
- 默认权限
|
|
102
102
|
|
|
103
|
-
计划 GenUI 示例(把 `<...>`
|
|
103
|
+
计划 GenUI 示例(把 `<...>` 换成真实值;注意没有任何输入控件):
|
|
104
104
|
|
|
105
105
|
```dsh-ui
|
|
106
|
-
{"title":"导入计划","items":[{"type":"text","size":"body","content":"看了下,明细 sheet 共 <行数> 行、<列数> 列;我先按明细建资源。"},{"type":"card","title":"<资源名>","items":[{"type":"keyvalue","pairs":[{"key":"
|
|
106
|
+
{"title":"导入计划","items":[{"type":"text","size":"body","content":"看了下,明细 sheet 共 <行数> 行、<列数> 列;我先按明细建资源。"},{"type":"card","title":"<资源名>","items":[{"type":"keyvalue","pairs":[{"key":"资源名","value":"<资源名>"},{"key":"去向","value":"新建资源"},{"key":"目标表","value":"<schema.table>"},{"key":"导入数据源","value":"<数据源名>"},{"key":"行数 / 字段数","value":"<行数> 行 / <字段数> 列"},{"key":"默认权限","value":"仅自己可见"}]},{"type":"table","columns":["原表头","目标字段","类型"],"rows":[["<原表头1>","<目标字段1>","<类型1>"],["<原表头2>","<目标字段2>","⚠️ 需确认"]]},{"type":"callout","tone":"warning","content":"以下类型需要确认:<列出低置信度字段>。"},{"type":"callout","tone":"info","content":"确认无误请直接回复「确认」或「开始吧」;要改表名、类型或资源名,直接说,我会重新出计划,不会执行。"}]}]}
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
规则:
|
|
110
110
|
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
- 确认后,计划折叠成一行“已按此计划执行”的回执,把屏幕让给工具流;
|
|
111
|
+
- 用户提出修改(「表名换成 xxx」「金额按文本存」「资源名改成 xxx」)→ 只在对话里改参数,然后重新调用 `dataops_collect_plan` / 调整计划,**重新出只读计划卡,不要执行**;
|
|
112
|
+
- 用户确认后,计划折叠成一行“已按此计划执行”的回执,把屏幕让给工具流;
|
|
114
113
|
- 确认后如果要改参数,先停,再出新计划。
|
|
115
114
|
|
|
116
115
|
### 6. 执行
|
|
117
116
|
|
|
118
|
-
|
|
117
|
+
用户明确回复「确认」「开始吧」「可以」后,使用计划卡上展示的资源名/目标表/描述调用:
|
|
119
118
|
|
|
120
119
|
`dataops_collect_execute({ session_id, data_source_urn, mode, target_schema?, target_table?, create_plan_json, mappings_json, resource_name, resource_description, resource_tags?, ai_visible?, access_level? })`
|
|
121
120
|
|
|
122
121
|
- `mode` 用 `plan.mode`;
|
|
123
122
|
- 新建表:`create_plan_json` 传 `JSON.stringify(plan.createTable)`,`mappings_json` 传 `"[]"`;
|
|
124
123
|
- 追加:`mappings_json` 传 `JSON.stringify(plan.mappings)`;
|
|
125
|
-
- `resource_name`
|
|
124
|
+
- `resource_name` 用计划卡上展示、用户确认过的资源名;
|
|
126
125
|
- `access_level` 默认 `PRIVATE`;`ai_visible` 默认 `true`。
|
|
127
126
|
|
|
128
127
|
执行期间只说阶段小结,例如:
|