dsh-form-fill-agent 0.2.8 → 0.2.10

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
@@ -43,7 +43,7 @@ dsh plugin --profile web add dsh-form-fill-agent@latest
43
43
 
44
44
  ## 使用边界与数据保存
45
45
 
46
- 补充要求为模型可读文本;字段或工作表范围请通过向导/工作台修改,不能仅改文字后假定执行范围已同步。任务 schema 5 可读取 schema 1–5;回退旧版本请使用升级前目录副本或独立任务目录。主包为 0.2.8,共享内核为 0.2.8,Provider 保持 0.2.0。
46
+ 补充要求为模型可读文本;字段或工作表范围请通过向导/工作台修改,不能仅改文字后假定执行范围已同步。任务 schema 5 可读取 schema 1–5;回退旧版本请使用升级前目录副本或独立任务目录。主包为 0.2.10,共享内核为 0.2.8,Provider 保持 0.2.0。
47
47
 
48
48
  支持企业完整登记名称或 18 位信用代码;简称、主体不一致或多候选不会自动猜选。仅有信用代码的表格也可作为查询锚点。连接候选检索工具时可检索简称,仍需人工确认;缺少工具时降级人工输入。字段映射采用确定性规则及人工确认,不让模型编造事实。支持普通内部公式、固定区域下拉、基础条件格式和普通表格对象;复杂 Excel 结构和 Word 暂不支持,保真边界见下方安全说明。
49
49
 
@@ -51,7 +51,7 @@ dsh plugin --profile web add dsh-form-fill-agent@latest
51
51
 
52
52
  显式设置 DSH_HOME 时,任务保存至其 form-fill-tasks 目录,默认 24 小时过期;未设置时使用内存。任务保存输入表格和归一化预览,不持久化原始 MCP 响应,可主动删除任务。模型及 MCP 凭据由宿主管理。
53
53
 
54
- 0.2.8 保存任务 schema 5,兼容读取旧 schema 1–4;回退旧版本须使用升级前任务目录副本或新目录,防止丢失已确认的字段/主体设置。
54
+ 0.2.10 保存任务 schema 5,兼容读取旧 schema 1–4;回退旧版本须使用升级前任务目录副本或新目录,防止丢失已确认的字段/主体设置。
55
55
 
56
56
  ## 文档与反馈
57
57
 
package/lib/brand.css CHANGED
@@ -45,3 +45,5 @@
45
45
  .ff-ui #history{padding:20px}.ff-ui #preview>.controls{padding:0 20px 20px}.ff-ui input[type=file]{width:100%;max-width:100%;min-width:0;font-size:12px}.ff-ui input[type=file]::file-selector-button{font:inherit;border:1px solid var(--line);border-radius:6px;padding:8px 12px;margin-right:10px;background:var(--surface);color:var(--text);cursor:pointer}.ff-ui #samples{margin-top:12px}.ff-ui .history-item{background:var(--surface);padding:16px;border:1px solid var(--line);border-radius:8px;margin-top:12px}
46
46
  @media(max-width:640px){.ff-ui .ff-shortcuts{justify-content:flex-start;padding-inline:12px;gap:8px}.ff-ui .ff-shortcuts button{flex:0 0 auto;min-width:92px;padding:7px 10px;font-size:12px}.ff-ui .card{margin:14px;padding:16px}.ff-ui #grid{margin:14px}.ff-ui .task-tabs{padding-inline:14px}.ff-ui #status{padding-inline:14px}.ff-ui #task-meta{margin-inline:14px;max-width:calc(100% - 28px)}.ff-ui .ff-execution-toolbar{margin-inline:14px}.ff-ui .ff-panel-head{padding:12px}.ff-ui .ff-panel-head .ff-brand{gap:8px}.ff-ui .ff-panel-head h2{font-size:14px}.ff-ui .ff-panel-actions{gap:4px}.ff-ui .ff-panel-actions button{font-size:11px;padding:6px}.ff-ui .ff-workbench-head{padding:16px}}
47
47
  .ff-ui .ff-field-picker{position:relative;width:100%;min-width:0}.ff-ui .ff-field-picker>summary{padding:10px 12px;border:1px solid var(--line);border-radius:8px;background:var(--surface);cursor:pointer;overflow-wrap:anywhere}.ff-ui .ff-field-picker[open]>summary{border-color:var(--blue)}.ff-ui .ff-field-panel{padding:12px;border:1px solid var(--line);border-radius:8px;background:var(--surface);margin-top:4px;min-width:0}.ff-ui .ff-field-panel select[data-column]{width:100%;height:auto;max-width:100%}.ff-ui .ff-field-panel optgroup{font-weight:600}.ff-ui .ff-field-panel option{padding:7px;font-weight:400}
48
+ .ff-ui #wizard .dialog-head{align-items:flex-start;border-bottom:0;padding:24px 24px 12px}.ff-ui #wizard .dialog-head h2{margin:0;font-size:22px}.ff-ui #wizard .dialog-head p{margin:8px 0 0;line-height:1.6}.ff-ui #wizard-close{border-radius:50%;width:44px;height:44px;min-width:44px;padding:0;font-size:26px;line-height:1}.ff-ui .ff-wizard-nav{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;padding:8px 24px 12px;flex-shrink:0}.ff-ui .ff-wizard-nav button{display:flex;align-items:center;gap:8px;min-height:52px;padding:10px;font-size:14px;border:1px solid var(--line);border-radius:8px;background:var(--surface);color:var(--text);text-align:left}.ff-ui .ff-step-number{display:inline-flex;align-items:center;justify-content:center;flex:0 0 28px;height:28px;border-radius:50%;background:var(--line);color:var(--muted);font-weight:600}.ff-ui .ff-wizard-nav button[aria-current=true]{border-color:var(--action);background:var(--soft);color:var(--action)}.ff-ui .ff-wizard-nav button[aria-current=true] .ff-step-number{background:var(--action);color:white}.ff-ui #wizard .dialog-body{padding:16px 24px 24px}.ff-ui #wizard h3{font-size:18px;margin:0 0 12px}.ff-ui #wizard p{line-height:1.65}.ff-ui .ff-upload-zone{padding:18px;background:var(--page);border:1px dashed var(--line);border-radius:8px}.ff-ui .ff-upload-zone label{display:grid;gap:12px;font-weight:600}.ff-ui .ff-service-note{padding:12px 16px;border-radius:8px;background:var(--page);color:var(--muted)}.ff-ui #wizard .dialog-footer{padding:16px 24px}.ff-ui #wizard .dialog-footer button{min-height:44px}
49
+ @media(max-width:640px){.ff-ui .ff-wizard-nav{grid-template-columns:repeat(2,minmax(0,1fr));padding:8px 14px;gap:6px}.ff-ui .ff-wizard-nav button{min-height:44px;padding:6px 8px}.ff-ui #wizard .dialog-head{padding:16px 14px 8px}.ff-ui #wizard .dialog-head h2{font-size:20px}.ff-ui #wizard .dialog-head p{font-size:13px}.ff-ui #wizard .dialog-body{padding:14px}.ff-ui #wizard .dialog-footer{padding:12px 14px}}
package/lib/ui.html CHANGED
@@ -24,10 +24,10 @@
24
24
  <div class="card" data-pane="download"><h2>确认并下载</h2><p>原文件不会被覆盖。下载包含全部已确认填写及完整清单,不受当前筛选影响。</p><button id="confirm" class="primary">确认这些填写,生成新副本</button><div id="downloads"></div></div>
25
25
  <div class="controls"><button id="refresh" class="secondary">刷新任务</button><button id="discard" class="secondary">删除任务</button></div>
26
26
  </section></main>
27
- <dialog id="wizard" aria-labelledby="wizard-title"><header class="dialog-head"><h2 id="wizard-title">生成 AI填表任务</h2><button id="wizard-close" aria-label="关闭提示词向导">关闭</button></header>
28
- <nav class="tabs" aria-label="提示词向导步骤"><button data-wizard-step="0">1 数据来源</button><button data-wizard-step="1">2 填写规则</button><button data-wizard-step="2">3 填写字段</button><button data-wizard-step="3">4 确认描述</button></nav>
27
+ <dialog id="wizard" aria-labelledby="wizard-title"><header class="dialog-head"><div><h2 id="wizard-title">生成 AI填表任务</h2><p class="muted">上传原表、确认填写范围,使用企查查 MCP 核验企业信息并填写空白单元格。</p></div><button id="wizard-close" aria-label="关闭提示词向导">×</button></header>
28
+ <nav class="ff-wizard-nav" aria-label="提示词向导步骤"><button data-wizard-step="0"><span class="ff-step-number">1</span> 数据来源</button><button data-wizard-step="1"><span class="ff-step-number">2</span> 填写规则</button><button data-wizard-step="2"><span class="ff-step-number">3</span> 填写字段</button><button data-wizard-step="3"><span class="ff-step-number">4</span> 确认描述</button></nav>
29
29
  <div class="dialog-body">
30
- <section class="ff-wizard-step" data-wizard-pane="0"><h3>上传已有表格</h3><p>仅支持 XLSX;选择文件后执行本地识别,不调用企查查。</p><label>选择 XLSX <input id="wizard-file" type="file" accept=".xlsx"></label><p id="wizard-source"></p></section>
30
+ <section class="ff-wizard-step" data-wizard-pane="0"><h3>选择需要填写的原表</h3><p class="muted">识别表头与空白字段,再由企查查 MCP 查询对应的企业信息,填写到原表位置。</p><div class="ff-upload-zone"><label>上传 Excel 表格 <input id="wizard-file" type="file" accept=".xlsx"></label><p class="muted">支持 XLSX,最大 8 MiB。保留已有内容,确认结果后生成新副本。</p></div><p id="wizard-source" class="ff-summary"></p><p class="ff-service-note">完成向导后,将任务描述回填到对话框;发送后使用已连接的企查查 MCP 执行核验与填写。</p></section>
31
31
  <section class="ff-wizard-step" data-wizard-pane="1"><h3>填写规则</h3><p>只填空白、保留已有内容,查询后预览,确认后生成副本。</p><p id="wizard-rules"></p><button id="wizard-edit-rules">到工作台修正字段映射</button></section>
32
32
  <section class="ff-wizard-step" data-wizard-pane="2"><h3>企业工商字段</h3><div class="toolbar"><label>搜索 <input id="field-search" type="search" placeholder="字段名称"></label><span id="field-count"></span><button id="fields-all">选择可用字段</button><button id="fields-clear">清空可选字段</button></div><p class="muted">主体锚点固定保留;只有原表已映射的字段可选。映射修改请返回工作台。</p><div id="wizard-fields" class="ff-field-grid"></div></section>
33
33
  <section class="ff-wizard-step" data-wizard-pane="3"><h3>确认任务描述</h3><p id="business-summary" class="ff-summary"></p><p id="scope-review" class="ff-summary"></p><label id="scope-ack-label" hidden><input id="scope-ack" type="checkbox">确认本次不填写以上列</label><label>补充要求<textarea id="requirements" rows="4" maxlength="1000" placeholder="补充核验说明;字段或范围变更请返回前面步骤设置。"></textarea></label><details><summary>兼容执行信息</summary><p id="qcc-command" class="ff-tech"></p></details></section>
@@ -84,7 +84,17 @@ function initWizard(){const structure=mappingVersion();if(wizardTask===current?.
84
84
  function scopeReview(){
85
85
  const omitted=current?.analysis.tables.flatMap(t=>t.mappings.filter(m=>!t.anchors.includes(m.field)&&(!m.field||!wizardFields.has(m.field))).map(m=>t.sheet+':'+m.label))||[];
86
86
  const missing=current?.analysis.incomplete.filter(i=>i.reason==='missing-anchor')||[];
87
- return {omitted,missing,blocked:missing.length>0&&!current?.analysis.opportunities.length};
87
+ let problem='';
88
+ const tables=current?.analysis.tables||[],issues=current?.analysis.incomplete||[];
89
+ if(!tables.length){
90
+ const conflicts=issues.filter(i=>i.reason==='ambiguous-mapping');
91
+ if(conflicts.length){
92
+ const details=conflicts.map(i=>{const mappings=current.configuration?.mappings?.filter(m=>m.sheet===i.sheet&&m.field)||[];const repeated=[...new Set(mappings.filter(m=>mappings.some(n=>n.column!==m.column&&n.field===m.field)).map(m=>current.catalog.find(f=>f.key===m.field)?.label||m.field))];return i.sheet+(repeated.length?':'+repeated.join('、'):'')});
93
+ problem='字段映射存在重复或冲突('+details.join(';')+')。请返回字段设置,同一目标字段保留一列,其他列选择不填写。';
94
+ }else problem='未识别到可执行的表头。请在字段设置确认表头行,保留企业名称或有值的信用代码,并至少映射一个需要填写的字段。';
95
+ }else if(!wizardFields.size)problem='尚未选择填写字段。请返回字段设置,为原表中至少一个待填列选择对应字段;企业名称仅用于定位主体。';
96
+ else if(missing.length&&!current.analysis.opportunities.length)problem='待填行缺少可用企业标识。请返回字段设置映射有值的企业名称列,或检查原表主体内容。';
97
+ return {omitted,missing,blocked:!!problem,problem};
88
98
  }
89
99
  function renderWizard(){
90
100
  initWizard();
@@ -129,14 +139,14 @@ function directScope(){
129
139
  wizardFields=new Set(current?.selectedFields||current?.analysis.tables.flatMap(t=>t.mappings.filter(m=>m.field&&!t.anchors.includes(m.field)).map(m=>m.field))||[]);
130
140
  const review=scopeReview(),signature=mappingVersion();
131
141
  if($('direct-ack').dataset.signature!==signature){$('direct-ack').checked=false;$('direct-ack').dataset.signature=signature}
132
- $('direct-scope').textContent=review.blocked?'缺少可用企业标识,请返回字段设置。':'将填写 '+wizardFields.size+' 个已确认字段。'+(review.omitted.length?'未纳入:'+review.omitted.join('、')+'。':'');
142
+ $('direct-scope').textContent=review.problem||'将填写 '+wizardFields.size+' 个已确认字段。'+(review.omitted.length?'未纳入:'+review.omitted.join('、')+'。':'');
133
143
  $('direct-ack-label').hidden=!review.omitted.length;
134
144
  return review;
135
145
  }
136
146
  async function directDraft(){
137
147
  if(busy||!current)return;
138
148
  const review=directScope();
139
- if(review.blocked||!wizardFields.size){$('status').textContent='请先完善企业标识和填写字段。';return}
149
+ if(review.blocked){$('status').textContent=review.problem;navigate('rules');$('anchor-alert').textContent=review.problem;$('anchor-alert').scrollIntoView({block:'center'});return}
140
150
  if(review.omitted.length&&!$('direct-ack').checked){$('status').textContent='请确认本页未纳入的字段,或返回字段设置完善映射。';return}
141
151
  $('scope-ack').checked=$('direct-ack').checked;
142
152
  if(parent===window){$('direct-command').hidden=false;$('direct-command').value=command();$('direct-command').focus();$('direct-command').select();$('status').textContent='指令已生成,请复制到 DSH 对话框发送。';return}
@@ -251,7 +261,7 @@ function readMapping(){const configuration={sheets:[],headers:[],mappings:[]};fo
251
261
  function mappingSignature(configuration){return JSON.stringify(configuration.headers.map(h=>({sheet:h.sheet,row:h.row,cells:current.structure.find(s=>s.name===h.sheet)?.rows.find(r=>r.number===h.row)?.cells}))) }
252
262
  $('mapping-save').onclick=()=>{try{if(!current||busy)return;const configuration=readMapping();localStorage.setItem('ff-mapping:'+owner,JSON.stringify({schema:1,signature:mappingSignature(configuration),configuration}));$('status').textContent='字段规则已保存在本浏览器。'}catch{$('status').textContent='浏览器无法保存字段规则。'}};
253
263
  $('mapping-load').onclick=async()=>{if(!current||busy)return;controls(true);try{const saved=JSON.parse(localStorage.getItem('ff-mapping:'+owner)||'null');if(saved?.schema!==1||saved.signature!==mappingSignature(saved.configuration))throw Error('没有与当前工作表及表头一致的规则,请手动设置。');render(await api('/configure',{id:current.id,configuration:saved.configuration}));$('status').textContent='字段规则已复用,请检查后重新核验。'}catch(e){$('status').textContent=e.message}finally{controls(false)}};
254
- $('configure').onclick=async()=>{if(!current||busy)return;const configuration=readMapping();controls(true);$('mapping-next').hidden=true;try{render(await api('/configure',{id:current.id,configuration}));$('status').textContent='字段设置已应用,旧预览已清除,请重新核验。';$('mapping-next').hidden=false;$('mapping-next').scrollIntoView({block:'nearest'});}catch(e){$('status').textContent=e.message}finally{controls(false)}};
264
+ $('configure').onclick=async()=>{if(!current||busy)return;const configuration=readMapping();controls(true);$('mapping-next').hidden=true;try{render(await api('/configure',{id:current.id,configuration}));const review=directScope();$('status').textContent=review.blocked?'设置已保存,但尚不能执行。'+review.problem:'字段设置已应用,旧预览已清除,请重新核验。';$('mapping-next').hidden=review.blocked;if(review.blocked){$('anchor-alert').textContent=review.problem;$('anchor-alert').scrollIntoView({block:'center'})}else $('mapping-next').scrollIntoView({block:'nearest'});}catch(e){$('status').textContent=e.message}finally{controls(false)}};
255
265
  $('samples').onclick=async e=>{if(!e.target.dataset.name||busy)return;const name=e.target.dataset.name;try{const response=await fetch(basePath+'/fixture/'+encodeURIComponent(name));if(!response.ok)throw Error('模板读取失败');await preview(await response.arrayBuffer(),name+'.xlsx',true)}catch(e){$('status').textContent=e.message}};
256
266
  $('confirm').onclick=async()=>{if(!current||busy)return;controls(true);try{await api('/confirm',{id:current.id,confirmChangeSetId:current.changeSet.changeSetId});current=await api('/task/'+current.id);render(current);downloads();$('status').textContent='副本已生成,原文件保持不变。'}catch(e){$('status').textContent=e.message}finally{controls(false)}};
257
267
  $('selection').onclick=async()=>{if(!current||busy)return;controls(true);try{render(await api('/select',{id:current.id,selectedIds:[...$('changes').querySelectorAll('input:checked')].map(x=>x.value)}));$('status').textContent='选择已应用,请核对新的预览。'}catch(e){$('status').textContent=e.message}finally{controls(false)}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-form-fill-agent",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "private": false,
5
5
  "description": "AI填表智能体:XLSX blank-cell preview, QCC enrichment and persistent tasks for DSH.",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/duhu2000/dsh-form-fill-agent.git", "directory": "packages/dsh-form-fill-agent" },