dsh-data-cleaning-agent 0.8.20 → 0.8.21

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/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.8.21] - 2026-09-09
8
+
9
+ - 从实际 Host 请求获取文件地址的协议和主机;修复本地 HTTP 链接被补成 HTTPS 的问题。
10
+ - 工具卡片支持同源完整文件地址,继续拒绝非同源链接。
11
+ - 已确认任务开始执行时自动展开工作台一次并跟随处理步骤;生成说明不触发,关闭后不反复弹出。
12
+ - 246 项测试及 10 组浏览器回归通过,无付费查询。
13
+
7
14
  ## [0.8.20] - 2026-09-09
8
15
 
9
16
  - 普通无记录/未披露字段保留为无数据说明,尊重共享契约 reviewRequired=false,不再误计为待核验。
package/README.en.md CHANGED
@@ -8,7 +8,7 @@ Related agents: [数据清洗补全](https://github.com/duhu2000/dsh-data-cleani
8
8
 
9
9
  > A data cleaning & completion agent plugin for DeepSeek Harness: local CSV/XLSX/JSON engine plus optional Qichacha (QCC) MCP enterprise-data enrichment. Initiated and maintained by the Qichacha (QCC) team.
10
10
  >
11
- > Current source version / 当前源码版本: **0.8.20** (stable release)
11
+ > Current source version / 当前源码版本: **0.8.21** (stable release)
12
12
 
13
13
  This release unifies spreadsheet previews, column mapping, and enrichment scope across the prompt wizard and workbench. Mapping statuses use color and text; mapped fields automatically enter the scope, with optional extra fields collapsed. Multiple confirmed output columns may share a non-identity field while preserving non-empty values. It fixes saved drafts resetting the scope to five defaults and detects stale Host versions. Preparing a draft does not query QCC; sending starts execution. Fully restart DSH after upgrading. Existing download artifacts are not rewritten.
14
14
 
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # dsh-data-cleaning-agent
2
2
 
3
- 本版统一向导与工作台的 Host 执行闭环:发送后自动生成新 Excel,原文件不修改,工作台入口可恢复;发布记录见 [发布说明](docs/RELEASE-0.8.20.md)。
3
+ 本版统一向导与工作台的 Host 执行闭环:发送后自动生成新 Excel,原文件不修改,工作台入口可恢复;发布记录见 [发布说明](docs/RELEASE-0.8.21.md)。
4
4
 
5
5
  ## 安装与三分钟上手
6
6
 
7
7
  数据清洗补全智能体:面向 Excel/CSV/JSON 企业名单,提供数据清洗、表格清洗、清洗补全、去重、企业数据补全与字段补全,支持企查查 MCP 和结果导出。
8
8
 
9
9
  ```sh
10
- dsh plugin --profile web add dsh-data-cleaning-agent@0.8.20
10
+ dsh plugin --profile web add dsh-data-cleaning-agent@0.8.21
11
11
  ```
12
12
 
13
13
  请先满足下文的 DSH、连接器及侧边栏依赖要求;安装后完整停止并重启对应 Profile。
@@ -25,7 +25,7 @@ dsh plugin --profile web add dsh-data-cleaning-agent@0.8.20
25
25
 
26
26
  > 在 DeepSeek Harness 中清洗、补全、画像企业名单数据的智能体插件:本地 CSV/XLSX/JSON 引擎 + 可选企查查(Qichacha/QCC)MCP 企业数据补全,由企查查(Qichacha/QCC)团队发起并维护。
27
27
  >
28
- > 当前源码版本 / Current source version: **0.8.20**(正式版本)
28
+ > 当前源码版本 / Current source version: **0.8.21**(正式版本)
29
29
 
30
30
  本版上传表格后展示完整清单及空白数量,用绿色、琥珀色、红色及文字区分已通过、待确认和未匹配映射。已映射字段自动纳入补全范围,额外字段默认折叠;多个原列可经确认使用同一非主体字段,分别补空并保留非空原值。修复 Host 草稿重置为默认五字段的问题,增加前后端版本错配提示。生成说明不查询,发送才执行;升级后需完整重启 DSH,历史下载文件不自动改写。
31
31
 
@@ -0,0 +1,9 @@
1
+ # 0.8.21 发布说明
2
+
3
+ 文件预览与下载地址使用实际请求的协议、主机,不硬编码 HTTPS 或端口;工具结果卡片接受同源完整地址。
4
+
5
+ 检测已暂存任务开始执行后自动展开右侧工作台一次,切到当前处理步骤。生成说明不会提前打开,用户关闭后同一监听不会重复打开。
6
+
7
+ 验证:246 项测试及 10 组浏览器回归。未重新执行付费查询,未修改上传文件。
8
+
9
+ 升级后完整重启 DSH,并重新生成后续任务说明。历史对话中的错误 HTTPS 文本链接不会被改写,可通过工作台打开历史文件。
package/lib/client.js CHANGED
@@ -1862,9 +1862,14 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
1862
1862
  return response.task;
1863
1863
  }
1864
1864
  function watchHostedCommand(actions, sessionId, command) {
1865
+ let openedForExecution = false;
1865
1866
  void waitForQccCommand(command.commandId, 1800, async (status) => {
1866
1867
  if (!status || status.state === 'prepared') return;
1867
1868
  await refreshHostedTask(actions, sessionId, command.taskId, null, true).catch(() => {});
1869
+ if (!openedForExecution && isCleaningSession(sessionId) && workflowTaskBySession.get(String(sessionId))?.id === command.taskId) {
1870
+ openedForExecution = true;
1871
+ openWorkbench(actions, workflowTaskBySession.get(String(sessionId))?.stage || 'match', sessionId);
1872
+ }
1868
1873
  if (isCleaningSession(sessionId) && workflowTaskBySession.get(String(sessionId))?.id === command.taskId) actions.setCommandProgress?.({ taskId: command.taskId, ...status.progress });
1869
1874
  }).then(async (completed) => {
1870
1875
  await refreshHostedTask(actions, sessionId, command.taskId, completed.run);
@@ -3228,7 +3233,7 @@ body:has(.dcAgentPromptBackdrop) { overflow: hidden; }
3228
3233
  meta.hint ? h('div', { className: 'dcAgentToolCardHint' }, meta.hint) : null,
3229
3234
  summary ? h('div', { className: 'dcAgentToolCardBody' }, summary) : null,
3230
3235
  toolName === 'data_cleaning_qcc_run' ? h('div', { className: 'dcAgentArtifactList' },
3231
- [...summary.matchAll(/\[([^\]]+)\]\((\/data-cleaning\/api\/workflow\/tasks\/[a-zA-Z0-9%_-]+\/artifacts\/[a-zA-Z0-9%_-]+(?:\?preview=1)?)\)/g)].map((match, index) =>
3236
+ [...summary.matchAll(/\[([^\]]+)\]\(((?:https?:\/\/[a-zA-Z0-9.:[\]-]+)?\/data-cleaning\/api\/workflow\/tasks\/[a-zA-Z0-9%_-]+\/artifacts\/[a-zA-Z0-9%_-]+(?:\?preview=1)?)\)/g)].filter(match => match[2].startsWith('/') || (typeof location !== 'undefined' && new URL(match[2]).origin === location.origin)).map((match, index) =>
3232
3237
  h('a', { key: index, className: 'dcAgentButton', href: match[2], target: '_blank', rel: 'noopener noreferrer' }, match[1]))) : null,
3233
3238
  );
3234
3239
  }
@@ -138,7 +138,7 @@ export class QccCommandStore {
138
138
  }
139
139
  }
140
140
 
141
- prepare(input) {
141
+ prepare(input, { artifactOrigin = '' } = {}) {
142
142
  this.cleanup(true);
143
143
  const normalized = normalizedInput(input);
144
144
  const estimate = normalized.kind === 'enrich'
@@ -148,6 +148,7 @@ export class QccCommandStore {
148
148
  const at = new Date(this.clock()).toISOString();
149
149
  const record = {
150
150
  commandId,
151
+ artifactOrigin,
151
152
  taskId: normalized.taskId,
152
153
  kind: normalized.kind,
153
154
  state: 'prepared',
@@ -265,7 +266,7 @@ export class QccCommandStore {
265
266
  state: run.state,
266
267
  summary: run.summary,
267
268
  ...(record.delivery ? { deliveryState: record.delivery.state, artifactCount: record.delivery.artifacts.length,
268
- artifacts: record.delivery.artifacts.filter(a => a.format === 'xlsx' && (a.kind !== 'review' || a.rowCount > 0)).map(a => ({ fileName: a.fileName, url: `/data-cleaning/api/workflow/tasks/${encodeURIComponent(record.taskId)}/artifacts/${encodeURIComponent(a.id)}` })) } : {}),
269
+ artifacts: record.delivery.artifacts.filter(a => a.format === 'xlsx' && (a.kind !== 'review' || a.rowCount > 0)).map(a => ({ fileName: a.fileName, url: `${record.artifactOrigin || ''}/data-cleaning/api/workflow/tasks/${encodeURIComponent(record.taskId)}/artifacts/${encodeURIComponent(a.id)}` })) } : {}),
269
270
  });
270
271
  }
271
272
  }
package/lib/web.js CHANGED
@@ -659,11 +659,20 @@ export function mountWebRoutes(wctx, { logger, report, TOOL_NAME, SKILL_NAME })
659
659
  }
660
660
  const payload = JSON.parse((await readBody(req)).toString('utf8'));
661
661
  if (payload.kind && payload.kind !== 'enrich') requirePaidConfirmation(payload);
662
+ // Resolve from the actual browser request, never from model/task input.
663
+ let artifactOrigin = '';
664
+ const requestHost = req.headers?.host;
665
+ if (typeof requestHost === 'string' && /^[a-zA-Z0-9.:[\]-]+$/.test(requestHost)) {
666
+ try {
667
+ const origin = new URL(req.headers.origin || `${req.socket?.encrypted ? 'https' : 'http'}://${requestHost}`);
668
+ if (['http:', 'https:'].includes(origin.protocol) && origin.host === requestHost) artifactOrigin = origin.origin;
669
+ } catch { /* Relative links remain available for hosts without an origin. */ }
670
+ }
662
671
  return writeJson(res, 201, {
663
672
  ok: true,
664
673
  marker: 'g5-agent-command',
665
674
  paidCalls: false,
666
- command: qccCommands.prepare(payload.workflowOwned ? await execution.prepare(payload) : payload),
675
+ command: qccCommands.prepare(payload.workflowOwned ? await execution.prepare(payload) : payload, { artifactOrigin }),
667
676
  });
668
677
  }
669
678
  if (rest.length === 1 && req.method === 'GET') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-data-cleaning-agent",
3
- "version": "0.8.20",
3
+ "version": "0.8.21",
4
4
  "description": "Data cleaning and data enrichment for CSV/XLSX/JSON enterprise lists in DeepSeek Harness, including spreadsheet cleaning, deduplication, profiling, optional Qichacha MCP and exports.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -40,7 +40,7 @@
40
40
  "docs/RELEASE-0.8.1.md",
41
41
  "docs/RELEASE-0.8.16.md",
42
42
  "docs/RELEASE-0.8.17.md",
43
- "docs/RELEASE-0.8.20.md",
43
+ "docs/RELEASE-0.8.21.md",
44
44
  "docs/TEMPLATE-MAPPING-ACCEPTANCE.md",
45
45
  "docs/UI-WORKFLOW-V2.md",
46
46
  "docs/UI-WORKFLOW-V2-MIGRATION.md",
@@ -1,11 +0,0 @@
1
- # 0.8.20 发布说明
2
-
3
- 修复未披露数据被误判为人工核验;保留未披露说明和空白值,不伪造数据。真正的多候选或无法判定的问题继续进入待核验。
4
-
5
- 当前页和历史列表同步最新任务状态。处理进度条区分处理中、完成、需核验和失败;100% 仅表示主体处理结束。摘要尚未生成时显示“结果统计中”。
6
-
7
- 任务展示名优先尊重自定义名称,否则由源文件名、行数和创建时间生成;当前页与历史一致。
8
-
9
- 验证:246 项自动测试、10 组浏览器回归。无付费查询,源文件未修改。
10
-
11
- 升级并完整重启 DSH 后生效;历史任务的旧异常文件和统计不会自动重算,不应仅为更新展示而重复查询。