dsh-data-cleaning-agent 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -1
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/docs/RELEASE-0.8.1.md +6 -0
- package/docs/USER-GUIDE.md +4 -1
- package/lib/client.js +38 -19
- package/lib/image-intake.js +25 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
本文件记录 `dsh-data-cleaning-agent` 的版本变更。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
-
## [
|
|
5
|
+
## [0.8.2] - 2026-09-06
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- 单一会话所有权:入口改用 `sessions.create({ workspaceId, sessionId })` 显式创建带 `session-dsh-data-cleaning-agent-` 前缀的独立原生会话,不再复用空白会话(否则会与「招投标」「访前尽调」共享同一会话,导致标题与右侧工作台面板归属冲突)。
|
|
9
|
+
- 图片暂存与 OCR 连接检查解耦:尚未连接本地文档解析时仍显示缩略图,允许继续选规则、字段并生成完整说明;执行 OCR 时仍严格检查连接,不调用远端 URL 接口读取本地文件。
|
|
10
|
+
- 图片连接状态随轮询更新;15 分钟暂存期内补齐连接后可直接使用原图片任务。
|
|
11
|
+
- Composer 和向导图片粘贴同时兼容剪贴板 `files` 与 `items`,拖入共用相同取图逻辑。
|
|
6
12
|
|
|
7
13
|
## [0.8.1] - 2026-09-05
|
|
8
14
|
|
package/README.en.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 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.
|
|
4
4
|
>
|
|
5
|
-
> Current source version / 当前源码版本: **0.8.
|
|
5
|
+
> Current source version / 当前源码版本: **0.8.2** (stable release)
|
|
6
6
|
|
|
7
7
|
[](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml)
|
|
8
8
|
[](https://www.npmjs.com/package/dsh-data-cleaning-agent)
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 在 DeepSeek Harness 中清洗、补全、画像企业名单数据的智能体插件:本地 CSV/XLSX/JSON 引擎 + 可选企查查(Qichacha/QCC)MCP 企业数据补全,由企查查(Qichacha/QCC)团队发起并维护。
|
|
4
4
|
>
|
|
5
|
-
> 当前源码版本 / Current source version: **0.8.
|
|
5
|
+
> 当前源码版本 / Current source version: **0.8.2**(正式版本)
|
|
6
6
|
|
|
7
7
|
[](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml)
|
|
8
8
|
[](https://www.npmjs.com/package/dsh-data-cleaning-agent)
|
package/docs/RELEASE-0.8.1.md
CHANGED
|
@@ -73,3 +73,9 @@ dsh web
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
回滚会恢复 0.8.0 的 Modlens 两轮图片流程;文本、Excel、主体匹配、字段补全和历史制品不受影响。
|
|
76
|
+
|
|
77
|
+
## 6. 发布结果
|
|
78
|
+
|
|
79
|
+
- 发布提交 `4d71308620115acaf78b215552565a1db4199168` 通过 Release workflow run `33977279908` 的标签校验、完整 `npm run check` 门禁、npm 发布和 GitHub Release 创建,全部成功。
|
|
80
|
+
- npm 通过 GitHub Actions OIDC Trusted Publishing 发布并签署 provenance,未使用长期 npm Token;registry 回读确认 `latest` 为 `0.8.1`。
|
|
81
|
+
- GitHub Release:<https://github.com/duhu2000/dsh-data-cleaning-agent/releases/tag/v0.8.1>。
|
package/docs/USER-GUIDE.md
CHANGED
|
@@ -67,7 +67,10 @@ bash <(curl -fsSL https://raw.githubusercontent.com/duhu2000/dsh-data-cleaning-a
|
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
市场中的远端 `qcc-document` 只接受公网 `file_url`,不能读取 Host 本地临时文件;只连接远端服务时,
|
|
70
|
-
|
|
70
|
+
图片仍可载入并显示缩略图、继续选择规则和字段,页面会单独提示配置本地服务;真正执行 OCR 时才
|
|
71
|
+
阻断不可用的连接,绝不会向远端接口传本地路径。15 分钟暂存期内补齐连接后无需重新选图,超时或 Host
|
|
72
|
+
重启后需重新选择。选图本身不会执行 OCR,回填并发送完整任务说明后才开始识别。
|
|
73
|
+
图片仅作为 0600 权限的 Host 临时文件,成功、解析失败、取消或
|
|
71
74
|
15 分钟超时后删除,不进入任务 KV、模型上下文或导出制品。`qcc-document-mcp` 会读取该临时文件并
|
|
72
75
|
上传到企查查文档解析网关;用户应只提交有权处理且允许上传的数据。文档解析与后续企业补全均使用
|
|
73
76
|
当前用户自己连接的企查查账号和额度;本插件不读取或保存凭据。
|
package/lib/client.js
CHANGED
|
@@ -1565,6 +1565,15 @@ window.__ModuleLoader__.load({
|
|
|
1565
1565
|
return mount;
|
|
1566
1566
|
}
|
|
1567
1567
|
|
|
1568
|
+
const CLEANING_SESSION_ID_PREFIX = 'session-dsh-data-cleaning-agent-';
|
|
1569
|
+
const SESSION_UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
1570
|
+
function createCleaningSessionId() {
|
|
1571
|
+
if (typeof globalThis.crypto?.randomUUID !== 'function') {
|
|
1572
|
+
throw new Error('当前浏览器不支持安全会话标识生成,请使用最新版浏览器');
|
|
1573
|
+
}
|
|
1574
|
+
return `${CLEANING_SESSION_ID_PREFIX}${globalThis.crypto.randomUUID()}`;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1568
1577
|
/** 使用 DSH 原生工作区、会话和输入机打开中央对话,不构造第二套聊天界面。 */
|
|
1569
1578
|
async function startCleaningSession(ctx) {
|
|
1570
1579
|
const workspace = ctx.workspaces.list.getSnapshot();
|
|
@@ -1577,21 +1586,17 @@ window.__ModuleLoader__.load({
|
|
|
1577
1586
|
if (targetWorkspaceId === undefined) {
|
|
1578
1587
|
throw new Error('请先选择一个工作空间,再打开数据清洗补全智能体');
|
|
1579
1588
|
}
|
|
1580
|
-
//
|
|
1581
|
-
//
|
|
1582
|
-
//
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
: typeof ctx.sessions.create === 'function'
|
|
1592
|
-
? await ctx.sessions.create({ workspaceId: targetWorkspaceId })
|
|
1593
|
-
: null;
|
|
1594
|
-
if (!sessionId) throw new Error('当前 DSH 版本没有可用的工作区会话创建能力');
|
|
1589
|
+
// 单一会话所有权:与招投标入口一致,显式创建带前缀的独立原生会话,绝不复用空白会话。
|
|
1590
|
+
// connectWorkspace 会把「招投标」尚为空白的工作台会话复用为清洗会话,
|
|
1591
|
+
// 使同一会话同时被招投标与清洗两个插件认领,进而争夺 Hero 标题与右侧工作台面板。
|
|
1592
|
+
if (typeof ctx.sessions.create !== 'function') {
|
|
1593
|
+
throw new Error('当前 DSH 版本没有可用的独立会话创建能力');
|
|
1594
|
+
}
|
|
1595
|
+
const requestedId = createCleaningSessionId();
|
|
1596
|
+
const sessionId = await ctx.sessions.create({ workspaceId: targetWorkspaceId, sessionId: requestedId });
|
|
1597
|
+
if (sessionId !== requestedId) {
|
|
1598
|
+
throw new Error('当前 DSH 版本不支持独立会话创建,请升级 DSH 后再打开数据清洗补全智能体');
|
|
1599
|
+
}
|
|
1595
1600
|
const conversation = typeof ctx.get === 'function' ? ctx.get('conversation') : ctx.conversation;
|
|
1596
1601
|
if (!conversation) throw new Error('DSH 对话服务尚未就绪,请稍后重试');
|
|
1597
1602
|
markCleaningSession(sessionId);
|
|
@@ -1975,6 +1980,16 @@ window.__ModuleLoader__.load({
|
|
|
1975
1980
|
}).filter(Boolean).slice(0, 50);
|
|
1976
1981
|
}
|
|
1977
1982
|
|
|
1983
|
+
function imageFilesFromTransfer(transfer) {
|
|
1984
|
+
const files = Array.from(transfer?.files || []).filter((file) => String(file?.type ?? '').startsWith('image/'));
|
|
1985
|
+
// Safari/Chromium clipboard implementations may expose screenshots only as items.
|
|
1986
|
+
if (files.length) return files;
|
|
1987
|
+
return Array.from(transfer?.items || [])
|
|
1988
|
+
.filter((item) => item.kind === 'file' && String(item.type ?? '').startsWith('image/'))
|
|
1989
|
+
.map((item) => item.getAsFile?.())
|
|
1990
|
+
.filter(Boolean);
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1978
1993
|
async function imageFileBase64(file) {
|
|
1979
1994
|
if (!file || typeof file.arrayBuffer !== 'function') throw new Error('图片文件不可读。');
|
|
1980
1995
|
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
@@ -2238,8 +2253,8 @@ window.__ModuleLoader__.load({
|
|
|
2238
2253
|
react.useEffect(() => {
|
|
2239
2254
|
if (!enabled) return undefined;
|
|
2240
2255
|
const handleWindowPaste = (event) => {
|
|
2241
|
-
const files = event.clipboardData
|
|
2242
|
-
const hasImage =
|
|
2256
|
+
const files = imageFilesFromTransfer(event.clipboardData);
|
|
2257
|
+
const hasImage = files.length > 0;
|
|
2243
2258
|
if (!hasImage) return;
|
|
2244
2259
|
const target = event.target;
|
|
2245
2260
|
const inComposer = !open && Boolean(target?.closest?.('textarea, [contenteditable="true"], [role="textbox"]'));
|
|
@@ -2341,7 +2356,7 @@ window.__ModuleLoader__.load({
|
|
|
2341
2356
|
role: 'group',
|
|
2342
2357
|
'aria-label': '粘贴、拖入或选择企业名单图片',
|
|
2343
2358
|
onPaste: (event) => {
|
|
2344
|
-
const files = event.clipboardData
|
|
2359
|
+
const files = imageFilesFromTransfer(event.clipboardData);
|
|
2345
2360
|
if (files?.length) {
|
|
2346
2361
|
event.preventDefault();
|
|
2347
2362
|
acceptImageFiles(files);
|
|
@@ -2352,7 +2367,7 @@ window.__ModuleLoader__.load({
|
|
|
2352
2367
|
onDrop: (event) => {
|
|
2353
2368
|
event.preventDefault();
|
|
2354
2369
|
setImageDropActive(false);
|
|
2355
|
-
acceptImageFiles(event.dataTransfer
|
|
2370
|
+
acceptImageFiles(imageFilesFromTransfer(event.dataTransfer));
|
|
2356
2371
|
},
|
|
2357
2372
|
},
|
|
2358
2373
|
h('p', null, '可直接在此粘贴图片,或拖入 / 选择 PNG、JPEG、WebP。图片会在对话框显示缩略图,点击可放大。'),
|
|
@@ -2432,6 +2447,9 @@ window.__ModuleLoader__.load({
|
|
|
2432
2447
|
h('pre', { className: 'dcAgentPromptPreview' }, buildTaskPrompt(promptConfig())),
|
|
2433
2448
|
) : null,
|
|
2434
2449
|
h('p', { className: 'dcAgentPromptNote' }, '向导只生成任务描述并保存任务设置。只有你在任务中明确确认后,才会使用当前客户自己的企查查 MCP 连接、账号和额度。'),
|
|
2450
|
+
mode === 'image' && imageCommand?.state === 'prepared' && imageCommand?.providerIssue
|
|
2451
|
+
? h('p', { className: 'dcAgentPromptNote', role: 'status' }, `图片已载入,可继续选择规则和字段;暂存有效期 15 分钟。${imageCommand.providerIssue.message}`)
|
|
2452
|
+
: null,
|
|
2435
2453
|
error ? h('p', { className: 'dcAgentPromptError', role: 'alert' }, error) : null,
|
|
2436
2454
|
h('div', { className: 'dcAgentPromptActions' },
|
|
2437
2455
|
h('button', { type: 'button', className: 'dcAgentPromptAction', disabled: wizardStep === 1, onClick: () => setWizardStep(Math.max(1, wizardStep - 1)) }, '上一步'),
|
|
@@ -3786,6 +3804,7 @@ window.__ModuleLoader__.load({
|
|
|
3786
3804
|
clearCleaningDraft,
|
|
3787
3805
|
entriesToDataset,
|
|
3788
3806
|
extractPromptEntries,
|
|
3807
|
+
imageFilesFromTransfer,
|
|
3789
3808
|
guessMappings,
|
|
3790
3809
|
deactivateCleaningSession,
|
|
3791
3810
|
installSessionOwnershipBridge,
|
package/lib/image-intake.js
CHANGED
|
@@ -395,16 +395,6 @@ export class ImageIntakeStore {
|
|
|
395
395
|
async prepare(input = {}) {
|
|
396
396
|
await this.cleanup();
|
|
397
397
|
const provider = this.providerDefinition();
|
|
398
|
-
if (!provider) {
|
|
399
|
-
const remoteOnly = Boolean(this.remoteDefinition());
|
|
400
|
-
throw new ImageIntakeError(
|
|
401
|
-
remoteOnly ? 'DC_IMAGE_LOCAL_DOCUMENT_PROVIDER_REQUIRED' : 'DC_IMAGE_PROVIDER_UNAVAILABLE',
|
|
402
|
-
remoteOnly
|
|
403
|
-
? '当前只连接了远端 qcc-document,它不能读取本地图片。请在 MCP 连接器中配置本地 qcc-document-mcp。'
|
|
404
|
-
: '当前 DSH 没有可用的企查查本地文档解析连接。请配置 qcc-document-mcp,或改用文本/Excel 名单。',
|
|
405
|
-
503,
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
398
|
const decoded = decodeImage(input.content);
|
|
409
399
|
const commandId = `dci-${randomUUID()}`;
|
|
410
400
|
const at = new Date(this.clock()).toISOString();
|
|
@@ -417,9 +407,9 @@ export class ImageIntakeStore {
|
|
|
417
407
|
fileName: safeName(input.fileName),
|
|
418
408
|
mimeType: decoded.mimeType,
|
|
419
409
|
sizeBytes: decoded.bytes.length,
|
|
420
|
-
provider:
|
|
421
|
-
parseTool: provider
|
|
422
|
-
resultTool: provider
|
|
410
|
+
provider: IMAGE_PROVIDER_QCC_DOCUMENT_LOCAL,
|
|
411
|
+
parseTool: provider?.parseName ?? null,
|
|
412
|
+
resultTool: provider?.resultName ?? null,
|
|
423
413
|
path,
|
|
424
414
|
createdAt: at,
|
|
425
415
|
updatedAt: at,
|
|
@@ -431,7 +421,7 @@ export class ImageIntakeStore {
|
|
|
431
421
|
};
|
|
432
422
|
this.records.set(commandId, record);
|
|
433
423
|
this.scheduleExpiry(record);
|
|
434
|
-
return
|
|
424
|
+
return this.status(commandId);
|
|
435
425
|
}
|
|
436
426
|
|
|
437
427
|
require(commandId) {
|
|
@@ -444,7 +434,23 @@ export class ImageIntakeStore {
|
|
|
444
434
|
}
|
|
445
435
|
|
|
446
436
|
status(commandId) {
|
|
447
|
-
|
|
437
|
+
const record = this.require(commandId);
|
|
438
|
+
const ready = Boolean(this.providerDefinition(record.parseTool));
|
|
439
|
+
return {
|
|
440
|
+
...publicRecord(record, this.clock()),
|
|
441
|
+
providerReady: ready,
|
|
442
|
+
providerIssue: ready ? null : this.providerIssue(),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
providerIssue() {
|
|
447
|
+
const remoteOnly = Boolean(this.remoteDefinition());
|
|
448
|
+
return {
|
|
449
|
+
code: remoteOnly ? 'DC_IMAGE_LOCAL_DOCUMENT_PROVIDER_REQUIRED' : 'DC_IMAGE_PROVIDER_UNAVAILABLE',
|
|
450
|
+
message: remoteOnly
|
|
451
|
+
? '当前只连接了远端 qcc-document,它不能读取本地图片。请在 MCP 连接器中配置本地 qcc-document-mcp 后再发送识别任务。'
|
|
452
|
+
: '当前 DSH 没有可用的企查查本地文档解析连接。请配置 qcc-document-mcp 后再发送识别任务,或改用文本/Excel 名单。',
|
|
453
|
+
};
|
|
448
454
|
}
|
|
449
455
|
|
|
450
456
|
async run(commandId, exec) {
|
|
@@ -456,8 +462,11 @@ export class ImageIntakeStore {
|
|
|
456
462
|
if (record.state === 'completed') return { commandId, ...record.result };
|
|
457
463
|
const provider = this.providerDefinition(record.parseTool);
|
|
458
464
|
if (!provider) {
|
|
459
|
-
|
|
465
|
+
const issue = this.providerIssue();
|
|
466
|
+
throw new ImageIntakeError(issue.code, issue.message, 503);
|
|
460
467
|
}
|
|
468
|
+
record.parseTool = provider.parseName;
|
|
469
|
+
record.resultTool = provider.resultName;
|
|
461
470
|
record.state = 'running';
|
|
462
471
|
record.updatedAt = new Date(this.clock()).toISOString();
|
|
463
472
|
const execute = (name, args) => this.tools.execute({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-data-cleaning-agent",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Clean, complete, and profile enterprise name lists in DeepSeek Harness — a data cleaning & completion agent plugin with local CSV/XLSX/JSON engine and optional Qichacha (QCC) MCP enrichment. Maintained by Qichacha/QCC.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|