dsh-data-cleaning-agent 0.8.3 → 0.8.5
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 +20 -0
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/lib/client.js +140 -44
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
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
|
+
## [0.8.5] - 2026-09-06
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- 清洗入口恢复使用 workspaceId 创建独立会话,修复仅 cwd 导致 DSH 首页缺少工作区、图片任务回填后发送按钮和模型控件持续禁用的问题。
|
|
9
|
+
- 增加可卸载的新会话兼容 Bridge:仅在清洗业务激活期间通过原生 create/open 创建普通会话,避免复用空白清洗会话;普通会话仍使用原生动作。
|
|
10
|
+
- 创建失败保留原会话和草稿,连续点击合并;等待期间切换其它智能体不被迟到的创建结果抢回页面。
|
|
11
|
+
|
|
12
|
+
### Tests
|
|
13
|
+
- 增加 DSH 原生 Hero 工作区禁用表达式回归、工作区关联和新会话生命周期测试;这些为代码/契约测试,不等于真实 QCC OCR 已通过。
|
|
14
|
+
|
|
15
|
+
## [0.8.4] - 2026-09-06
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- 向导数据来源页在文本模式也接管图片粘贴,避免 Modlens 图片路径被当成企业名称;文本名单明确拒绝本地图片路径。
|
|
19
|
+
- 右侧上传允许 PNG/JPEG/WebP,并通过同会话事件桥转交图片向导;接收失败保留工作台并说明原因。
|
|
20
|
+
- 回填前检查原生输入机提交状态,回填后核对草稿;输入机未接收时保留向导并提示,不强制启用发送按钮。
|
|
21
|
+
- 移除通过全局捕获点击和按钮文案推测导航的逻辑,改为订阅真实会话选择,目标打开失败时保留清洗首页、工作台和草稿。
|
|
22
|
+
- 兼容无回调参数的 Store 通知;成功退出时只清理插件默认文案,保留用户自写草稿。
|
|
23
|
+
- 新清洗会话在准备和选择成功后才激活,避免提前撤销旧会话归属。
|
|
24
|
+
|
|
5
25
|
## [0.8.3] - 2026-09-06
|
|
6
26
|
|
|
7
27
|
### Fixed
|
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.5** (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.5**(正式版本)
|
|
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/lib/client.js
CHANGED
|
@@ -1105,11 +1105,11 @@ window.__ModuleLoader__.load({
|
|
|
1105
1105
|
}
|
|
1106
1106
|
|
|
1107
1107
|
/**
|
|
1108
|
-
*
|
|
1109
|
-
*
|
|
1110
|
-
* 提示词、能力栏或工作台。点击本插件自己的顶部入口不会提前撤销。
|
|
1108
|
+
* 以实际会话选择提交作为退出条件;点击其它入口但创建失败时,保留清洗首页、
|
|
1109
|
+
* 面板和用户草稿。新版本入口始终创建独立会话,不再依赖 DOM 文案推测导航。
|
|
1111
1110
|
*/
|
|
1112
1111
|
function installSessionOwnershipBridge(ctx) {
|
|
1112
|
+
const releaseNewSessionBridge = installNewSessionBridge(ctx);
|
|
1113
1113
|
let staleDraftObserver = null;
|
|
1114
1114
|
let staleDraftObserverTimer = null;
|
|
1115
1115
|
const clearStaleDefaultDraft = () => {
|
|
@@ -1124,26 +1124,9 @@ window.__ModuleLoader__.load({
|
|
|
1124
1124
|
};
|
|
1125
1125
|
const leaveSubsystem = () => {
|
|
1126
1126
|
const sessionId = activeCleaningSessionId;
|
|
1127
|
-
if (sessionId) clearCleaningDraft(ctx, sessionId);
|
|
1127
|
+
if (sessionId) clearCleaningDraft(ctx, sessionId, true);
|
|
1128
1128
|
deactivateCleaningSession(sessionId);
|
|
1129
1129
|
};
|
|
1130
|
-
const handleSidebarNavigation = (event) => {
|
|
1131
|
-
if (!activeCleaningSessionId) return;
|
|
1132
|
-
const target = event?.target;
|
|
1133
|
-
if (!target || typeof target.closest !== 'function') return;
|
|
1134
|
-
if (target.closest('.dcAgentLauncher, .dcAgentExperience, .dcAgentPromptPopover, .dcAgentWorkbench, .dcAgentHeaderAction')) return;
|
|
1135
|
-
|
|
1136
|
-
const actionable = target.closest('button, a, [role="button"]');
|
|
1137
|
-
if (!actionable) return;
|
|
1138
|
-
const workspaceSlot = document.querySelector?.(SIDEBAR_WORKSPACES_SELECTOR);
|
|
1139
|
-
const sidebarRoot = workspaceSlot?.closest?.('aside, nav, [data-slot="sidebar"], [class*="sidebar"]')
|
|
1140
|
-
?? workspaceSlot?.parentElement;
|
|
1141
|
-
const label = `${actionable.getAttribute?.('aria-label') ?? ''} ${actionable.textContent ?? ''}`.trim();
|
|
1142
|
-
const sessionOrAgentEntry = /新建?会话|新会话|new\s+session|智能体|agent|尽调/i.test(label);
|
|
1143
|
-
if (sidebarRoot?.contains?.(actionable) || sessionOrAgentEntry) leaveSubsystem();
|
|
1144
|
-
};
|
|
1145
|
-
|
|
1146
|
-
document.addEventListener?.('click', handleSidebarNavigation, true);
|
|
1147
1130
|
// 升级 / 刷新后不恢复子系统所有权;仅清理由旧版本遗留且仍未被用户编辑的默认文案。
|
|
1148
1131
|
clearStaleDefaultDraft();
|
|
1149
1132
|
// DSH 会在插件 apply() 之后异步恢复 composer draft。在首屏稳定期内
|
|
@@ -1163,15 +1146,18 @@ window.__ModuleLoader__.load({
|
|
|
1163
1146
|
}, 10_000);
|
|
1164
1147
|
}
|
|
1165
1148
|
const unsubscribe = ctx.sessions?.list?.subscribe?.((snapshot) => {
|
|
1166
|
-
|
|
1167
|
-
|
|
1149
|
+
// Store subscriptions may signal without a payload; read authoritative state.
|
|
1150
|
+
const selected = ctx.sessions?.list?.getSnapshot?.() ?? snapshot;
|
|
1151
|
+
if (!selected) return;
|
|
1152
|
+
const nextSessionId = selected.current;
|
|
1153
|
+
if (activeCleaningSessionId && nextSessionId !== activeCleaningSessionId) {
|
|
1168
1154
|
leaveSubsystem();
|
|
1169
1155
|
} else if (!activeCleaningSessionId) {
|
|
1170
1156
|
clearStaleDefaultDraft();
|
|
1171
1157
|
}
|
|
1172
1158
|
});
|
|
1173
1159
|
return () => {
|
|
1174
|
-
|
|
1160
|
+
releaseNewSessionBridge();
|
|
1175
1161
|
staleDraftObserver?.disconnect();
|
|
1176
1162
|
if (staleDraftObserverTimer !== null) clearTimeout(staleDraftObserverTimer);
|
|
1177
1163
|
if (typeof unsubscribe === 'function') unsubscribe();
|
|
@@ -1565,6 +1551,45 @@ window.__ModuleLoader__.load({
|
|
|
1565
1551
|
return mount;
|
|
1566
1552
|
}
|
|
1567
1553
|
|
|
1554
|
+
// rc/alpha 兼容边界:原生 startSession 会复用工作区内的空白会话。
|
|
1555
|
+
// 只在清洗会话激活时替换此动作,创建普通会话;不改 connectWorkspace 或全局点击。
|
|
1556
|
+
function installNewSessionBridge(ctx) {
|
|
1557
|
+
const workspaces = ctx.workspaces;
|
|
1558
|
+
const original = workspaces?.startSession;
|
|
1559
|
+
if (typeof original !== 'function' || typeof ctx.sessions?.create !== 'function') return () => {};
|
|
1560
|
+
const ownDescriptor = Object.getOwnPropertyDescriptor(workspaces, 'startSession');
|
|
1561
|
+
let pending = null;
|
|
1562
|
+
let disposed = false;
|
|
1563
|
+
const wrapped = function (workspaceId) {
|
|
1564
|
+
if (disposed) return original.call(this, workspaceId);
|
|
1565
|
+
const current = ctx.sessions.list.getSnapshot().current;
|
|
1566
|
+
if (!isCleaningSession(current)) return original.call(this, workspaceId);
|
|
1567
|
+
if (pending) return;
|
|
1568
|
+
const snapshot = workspaces.list.getSnapshot();
|
|
1569
|
+
const target = workspaceId
|
|
1570
|
+
?? snapshot.items.find((item) => item.sessionIds?.includes(current))?.workspaceId
|
|
1571
|
+
?? snapshot.recentWorkspaceId;
|
|
1572
|
+
if (!target) return original.call(this, workspaceId);
|
|
1573
|
+
pending = Promise.resolve().then(() => ctx.sessions.create({ workspaceId: target }))
|
|
1574
|
+
.then((sessionId) => {
|
|
1575
|
+
// 用户在等待期间转往其它智能体时不抢回页面;成功导航才撤销业务归属。
|
|
1576
|
+
if (!disposed && isCleaningSession(current) && ctx.sessions.list.getSnapshot().current === current) {
|
|
1577
|
+
ctx.sessions.open(sessionId);
|
|
1578
|
+
}
|
|
1579
|
+
}).catch((error) => {
|
|
1580
|
+
console.warn('[dc-agent] new session failed; current draft retained:', error instanceof Error ? error.message : String(error));
|
|
1581
|
+
}).finally(() => { pending = null; });
|
|
1582
|
+
};
|
|
1583
|
+
try { workspaces.startSession = wrapped; }
|
|
1584
|
+
catch (_error) { console.warn('[dc-agent] New Session compatibility bridge unavailable'); return () => {}; }
|
|
1585
|
+
return () => {
|
|
1586
|
+
disposed = true;
|
|
1587
|
+
if (workspaces.startSession !== wrapped) return;
|
|
1588
|
+
if (ownDescriptor) Object.defineProperty(workspaces, 'startSession', ownDescriptor);
|
|
1589
|
+
else delete workspaces.startSession;
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1568
1593
|
const CLEANING_SESSION_ID_PREFIX = 'session-dsh-data-cleaning-agent-';
|
|
1569
1594
|
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
1595
|
function createCleaningSessionId() {
|
|
@@ -1583,28 +1608,28 @@ window.__ModuleLoader__.load({
|
|
|
1583
1608
|
? undefined
|
|
1584
1609
|
: items.find((item) => Array.isArray(item.sessionIds) && item.sessionIds.includes(current));
|
|
1585
1610
|
const recentWorkspace = items.find((item) => item.workspaceId === workspace.recentWorkspaceId);
|
|
1586
|
-
const
|
|
1587
|
-
if (
|
|
1611
|
+
const targetWorkspace = currentWorkspace ?? recentWorkspace ?? items[0];
|
|
1612
|
+
if (!targetWorkspace?.workspaceId) {
|
|
1588
1613
|
throw new Error('请先选择一个工作空间,再打开数据清洗补全智能体');
|
|
1589
1614
|
}
|
|
1590
1615
|
// 单一会话所有权:与招投标入口一致,显式创建带前缀的独立原生会话,绝不复用空白会话。
|
|
1591
1616
|
// connectWorkspace 会把「招投标」尚为空白的工作台会话复用为清洗会话,
|
|
1592
1617
|
// 使同一会话同时被招投标与清洗两个插件认领,进而争夺 Hero 标题与右侧工作台面板。
|
|
1593
|
-
//
|
|
1594
|
-
//
|
|
1618
|
+
// 原生 Hero 通过 workspace.sessionIds 判断输入是否可用。仅 cwd 会导致
|
|
1619
|
+
// chipTitle 缺失、inert=true;必须关联工作区,空白复用由 New Session Bridge 隔离。
|
|
1595
1620
|
if (typeof ctx.sessions.create !== 'function') {
|
|
1596
1621
|
throw new Error('当前 DSH 版本没有可用的独立会话创建能力');
|
|
1597
1622
|
}
|
|
1598
1623
|
const requestedId = createCleaningSessionId();
|
|
1599
|
-
const sessionId = await ctx.sessions.create({
|
|
1624
|
+
const sessionId = await ctx.sessions.create({ workspaceId: targetWorkspace.workspaceId, sessionId: requestedId });
|
|
1600
1625
|
if (sessionId !== requestedId) {
|
|
1601
1626
|
throw new Error('当前 DSH 版本不支持独立会话创建,请升级 DSH 后再打开数据清洗补全智能体');
|
|
1602
1627
|
}
|
|
1603
1628
|
const conversation = typeof ctx.get === 'function' ? ctx.get('conversation') : ctx.conversation;
|
|
1604
1629
|
if (!conversation) throw new Error('DSH 对话服务尚未就绪,请稍后重试');
|
|
1605
|
-
markCleaningSession(sessionId);
|
|
1606
1630
|
conversation.input.shell(sessionId).setDraft(DEFAULT_SESSION_PROMPT);
|
|
1607
1631
|
ctx.sessions.open(sessionId);
|
|
1632
|
+
markCleaningSession(sessionId);
|
|
1608
1633
|
return sessionId;
|
|
1609
1634
|
}
|
|
1610
1635
|
|
|
@@ -1993,6 +2018,18 @@ window.__ModuleLoader__.load({
|
|
|
1993
2018
|
.filter(Boolean);
|
|
1994
2019
|
}
|
|
1995
2020
|
|
|
2021
|
+
function isImagePathEntry(value) {
|
|
2022
|
+
return /^(?:file:\/\/|\/|[a-z]:[\\/])/i.test(String(value).trim().replace(/^["']/, ''))
|
|
2023
|
+
&& /\.(?:png|jpe?g|webp|gif|heic|bmp)(?:["']|\s)*$/i.test(String(value));
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
function requestPromptImage(sessionId, file = null) {
|
|
2027
|
+
if (!sessionId || typeof document.createEvent !== 'function') return false;
|
|
2028
|
+
const event = document.createEvent('CustomEvent');
|
|
2029
|
+
event.initCustomEvent('dsh:data-cleaning-prompt-image', false, true, { sessionId, file });
|
|
2030
|
+
return !document.dispatchEvent(event);
|
|
2031
|
+
}
|
|
2032
|
+
|
|
1996
2033
|
async function imageFileBase64(file) {
|
|
1997
2034
|
if (!file || typeof file.arrayBuffer !== 'function') throw new Error('图片文件不可读。');
|
|
1998
2035
|
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
@@ -2016,7 +2053,7 @@ window.__ModuleLoader__.load({
|
|
|
2016
2053
|
}
|
|
2017
2054
|
|
|
2018
2055
|
function PromptGenerator(props) {
|
|
2019
|
-
const { sessionId, inputActions, attachImages, removeImage } = props;
|
|
2056
|
+
const { sessionId, inputActions, attachImages, removeImage, getInputSnapshot } = props;
|
|
2020
2057
|
const enabled = useCleaningSession(sessionId);
|
|
2021
2058
|
const [open, setOpen] = react.useState(false);
|
|
2022
2059
|
const [wizardStep, setWizardStep] = react.useState(1);
|
|
@@ -2177,6 +2214,7 @@ window.__ModuleLoader__.load({
|
|
|
2177
2214
|
const validateSource = () => {
|
|
2178
2215
|
const values = selectedEntries();
|
|
2179
2216
|
if (mode === 'text' && !values.length) return '请先录入企业名称或统一社会信用代码,每行一条。';
|
|
2217
|
+
if (mode === 'text' && values.some(isImagePathEntry)) return '检测到图片文件路径,不是企业名单。请切换「上传图片识别」,重新选择或粘贴图片。';
|
|
2180
2218
|
if (mode === 'excel' && !values.length) return '请先上传并解析 Excel / CSV / JSON 文件。';
|
|
2181
2219
|
if (mode === 'image' && !imageCommand) return '请先选择、粘贴或拖入一张包含企业名单的图片。';
|
|
2182
2220
|
if (mode === 'image' && imageCommand?.state === 'failed') return imageCommand.error?.message || '图片识别失败,请重新选择图片。';
|
|
@@ -2205,13 +2243,26 @@ window.__ModuleLoader__.load({
|
|
|
2205
2243
|
setError('当前会话输入机尚未就绪,请稍后重试。');
|
|
2206
2244
|
return;
|
|
2207
2245
|
}
|
|
2246
|
+
const inputSnapshot = getInputSnapshot?.(sessionId);
|
|
2247
|
+
if (['adjudicating', 'submitting'].includes(inputSnapshot?.phase)) {
|
|
2248
|
+
setError('当前对话正在提交,请等待完成后再回填。图片与字段选择已保留。');
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2208
2251
|
const config = promptConfig();
|
|
2209
2252
|
if (mode === 'image' && imageCommand?.state === 'prepared' && imageAttachment && typeof removeImage === 'function') {
|
|
2210
2253
|
// qcc-document-mcp 只读取 Host 暂存文件;发送前释放原生附件,文本模型不会收到图片。
|
|
2211
2254
|
removeImage(sessionId, imageAttachment);
|
|
2212
2255
|
setImageAttachment(null);
|
|
2213
2256
|
}
|
|
2214
|
-
|
|
2257
|
+
const prompt = buildTaskPrompt(config);
|
|
2258
|
+
try {
|
|
2259
|
+
inputActions.setDraft(prompt);
|
|
2260
|
+
const updatedInput = getInputSnapshot?.(sessionId);
|
|
2261
|
+
if (updatedInput && updatedInput.draft !== prompt) throw new Error('当前对话尚未接收任务说明,请稍后重试回填。');
|
|
2262
|
+
} catch (draftError) {
|
|
2263
|
+
setError(draftError instanceof Error ? draftError.message : String(draftError));
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2215
2266
|
if (mode === 'text') {
|
|
2216
2267
|
requestWorkbenchDataset(entriesToDataset(config.entries), sessionId, { type: 'text', fileName: '' });
|
|
2217
2268
|
}
|
|
@@ -2260,22 +2311,38 @@ window.__ModuleLoader__.load({
|
|
|
2260
2311
|
const hasImage = files.length > 0;
|
|
2261
2312
|
if (!hasImage) return;
|
|
2262
2313
|
const target = event.target;
|
|
2263
|
-
const inComposer = !open &&
|
|
2264
|
-
|
|
2265
|
-
|
|
2314
|
+
const inComposer = !open && !target?.closest?.('.dcAgentWorkbench')
|
|
2315
|
+
&& Boolean(target?.closest?.('textarea, [contenteditable="true"], [role="textbox"]'));
|
|
2316
|
+
const inSourceStep = open && wizardStep === 1 && Boolean(target?.closest?.('.dcAgentPromptPanel'));
|
|
2317
|
+
if (!inComposer && !inSourceStep) return;
|
|
2266
2318
|
event.preventDefault();
|
|
2267
2319
|
event.stopImmediatePropagation?.();
|
|
2268
|
-
if (
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
}
|
|
2320
|
+
if (busy || imageCommand?.state === 'running') return;
|
|
2321
|
+
setMode('image');
|
|
2322
|
+
setWizardStep(1);
|
|
2323
|
+
setOpen(true);
|
|
2324
|
+
setError(null);
|
|
2274
2325
|
acceptImageFiles(files);
|
|
2275
2326
|
};
|
|
2276
2327
|
window.addEventListener?.('paste', handleWindowPaste, true);
|
|
2277
2328
|
return () => window.removeEventListener?.('paste', handleWindowPaste, true);
|
|
2278
|
-
}, [enabled, open, wizardStep, mode, busy, sessionId]);
|
|
2329
|
+
}, [enabled, open, wizardStep, mode, busy, sessionId, imageCommand, imageAttachment]);
|
|
2330
|
+
|
|
2331
|
+
// 右侧工作台共用此图片入口;仅由当前会话确认接收,不把图片交给表格解析器。
|
|
2332
|
+
react.useEffect(() => {
|
|
2333
|
+
if (!enabled) return undefined;
|
|
2334
|
+
const receiveImage = (event) => {
|
|
2335
|
+
if (event.detail?.sessionId !== sessionId || busy || imageCommand?.state === 'running') return;
|
|
2336
|
+
event.preventDefault();
|
|
2337
|
+
setMode('image');
|
|
2338
|
+
setWizardStep(1);
|
|
2339
|
+
setOpen(true);
|
|
2340
|
+
setError(null);
|
|
2341
|
+
if (event.detail.file) acceptImageFiles([event.detail.file]);
|
|
2342
|
+
};
|
|
2343
|
+
document.addEventListener('dsh:data-cleaning-prompt-image', receiveImage);
|
|
2344
|
+
return () => document.removeEventListener('dsh:data-cleaning-prompt-image', receiveImage);
|
|
2345
|
+
}, [enabled, sessionId, busy, imageCommand, imageAttachment]);
|
|
2279
2346
|
|
|
2280
2347
|
if (!enabled) return null;
|
|
2281
2348
|
|
|
@@ -2858,9 +2925,22 @@ window.__ModuleLoader__.load({
|
|
|
2858
2925
|
}
|
|
2859
2926
|
};
|
|
2860
2927
|
|
|
2928
|
+
const openImageIntake = (file = null) => {
|
|
2929
|
+
if (!requestPromptImage(activeSessionId, file)) {
|
|
2930
|
+
actions.setError('图片入口尚未就绪或正在处理图片,请回到当前清洗会话稍后重试。');
|
|
2931
|
+
return false;
|
|
2932
|
+
}
|
|
2933
|
+
actions.close();
|
|
2934
|
+
return true;
|
|
2935
|
+
};
|
|
2861
2936
|
const handleFile = async (event) => {
|
|
2862
2937
|
const file = event.target && event.target.files && event.target.files[0];
|
|
2863
2938
|
if (!file) return;
|
|
2939
|
+
if (String(file.type ?? '').startsWith('image/')) {
|
|
2940
|
+
openImageIntake(file);
|
|
2941
|
+
event.target.value = '';
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2864
2944
|
actions.setBusy(true);
|
|
2865
2945
|
actions.setError(null);
|
|
2866
2946
|
try {
|
|
@@ -3606,19 +3686,28 @@ window.__ModuleLoader__.load({
|
|
|
3606
3686
|
);
|
|
3607
3687
|
} else {
|
|
3608
3688
|
pane = h('div', { className: 'dcAgentPane' },
|
|
3609
|
-
h('p', { className: 'dcAgentHint' }, '上传 CSV / XLSX / XLS / JSON
|
|
3689
|
+
h('p', { className: 'dcAgentHint' }, '上传 CSV / XLSX / XLS / JSON,或粘贴数据。也可选择图片,通过企查查智能文档解析提取名单后继续清洗补全。'),
|
|
3610
3690
|
h('input', {
|
|
3611
3691
|
type: 'file',
|
|
3612
|
-
accept: '.csv,.json,.xlsx,.xls',
|
|
3692
|
+
accept: '.csv,.json,.xlsx,.xls,image/png,image/jpeg,image/webp',
|
|
3613
3693
|
className: 'dcAgentField',
|
|
3614
3694
|
'aria-label': '选择数据文件',
|
|
3615
3695
|
onChange: handleFile,
|
|
3616
3696
|
}),
|
|
3697
|
+
h('button', { type: 'button', className: 'dcAgentButton', disabled: busy,
|
|
3698
|
+
onClick: () => openImageIntake() }, '图片识别(粘贴 / 选择)'),
|
|
3617
3699
|
h('textarea', {
|
|
3618
3700
|
className: 'dcAgentTextarea',
|
|
3619
3701
|
placeholder: '粘贴 CSV 文本,或 JSON 数组(例如 [{"name":"某公司","amount":"100"}])…',
|
|
3620
3702
|
'aria-label': '粘贴数据',
|
|
3621
3703
|
value: input,
|
|
3704
|
+
onPaste: (event) => {
|
|
3705
|
+
const files = imageFilesFromTransfer(event.clipboardData);
|
|
3706
|
+
if (files.length && openImageIntake(files[0])) {
|
|
3707
|
+
event.preventDefault();
|
|
3708
|
+
event.stopPropagation?.();
|
|
3709
|
+
}
|
|
3710
|
+
},
|
|
3622
3711
|
onInput: (event) => actions.setInput(event.target.value),
|
|
3623
3712
|
}),
|
|
3624
3713
|
dataset ? h('div', { className: 'dcAgentChips' },
|
|
@@ -3770,6 +3859,10 @@ window.__ModuleLoader__.load({
|
|
|
3770
3859
|
inject: () => ({
|
|
3771
3860
|
attachImages: (sessionId, files) => attachPromptImages(ctx, sessionId, files),
|
|
3772
3861
|
removeImage: (sessionId, attachment) => removePromptImage(ctx, sessionId, attachment),
|
|
3862
|
+
getInputSnapshot: (sessionId) => {
|
|
3863
|
+
const conversation = typeof ctx.get === 'function' ? ctx.get('conversation') : ctx.conversation;
|
|
3864
|
+
return conversation?.input?.shell?.(sessionId)?.snapshot;
|
|
3865
|
+
},
|
|
3773
3866
|
}),
|
|
3774
3867
|
}, PromptGenerator));
|
|
3775
3868
|
|
|
@@ -3808,9 +3901,12 @@ window.__ModuleLoader__.load({
|
|
|
3808
3901
|
entriesToDataset,
|
|
3809
3902
|
extractPromptEntries,
|
|
3810
3903
|
imageFilesFromTransfer,
|
|
3904
|
+
isImagePathEntry,
|
|
3905
|
+
requestPromptImage,
|
|
3811
3906
|
guessMappings,
|
|
3812
3907
|
deactivateCleaningSession,
|
|
3813
3908
|
installSessionOwnershipBridge,
|
|
3909
|
+
installNewSessionBridge,
|
|
3814
3910
|
isCleaningSession,
|
|
3815
3911
|
isKnownCleaningDraft,
|
|
3816
3912
|
markCleaningSession,
|
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.5",
|
|
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",
|