dsh-pre-duediligence 0.1.34 → 0.1.35
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 -0
- package/README.md +5 -5
- package/docs/RELEASE-0.1.35.md +8 -0
- package/install.sh +1 -1
- package/lib/client.js +38 -10
- package/lib/index.js +29 -9
- package/lib/index.js.map +3 -3
- package/lib/types/composer-model.d.ts +1 -0
- package/lib/types/previsit-dock.d.ts +2 -0
- package/lib/types/report-time.d.ts +3 -0
- package/package.json +1 -1
- package/skills/qcc-previsit-onepager/SKILL.md +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
## [Unreleased]
|
|
3
3
|
|
|
4
|
+
## [0.1.35] - 2026-09-15
|
|
5
|
+
|
|
6
|
+
- 任务提交后对象与目标改为只读,覆盖尚未调用 begin 的运行阶段,阻止重复启动。
|
|
7
|
+
- 提示词生成器明确访前技能路由,投资机构深度尽调不误切投委会流程;保留主体绑定保护并给出恢复步骤。
|
|
8
|
+
- 报告预览、HTML、Word/PDF 使用保存时的北京时间至秒,旧日期记录不虚构精度。
|
|
9
|
+
- 下载按钮统一为 HTML、Word、PDF,保留 PDF 主按钮。
|
|
10
|
+
|
|
4
11
|
## [0.1.34] - 2026-09-15
|
|
5
12
|
|
|
6
13
|
- 修正启动导航:主体确认前停留对象与目标,确认后展示范围,再进入实际查询阶段。
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# dsh-pre-duediligence
|
|
2
2
|
|
|
3
|
-
0.1.
|
|
3
|
+
0.1.35 分离资料采集与证据核验:采集仅展示来源数据,核验前置风险研判并展示机会假设、支持、反证及证据缺口。支持有真实引用的阶段性风险分级;无需执行项目合并说明。“现场验证”改为“证据验证”,待沟通事项明确为“待客户确认”。保留报告版本、历史回看及离线中文 PDF/Word 导出。详见 [发布说明](docs/RELEASE-0.1.35.md)。
|
|
4
4
|
|
|
5
5
|
## 安装与三分钟上手
|
|
6
6
|
|
|
7
7
|
访前尽调智能体:面向拜访前调查,提供企业尽调、客户尽调、客户背景调查、工商核验与风险信息整理;使用企查查 MCP,提供固定路由连续调用、主体确认、动态进度和可下载报告。
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
dsh plugin --profile web add dsh-pre-duediligence@0.1.
|
|
10
|
+
dsh plugin --profile web add dsh-pre-duediligence@0.1.35
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
请先满足下文的 DSH 与连接器要求;Better Sidebar 仅在需要可视化工作台时选装。安装后完整停止并重启对应 Profile。
|
|
@@ -92,7 +92,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/duhu2000/dsh-pre-duediligenc
|
|
|
92
92
|
安装脚本默认向 DSH Web profile 安装两个基础 Bundle:
|
|
93
93
|
|
|
94
94
|
1. `dsh-mcp-connector@0.2.32`:提供通用 MCP 连接器与市场,通过“企查查·企业工商”完成 OAuth,并动态挂载企业、风险、知产、经营、历史和董监高 MCP。
|
|
95
|
-
2. `dsh-pre-duediligence@0.1.
|
|
95
|
+
2. `dsh-pre-duediligence@0.1.35`:提供左侧智能体入口、原生会话首页、提示词生成器、Host 执行门和 Skill。
|
|
96
96
|
|
|
97
97
|
默认不会安装 Better Sidebar。需要 Session 级可视化工作台时显式开启:
|
|
98
98
|
|
|
@@ -120,7 +120,7 @@ dsh web
|
|
|
120
120
|
|
|
121
121
|
~~~bash
|
|
122
122
|
dsh plugin --profile web add dsh-mcp-connector@0.2.32
|
|
123
|
-
dsh plugin --profile web add dsh-pre-duediligence@0.1.
|
|
123
|
+
dsh plugin --profile web add dsh-pre-duediligence@0.1.35 --allow-build=node-pty,dsh-pre-duediligence
|
|
124
124
|
dsh web
|
|
125
125
|
~~~
|
|
126
126
|
|
|
@@ -136,7 +136,7 @@ dsh web
|
|
|
136
136
|
dsh plugin --profile web list --depth 0
|
|
137
137
|
~~~
|
|
138
138
|
|
|
139
|
-
应始终看到所选基线的 Connector 和 `dsh-pre-duediligence@0.1.
|
|
139
|
+
应始终看到所选基线的 Connector 和 `dsh-pre-duediligence@0.1.35`;仅在显式启用或原本已安装时看到 Sidebar。重启 DSH 并连接“企查查·企业工商”后,在左侧菜单点击“访前尽调”进入初始原生会话。无 Sidebar 时可继续生成提示词、回填/发送草稿并在会话中阅读报告;五项流程按钮会提示安装可选工作台。安装 Sidebar 时,右侧工作台初始仍关闭;点击业务按钮可主动打开,正式发送任务被接纳后会自动展开一次。仅回填不会展开,收起后轮询不会重新打开。其右侧“+”菜单不会列出该入口,普通会话保持 DSH 原样。
|
|
140
140
|
|
|
141
141
|
### 无 Sidebar 功能边界
|
|
142
142
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# 0.1.35
|
|
2
|
+
|
|
3
|
+
- 已发送的任务在对象与目标页显示只读状态,包含模型运行但尚未创建任务记录的阶段;阻止重复开始。
|
|
4
|
+
- 提示词生成器明确访前技能与主体绑定路由,投资机构/深度/完整报告仍走访前流程;保留原始 MCP 防绕过保护,错误提供恢复步骤。未修改全局投资技能。
|
|
5
|
+
- 当前、历史 HTML 预览及下载、Word/PDF 使用保存时间,显示北京时间至秒;仅有日期的历史记录保留精度。原报告不修改,衍生文件使用新渲染缓存标识。
|
|
6
|
+
- 下载顺序 HTML、Word、PDF,PDF 为主按钮。
|
|
7
|
+
|
|
8
|
+
首页初始引导由总账统一规划,不属于本次变更。模型实际技能选择仍需在真实 DSH 投资机构场景回归;自动化测试不是生产宿主验收。
|
package/install.sh
CHANGED
|
@@ -5,7 +5,7 @@ set -euo pipefail
|
|
|
5
5
|
readonly PROFILE_NAME="${DSH_PROFILE:-web}"
|
|
6
6
|
readonly BASELINE="${DSH_PREVISIT_BASELINE:-stable}"
|
|
7
7
|
readonly WORKBENCH_MODE="${DSH_PREVISIT_WORKBENCH:-off}"
|
|
8
|
-
readonly PREVISIT_SPEC="${DSH_PRE_DUEDILIGENCE_SPEC:-dsh-pre-duediligence@0.1.
|
|
8
|
+
readonly PREVISIT_SPEC="${DSH_PRE_DUEDILIGENCE_SPEC:-dsh-pre-duediligence@0.1.35}"
|
|
9
9
|
readonly LEGACY_PREVISIT_NAME="qcc-previsit-dsh"
|
|
10
10
|
readonly LEGACY_QCC_OAUTH_NAME="qcc-dsh-mcp-oauth"
|
|
11
11
|
|
package/lib/client.js
CHANGED
|
@@ -234,6 +234,10 @@ function optionById(options, id) {
|
|
|
234
234
|
function hasSelection(selection) {
|
|
235
235
|
return selection.role !== void 0 || selection.purpose !== void 0 || selection.focus.length > 0 || selection.budget !== void 0 || selection.output !== void 0;
|
|
236
236
|
}
|
|
237
|
+
function routePrevisitPrompt(text) {
|
|
238
|
+
if (!text.trim() || text.includes("\u8BF7\u4F7F\u7528 qcc-previsit-onepager")) return text;
|
|
239
|
+
return text + "\n\u8BF7\u4F7F\u7528 qcc-previsit-onepager Skill \u6267\u884C\u8BBF\u524D\u5C3D\u8C03\uFF1B\u6295\u8D44\u673A\u6784\u89D2\u8272\u3001\u6DF1\u5EA6\u5C3D\u8C03\u548C\u5B8C\u6574\u62A5\u544A\u4E5F\u6CBF\u7528\u672C\u6D41\u7A0B\uFF0C\u4E0D\u5207\u6362\u4E3A ic-memo-qcc\u3002\u5148 previsit_begin\uFF0C\u518D\u901A\u8FC7 previsit_query \u7684 entity_search \u6838\u5B9E\u4E3B\u4F53\uFF0C\u4F7F\u7528 previsit_confirm_entity \u7ED1\u5B9A\u540E\u7EE7\u7EED\u67E5\u8BE2\u3002";
|
|
240
|
+
}
|
|
237
241
|
function composeFullSentence(selection, company = "") {
|
|
238
242
|
if (!hasSelection(selection) && company.trim() === "") {
|
|
239
243
|
return "";
|
|
@@ -378,6 +382,22 @@ async function createPrevisitSession(ctx) {
|
|
|
378
382
|
return created;
|
|
379
383
|
}
|
|
380
384
|
|
|
385
|
+
// src/report-time.ts
|
|
386
|
+
function formatReportTime(value) {
|
|
387
|
+
if (!value) return "";
|
|
388
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value)) return value;
|
|
389
|
+
const date = new Date(value);
|
|
390
|
+
if (!Number.isFinite(date.getTime())) return value;
|
|
391
|
+
const parts = new Intl.DateTimeFormat("en-GB", { timeZone: "Asia/Shanghai", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23" }).formatToParts(date);
|
|
392
|
+
const get = (key) => parts.find((part) => part.type === key)?.value ?? "";
|
|
393
|
+
return `${get("year")}-${get("month")}-${get("day")} ${get("hour")}:${get("minute")}:${get("second")}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
|
|
394
|
+
}
|
|
395
|
+
function reportTextWithTime(markdown, completedAt) {
|
|
396
|
+
const stamp = "\u751F\u6210\u65F6\u95F4\uFF1A" + formatReportTime(completedAt);
|
|
397
|
+
const pattern = /生成时间[::]\s*\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?(?:(北京时间))?/g;
|
|
398
|
+
return pattern.test(markdown) ? markdown.replace(pattern, stamp) : stamp + "\n\n" + markdown;
|
|
399
|
+
}
|
|
400
|
+
|
|
381
401
|
// src/report-export.ts
|
|
382
402
|
function contentText(value) {
|
|
383
403
|
if (typeof value === "string") return value;
|
|
@@ -639,11 +659,11 @@ footer{padding:14px 26px 20px;border-top:1px solid var(--line);color:var(--muted
|
|
|
639
659
|
function deriveMeta(md, meta) {
|
|
640
660
|
const first = /^#?\s*(?:访前尽调报告|拜访作战卡)\s*·\s*([^\n锚]+)/m.exec(md);
|
|
641
661
|
const anchorLine = /锚定主体:([^\n]+?)(?:\s*生成时间|$)/m.exec(md);
|
|
642
|
-
const whenLine = /生成时间[::]\s*([
|
|
662
|
+
const whenLine = /生成时间[::]\s*(\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?(?:(北京时间))?)/.exec(md);
|
|
643
663
|
return {
|
|
644
664
|
company: meta?.company || (first?.[1]?.trim() ?? "\u8BBF\u524D\u5C3D\u8C03\u62A5\u544A"),
|
|
645
665
|
anchor: (anchorLine?.[1] ?? "").trim().replace(/[||·\s]+$/, ""),
|
|
646
|
-
when: meta?.generatedAt ||
|
|
666
|
+
when: formatReportTime(meta?.generatedAt || whenLine?.[1])
|
|
647
667
|
};
|
|
648
668
|
}
|
|
649
669
|
function wrapPrevisitReport(bodyHtml, m) {
|
|
@@ -658,6 +678,7 @@ function wrapPrevisitReport(bodyHtml, m) {
|
|
|
658
678
|
</div></body></html>`;
|
|
659
679
|
}
|
|
660
680
|
function buildPrevisitReportHtml(cardMarkdown, meta) {
|
|
681
|
+
if (meta?.generatedAt) cardMarkdown = reportTextWithTime(cardMarkdown, meta.generatedAt);
|
|
661
682
|
const m = deriveMeta(cardMarkdown, meta);
|
|
662
683
|
const body = cardMarkdown.replace(/^#\s*(?:访前尽调报告|拜访作战卡).*$/m, "").replace(/^锚定主体:.*$/m, "").trim();
|
|
663
684
|
return wrapPrevisitReport(renderCardMarkdown(body), m);
|
|
@@ -1615,8 +1636,8 @@ function ReportFilesPanel({ taskId, sessionId, onHtml }) {
|
|
|
1615
1636
|
}
|
|
1616
1637
|
};
|
|
1617
1638
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "qccPwDownloadActions", "aria-label": "\u4E0B\u8F7D\u62A5\u544A", children: [
|
|
1618
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "qccPwSecondary", disabled: busy !== null, onClick: () => void generate("docx"), children: busy === "docx" ? "\u6B63\u5728\u751F\u6210 Word\u2026" : "\u4E0B\u8F7D Word" }),
|
|
1619
1639
|
onHtml ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "qccPwSecondary", disabled: busy !== null, onClick: onHtml, children: "\u4E0B\u8F7D HTML" }) : null,
|
|
1640
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "qccPwSecondary", disabled: busy !== null, onClick: () => void generate("docx"), children: busy === "docx" ? "\u6B63\u5728\u751F\u6210 Word\u2026" : "\u4E0B\u8F7D Word" }),
|
|
1620
1641
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "qccPwPrimary", disabled: busy !== null, onClick: () => void generate("pdf"), children: busy === "pdf" ? "\u6B63\u5728\u751F\u6210 PDF\u2026" : "\u4E0B\u8F7D PDF" }),
|
|
1621
1642
|
busy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "status", children: "\u751F\u6210\u5B8C\u6210\u540E\u81EA\u52A8\u4E0B\u8F7D" }) : null,
|
|
1622
1643
|
error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { role: "alert", children: error }) : null
|
|
@@ -1936,7 +1957,7 @@ function usePrevisitComposer(args) {
|
|
|
1936
1957
|
setError(void 0);
|
|
1937
1958
|
};
|
|
1938
1959
|
const startTask = async () => {
|
|
1939
|
-
if (inFlight.current) return;
|
|
1960
|
+
if (inFlight.current || args.blocked || isolated && store.get(sessionId).task !== void 0) return;
|
|
1940
1961
|
const lifetimeToken = lifetime.current;
|
|
1941
1962
|
const current = store.get(sessionId);
|
|
1942
1963
|
const text = (isolated ? composeFullSentence(current.selection, current.company) : args.readDraft()).trim();
|
|
@@ -1982,6 +2003,12 @@ function PrevisitFields(props) {
|
|
|
1982
2003
|
}));
|
|
1983
2004
|
const { actions: a } = props;
|
|
1984
2005
|
const st = a.state;
|
|
2006
|
+
if (props.locked) return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "qccDockBody", role: "status", children: [
|
|
2007
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "\u4EFB\u52A1\u5DF2\u63D0\u4EA4" }),
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "\u8BF7\u67E5\u770B\u4EFB\u52A1\u8FDB\u5C55\uFF1B\u5982\u9700\u786E\u8BA4\u4E3B\u4F53\uFF0C\u8BF7\u5728\u5BF9\u8BDD\u4E2D\u9009\u62E9\u3002\u65E0\u9700\u91CD\u590D\u63D0\u4EA4\u3002" }),
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: st.task?.company ?? st.company }),
|
|
2010
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "\u672C\u6B21\u8BBE\u7F6E\u4EE5\u5DF2\u53D1\u9001\u7684\u4EFB\u52A1\u4E3A\u51C6\u3002\u5F00\u59CB\u53E6\u4E00\u9879\u8C03\u67E5\u8BF7\u4F7F\u7528\u300C\u65B0\u7684\u5C3D\u8C03\u300D\u3002" })
|
|
2011
|
+
] });
|
|
1985
2012
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(DropZone, { className: "qccDockBody", onFile: (file) => {
|
|
1986
2013
|
if (props.send !== void 0) void imports.accept(file);
|
|
1987
2014
|
}, children: [
|
|
@@ -2254,7 +2281,7 @@ function PrevisitPromptGenerator(props) {
|
|
|
2254
2281
|
setOpen(false);
|
|
2255
2282
|
window.setTimeout(() => triggerRef.current?.focus(), 0);
|
|
2256
2283
|
};
|
|
2257
|
-
const generated = composeFullSentence(selection, company);
|
|
2284
|
+
const generated = routePrevisitPrompt(composeFullSentence(selection, company));
|
|
2258
2285
|
const commit = (mode) => {
|
|
2259
2286
|
if (props.inputActions === void 0) {
|
|
2260
2287
|
setError("\u5F53\u524D\u4F1A\u8BDD\u8F93\u5165\u6846\u5C1A\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
@@ -3276,7 +3303,8 @@ function SetupPanel(props) {
|
|
|
3276
3303
|
},
|
|
3277
3304
|
start: props.start,
|
|
3278
3305
|
onStarted: props.onStarted,
|
|
3279
|
-
draftMode: "isolated"
|
|
3306
|
+
draftMode: "isolated",
|
|
3307
|
+
blocked: props.busy || props.task !== void 0
|
|
3280
3308
|
});
|
|
3281
3309
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { className: "qccPwPanel", children: [
|
|
3282
3310
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("header", { className: "qccPwPageHeading", children: [
|
|
@@ -3287,7 +3315,7 @@ function SetupPanel(props) {
|
|
|
3287
3315
|
] }),
|
|
3288
3316
|
props.task === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "qccPwTaskId", children: taskDisplayLabel(props.task.id) })
|
|
3289
3317
|
] }),
|
|
3290
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "qccPwCard qccPwSetupCard", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PrevisitFields, { actions, idPrefix: `qccPw-${props.sessionId}`, sessionId: props.sessionId, send: props.send }) }),
|
|
3318
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "qccPwCard qccPwSetupCard", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PrevisitFields, { actions, locked: props.busy || props.task !== void 0, idPrefix: `qccPw-${props.sessionId}`, sessionId: props.sessionId, send: props.send }) }),
|
|
3291
3319
|
props.task === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "qccPwCard", children: [
|
|
3292
3320
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "qccPwCardHeader", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { children: "\u5DF2\u53D1\u9001\u7684\u4EFB\u52A1" }) }) }),
|
|
3293
3321
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("pre", { className: "qccPwPrompt", children: props.task.prompt })
|
|
@@ -3537,7 +3565,7 @@ function HistoryPanel(props) {
|
|
|
3537
3565
|
const item = props.selected;
|
|
3538
3566
|
const status = hostedStatus(item);
|
|
3539
3567
|
const origin = hostedTaskOrigin(item);
|
|
3540
|
-
const reportHtml = selectedReport === void 0 || selectedReport === "" ? null : buildPrevisitReportHtml(selectedReport);
|
|
3568
|
+
const reportHtml = selectedReport === void 0 || selectedReport === "" ? null : buildPrevisitReportHtml(selectedReport, item.completedAt ? { generatedAt: item.completedAt } : void 0);
|
|
3541
3569
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("section", { className: "qccPwPanel", children: [
|
|
3542
3570
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("header", { className: "qccPwPageHeading qccPwHistoryHeading", children: [
|
|
3543
3571
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
@@ -3881,7 +3909,7 @@ function PrevisitWorkbenchTab(props) {
|
|
|
3881
3909
|
const status = hostStatus ?? deriveWorkbenchStatus(progressInput);
|
|
3882
3910
|
const phaseStates = derivePhaseStates(progressInput);
|
|
3883
3911
|
const insights = (0, import_react9.useMemo)(() => parseCardInsights(cardText), [cardText]);
|
|
3884
|
-
const reportHtml = (0, import_react9.useMemo)(() => cardText === null ? null : buildPrevisitReportHtml(cardText), [cardText]);
|
|
3912
|
+
const reportHtml = (0, import_react9.useMemo)(() => cardText === null ? null : buildPrevisitReportHtml(cardText, hostedTask?.completedAt ? { generatedAt: hostedTask.completedAt } : void 0), [cardText, hostedTask?.completedAt]);
|
|
3885
3913
|
const newTask = () => {
|
|
3886
3914
|
manualNavigation.current = false;
|
|
3887
3915
|
props.shared.update(sessionId, (state) => ({
|
|
@@ -4022,7 +4050,7 @@ function PrevisitWorkbenchTab(props) {
|
|
|
4022
4050
|
await props.startPrompt(sessionId, message, true);
|
|
4023
4051
|
setDismissedPlanIds((ids) => [...ids, pendingPlan.id]);
|
|
4024
4052
|
}, onDismiss: () => setDismissedPlanIds((ids) => [...ids, pendingPlan.id]) }, pendingPlan.id) : null,
|
|
4025
|
-
shared.view === "target" && pendingPlan === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SetupPanel, { sessionId, store: props.shared, task, input: resolveSessionInput(props.ctx, sessionId), send: async (prompt) => {
|
|
4053
|
+
shared.view === "target" && pendingPlan === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SetupPanel, { busy: runtime.running, sessionId, store: props.shared, task, input: resolveSessionInput(props.ctx, sessionId), send: async (prompt) => {
|
|
4026
4054
|
await props.startPrompt(sessionId, prompt, true);
|
|
4027
4055
|
}, start: (prompt) => props.startPrompt(sessionId, prompt), onStarted: () => {
|
|
4028
4056
|
manualNavigation.current = false;
|
package/lib/index.js
CHANGED
|
@@ -322,11 +322,30 @@ import { createHash } from "node:crypto";
|
|
|
322
322
|
import { readFile } from "node:fs/promises";
|
|
323
323
|
import { Document, Packer, Paragraph, TextRun, HeadingLevel } from "docx";
|
|
324
324
|
import PDFDocument from "pdfkit";
|
|
325
|
+
|
|
326
|
+
// src/report-time.ts
|
|
327
|
+
function formatReportTime(value) {
|
|
328
|
+
if (!value) return "";
|
|
329
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value)) return value;
|
|
330
|
+
const date = new Date(value);
|
|
331
|
+
if (!Number.isFinite(date.getTime())) return value;
|
|
332
|
+
const parts = new Intl.DateTimeFormat("en-GB", { timeZone: "Asia/Shanghai", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23" }).formatToParts(date);
|
|
333
|
+
const get = (key) => parts.find((part) => part.type === key)?.value ?? "";
|
|
334
|
+
return `${get("year")}-${get("month")}-${get("day")} ${get("hour")}:${get("minute")}:${get("second")}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
|
|
335
|
+
}
|
|
336
|
+
function reportTextWithTime(markdown, completedAt) {
|
|
337
|
+
const stamp = "\u751F\u6210\u65F6\u95F4\uFF1A" + formatReportTime(completedAt);
|
|
338
|
+
const pattern = /生成时间[::]\s*\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?(?:(北京时间))?/g;
|
|
339
|
+
return pattern.test(markdown) ? markdown.replace(pattern, stamp) : stamp + "\n\n" + markdown;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/report-files.ts
|
|
325
343
|
var hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
326
344
|
var mime = { pdf: "application/pdf", docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" };
|
|
327
345
|
async function renderReportFile(task, format, fontPath = process.env.DSH_PREVISIT_PDF_FONT) {
|
|
328
346
|
if (!task.reportMarkdown || !task.completedAt) throw new Error("\u62A5\u544A\u5C1A\u672A\u4FDD\u5B58");
|
|
329
|
-
const
|
|
347
|
+
const reportText = reportTextWithTime(task.reportMarkdown, task.completedAt);
|
|
348
|
+
const lines = reportText.split(/\r?\n/);
|
|
330
349
|
const identity = `${task.entity?.fullName ?? task.query} \xB7 V${task.reportVersion ?? 1} \xB7 ${task.id}`;
|
|
331
350
|
if (format === "docx") {
|
|
332
351
|
const fontName = process.env.DSH_PREVISIT_DOCX_FONT ?? "Arial Unicode MS";
|
|
@@ -345,7 +364,7 @@ async function renderReportFile(task, format, fontPath = process.env.DSH_PREVISI
|
|
|
345
364
|
pdf.on("end", () => resolve(Buffer.concat(chunks)));
|
|
346
365
|
try {
|
|
347
366
|
pdf.font(font).fontSize(16).text(identity).moveDown();
|
|
348
|
-
pdf.fontSize(11).text(
|
|
367
|
+
pdf.fontSize(11).text(reportText, { lineGap: 4 });
|
|
349
368
|
pdf.end();
|
|
350
369
|
} catch (error) {
|
|
351
370
|
pdf.destroy();
|
|
@@ -390,7 +409,7 @@ var ReportFiles = class {
|
|
|
390
409
|
await this.ready;
|
|
391
410
|
if (!["completed", "partial"].includes(task.state) || !task.reportMarkdown) throw new Error("\u53EA\u80FD\u5BFC\u51FA\u5DF2\u4FDD\u5B58\u62A5\u544A");
|
|
392
411
|
const sourceHash = hash(task.reportMarkdown);
|
|
393
|
-
const id = `RF-${hash(`${task.id}:${sourceHash}:${format}:text-
|
|
412
|
+
const id = `RF-${hash(`${task.id}:${sourceHash}:${task.completedAt}:${format}:text-v2`).slice(0, 32)}`;
|
|
394
413
|
let stored = this.files.get(id);
|
|
395
414
|
if (!stored) {
|
|
396
415
|
const bytes = await renderReportFile(task, format);
|
|
@@ -588,11 +607,11 @@ footer{padding:14px 26px 20px;border-top:1px solid var(--line);color:var(--muted
|
|
|
588
607
|
function deriveMeta(md, meta) {
|
|
589
608
|
const first = /^#?\s*(?:访前尽调报告|拜访作战卡)\s*·\s*([^\n锚]+)/m.exec(md);
|
|
590
609
|
const anchorLine = /锚定主体:([^\n]+?)(?:\s*生成时间|$)/m.exec(md);
|
|
591
|
-
const whenLine = /生成时间[::]\s*([
|
|
610
|
+
const whenLine = /生成时间[::]\s*(\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?(?:(北京时间))?)/.exec(md);
|
|
592
611
|
return {
|
|
593
612
|
company: meta?.company || (first?.[1]?.trim() ?? "\u8BBF\u524D\u5C3D\u8C03\u62A5\u544A"),
|
|
594
613
|
anchor: (anchorLine?.[1] ?? "").trim().replace(/[||·\s]+$/, ""),
|
|
595
|
-
when: meta?.generatedAt ||
|
|
614
|
+
when: formatReportTime(meta?.generatedAt || whenLine?.[1])
|
|
596
615
|
};
|
|
597
616
|
}
|
|
598
617
|
function wrapPrevisitReport(bodyHtml, m) {
|
|
@@ -607,6 +626,7 @@ function wrapPrevisitReport(bodyHtml, m) {
|
|
|
607
626
|
</div></body></html>`;
|
|
608
627
|
}
|
|
609
628
|
function buildPrevisitReportHtml(cardMarkdown, meta) {
|
|
629
|
+
if (meta?.generatedAt) cardMarkdown = reportTextWithTime(cardMarkdown, meta.generatedAt);
|
|
610
630
|
const m = deriveMeta(cardMarkdown, meta);
|
|
611
631
|
const body = cardMarkdown.replace(/^#\s*(?:访前尽调报告|拜访作战卡).*$/m, "").replace(/^锚定主体:.*$/m, "").trim();
|
|
612
632
|
return wrapPrevisitReport(renderCardMarkdown(body), m);
|
|
@@ -1240,7 +1260,7 @@ function mountPrevisitWebRoutes(webServer, workflow, files = new ReportFiles())
|
|
|
1240
1260
|
}
|
|
1241
1261
|
const html = buildPrevisitReportHtml(task.reportMarkdown, {
|
|
1242
1262
|
...task.entity?.fullName === void 0 ? {} : { company: task.entity.fullName },
|
|
1243
|
-
...task.completedAt === void 0 ? {} : { generatedAt: task.completedAt
|
|
1263
|
+
...task.completedAt === void 0 ? {} : { generatedAt: task.completedAt }
|
|
1244
1264
|
});
|
|
1245
1265
|
const ascii = task.artifact.fileName.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "_");
|
|
1246
1266
|
res.writeHead(200, {
|
|
@@ -1478,7 +1498,7 @@ function registerPrevisitTools(ctx, workflow = new PrevisitWorkflowStore(), opti
|
|
|
1478
1498
|
disposers.push(ctx.tools.guard((exec) => {
|
|
1479
1499
|
if (!exec.agent || !isPrevisitSession(exec.agent.session.id) || !qccTool(exec.name)) return void 0;
|
|
1480
1500
|
const permit = permits.get(exec.callId);
|
|
1481
|
-
return !disposed && permit?.owner === ownerOf(exec.agent) && permit.name === exec.name && permit.parent === exec.parent ? void 0 : "\u8BBF\u524D\u4F01\u67E5\u67E5\u8C03\u7528\u5FC5\u987B\u901A\u8FC7 previsit_begin / previsit_confirm_entity / previsit_query\uFF1B\u7981\u6B62\u7ED5\u8FC7\u4E3B\u4F53\u7ED1\u5B9A\u4E0E\u56FA\u5B9A\u4E1A\u52A1\u8DEF\u7531\u3002";
|
|
1501
|
+
return !disposed && permit?.owner === ownerOf(exec.agent) && permit.name === exec.name && permit.parent === exec.parent ? void 0 : "\u8BBF\u524D\u4F01\u67E5\u67E5\u8C03\u7528\u5FC5\u987B\u901A\u8FC7 previsit_begin / previsit_confirm_entity / previsit_query\uFF1B\u7981\u6B62\u7ED5\u8FC7\u4E3B\u4F53\u7ED1\u5B9A\u4E0E\u56FA\u5B9A\u4E1A\u52A1\u8DEF\u7531\u3002\u8BF7\u4F7F\u7528 qcc-previsit-onepager Skill\uFF0C\u4E0D\u56E0\u6295\u8D44\u673A\u6784\u89D2\u8272\u5207\u6362\u4E3A ic-memo-qcc\u3002\u5C1A\u65E0\u4EFB\u52A1\u65F6\u5148 previsit_begin\uFF0C\u518D previsit_query(dimension=entity_search)\uFF0C\u53D6\u5F97\u5019\u9009\u540E previsit_confirm_entity\uFF1B\u5DF2\u6709\u4EFB\u52A1\u8BF7\u6CBF\u7528 taskId\uFF0C\u4E0D\u91CD\u590D\u521B\u5EFA\u3002";
|
|
1482
1502
|
}));
|
|
1483
1503
|
const register = (name, description, properties, required, execute) => {
|
|
1484
1504
|
disposers.push(ctx.tools.register({
|
|
@@ -1889,7 +1909,7 @@ function loadBundledSkill() {
|
|
|
1889
1909
|
return {
|
|
1890
1910
|
name: SKILL_NAME,
|
|
1891
1911
|
description: SKILL_DESCRIPTION,
|
|
1892
|
-
whenToUse: "\u7528\u6237\u8981\u6C42\u51C6\u5907\u5BA2\u6237\u62DC\u8BBF\u3001\u8BBF\u524D\u5C3D\u8C03\u3001\u4E00\u9875\u7EB8\u7B80\u62A5\u3001\u6388\u4FE1\u9762\u8C08\u3001\u5546\u52A1\u8C08\u5224\u3001\u7B7E\u7EA6\u6838\u67E5\u3001\u590D\u8BBF\u66F4\u65B0\u3001\u89E6\u8FBE\u8DEF\u5F84\u6216\u5F53\u9762\u63D0\u95EE\u6E05\u5355\u65F6\u4F7F\u7528\u3002",
|
|
1912
|
+
whenToUse: "\u7528\u6237\u8981\u6C42\u51C6\u5907\u5BA2\u6237\u62DC\u8BBF\u3001\u8BBF\u524D\u5C3D\u8C03\u3001\u4E00\u9875\u7EB8\u7B80\u62A5\u3001\u6388\u4FE1\u9762\u8C08\u3001\u5546\u52A1\u8C08\u5224\u3001\u7B7E\u7EA6\u6838\u67E5\u3001\u590D\u8BBF\u66F4\u65B0\u3001\u89E6\u8FBE\u8DEF\u5F84\u6216\u5F53\u9762\u63D0\u95EE\u6E05\u5355\u65F6\u4F7F\u7528\u3002\u4ECE\u8BBF\u524D\u5C3D\u8C03\u5165\u53E3\u53D1\u8D77\u7684\u6295\u8D44\u673A\u6784\u4EBA\u5458\u3001\u6DF1\u5EA6\u5C3D\u8C03\u3001\u5B8C\u6574\u62A5\u544A\u4EFB\u52A1\u4E5F\u4F7F\u7528\u672C\u6280\u80FD\uFF0C\u4E0D\u8F6C\u4E3A ic-memo-qcc\u3002",
|
|
1893
1913
|
content,
|
|
1894
1914
|
source: "bundled",
|
|
1895
1915
|
resourceBase: {
|
|
@@ -1898,7 +1918,7 @@ function loadBundledSkill() {
|
|
|
1898
1918
|
},
|
|
1899
1919
|
metadata: {
|
|
1900
1920
|
author: "QCC",
|
|
1901
|
-
version: "0.1.
|
|
1921
|
+
version: "0.1.35",
|
|
1902
1922
|
industry: "enterprise-services",
|
|
1903
1923
|
mcpServers: ["qcc-company", "qcc-risk", "qcc-ipr", "qcc-operation", "qcc-executive"]
|
|
1904
1924
|
}
|