helixlife-expert-cli 0.1.0
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 +159 -0
- package/cli/bin/helixlife-expert.js +3 -0
- package/cli/dist/core/api/client.d.ts +31 -0
- package/cli/dist/core/api/client.d.ts.map +1 -0
- package/cli/dist/core/api/client.js +183 -0
- package/cli/dist/core/api/client.js.map +1 -0
- package/cli/dist/core/api/http.d.ts +3 -0
- package/cli/dist/core/api/http.d.ts.map +1 -0
- package/cli/dist/core/api/http.js +22 -0
- package/cli/dist/core/api/http.js.map +1 -0
- package/cli/dist/core/api/response.d.ts +48 -0
- package/cli/dist/core/api/response.d.ts.map +1 -0
- package/cli/dist/core/api/response.js +84 -0
- package/cli/dist/core/api/response.js.map +1 -0
- package/cli/dist/core/api/sse.d.ts +6 -0
- package/cli/dist/core/api/sse.d.ts.map +1 -0
- package/cli/dist/core/api/sse.js +60 -0
- package/cli/dist/core/api/sse.js.map +1 -0
- package/cli/dist/core/auth/index.d.ts +35 -0
- package/cli/dist/core/auth/index.d.ts.map +1 -0
- package/cli/dist/core/auth/index.js +150 -0
- package/cli/dist/core/auth/index.js.map +1 -0
- package/cli/dist/core/env-config.d.ts +16 -0
- package/cli/dist/core/env-config.d.ts.map +1 -0
- package/cli/dist/core/env-config.js +80 -0
- package/cli/dist/core/env-config.js.map +1 -0
- package/cli/dist/core/errors.d.ts +20 -0
- package/cli/dist/core/errors.d.ts.map +1 -0
- package/cli/dist/core/errors.js +28 -0
- package/cli/dist/core/errors.js.map +1 -0
- package/cli/dist/core/index.d.ts +16 -0
- package/cli/dist/core/index.d.ts.map +1 -0
- package/cli/dist/core/index.js +16 -0
- package/cli/dist/core/index.js.map +1 -0
- package/cli/dist/core/interactive.d.ts +18 -0
- package/cli/dist/core/interactive.d.ts.map +1 -0
- package/cli/dist/core/interactive.js +74 -0
- package/cli/dist/core/interactive.js.map +1 -0
- package/cli/dist/core/output/index.d.ts +29 -0
- package/cli/dist/core/output/index.d.ts.map +1 -0
- package/cli/dist/core/output/index.js +153 -0
- package/cli/dist/core/output/index.js.map +1 -0
- package/cli/dist/core/parse.d.ts +9 -0
- package/cli/dist/core/parse.d.ts.map +1 -0
- package/cli/dist/core/parse.js +31 -0
- package/cli/dist/core/parse.js.map +1 -0
- package/cli/dist/core/paths.d.ts +21 -0
- package/cli/dist/core/paths.d.ts.map +1 -0
- package/cli/dist/core/paths.js +65 -0
- package/cli/dist/core/paths.js.map +1 -0
- package/cli/dist/core/sse-utils.d.ts +49 -0
- package/cli/dist/core/sse-utils.d.ts.map +1 -0
- package/cli/dist/core/sse-utils.js +181 -0
- package/cli/dist/core/sse-utils.js.map +1 -0
- package/cli/dist/core/terminal.d.ts +2 -0
- package/cli/dist/core/terminal.d.ts.map +1 -0
- package/cli/dist/core/terminal.js +6 -0
- package/cli/dist/core/terminal.js.map +1 -0
- package/cli/dist/core/token/index.d.ts +5 -0
- package/cli/dist/core/token/index.d.ts.map +1 -0
- package/cli/dist/core/token/index.js +26 -0
- package/cli/dist/core/token/index.js.map +1 -0
- package/cli/dist/core/types/config.d.ts +8 -0
- package/cli/dist/core/types/config.d.ts.map +1 -0
- package/cli/dist/core/types/config.js +30 -0
- package/cli/dist/core/types/config.js.map +1 -0
- package/cli/dist/core/version.d.ts +2 -0
- package/cli/dist/core/version.d.ts.map +1 -0
- package/cli/dist/core/version.js +10 -0
- package/cli/dist/core/version.js.map +1 -0
- package/cli/dist/index.d.ts +2 -0
- package/cli/dist/index.d.ts.map +1 -0
- package/cli/dist/index.js +23 -0
- package/cli/dist/index.js.map +1 -0
- package/cli/dist/modules/analysis/constants.d.ts +3 -0
- package/cli/dist/modules/analysis/constants.d.ts.map +1 -0
- package/cli/dist/modules/analysis/constants.js +3 -0
- package/cli/dist/modules/analysis/constants.js.map +1 -0
- package/cli/dist/modules/analysis/index.d.ts +3 -0
- package/cli/dist/modules/analysis/index.d.ts.map +1 -0
- package/cli/dist/modules/analysis/index.js +23 -0
- package/cli/dist/modules/analysis/index.js.map +1 -0
- package/cli/dist/modules/auth/index.d.ts +3 -0
- package/cli/dist/modules/auth/index.d.ts.map +1 -0
- package/cli/dist/modules/auth/index.js +76 -0
- package/cli/dist/modules/auth/index.js.map +1 -0
- package/cli/dist/modules/expert-scaffold.d.ts +27 -0
- package/cli/dist/modules/expert-scaffold.d.ts.map +1 -0
- package/cli/dist/modules/expert-scaffold.js +25 -0
- package/cli/dist/modules/expert-scaffold.js.map +1 -0
- package/cli/dist/modules/fund/clinical/apps.d.ts +19 -0
- package/cli/dist/modules/fund/clinical/apps.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/apps.js +14 -0
- package/cli/dist/modules/fund/clinical/apps.js.map +1 -0
- package/cli/dist/modules/fund/clinical/command-utils.d.ts +4 -0
- package/cli/dist/modules/fund/clinical/command-utils.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/command-utils.js +40 -0
- package/cli/dist/modules/fund/clinical/command-utils.js.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/article-create.d.ts +9 -0
- package/cli/dist/modules/fund/clinical/commands/article-create.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/article-create.js +99 -0
- package/cli/dist/modules/fund/clinical/commands/article-create.js.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/article-download.d.ts +30 -0
- package/cli/dist/modules/fund/clinical/commands/article-download.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/article-download.js +153 -0
- package/cli/dist/modules/fund/clinical/commands/article-download.js.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-create.d.ts +7 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-create.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-create.js +81 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-create.js.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-precheck.d.ts +7 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-precheck.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-precheck.js +76 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-precheck.js.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-update.d.ts +16 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-update.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-update.js +108 -0
- package/cli/dist/modules/fund/clinical/commands/four-elements-update.js.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/sample-size.d.ts +14 -0
- package/cli/dist/modules/fund/clinical/commands/sample-size.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/sample-size.js +164 -0
- package/cli/dist/modules/fund/clinical/commands/sample-size.js.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/topic-evaluation-create.d.ts +25 -0
- package/cli/dist/modules/fund/clinical/commands/topic-evaluation-create.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/topic-evaluation-create.js +228 -0
- package/cli/dist/modules/fund/clinical/commands/topic-evaluation-create.js.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/topic-evaluation-parameters.d.ts +45 -0
- package/cli/dist/modules/fund/clinical/commands/topic-evaluation-parameters.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/commands/topic-evaluation-parameters.js +211 -0
- package/cli/dist/modules/fund/clinical/commands/topic-evaluation-parameters.js.map +1 -0
- package/cli/dist/modules/fund/clinical/constants.d.ts +85 -0
- package/cli/dist/modules/fund/clinical/constants.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/constants.js +161 -0
- package/cli/dist/modules/fund/clinical/constants.js.map +1 -0
- package/cli/dist/modules/fund/clinical/form-rules.d.ts +56 -0
- package/cli/dist/modules/fund/clinical/form-rules.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/form-rules.js +542 -0
- package/cli/dist/modules/fund/clinical/form-rules.js.map +1 -0
- package/cli/dist/modules/fund/clinical/four-elements.d.ts +46 -0
- package/cli/dist/modules/fund/clinical/four-elements.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/four-elements.js +190 -0
- package/cli/dist/modules/fund/clinical/four-elements.js.map +1 -0
- package/cli/dist/modules/fund/clinical/index.d.ts +3 -0
- package/cli/dist/modules/fund/clinical/index.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/index.js +176 -0
- package/cli/dist/modules/fund/clinical/index.js.map +1 -0
- package/cli/dist/modules/fund/clinical/parameters.d.ts +51 -0
- package/cli/dist/modules/fund/clinical/parameters.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/parameters.js +145 -0
- package/cli/dist/modules/fund/clinical/parameters.js.map +1 -0
- package/cli/dist/modules/fund/clinical/sample-size.d.ts +130 -0
- package/cli/dist/modules/fund/clinical/sample-size.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/sample-size.js +356 -0
- package/cli/dist/modules/fund/clinical/sample-size.js.map +1 -0
- package/cli/dist/modules/fund/clinical/session-cache.d.ts +25 -0
- package/cli/dist/modules/fund/clinical/session-cache.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/session-cache.js +100 -0
- package/cli/dist/modules/fund/clinical/session-cache.js.map +1 -0
- package/cli/dist/modules/fund/clinical/sse-utils.d.ts +22 -0
- package/cli/dist/modules/fund/clinical/sse-utils.d.ts.map +1 -0
- package/cli/dist/modules/fund/clinical/sse-utils.js +80 -0
- package/cli/dist/modules/fund/clinical/sse-utils.js.map +1 -0
- package/cli/dist/modules/fund/constants.d.ts +3 -0
- package/cli/dist/modules/fund/constants.d.ts.map +1 -0
- package/cli/dist/modules/fund/constants.js +3 -0
- package/cli/dist/modules/fund/constants.js.map +1 -0
- package/cli/dist/modules/fund/index.d.ts +3 -0
- package/cli/dist/modules/fund/index.d.ts.map +1 -0
- package/cli/dist/modules/fund/index.js +28 -0
- package/cli/dist/modules/fund/index.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/apps.d.ts +54 -0
- package/cli/dist/modules/fund/nsfc/apps.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/apps.js +76 -0
- package/cli/dist/modules/fund/nsfc/apps.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/article-cache.d.ts +14 -0
- package/cli/dist/modules/fund/nsfc/article-cache.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/article-cache.js +79 -0
- package/cli/dist/modules/fund/nsfc/article-cache.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/article-create.d.ts +8 -0
- package/cli/dist/modules/fund/nsfc/commands/article-create.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/article-create.js +108 -0
- package/cli/dist/modules/fund/nsfc/commands/article-create.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/article-download.d.ts +42 -0
- package/cli/dist/modules/fund/nsfc/commands/article-download.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/article-download.js +189 -0
- package/cli/dist/modules/fund/nsfc/commands/article-download.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/format-convert.d.ts +9 -0
- package/cli/dist/modules/fund/nsfc/commands/format-convert.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/format-convert.js +98 -0
- package/cli/dist/modules/fund/nsfc/commands/format-convert.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-analyze.d.ts +12 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-analyze.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-analyze.js +351 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-analyze.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-create.d.ts +21 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-create.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-create.js +197 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-create.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-figure.d.ts +9 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-figure.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-figure.js +120 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-figure.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-genes-select.d.ts +11 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-genes-select.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-genes-select.js +125 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-genes-select.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-pick.d.ts +11 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-pick.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-pick.js +109 -0
- package/cli/dist/modules/fund/nsfc/commands/hypothesis-pick.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/outline-confirm.d.ts +9 -0
- package/cli/dist/modules/fund/nsfc/commands/outline-confirm.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/outline-confirm.js +64 -0
- package/cli/dist/modules/fund/nsfc/commands/outline-confirm.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/outline-create.d.ts +8 -0
- package/cli/dist/modules/fund/nsfc/commands/outline-create.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/outline-create.js +103 -0
- package/cli/dist/modules/fund/nsfc/commands/outline-create.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/research-path-confirm.d.ts +9 -0
- package/cli/dist/modules/fund/nsfc/commands/research-path-confirm.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/research-path-confirm.js +176 -0
- package/cli/dist/modules/fund/nsfc/commands/research-path-confirm.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/tech-roadmap.d.ts +9 -0
- package/cli/dist/modules/fund/nsfc/commands/tech-roadmap.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/commands/tech-roadmap.js +190 -0
- package/cli/dist/modules/fund/nsfc/commands/tech-roadmap.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/constants.d.ts +96 -0
- package/cli/dist/modules/fund/nsfc/constants.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/constants.js +156 -0
- package/cli/dist/modules/fund/nsfc/constants.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/graph-render.d.ts +25 -0
- package/cli/dist/modules/fund/nsfc/graph-render.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/graph-render.js +212 -0
- package/cli/dist/modules/fund/nsfc/graph-render.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/index.d.ts +4 -0
- package/cli/dist/modules/fund/nsfc/index.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/index.js +238 -0
- package/cli/dist/modules/fund/nsfc/index.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/mermaid-render.d.ts +18 -0
- package/cli/dist/modules/fund/nsfc/mermaid-render.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/mermaid-render.js +47 -0
- package/cli/dist/modules/fund/nsfc/mermaid-render.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/parse-hypothesis.d.ts +76 -0
- package/cli/dist/modules/fund/nsfc/parse-hypothesis.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/parse-hypothesis.js +278 -0
- package/cli/dist/modules/fund/nsfc/parse-hypothesis.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/parse-outline.d.ts +23 -0
- package/cli/dist/modules/fund/nsfc/parse-outline.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/parse-outline.js +91 -0
- package/cli/dist/modules/fund/nsfc/parse-outline.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/png-watermark.d.ts +24 -0
- package/cli/dist/modules/fund/nsfc/png-watermark.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/png-watermark.js +78 -0
- package/cli/dist/modules/fund/nsfc/png-watermark.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/prompt-hypothesis.d.ts +20 -0
- package/cli/dist/modules/fund/nsfc/prompt-hypothesis.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/prompt-hypothesis.js +107 -0
- package/cli/dist/modules/fund/nsfc/prompt-hypothesis.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/prompt-step2.d.ts +18 -0
- package/cli/dist/modules/fund/nsfc/prompt-step2.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/prompt-step2.js +107 -0
- package/cli/dist/modules/fund/nsfc/prompt-step2.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/request-body.d.ts +47 -0
- package/cli/dist/modules/fund/nsfc/request-body.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/request-body.js +38 -0
- package/cli/dist/modules/fund/nsfc/request-body.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/session-cache.d.ts +48 -0
- package/cli/dist/modules/fund/nsfc/session-cache.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/session-cache.js +90 -0
- package/cli/dist/modules/fund/nsfc/session-cache.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/sse-utils.d.ts +22 -0
- package/cli/dist/modules/fund/nsfc/sse-utils.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/sse-utils.js +81 -0
- package/cli/dist/modules/fund/nsfc/sse-utils.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/upload.d.ts +3 -0
- package/cli/dist/modules/fund/nsfc/upload.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/upload.js +59 -0
- package/cli/dist/modules/fund/nsfc/upload.js.map +1 -0
- package/cli/dist/modules/fund/nsfc/validate.d.ts +30 -0
- package/cli/dist/modules/fund/nsfc/validate.d.ts.map +1 -0
- package/cli/dist/modules/fund/nsfc/validate.js +91 -0
- package/cli/dist/modules/fund/nsfc/validate.js.map +1 -0
- package/cli/dist/modules/literature/commands/workflow-list.d.ts +5 -0
- package/cli/dist/modules/literature/commands/workflow-list.d.ts.map +1 -0
- package/cli/dist/modules/literature/commands/workflow-list.js +9 -0
- package/cli/dist/modules/literature/commands/workflow-list.js.map +1 -0
- package/cli/dist/modules/literature/commands/workflow-params.d.ts +6 -0
- package/cli/dist/modules/literature/commands/workflow-params.d.ts.map +1 -0
- package/cli/dist/modules/literature/commands/workflow-params.js +9 -0
- package/cli/dist/modules/literature/commands/workflow-params.js.map +1 -0
- package/cli/dist/modules/literature/commands/workflow-run.d.ts +7 -0
- package/cli/dist/modules/literature/commands/workflow-run.d.ts.map +1 -0
- package/cli/dist/modules/literature/commands/workflow-run.js +10 -0
- package/cli/dist/modules/literature/commands/workflow-run.js.map +1 -0
- package/cli/dist/modules/literature/constants.d.ts +20 -0
- package/cli/dist/modules/literature/constants.d.ts.map +1 -0
- package/cli/dist/modules/literature/constants.js +56 -0
- package/cli/dist/modules/literature/constants.js.map +1 -0
- package/cli/dist/modules/literature/index.d.ts +3 -0
- package/cli/dist/modules/literature/index.d.ts.map +1 -0
- package/cli/dist/modules/literature/index.js +63 -0
- package/cli/dist/modules/literature/index.js.map +1 -0
- package/cli/dist/modules/meta/analysis.d.ts +127 -0
- package/cli/dist/modules/meta/analysis.d.ts.map +1 -0
- package/cli/dist/modules/meta/analysis.js +1165 -0
- package/cli/dist/modules/meta/analysis.js.map +1 -0
- package/cli/dist/modules/meta/api.d.ts +11 -0
- package/cli/dist/modules/meta/api.d.ts.map +1 -0
- package/cli/dist/modules/meta/api.js +40 -0
- package/cli/dist/modules/meta/api.js.map +1 -0
- package/cli/dist/modules/meta/commands.d.ts +3 -0
- package/cli/dist/modules/meta/commands.d.ts.map +1 -0
- package/cli/dist/modules/meta/commands.js +1185 -0
- package/cli/dist/modules/meta/commands.js.map +1 -0
- package/cli/dist/modules/meta/constants.d.ts +8 -0
- package/cli/dist/modules/meta/constants.d.ts.map +1 -0
- package/cli/dist/modules/meta/constants.js +9 -0
- package/cli/dist/modules/meta/constants.js.map +1 -0
- package/cli/dist/modules/meta/extraction.d.ts +206 -0
- package/cli/dist/modules/meta/extraction.d.ts.map +1 -0
- package/cli/dist/modules/meta/extraction.js +1248 -0
- package/cli/dist/modules/meta/extraction.js.map +1 -0
- package/cli/dist/modules/meta/index.d.ts +3 -0
- package/cli/dist/modules/meta/index.d.ts.map +1 -0
- package/cli/dist/modules/meta/index.js +19 -0
- package/cli/dist/modules/meta/index.js.map +1 -0
- package/cli/dist/modules/meta/node.d.ts +70 -0
- package/cli/dist/modules/meta/node.d.ts.map +1 -0
- package/cli/dist/modules/meta/node.js +167 -0
- package/cli/dist/modules/meta/node.js.map +1 -0
- package/cli/dist/modules/meta/plot-params.d.ts +82 -0
- package/cli/dist/modules/meta/plot-params.d.ts.map +1 -0
- package/cli/dist/modules/meta/plot-params.js +1179 -0
- package/cli/dist/modules/meta/plot-params.js.map +1 -0
- package/cli/dist/modules/meta/prisma-flow-docx.d.ts +23 -0
- package/cli/dist/modules/meta/prisma-flow-docx.d.ts.map +1 -0
- package/cli/dist/modules/meta/prisma-flow-docx.js +302 -0
- package/cli/dist/modules/meta/prisma-flow-docx.js.map +1 -0
- package/cli/dist/modules/meta/retrieve.d.ts +113 -0
- package/cli/dist/modules/meta/retrieve.d.ts.map +1 -0
- package/cli/dist/modules/meta/retrieve.js +913 -0
- package/cli/dist/modules/meta/retrieve.js.map +1 -0
- package/cli/dist/modules/meta/screening.d.ts +179 -0
- package/cli/dist/modules/meta/screening.d.ts.map +1 -0
- package/cli/dist/modules/meta/screening.js +1255 -0
- package/cli/dist/modules/meta/screening.js.map +1 -0
- package/cli/dist/modules/meta/topic.d.ts +110 -0
- package/cli/dist/modules/meta/topic.d.ts.map +1 -0
- package/cli/dist/modules/meta/topic.js +865 -0
- package/cli/dist/modules/meta/topic.js.map +1 -0
- package/cli/dist/modules/meta/types.d.ts +49 -0
- package/cli/dist/modules/meta/types.d.ts.map +1 -0
- package/cli/dist/modules/meta/types.js +2 -0
- package/cli/dist/modules/meta/types.js.map +1 -0
- package/cli/dist/modules/meta/workflow.d.ts +154 -0
- package/cli/dist/modules/meta/workflow.d.ts.map +1 -0
- package/cli/dist/modules/meta/workflow.js +956 -0
- package/cli/dist/modules/meta/workflow.js.map +1 -0
- package/cli/dist/modules/meta/writing.d.ts +83 -0
- package/cli/dist/modules/meta/writing.d.ts.map +1 -0
- package/cli/dist/modules/meta/writing.js +1077 -0
- package/cli/dist/modules/meta/writing.js.map +1 -0
- package/cli/dist/modules/proposal/artifacts.d.ts +18 -0
- package/cli/dist/modules/proposal/artifacts.d.ts.map +1 -0
- package/cli/dist/modules/proposal/artifacts.js +43 -0
- package/cli/dist/modules/proposal/artifacts.js.map +1 -0
- package/cli/dist/modules/proposal/commands/basic-create.d.ts +22 -0
- package/cli/dist/modules/proposal/commands/basic-create.d.ts.map +1 -0
- package/cli/dist/modules/proposal/commands/basic-create.js +81 -0
- package/cli/dist/modules/proposal/commands/basic-create.js.map +1 -0
- package/cli/dist/modules/proposal/commands/basic-result.d.ts +24 -0
- package/cli/dist/modules/proposal/commands/basic-result.d.ts.map +1 -0
- package/cli/dist/modules/proposal/commands/basic-result.js +96 -0
- package/cli/dist/modules/proposal/commands/basic-result.js.map +1 -0
- package/cli/dist/modules/proposal/commands/bioinfo-params.d.ts +5 -0
- package/cli/dist/modules/proposal/commands/bioinfo-params.d.ts.map +1 -0
- package/cli/dist/modules/proposal/commands/bioinfo-params.js +9 -0
- package/cli/dist/modules/proposal/commands/bioinfo-params.js.map +1 -0
- package/cli/dist/modules/proposal/commands/bioinfo-run.d.ts +6 -0
- package/cli/dist/modules/proposal/commands/bioinfo-run.d.ts.map +1 -0
- package/cli/dist/modules/proposal/commands/bioinfo-run.js +10 -0
- package/cli/dist/modules/proposal/commands/bioinfo-run.js.map +1 -0
- package/cli/dist/modules/proposal/commands/clinical-list.d.ts +5 -0
- package/cli/dist/modules/proposal/commands/clinical-list.d.ts.map +1 -0
- package/cli/dist/modules/proposal/commands/clinical-list.js +9 -0
- package/cli/dist/modules/proposal/commands/clinical-list.js.map +1 -0
- package/cli/dist/modules/proposal/commands/clinical-params.d.ts +6 -0
- package/cli/dist/modules/proposal/commands/clinical-params.d.ts.map +1 -0
- package/cli/dist/modules/proposal/commands/clinical-params.js +9 -0
- package/cli/dist/modules/proposal/commands/clinical-params.js.map +1 -0
- package/cli/dist/modules/proposal/commands/clinical-run.d.ts +7 -0
- package/cli/dist/modules/proposal/commands/clinical-run.d.ts.map +1 -0
- package/cli/dist/modules/proposal/commands/clinical-run.js +10 -0
- package/cli/dist/modules/proposal/commands/clinical-run.js.map +1 -0
- package/cli/dist/modules/proposal/constants.d.ts +34 -0
- package/cli/dist/modules/proposal/constants.d.ts.map +1 -0
- package/cli/dist/modules/proposal/constants.js +75 -0
- package/cli/dist/modules/proposal/constants.js.map +1 -0
- package/cli/dist/modules/proposal/index.d.ts +3 -0
- package/cli/dist/modules/proposal/index.d.ts.map +1 -0
- package/cli/dist/modules/proposal/index.js +95 -0
- package/cli/dist/modules/proposal/index.js.map +1 -0
- package/cli/dist/modules/proposal/newidea-env.d.ts +28 -0
- package/cli/dist/modules/proposal/newidea-env.d.ts.map +1 -0
- package/cli/dist/modules/proposal/newidea-env.js +69 -0
- package/cli/dist/modules/proposal/newidea-env.js.map +1 -0
- package/cli/dist/modules/proposal/newidea-runner.d.ts +25 -0
- package/cli/dist/modules/proposal/newidea-runner.d.ts.map +1 -0
- package/cli/dist/modules/proposal/newidea-runner.js +158 -0
- package/cli/dist/modules/proposal/newidea-runner.js.map +1 -0
- package/cli/dist/modules/proposal/parse.d.ts +25 -0
- package/cli/dist/modules/proposal/parse.d.ts.map +1 -0
- package/cli/dist/modules/proposal/parse.js +173 -0
- package/cli/dist/modules/proposal/parse.js.map +1 -0
- package/cli/dist/modules/sci/apps.d.ts +48 -0
- package/cli/dist/modules/sci/apps.d.ts.map +1 -0
- package/cli/dist/modules/sci/apps.js +43 -0
- package/cli/dist/modules/sci/apps.js.map +1 -0
- package/cli/dist/modules/sci/article-cache.d.ts +32 -0
- package/cli/dist/modules/sci/article-cache.d.ts.map +1 -0
- package/cli/dist/modules/sci/article-cache.js +163 -0
- package/cli/dist/modules/sci/article-cache.js.map +1 -0
- package/cli/dist/modules/sci/article-validate.d.ts +11 -0
- package/cli/dist/modules/sci/article-validate.d.ts.map +1 -0
- package/cli/dist/modules/sci/article-validate.js +43 -0
- package/cli/dist/modules/sci/article-validate.js.map +1 -0
- package/cli/dist/modules/sci/commands/article-create.d.ts +46 -0
- package/cli/dist/modules/sci/commands/article-create.d.ts.map +1 -0
- package/cli/dist/modules/sci/commands/article-create.js +315 -0
- package/cli/dist/modules/sci/commands/article-create.js.map +1 -0
- package/cli/dist/modules/sci/commands/article-download.d.ts +50 -0
- package/cli/dist/modules/sci/commands/article-download.d.ts.map +1 -0
- package/cli/dist/modules/sci/commands/article-download.js +281 -0
- package/cli/dist/modules/sci/commands/article-download.js.map +1 -0
- package/cli/dist/modules/sci/commands/outline.d.ts +36 -0
- package/cli/dist/modules/sci/commands/outline.d.ts.map +1 -0
- package/cli/dist/modules/sci/commands/outline.js +491 -0
- package/cli/dist/modules/sci/commands/outline.js.map +1 -0
- package/cli/dist/modules/sci/commands/template.d.ts +9 -0
- package/cli/dist/modules/sci/commands/template.d.ts.map +1 -0
- package/cli/dist/modules/sci/commands/template.js +12 -0
- package/cli/dist/modules/sci/commands/template.js.map +1 -0
- package/cli/dist/modules/sci/commands.d.ts +3 -0
- package/cli/dist/modules/sci/commands.d.ts.map +1 -0
- package/cli/dist/modules/sci/commands.js +108 -0
- package/cli/dist/modules/sci/commands.js.map +1 -0
- package/cli/dist/modules/sci/constants.d.ts +117 -0
- package/cli/dist/modules/sci/constants.d.ts.map +1 -0
- package/cli/dist/modules/sci/constants.js +152 -0
- package/cli/dist/modules/sci/constants.js.map +1 -0
- package/cli/dist/modules/sci/content-source.d.ts +18 -0
- package/cli/dist/modules/sci/content-source.d.ts.map +1 -0
- package/cli/dist/modules/sci/content-source.js +54 -0
- package/cli/dist/modules/sci/content-source.js.map +1 -0
- package/cli/dist/modules/sci/index.d.ts +3 -0
- package/cli/dist/modules/sci/index.d.ts.map +1 -0
- package/cli/dist/modules/sci/index.js +19 -0
- package/cli/dist/modules/sci/index.js.map +1 -0
- package/cli/dist/modules/sci/outline-transform.d.ts +16 -0
- package/cli/dist/modules/sci/outline-transform.d.ts.map +1 -0
- package/cli/dist/modules/sci/outline-transform.js +67 -0
- package/cli/dist/modules/sci/outline-transform.js.map +1 -0
- package/cli/dist/modules/sci/precheck-cache.d.ts +23 -0
- package/cli/dist/modules/sci/precheck-cache.d.ts.map +1 -0
- package/cli/dist/modules/sci/precheck-cache.js +141 -0
- package/cli/dist/modules/sci/precheck-cache.js.map +1 -0
- package/cli/dist/modules/sci/prompt-article.d.ts +12 -0
- package/cli/dist/modules/sci/prompt-article.d.ts.map +1 -0
- package/cli/dist/modules/sci/prompt-article.js +37 -0
- package/cli/dist/modules/sci/prompt-article.js.map +1 -0
- package/cli/dist/modules/sci/prompt-precheck.d.ts +16 -0
- package/cli/dist/modules/sci/prompt-precheck.d.ts.map +1 -0
- package/cli/dist/modules/sci/prompt-precheck.js +61 -0
- package/cli/dist/modules/sci/prompt-precheck.js.map +1 -0
- package/cli/dist/modules/sci/sse-utils.d.ts +50 -0
- package/cli/dist/modules/sci/sse-utils.d.ts.map +1 -0
- package/cli/dist/modules/sci/sse-utils.js +207 -0
- package/cli/dist/modules/sci/sse-utils.js.map +1 -0
- package/cli/dist/modules/shared/result-wrapper.d.ts +71 -0
- package/cli/dist/modules/shared/result-wrapper.d.ts.map +1 -0
- package/cli/dist/modules/shared/result-wrapper.js +95 -0
- package/cli/dist/modules/shared/result-wrapper.js.map +1 -0
- package/cli/dist/modules/shared/workflow-app/api.d.ts +6 -0
- package/cli/dist/modules/shared/workflow-app/api.d.ts.map +1 -0
- package/cli/dist/modules/shared/workflow-app/api.js +31 -0
- package/cli/dist/modules/shared/workflow-app/api.js.map +1 -0
- package/cli/dist/modules/shared/workflow-app/command.d.ts +30 -0
- package/cli/dist/modules/shared/workflow-app/command.d.ts.map +1 -0
- package/cli/dist/modules/shared/workflow-app/command.js +183 -0
- package/cli/dist/modules/shared/workflow-app/command.js.map +1 -0
- package/cli/dist/modules/shared/workflow-app/constants.d.ts +12 -0
- package/cli/dist/modules/shared/workflow-app/constants.d.ts.map +1 -0
- package/cli/dist/modules/shared/workflow-app/constants.js +21 -0
- package/cli/dist/modules/shared/workflow-app/constants.js.map +1 -0
- package/cli/dist/modules/shared/workflow-app/form.d.ts +21 -0
- package/cli/dist/modules/shared/workflow-app/form.d.ts.map +1 -0
- package/cli/dist/modules/shared/workflow-app/form.js +123 -0
- package/cli/dist/modules/shared/workflow-app/form.js.map +1 -0
- package/cli/dist/modules/shared/workflow-app/index.d.ts +8 -0
- package/cli/dist/modules/shared/workflow-app/index.d.ts.map +1 -0
- package/cli/dist/modules/shared/workflow-app/index.js +7 -0
- package/cli/dist/modules/shared/workflow-app/index.js.map +1 -0
- package/cli/dist/modules/shared/workflow-app/run.d.ts +15 -0
- package/cli/dist/modules/shared/workflow-app/run.d.ts.map +1 -0
- package/cli/dist/modules/shared/workflow-app/run.js +71 -0
- package/cli/dist/modules/shared/workflow-app/run.js.map +1 -0
- package/cli/dist/modules/shared/workflow-app/types.d.ts +74 -0
- package/cli/dist/modules/shared/workflow-app/types.d.ts.map +1 -0
- package/cli/dist/modules/shared/workflow-app/types.js +3 -0
- package/cli/dist/modules/shared/workflow-app/types.js.map +1 -0
- package/cli/dist/modules/site-configs.d.ts +4 -0
- package/cli/dist/modules/site-configs.d.ts.map +1 -0
- package/cli/dist/modules/site-configs.js +21 -0
- package/cli/dist/modules/site-configs.js.map +1 -0
- package/cli/dist/modules/upgrade/index.d.ts +6 -0
- package/cli/dist/modules/upgrade/index.d.ts.map +1 -0
- package/cli/dist/modules/upgrade/index.js +105 -0
- package/cli/dist/modules/upgrade/index.js.map +1 -0
- package/cli/dist/program.d.ts +3 -0
- package/cli/dist/program.d.ts.map +1 -0
- package/cli/dist/program.js +69 -0
- package/cli/dist/program.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
3
|
+
import { Resvg } from "@resvg/resvg-js";
|
|
4
|
+
import { PDFDocument } from "pdf-lib";
|
|
5
|
+
import { createStderrProgress, ApiClient, helixlifeError, helixlifeErrorCode, printOutput, sanitizeFilename, } from "../../core/index.js";
|
|
6
|
+
import { requestMeta } from "./api.js";
|
|
7
|
+
import { META_API_PREFIX } from "./constants.js";
|
|
8
|
+
import { getMetaNodeOutput, pollMetaNodeStatus } from "./node.js";
|
|
9
|
+
import { buildEditableParamsDocument, buildQualityPlotEditableParams, EDIT_PLOT_PARAMS_INSTRUCTION, EDIT_QUALITY_PLOT_PARAMS_INSTRUCTION, } from "./plot-params.js";
|
|
10
|
+
import { buildPrismaFlowDocx, } from "./prisma-flow-docx.js";
|
|
11
|
+
const META_ANALYSIS_NODE_TYPES = {
|
|
12
|
+
OUTCOME: "outcome_indicator_extraction",
|
|
13
|
+
QUALITY: "quality_assessment",
|
|
14
|
+
FULLTEXT_SCREENING: "fulltext_screening",
|
|
15
|
+
EFFECT: "effect_quantity",
|
|
16
|
+
BIAS: "publication_bias",
|
|
17
|
+
ASSESSMENT: "quality_assessment_plot",
|
|
18
|
+
PRISMA: "prisma",
|
|
19
|
+
};
|
|
20
|
+
const DEFAULT_EFFECT_PLOT_DATA = {
|
|
21
|
+
table: { line_width: "0.75pt", text_size: "6pt", plot_width: 1 },
|
|
22
|
+
line: { color: ["#000000"], type: "实线", size: "0.75pt", alpha: 1 },
|
|
23
|
+
point: {
|
|
24
|
+
fill: ["#4DBBD5"],
|
|
25
|
+
color: ["#4DBBD5"],
|
|
26
|
+
shape: "菱形",
|
|
27
|
+
size: 1,
|
|
28
|
+
alpha: 1,
|
|
29
|
+
},
|
|
30
|
+
cutoff: { position: "1", color: ["#000000"], type: "虚线", size: "0.75pt" },
|
|
31
|
+
title: { title: "", size: "7pt" },
|
|
32
|
+
axis: { limits_x: "" },
|
|
33
|
+
figure: { width: "10", height: "6", family: "Arial" },
|
|
34
|
+
analysis: {
|
|
35
|
+
summary_measure: "RR(相对危险度)",
|
|
36
|
+
method: "MH",
|
|
37
|
+
model: "随机效应",
|
|
38
|
+
method_tau: "PM",
|
|
39
|
+
method_random_ci: true,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const DEFAULT_BIAS_PLOT_DATA = {
|
|
43
|
+
method: { model: "Trim剪补法前" },
|
|
44
|
+
style: { type: "标准漏斗图" },
|
|
45
|
+
point: {
|
|
46
|
+
fill: ["#4DBBD5", "#E64B35"],
|
|
47
|
+
color: ["#000000"],
|
|
48
|
+
shape: "圆形",
|
|
49
|
+
size: 2,
|
|
50
|
+
alpha: 0.6,
|
|
51
|
+
},
|
|
52
|
+
line: { color: ["#C9C9C9"], type: "虚线", size: "0.75pt", alpha: 1 },
|
|
53
|
+
title: { title: "", x: "", y: "" },
|
|
54
|
+
legend: { show: true, title: "", position: "默认" },
|
|
55
|
+
axis: { show_x: true, show_y: true, limits_x: "" },
|
|
56
|
+
theme: { border: true, panel: false, size: "7pt" },
|
|
57
|
+
figure: { width: "6", height: "5", family: "Arial" },
|
|
58
|
+
analysis: {
|
|
59
|
+
summary_measure: "RR(相对危险度)",
|
|
60
|
+
method: "MH",
|
|
61
|
+
model: "随机效应",
|
|
62
|
+
method_tau: "PM",
|
|
63
|
+
method_random_ci: true,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const DEFAULT_ASSESSMENT_PLOT_DATA = {
|
|
67
|
+
summary: {
|
|
68
|
+
bar: {
|
|
69
|
+
fill: [
|
|
70
|
+
"#02C100",
|
|
71
|
+
"#E2DF07",
|
|
72
|
+
"#BF0000",
|
|
73
|
+
"#3C5488",
|
|
74
|
+
"#F39B7F",
|
|
75
|
+
"#8491B4",
|
|
76
|
+
"#91D1C2",
|
|
77
|
+
"#DC0000",
|
|
78
|
+
"#7E6148",
|
|
79
|
+
"#B09C85",
|
|
80
|
+
],
|
|
81
|
+
color: [
|
|
82
|
+
"#000000",
|
|
83
|
+
"#000000",
|
|
84
|
+
"#000000",
|
|
85
|
+
"#000000",
|
|
86
|
+
"#000000",
|
|
87
|
+
"#000000",
|
|
88
|
+
"#000000",
|
|
89
|
+
"#000000",
|
|
90
|
+
"#000000",
|
|
91
|
+
"#000000",
|
|
92
|
+
],
|
|
93
|
+
size: "0.25pt",
|
|
94
|
+
width: 0.8,
|
|
95
|
+
alpha: 1,
|
|
96
|
+
},
|
|
97
|
+
title: { title: "", x: "", y: "" },
|
|
98
|
+
legend: { show: true, title: "", position: "下" },
|
|
99
|
+
axis: { angle_x: "0" },
|
|
100
|
+
theme: { border: false, panel: false, size: "6pt" },
|
|
101
|
+
figure: { width: "7", height: "5", family: "Arial" },
|
|
102
|
+
},
|
|
103
|
+
traffic_light: {
|
|
104
|
+
point: {
|
|
105
|
+
fill: [
|
|
106
|
+
"#02C100",
|
|
107
|
+
"#E2DF07",
|
|
108
|
+
"#BF0000",
|
|
109
|
+
"#3C5488",
|
|
110
|
+
"#F39B7F",
|
|
111
|
+
"#8491B4",
|
|
112
|
+
"#91D1C2",
|
|
113
|
+
"#DC0000",
|
|
114
|
+
"#7E6148",
|
|
115
|
+
"#B09C85",
|
|
116
|
+
],
|
|
117
|
+
color: [
|
|
118
|
+
"#000000",
|
|
119
|
+
"#000000",
|
|
120
|
+
"#000000",
|
|
121
|
+
"#000000",
|
|
122
|
+
"#000000",
|
|
123
|
+
"#000000",
|
|
124
|
+
"#000000",
|
|
125
|
+
"#000000",
|
|
126
|
+
"#000000",
|
|
127
|
+
"#000000",
|
|
128
|
+
],
|
|
129
|
+
style: ["加号", "减号", "叉号", "问号"],
|
|
130
|
+
size: 3,
|
|
131
|
+
alpha: 1,
|
|
132
|
+
},
|
|
133
|
+
title: { title: "", x: "", y: "" },
|
|
134
|
+
legend: { show: true, title: "", labels: "", position: "默认" },
|
|
135
|
+
theme: { border: true, panel: false, size: "7pt" },
|
|
136
|
+
figure: { width: "9", height: "6", family: "Arial" },
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
/** 图表 get/create 后必须展示 PDF 产物,禁止只报参数或口头声称已展示。 */
|
|
140
|
+
const ANALYSIS_ARTIFACT_DISPLAY_INSTRUCTION = "必须从响应 artifact 打开/嵌入预览 PDF 产物文件本身(与正常 create 流程一致);" +
|
|
141
|
+
"可附带当前参数说明;禁止只输出参数列表、文件路径文字或声称「已展示」而不展示文件。" +
|
|
142
|
+
"展示完成后按该节点操作选项以 Markdown 无序列表列出后停顿,禁止弹选择器,禁止用 / 同行拼接。";
|
|
143
|
+
/** PRISMA create/get-image 后必须展示 PNG,且不得提前索要写作语言。 */
|
|
144
|
+
const PRISMA_IMAGE_DISPLAY_INSTRUCTION = "必须从响应 artifact 打开/嵌入预览 PRISMA PNG 产物;" +
|
|
145
|
+
"禁止只输出元数据或声称已展示;展示后按 PRISMA 操作选项以 Markdown 无序列表列出后停顿:\n" +
|
|
146
|
+
"- 导出其他格式\n- 确认无误,下一步(辅助写作)\n" +
|
|
147
|
+
"禁止弹选择器;禁止用 / 同行拼接;禁止此时弹全文语言选择器;禁止声称即将进入辅助写作并索要语言。" +
|
|
148
|
+
"仅当用户点选「确认无误,下一步(辅助写作)」后,才必须弹出可点击交互选择器选择 English / 中文(禁止聊天列表代替),选定后再进入写作。";
|
|
149
|
+
/** PRISMA 节点操作选项(与 workflow guidance 保持一致;本节点无编辑操作)。 */
|
|
150
|
+
const PRISMA_OPERATION_OPTIONS = [
|
|
151
|
+
"导出其他格式",
|
|
152
|
+
"确认无误,下一步(辅助写作)",
|
|
153
|
+
];
|
|
154
|
+
const DEFAULT_PRISMA_DATA = {
|
|
155
|
+
pubmed_retrieval_count: 0,
|
|
156
|
+
registers_count: 0,
|
|
157
|
+
duplicate_count: 0,
|
|
158
|
+
automation_excluded_count: 0,
|
|
159
|
+
other_exclusion_count: 0,
|
|
160
|
+
screened_count: 0,
|
|
161
|
+
tiab_excluded_count: 0,
|
|
162
|
+
reports_sought_count: 0,
|
|
163
|
+
reports_not_retrieved_count: 0,
|
|
164
|
+
fulltext_count: 0,
|
|
165
|
+
wrong_population_count: 0,
|
|
166
|
+
wrong_intervention_count: 0,
|
|
167
|
+
wrong_comparator_count: 0,
|
|
168
|
+
wrong_study_design_count: 0,
|
|
169
|
+
wrong_outcomes_count: 0,
|
|
170
|
+
irrelevant_count: 0,
|
|
171
|
+
included_count: 0,
|
|
172
|
+
included_reports_count: 0,
|
|
173
|
+
};
|
|
174
|
+
function parseJsonObject(raw, optionName) {
|
|
175
|
+
const jsonText = raw?.replace(/^\uFEFF/, "").trim();
|
|
176
|
+
if (!jsonText) {
|
|
177
|
+
return {};
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
const parsed = JSON.parse(jsonText);
|
|
181
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
182
|
+
throw new Error("not an object");
|
|
183
|
+
}
|
|
184
|
+
return parsed;
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `${optionName} 须为合法 JSON 对象`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function parseJsonObjectFileInput(raw, filePath, optionName) {
|
|
191
|
+
if (filePath?.trim()) {
|
|
192
|
+
return parseJsonObject(await readFile(filePath.trim(), "utf8"), "--data-file");
|
|
193
|
+
}
|
|
194
|
+
return parseJsonObject(raw, optionName);
|
|
195
|
+
}
|
|
196
|
+
async function parseConfigInput(raw, filePath, defaultConfig) {
|
|
197
|
+
if (filePath?.trim()) {
|
|
198
|
+
return parseJsonObject(await readFile(filePath.trim(), "utf8"), "--config-file");
|
|
199
|
+
}
|
|
200
|
+
if (raw?.trim()) {
|
|
201
|
+
return parseJsonObject(raw, "--config-json");
|
|
202
|
+
}
|
|
203
|
+
return defaultConfig;
|
|
204
|
+
}
|
|
205
|
+
function mergeConfigRecords(base, patch) {
|
|
206
|
+
const merged = { ...base };
|
|
207
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
208
|
+
const current = merged[key];
|
|
209
|
+
if (current && typeof current === "object" && !Array.isArray(current) &&
|
|
210
|
+
value && typeof value === "object" && !Array.isArray(value)) {
|
|
211
|
+
merged[key] = mergeConfigRecords(current, value);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
merged[key] = value;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return merged;
|
|
218
|
+
}
|
|
219
|
+
/** 质量评估图顶层子树 key */
|
|
220
|
+
const QUALITY_PLOT_TYPES = ["traffic_light", "summary"];
|
|
221
|
+
/**
|
|
222
|
+
* 将质量评估图 config 补丁规范为只含选定图类型。
|
|
223
|
+
* 支持扁平/嵌套写法;丢弃另一组。
|
|
224
|
+
*/
|
|
225
|
+
function normalizeQualityPlotConfigPatch(patch, plotType) {
|
|
226
|
+
const otherType = plotType === "traffic_light" ? "summary" : "traffic_light";
|
|
227
|
+
const hasSelected = Object.prototype.hasOwnProperty.call(patch, plotType);
|
|
228
|
+
const hasOther = Object.prototype.hasOwnProperty.call(patch, otherType);
|
|
229
|
+
const hasAnyPlotKey = QUALITY_PLOT_TYPES.some((key) => Object.prototype.hasOwnProperty.call(patch, key));
|
|
230
|
+
if (hasAnyPlotKey) {
|
|
231
|
+
if (!hasSelected) {
|
|
232
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `--plot-type=${plotType} 但 config 缺少 ${plotType}` +
|
|
233
|
+
(hasOther ? `(禁止改 ${otherType})` : ""));
|
|
234
|
+
}
|
|
235
|
+
const selected = patch[plotType];
|
|
236
|
+
if (!selected || typeof selected !== "object" || Array.isArray(selected)) {
|
|
237
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `${plotType} 配置须为 JSON 对象`);
|
|
238
|
+
}
|
|
239
|
+
return { [plotType]: selected };
|
|
240
|
+
}
|
|
241
|
+
if (Object.keys(patch).length === 0) {
|
|
242
|
+
return {};
|
|
243
|
+
}
|
|
244
|
+
return { [plotType]: patch };
|
|
245
|
+
}
|
|
246
|
+
/** 有改参时必须提供 plotType,限定只改红绿灯或总结图。 */
|
|
247
|
+
function resolveQualityPlotType(plotType, patch) {
|
|
248
|
+
const normalized = plotType?.trim();
|
|
249
|
+
if (!normalized) {
|
|
250
|
+
if (Object.keys(patch).length === 0) {
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "质量评估图改参须传 --plot-type traffic_light|summary");
|
|
254
|
+
}
|
|
255
|
+
if (normalized !== "traffic_light" && normalized !== "summary") {
|
|
256
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "--plot-type 仅支持 traffic_light 或 summary");
|
|
257
|
+
}
|
|
258
|
+
return normalized;
|
|
259
|
+
}
|
|
260
|
+
function extractNodeOutput(node) {
|
|
261
|
+
if (node.output &&
|
|
262
|
+
typeof node.output === "object" &&
|
|
263
|
+
"output" in node.output) {
|
|
264
|
+
return node.output.output;
|
|
265
|
+
}
|
|
266
|
+
return node.output ?? {};
|
|
267
|
+
}
|
|
268
|
+
async function getWorkflow(workflowId, configPath) {
|
|
269
|
+
return requestMeta(`${META_API_PREFIX}/workflow/runs/${encodeURIComponent(workflowId)}`, { configPath, action: "查询 Meta 工作流" });
|
|
270
|
+
}
|
|
271
|
+
function findLatestNode(workflow, nodeType) {
|
|
272
|
+
return [...(workflow.nodes ?? [])]
|
|
273
|
+
.reverse()
|
|
274
|
+
.find((node) => node.node_type === nodeType);
|
|
275
|
+
}
|
|
276
|
+
async function resolveNodeId(options) {
|
|
277
|
+
if (options.nodeId?.trim()) {
|
|
278
|
+
return options.nodeId.trim();
|
|
279
|
+
}
|
|
280
|
+
const workflow = await getWorkflow(options.workflowId, options.configPath);
|
|
281
|
+
const node = findLatestNode(workflow, options.nodeType);
|
|
282
|
+
if (!node) {
|
|
283
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `当前工作流中未找到 ${options.label} 节点`);
|
|
284
|
+
}
|
|
285
|
+
return node.id;
|
|
286
|
+
}
|
|
287
|
+
function kindConfig(kind) {
|
|
288
|
+
if (kind === "effect") {
|
|
289
|
+
return {
|
|
290
|
+
nodeType: META_ANALYSIS_NODE_TYPES.EFFECT,
|
|
291
|
+
sourceNodeType: META_ANALYSIS_NODE_TYPES.OUTCOME,
|
|
292
|
+
sourceParam: "outcome_indicator_extraction_id",
|
|
293
|
+
executePath: "effect-quantity-plot/execute",
|
|
294
|
+
plotPath: "effect-quantity-plot/plot",
|
|
295
|
+
rerunPath: "effect-quantity-plot/re-execute",
|
|
296
|
+
label: "森林图",
|
|
297
|
+
defaultConfig: DEFAULT_EFFECT_PLOT_DATA,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
if (kind === "bias") {
|
|
301
|
+
return {
|
|
302
|
+
nodeType: META_ANALYSIS_NODE_TYPES.BIAS,
|
|
303
|
+
sourceNodeType: META_ANALYSIS_NODE_TYPES.OUTCOME,
|
|
304
|
+
sourceParam: "outcome_indicator_extraction_id",
|
|
305
|
+
executePath: "publication-bias-plot/execute",
|
|
306
|
+
plotPath: "publication-bias-plot/plot",
|
|
307
|
+
rerunPath: "publication-bias-plot/re-execute",
|
|
308
|
+
label: "漏斗图",
|
|
309
|
+
defaultConfig: DEFAULT_BIAS_PLOT_DATA,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
nodeType: META_ANALYSIS_NODE_TYPES.ASSESSMENT,
|
|
314
|
+
sourceNodeType: META_ANALYSIS_NODE_TYPES.QUALITY,
|
|
315
|
+
sourceParam: "quality_assessment_id",
|
|
316
|
+
executePath: "quality-assessment-plot/execute",
|
|
317
|
+
plotPath: "quality-assessment-plot/plot",
|
|
318
|
+
rerunPath: "quality-assessment-plot/re-execute",
|
|
319
|
+
label: "质量评估图",
|
|
320
|
+
defaultConfig: DEFAULT_ASSESSMENT_PLOT_DATA,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Enforces both server-side plot validations before statistical analysis starts.
|
|
325
|
+
* Any validation error propagates and keeps the workflow in data extraction.
|
|
326
|
+
*/
|
|
327
|
+
async function verifyAnalysisPrerequisites(options) {
|
|
328
|
+
const qualityNodeId = await resolveNodeId({
|
|
329
|
+
workflowId: options.workflowId,
|
|
330
|
+
nodeType: META_ANALYSIS_NODE_TYPES.QUALITY,
|
|
331
|
+
label: "quality assessment",
|
|
332
|
+
configPath: options.configPath,
|
|
333
|
+
});
|
|
334
|
+
await requestMeta(`${META_API_PREFIX}/quality-assessment/verify-for-plot`, {
|
|
335
|
+
params: { run_id: options.workflowId, node_id: qualityNodeId },
|
|
336
|
+
configPath: options.configPath,
|
|
337
|
+
action: "校验质量评估绘图数据",
|
|
338
|
+
});
|
|
339
|
+
const outcomeNodeId = await resolveNodeId({
|
|
340
|
+
workflowId: options.workflowId,
|
|
341
|
+
nodeType: META_ANALYSIS_NODE_TYPES.OUTCOME,
|
|
342
|
+
label: "outcome indicator extraction",
|
|
343
|
+
configPath: options.configPath,
|
|
344
|
+
});
|
|
345
|
+
await requestMeta(`${META_API_PREFIX}/outcome-indicator-extraction/verify-for-plot`, {
|
|
346
|
+
params: { run_id: options.workflowId, node_id: outcomeNodeId },
|
|
347
|
+
configPath: options.configPath,
|
|
348
|
+
action: "校验结局指标绘图数据",
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
async function createAnalysis(kind, options) {
|
|
352
|
+
await verifyAnalysisPrerequisites(options);
|
|
353
|
+
const meta = kindConfig(kind);
|
|
354
|
+
const configPatch = await parseConfigInput(options.configJson, options.configFile, meta.defaultConfig);
|
|
355
|
+
const config = mergeConfigRecords(meta.defaultConfig, configPatch);
|
|
356
|
+
const sourceNodeId = options.sourceNodeId?.trim() ??
|
|
357
|
+
(await resolveNodeId({
|
|
358
|
+
workflowId: options.workflowId,
|
|
359
|
+
nodeType: meta.sourceNodeType,
|
|
360
|
+
label: kind === "assessment" ? "质量评估" : "结局指标",
|
|
361
|
+
configPath: options.configPath,
|
|
362
|
+
}));
|
|
363
|
+
const progress = createStderrProgress(options.format === "json");
|
|
364
|
+
progress.start(`正在生成${meta.label}...`);
|
|
365
|
+
try {
|
|
366
|
+
const created = await requestMeta(`${META_API_PREFIX}/${meta.executePath}`, {
|
|
367
|
+
method: "POST",
|
|
368
|
+
params: {
|
|
369
|
+
run_id: options.workflowId,
|
|
370
|
+
[meta.sourceParam]: sourceNodeId,
|
|
371
|
+
},
|
|
372
|
+
body: config,
|
|
373
|
+
configPath: options.configPath,
|
|
374
|
+
action: `生成${meta.label}`,
|
|
375
|
+
});
|
|
376
|
+
if (!created.node_id) {
|
|
377
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, `生成${meta.label}响应缺少 node_id`);
|
|
378
|
+
}
|
|
379
|
+
await pollMetaNodeStatus({
|
|
380
|
+
workflowId: options.workflowId,
|
|
381
|
+
nodeId: created.node_id,
|
|
382
|
+
configPath: options.configPath,
|
|
383
|
+
});
|
|
384
|
+
const output = await getMetaNodeOutput({
|
|
385
|
+
workflowId: options.workflowId,
|
|
386
|
+
nodeId: created.node_id,
|
|
387
|
+
configPath: options.configPath,
|
|
388
|
+
});
|
|
389
|
+
progress.finish(`${meta.label}生成完成,正在输出结果...`);
|
|
390
|
+
return {
|
|
391
|
+
runnerId: options.workflowId,
|
|
392
|
+
nodeId: created.node_id,
|
|
393
|
+
nodeType: meta.nodeType,
|
|
394
|
+
status: output.data.status,
|
|
395
|
+
input: config,
|
|
396
|
+
output: extractNodeOutput(output.data),
|
|
397
|
+
node: output.data,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
progress.dispose();
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* 读取节点当前绘图配置,并与默认值深度合并。
|
|
407
|
+
*
|
|
408
|
+
* @param kind 图表类型
|
|
409
|
+
* @param options 工作流与节点定位
|
|
410
|
+
* @param knownInput 已有 input(create/rerun 结果可直接传入,避免重复查询)
|
|
411
|
+
*/
|
|
412
|
+
async function resolveCurrentPlotConfig(kind, options, knownInput) {
|
|
413
|
+
const meta = kindConfig(kind);
|
|
414
|
+
if (knownInput) {
|
|
415
|
+
return mergeConfigRecords(meta.defaultConfig, knownInput);
|
|
416
|
+
}
|
|
417
|
+
const nodeId = await resolveNodeId({
|
|
418
|
+
workflowId: options.workflowId,
|
|
419
|
+
nodeId: options.nodeId,
|
|
420
|
+
nodeType: meta.nodeType,
|
|
421
|
+
label: meta.label,
|
|
422
|
+
configPath: options.configPath,
|
|
423
|
+
});
|
|
424
|
+
const workflow = await getWorkflow(options.workflowId, options.configPath);
|
|
425
|
+
const currentNode = (workflow.nodes ?? []).find((node) => node.id === nodeId);
|
|
426
|
+
const currentInput = currentNode?.input && typeof currentNode.input === "object"
|
|
427
|
+
? currentNode.input
|
|
428
|
+
: {};
|
|
429
|
+
return mergeConfigRecords(meta.defaultConfig, currentInput);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* 为图表结果附加可编辑参数清单,供 Agent 在「编辑参数」时原样展示。
|
|
433
|
+
*
|
|
434
|
+
* @param kind 图表类型
|
|
435
|
+
* @param result 原始图表结果
|
|
436
|
+
* @param options 工作流定位
|
|
437
|
+
* @param knownInput 已知配置(可选)
|
|
438
|
+
*/
|
|
439
|
+
async function withEditablePlotParams(kind, result, options, knownInput) {
|
|
440
|
+
const currentConfig = await resolveCurrentPlotConfig(kind, options, knownInput ??
|
|
441
|
+
("input" in result &&
|
|
442
|
+
result.input &&
|
|
443
|
+
typeof result.input === "object" &&
|
|
444
|
+
!Array.isArray(result.input)
|
|
445
|
+
? result.input
|
|
446
|
+
: undefined));
|
|
447
|
+
// 质量评估图:两组参数分开返回,由 Agent 先弹选择器再单组展示
|
|
448
|
+
if (kind === "assessment") {
|
|
449
|
+
const qualityParams = buildQualityPlotEditableParams(currentConfig);
|
|
450
|
+
return {
|
|
451
|
+
...result,
|
|
452
|
+
currentConfig,
|
|
453
|
+
editableParamsMarkdownTrafficLight: qualityParams.trafficLight.markdown,
|
|
454
|
+
editableParamsSectionsTrafficLight: qualityParams.trafficLight.sections,
|
|
455
|
+
requiredSectionLabelsTrafficLight: qualityParams.trafficLight.requiredSectionLabels,
|
|
456
|
+
editableParamsMarkdownSummary: qualityParams.summary.markdown,
|
|
457
|
+
editableParamsSectionsSummary: qualityParams.summary.sections,
|
|
458
|
+
requiredSectionLabelsSummary: qualityParams.summary.requiredSectionLabels,
|
|
459
|
+
editInstruction: EDIT_QUALITY_PLOT_PARAMS_INSTRUCTION,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
// 森林图 / 漏斗图:同一套分组结构,禁止图种特例合并省略
|
|
463
|
+
const doc = buildEditableParamsDocument(kind, currentConfig);
|
|
464
|
+
return {
|
|
465
|
+
...result,
|
|
466
|
+
currentConfig,
|
|
467
|
+
editableParamsMarkdown: doc.markdown,
|
|
468
|
+
editableParamsSections: doc.sections,
|
|
469
|
+
requiredSectionLabels: doc.requiredSectionLabels,
|
|
470
|
+
editInstruction: EDIT_PLOT_PARAMS_INSTRUCTION,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
async function getAnalysis(kind, options) {
|
|
474
|
+
const meta = kindConfig(kind);
|
|
475
|
+
const nodeId = await resolveNodeId({
|
|
476
|
+
workflowId: options.workflowId,
|
|
477
|
+
nodeId: options.nodeId,
|
|
478
|
+
nodeType: meta.nodeType,
|
|
479
|
+
label: meta.label,
|
|
480
|
+
configPath: options.configPath,
|
|
481
|
+
});
|
|
482
|
+
const data = await requestMeta(`${META_API_PREFIX}/${meta.plotPath}`, {
|
|
483
|
+
params: { run_id: options.workflowId, node_id: nodeId },
|
|
484
|
+
configPath: options.configPath,
|
|
485
|
+
action: `获取${meta.label}`,
|
|
486
|
+
});
|
|
487
|
+
return { runnerId: options.workflowId, nodeId, data };
|
|
488
|
+
}
|
|
489
|
+
/** Downloads plot URLs using fixed website-step names instead of backend URL basenames. */
|
|
490
|
+
async function materializeAnalysisArtifacts(kind, data) {
|
|
491
|
+
const record = data && typeof data === "object" && !Array.isArray(data)
|
|
492
|
+
? data
|
|
493
|
+
: {};
|
|
494
|
+
const targets = kind === "effect"
|
|
495
|
+
? [{ key: "file", filename: "森林图.pdf" }]
|
|
496
|
+
: kind === "bias"
|
|
497
|
+
? [{ key: "file", filename: "漏斗图.pdf" }]
|
|
498
|
+
: [
|
|
499
|
+
{ key: "traffic_light_file", filename: "质量评估红绿灯图.pdf" },
|
|
500
|
+
{ key: "summary_file", filename: "质量评估总结图.pdf" },
|
|
501
|
+
];
|
|
502
|
+
const client = new ApiClient();
|
|
503
|
+
const artifacts = [];
|
|
504
|
+
for (const target of targets) {
|
|
505
|
+
const sourceUrl = record[target.key];
|
|
506
|
+
const url = typeof sourceUrl === "string" ? sourceUrl.trim() : "";
|
|
507
|
+
if (!url) {
|
|
508
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, `${target.filename.replace(/\.pdf$/i, "")}产物地址为空`);
|
|
509
|
+
}
|
|
510
|
+
const response = await client.requestBinary(url);
|
|
511
|
+
if (response.buffer.length === 0) {
|
|
512
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, `${target.filename}下载内容为空`);
|
|
513
|
+
}
|
|
514
|
+
const output = resolve(target.filename);
|
|
515
|
+
await mkdir(dirname(output), { recursive: true });
|
|
516
|
+
await writeFile(output, response.buffer);
|
|
517
|
+
artifacts.push({
|
|
518
|
+
output,
|
|
519
|
+
filename: target.filename,
|
|
520
|
+
contentType: response.contentType ?? undefined,
|
|
521
|
+
size: response.buffer.length,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
return artifacts.length === 1 ? artifacts[0] : artifacts;
|
|
525
|
+
}
|
|
526
|
+
async function rerunAnalysis(kind, options) {
|
|
527
|
+
const meta = kindConfig(kind);
|
|
528
|
+
const nodeId = await resolveNodeId({
|
|
529
|
+
workflowId: options.workflowId,
|
|
530
|
+
nodeId: options.nodeId,
|
|
531
|
+
nodeType: meta.nodeType,
|
|
532
|
+
label: meta.label,
|
|
533
|
+
configPath: options.configPath,
|
|
534
|
+
});
|
|
535
|
+
let patch = await parseConfigInput(options.configJson, options.configFile, {});
|
|
536
|
+
let editedPlotType;
|
|
537
|
+
// 质量评估图:只合并选定子树
|
|
538
|
+
if (kind === "assessment") {
|
|
539
|
+
editedPlotType = resolveQualityPlotType(options.plotType, patch);
|
|
540
|
+
if (editedPlotType) {
|
|
541
|
+
patch = normalizeQualityPlotConfigPatch(patch, editedPlotType);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
const workflow = await getWorkflow(options.workflowId, options.configPath);
|
|
545
|
+
const currentNode = (workflow.nodes ?? []).find((node) => node.id === nodeId);
|
|
546
|
+
const currentInput = currentNode?.input && typeof currentNode.input === "object"
|
|
547
|
+
? currentNode.input
|
|
548
|
+
: {};
|
|
549
|
+
const input = mergeConfigRecords(mergeConfigRecords(meta.defaultConfig, currentInput), patch);
|
|
550
|
+
const data = await requestMeta(`${META_API_PREFIX}/${meta.rerunPath}`, {
|
|
551
|
+
method: "POST",
|
|
552
|
+
params: { run_id: options.workflowId, node_id: nodeId },
|
|
553
|
+
body: input,
|
|
554
|
+
configPath: options.configPath,
|
|
555
|
+
action: `重新生成${meta.label}`,
|
|
556
|
+
});
|
|
557
|
+
await pollMetaNodeStatus({
|
|
558
|
+
workflowId: options.workflowId,
|
|
559
|
+
nodeId,
|
|
560
|
+
configPath: options.configPath,
|
|
561
|
+
});
|
|
562
|
+
return {
|
|
563
|
+
runnerId: options.workflowId,
|
|
564
|
+
nodeId,
|
|
565
|
+
input,
|
|
566
|
+
data,
|
|
567
|
+
editedPlotType,
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* 获取 PRISMA 流程图数据。
|
|
572
|
+
*
|
|
573
|
+
* 网站使用全文复筛节点作为 PRISMA 数据来源,CLI 保持同样的 run_id/node_id 参数关系。
|
|
574
|
+
*
|
|
575
|
+
* @param options PRISMA 数据查询参数。
|
|
576
|
+
* @returns PRISMA 流程图结构化数据。
|
|
577
|
+
*/
|
|
578
|
+
async function getPrismaFlowData(options) {
|
|
579
|
+
const nodeId = options.sourceNodeId?.trim() ??
|
|
580
|
+
(await resolveNodeId({
|
|
581
|
+
workflowId: options.workflowId,
|
|
582
|
+
nodeType: META_ANALYSIS_NODE_TYPES.FULLTEXT_SCREENING,
|
|
583
|
+
label: "全文复筛",
|
|
584
|
+
configPath: options.configPath,
|
|
585
|
+
}));
|
|
586
|
+
const data = await requestMeta(`${META_API_PREFIX}/prisma`, {
|
|
587
|
+
params: { run_id: options.workflowId, node_id: nodeId },
|
|
588
|
+
configPath: options.configPath,
|
|
589
|
+
action: "获取 PRISMA 流程图数据",
|
|
590
|
+
});
|
|
591
|
+
return { runnerId: options.workflowId, nodeId, data };
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* 更新 PRISMA 流程图数据。
|
|
595
|
+
*
|
|
596
|
+
* @param options PRISMA 数据更新参数,数据字段需与网站 PRISMA 表单字段一致。
|
|
597
|
+
* @returns 后端保存结果。
|
|
598
|
+
*/
|
|
599
|
+
async function updatePrismaFlowData(options) {
|
|
600
|
+
const nodeId = options.sourceNodeId?.trim() ??
|
|
601
|
+
(await resolveNodeId({
|
|
602
|
+
workflowId: options.workflowId,
|
|
603
|
+
nodeType: META_ANALYSIS_NODE_TYPES.FULLTEXT_SCREENING,
|
|
604
|
+
label: "全文复筛",
|
|
605
|
+
configPath: options.configPath,
|
|
606
|
+
}));
|
|
607
|
+
const patch = await parseJsonObjectFileInput(options.dataJson, options.dataFile, "--data-json");
|
|
608
|
+
const current = normalizePrismaFlowData((await getPrismaFlowData({
|
|
609
|
+
workflowId: options.workflowId,
|
|
610
|
+
sourceNodeId: nodeId,
|
|
611
|
+
configPath: options.configPath,
|
|
612
|
+
})).data);
|
|
613
|
+
const input = { ...current, ...patch };
|
|
614
|
+
const data = await requestMeta(`${META_API_PREFIX}/prisma-flow-data`, {
|
|
615
|
+
method: "PUT",
|
|
616
|
+
params: { run_id: options.workflowId, node_id: nodeId },
|
|
617
|
+
body: input,
|
|
618
|
+
configPath: options.configPath,
|
|
619
|
+
action: "更新 PRISMA 流程图数据",
|
|
620
|
+
});
|
|
621
|
+
return { runnerId: options.workflowId, nodeId, input, data };
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* 从后端读取已上传的 PRISMA 图片原始响应(不落盘)。
|
|
625
|
+
*
|
|
626
|
+
* @param options 工作流参数。
|
|
627
|
+
* @returns 后端原始图片响应。
|
|
628
|
+
*/
|
|
629
|
+
async function fetchPrismaImageData(options) {
|
|
630
|
+
return requestMeta(`${META_API_PREFIX}/prisma/image`, {
|
|
631
|
+
params: { run_id: options.workflowId },
|
|
632
|
+
configPath: options.configPath,
|
|
633
|
+
action: "获取 PRISMA 图片",
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* 获取已上传到后端的 PRISMA 图片,并落盘为可展示的 PNG 产物。
|
|
638
|
+
*
|
|
639
|
+
* @param options PRISMA 图片查询参数。
|
|
640
|
+
* @returns 图片数据与本地 artifact(供历史切换后展示)。
|
|
641
|
+
*/
|
|
642
|
+
async function getPrismaImage(options) {
|
|
643
|
+
const data = await fetchPrismaImageData(options);
|
|
644
|
+
const extracted = extractImageBuffer(data);
|
|
645
|
+
const filename = "PRISMA流程图.png";
|
|
646
|
+
const outputOpt = options.output?.trim();
|
|
647
|
+
let output;
|
|
648
|
+
if (!outputOpt) {
|
|
649
|
+
output = resolve(filename);
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
const outputStat = await stat(outputOpt).catch(() => undefined);
|
|
653
|
+
if (outputStat?.isDirectory() ||
|
|
654
|
+
outputOpt.endsWith("/") ||
|
|
655
|
+
outputOpt.endsWith("\\")) {
|
|
656
|
+
output = join(outputOpt, filename);
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
output = extname(outputOpt) ? resolve(outputOpt) : join(outputOpt, filename);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
await mkdir(dirname(output), { recursive: true });
|
|
663
|
+
await writeFile(output, extracted.buffer);
|
|
664
|
+
return {
|
|
665
|
+
runnerId: options.workflowId,
|
|
666
|
+
data,
|
|
667
|
+
artifact: {
|
|
668
|
+
output,
|
|
669
|
+
filename,
|
|
670
|
+
contentType: "image/png",
|
|
671
|
+
size: extracted.buffer.length,
|
|
672
|
+
},
|
|
673
|
+
displayInstruction: PRISMA_IMAGE_DISPLAY_INSTRUCTION,
|
|
674
|
+
operationOptions: [...PRISMA_OPERATION_OPTIONS],
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* 上传 PRISMA 流程图图片,供后续全文写作接口使用。
|
|
679
|
+
*
|
|
680
|
+
* @param options 图片上传参数。
|
|
681
|
+
* @returns 后端上传结果。
|
|
682
|
+
*/
|
|
683
|
+
async function uploadPrismaImage(options) {
|
|
684
|
+
const bytes = await readFile(options.file);
|
|
685
|
+
const filename = options.file.split(/[\\/]/).pop() || "prisma-flow.png";
|
|
686
|
+
const data = await uploadPrismaImageBuffer({
|
|
687
|
+
workflowId: options.workflowId,
|
|
688
|
+
configPath: options.configPath,
|
|
689
|
+
buffer: bytes,
|
|
690
|
+
filename,
|
|
691
|
+
});
|
|
692
|
+
return { runnerId: options.workflowId, input: { file: options.file }, data };
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* 上传内存中的 PRISMA PNG 图片,供默认生成流程在生成图片后立即同步到后端。
|
|
696
|
+
*
|
|
697
|
+
* @param options 上传参数,包含工作流、图片内容与文件名。
|
|
698
|
+
* @returns 后端上传响应。
|
|
699
|
+
*/
|
|
700
|
+
async function uploadPrismaImageBuffer(options) {
|
|
701
|
+
const form = new FormData();
|
|
702
|
+
form.append("file", new Blob([new Uint8Array(options.buffer)], { type: "image/png" }), options.filename || "prisma-flow.png");
|
|
703
|
+
return requestMeta(`${META_API_PREFIX}/prisma/upload`, {
|
|
704
|
+
method: "POST",
|
|
705
|
+
params: { run_id: options.workflowId },
|
|
706
|
+
body: form,
|
|
707
|
+
configPath: options.configPath,
|
|
708
|
+
action: "上传 PRISMA 图片",
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* 上传本次生成的 PRISMA PNG,并立即从后端读取校验,确保后续全文写作能拿到同一张图。
|
|
713
|
+
*
|
|
714
|
+
* @param options 上传和校验参数。
|
|
715
|
+
* @returns 上传接口响应与后端已保存图片信息。
|
|
716
|
+
* @throws {helixlifeError} 上传失败、后端未保存图片或保存内容不是本次生成图片时抛出。
|
|
717
|
+
*/
|
|
718
|
+
async function uploadAndVerifyPrismaImage(options) {
|
|
719
|
+
let upload;
|
|
720
|
+
try {
|
|
721
|
+
upload = await uploadPrismaImageBuffer(options);
|
|
722
|
+
}
|
|
723
|
+
catch (error) {
|
|
724
|
+
const message = error instanceof Error && error.message ? `:${error.message}` : "";
|
|
725
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, `PRISMA 图片上传失败${message}`);
|
|
726
|
+
}
|
|
727
|
+
let saved;
|
|
728
|
+
try {
|
|
729
|
+
saved = extractImageBuffer(await fetchPrismaImageData({
|
|
730
|
+
workflowId: options.workflowId,
|
|
731
|
+
configPath: options.configPath,
|
|
732
|
+
}));
|
|
733
|
+
}
|
|
734
|
+
catch (error) {
|
|
735
|
+
const message = error instanceof Error && error.message ? `:${error.message}` : "";
|
|
736
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, `PRISMA 图片上传后校验失败,后端未返回已保存图片${message}`);
|
|
737
|
+
}
|
|
738
|
+
if (!saved.buffer.equals(options.buffer)) {
|
|
739
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, "PRISMA 图片上传后校验失败,后端保存的图片不是本次生成的最新图片");
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
upload,
|
|
743
|
+
image: {
|
|
744
|
+
filename: saved.filename,
|
|
745
|
+
size: saved.buffer.byteLength,
|
|
746
|
+
},
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
function normalizePrismaFlowData(data) {
|
|
750
|
+
const payload = data && typeof data === "object" && "data" in data
|
|
751
|
+
? data.data
|
|
752
|
+
: data;
|
|
753
|
+
const record = payload && typeof payload === "object"
|
|
754
|
+
? payload
|
|
755
|
+
: {};
|
|
756
|
+
const normalized = { ...DEFAULT_PRISMA_DATA };
|
|
757
|
+
const numericKeys = Object.keys(DEFAULT_PRISMA_DATA);
|
|
758
|
+
for (const key of numericKeys) {
|
|
759
|
+
const value = record[key];
|
|
760
|
+
const numericValue = typeof value === "number" ? value : Number(value ?? 0);
|
|
761
|
+
normalized[key] = Number.isFinite(numericValue)
|
|
762
|
+
? numericValue
|
|
763
|
+
: 0;
|
|
764
|
+
}
|
|
765
|
+
if (typeof record.prisma_text === "string") {
|
|
766
|
+
normalized.prisma_text = record.prisma_text;
|
|
767
|
+
}
|
|
768
|
+
if (normalized.included_reports_count === 0) {
|
|
769
|
+
normalized.included_reports_count = normalized.included_count;
|
|
770
|
+
}
|
|
771
|
+
return normalized;
|
|
772
|
+
}
|
|
773
|
+
function parsePrismaExportFormat(raw) {
|
|
774
|
+
const value = (raw ?? "png").trim().toLowerCase();
|
|
775
|
+
if (value === "png" || value === "pdf" || value === "docx") {
|
|
776
|
+
return value;
|
|
777
|
+
}
|
|
778
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "--export-format 仅支持 png、pdf、docx");
|
|
779
|
+
}
|
|
780
|
+
async function resolvePrismaExportPath(options) {
|
|
781
|
+
const filename = sanitizeFilename(`PRISMA流程图.${options.format}`);
|
|
782
|
+
const output = options.output?.trim();
|
|
783
|
+
if (!output) {
|
|
784
|
+
return join(".", filename);
|
|
785
|
+
}
|
|
786
|
+
const outputStat = await stat(output).catch(() => undefined);
|
|
787
|
+
if (outputStat?.isDirectory() ||
|
|
788
|
+
output.endsWith("/") ||
|
|
789
|
+
output.endsWith("\\")) {
|
|
790
|
+
return join(output, filename);
|
|
791
|
+
}
|
|
792
|
+
return extname(output) ? output : join(output, filename);
|
|
793
|
+
}
|
|
794
|
+
async function buildPrismaDocx(data) {
|
|
795
|
+
return buildPrismaFlowDocx(data);
|
|
796
|
+
}
|
|
797
|
+
function escapeSvgText(value) {
|
|
798
|
+
return String(value)
|
|
799
|
+
.replace(/&/g, "&")
|
|
800
|
+
.replace(/</g, "<")
|
|
801
|
+
.replace(/>/g, ">")
|
|
802
|
+
.replace(/"/g, """);
|
|
803
|
+
}
|
|
804
|
+
function svgTextLines(lines, x, y, options = {}) {
|
|
805
|
+
const size = options.size ?? 16;
|
|
806
|
+
const weight = options.weight ?? "400";
|
|
807
|
+
const anchor = options.anchor ?? "start";
|
|
808
|
+
const fill = options.fill ?? "#1C1F21";
|
|
809
|
+
const lineHeight = options.lineHeight ?? 21;
|
|
810
|
+
const style = options.italic ? ' font-style="italic"' : "";
|
|
811
|
+
return lines
|
|
812
|
+
.map((line, index) => {
|
|
813
|
+
return `<text x="${x}" y="${y + index * lineHeight}" font-family="Arial, Helvetica, sans-serif" font-size="${size}" font-weight="${weight}" text-anchor="${anchor}" fill="${fill}"${style}>${escapeSvgText(line)}</text>`;
|
|
814
|
+
})
|
|
815
|
+
.join("");
|
|
816
|
+
}
|
|
817
|
+
function estimateSvgTextWidth(value, fontSize) {
|
|
818
|
+
return [...value].reduce((width, character) => {
|
|
819
|
+
if (character === " ")
|
|
820
|
+
return width + fontSize * 0.28;
|
|
821
|
+
if (/[A-Z]/.test(character))
|
|
822
|
+
return width + fontSize * 0.62;
|
|
823
|
+
if (/[0-9]/.test(character))
|
|
824
|
+
return width + fontSize * 0.56;
|
|
825
|
+
if (/[,.:;()=*%\-]/.test(character))
|
|
826
|
+
return width + fontSize * 0.32;
|
|
827
|
+
return width + fontSize * 0.52;
|
|
828
|
+
}, 0);
|
|
829
|
+
}
|
|
830
|
+
function wrapSvgTextLines(lines, maxWidth, fontSize) {
|
|
831
|
+
const wrapped = [];
|
|
832
|
+
for (const rawLine of lines) {
|
|
833
|
+
const words = String(rawLine).trim().split(/\s+/).filter(Boolean);
|
|
834
|
+
if (words.length === 0) {
|
|
835
|
+
wrapped.push("");
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
let current = words[0];
|
|
839
|
+
for (const word of words.slice(1)) {
|
|
840
|
+
const candidate = `${current} ${word}`;
|
|
841
|
+
if (estimateSvgTextWidth(candidate, fontSize) <= maxWidth) {
|
|
842
|
+
current = candidate;
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
wrapped.push(current);
|
|
846
|
+
current = word;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
wrapped.push(current);
|
|
850
|
+
}
|
|
851
|
+
return wrapped;
|
|
852
|
+
}
|
|
853
|
+
function prismaBox(x, y, width, height, lines, fill = "#ffffff") {
|
|
854
|
+
const fontSize = 16;
|
|
855
|
+
const wrappedLines = wrapSvgTextLines(lines, width - 28, fontSize);
|
|
856
|
+
return [
|
|
857
|
+
`<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="2" fill="${fill}" stroke="#1C1F21" stroke-width="1"/>`,
|
|
858
|
+
`<path d="M ${x + 1} ${y + height} L ${x + width + 1} ${y + height}" stroke="rgba(0,0,0,0.1)" stroke-width="1"/>`,
|
|
859
|
+
svgTextLines(wrappedLines, x + 14, y + 23, { size: fontSize, fill: "#1C1F21", lineHeight: 20 }),
|
|
860
|
+
].join("");
|
|
861
|
+
}
|
|
862
|
+
function prismaArrowDown(x, y1, y2) {
|
|
863
|
+
return `<path d="M ${x} ${y1} L ${x} ${y2}" stroke="#101828" stroke-width="1.2" fill="none" marker-end="url(#arrow)"/>`;
|
|
864
|
+
}
|
|
865
|
+
function prismaArrowRight(x1, x2, y) {
|
|
866
|
+
return `<path d="M ${x1} ${y} L ${x2} ${y}" stroke="#101828" stroke-width="1.2" fill="none" marker-end="url(#arrow)"/>`;
|
|
867
|
+
}
|
|
868
|
+
function prismaSideLabel(label, x, y, height) {
|
|
869
|
+
return [
|
|
870
|
+
`<rect x="${x}" y="${y}" width="45" height="${height}" rx="6" fill="#9DC3E6"/>`,
|
|
871
|
+
`<path d="M ${x + 1} ${y + height} L ${x + 46} ${y + height}" stroke="rgba(0,0,0,0.1)" stroke-width="1"/>`,
|
|
872
|
+
`<text x="${x + 29}" y="${y + height / 2}" font-family="Arial, Helvetica, sans-serif" font-size="16" font-weight="700" fill="#101828" text-anchor="middle" dominant-baseline="middle" transform="rotate(-90 ${x + 29} ${y + height / 2})">${escapeSvgText(label)}</text>`,
|
|
873
|
+
].join("");
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* 根据 PRISMA 结构化数据生成网站同类流程图的 SVG 画布。
|
|
877
|
+
*
|
|
878
|
+
* @param data PRISMA 结构化数据。
|
|
879
|
+
* @returns 可渲染为 PNG 的 SVG 字符串。
|
|
880
|
+
*/
|
|
881
|
+
function buildPrismaSvg(data) {
|
|
882
|
+
const canvasW = 1040;
|
|
883
|
+
const canvasH = 960;
|
|
884
|
+
const sidebarX = 60;
|
|
885
|
+
const leftX = 125;
|
|
886
|
+
const rightX = 525;
|
|
887
|
+
const boxW = 305;
|
|
888
|
+
const rightW = 390;
|
|
889
|
+
const arrowX = leftX + boxW / 2;
|
|
890
|
+
const rightArrowStartX = leftX + boxW;
|
|
891
|
+
const footerX = 20;
|
|
892
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
893
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${canvasW}" height="${canvasH}" viewBox="0 0 ${canvasW} ${canvasH}">
|
|
894
|
+
<defs>
|
|
895
|
+
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
|
896
|
+
<path d="M0,0 L0,6 L9,3 z" fill="#101828"/>
|
|
897
|
+
</marker>
|
|
898
|
+
</defs>
|
|
899
|
+
<rect width="${canvasW}" height="${canvasH}" rx="20" fill="#ffffff"/>
|
|
900
|
+
${svgTextLines(["PRISMA 2020 flow diagram for new systematic reviews which included", "searches of databases and registers only"], canvasW / 2, 34, { size: 18, weight: "700", anchor: "middle", fill: "#61666D", lineHeight: 20 })}
|
|
901
|
+
<rect x="${leftX}" y="82" width="${rightX + rightW - leftX}" height="45" rx="6" fill="#FFC000"/>
|
|
902
|
+
${svgTextLines(["Identification of studies via databases and registers"], (leftX + rightX + rightW) / 2, 110, { size: 16, weight: "700", anchor: "middle", fill: "#1C1F21" })}
|
|
903
|
+
${prismaSideLabel("Identification", sidebarX, 145, 125)}
|
|
904
|
+
${prismaBox(leftX, 145, boxW, 125, ["Records identified from*:", ` Databases (n = ${data.pubmed_retrieval_count})`, ` Registers (n = ${data.registers_count})`])}
|
|
905
|
+
${prismaBox(rightX, 145, rightW, 125, ["Records removed before screening:", `Duplicate records removed (n = ${data.duplicate_count})`, `Records marked as ineligible by automation tools (n = ${data.automation_excluded_count})`, `Records removed for other reasons (n = ${data.other_exclusion_count})`])}
|
|
906
|
+
${prismaArrowRight(rightArrowStartX, rightX, 207)}
|
|
907
|
+
${prismaArrowDown(arrowX, 270, 315)}
|
|
908
|
+
${prismaSideLabel("Screening", sidebarX, 315, 282)}
|
|
909
|
+
${prismaBox(leftX, 315, boxW, 75, ["Records screened", `(n = ${data.screened_count})`])}
|
|
910
|
+
${prismaBox(rightX, 315, rightW, 75, ["Records excluded**", `(n = ${data.tiab_excluded_count})`])}
|
|
911
|
+
${prismaArrowRight(rightArrowStartX, rightX, 352)}
|
|
912
|
+
${prismaArrowDown(arrowX, 390, 430)}
|
|
913
|
+
${prismaBox(leftX, 430, boxW, 75, ["Reports sought for retrieval", `(n = ${data.reports_sought_count})`])}
|
|
914
|
+
${prismaBox(rightX, 430, rightW, 75, ["Reports not retrieved", `(n = ${data.reports_not_retrieved_count})`])}
|
|
915
|
+
${prismaArrowRight(rightArrowStartX, rightX, 467)}
|
|
916
|
+
${prismaArrowDown(arrowX, 505, 545)}
|
|
917
|
+
${prismaBox(leftX, 545, boxW, 75, ["Reports assessed for eligibility", `(n = ${data.fulltext_count})`])}
|
|
918
|
+
${prismaBox(rightX, 545, rightW, 225, ["Reports excluded:", ` Inappropriate population (n = ${data.wrong_population_count})`, ` Inappropriate intervention (n = ${data.wrong_intervention_count})`, ` Inappropriate comparator (n = ${data.wrong_comparator_count})`, ` Inappropriate study design (n = ${data.wrong_study_design_count})`, ` Inappropriate outcome (n = ${data.wrong_outcomes_count})`, ` Irrelevant (n = ${data.irrelevant_count})`, " etc."])}
|
|
919
|
+
${prismaArrowRight(rightArrowStartX, rightX, 582)}
|
|
920
|
+
${prismaArrowDown(arrowX, 620, 660)}
|
|
921
|
+
${prismaSideLabel("Included", sidebarX, 660, 110)}
|
|
922
|
+
${prismaBox(leftX, 660, boxW, 110, ["Studies included in review", `(n = ${data.included_count})`, "Reports of included studies", `(n = ${data.included_reports_count})`])}
|
|
923
|
+
${svgTextLines(["*Consider, if feasible to do so, reporting the number of records identified from each database or register searched (rather than the", "total number across all databases/registers).", "**If automation tools were used, indicate how many records were excluded by a human and how many were excluded by automation", "tools."], footerX, 800, { size: 15, weight: "700", fill: "#61666D", lineHeight: 19 })}
|
|
924
|
+
${svgTextLines(["Source: Page MJ, et al. BMJ 2021;372:n71. doi: 10.1136/bmj.n71."], footerX, 895, { size: 15, weight: "700", fill: "#93999F" })}
|
|
925
|
+
${svgTextLines(["This work is licensed under CC BY 4.0. To view a copy of this license, visit"], footerX, 920, { size: 15, weight: "700", fill: "#93999F" })}
|
|
926
|
+
${svgTextLines(["https://creativecommons.org/licenses/by/4.0"], footerX, 943, { size: 15, weight: "700", fill: "#9747FF" })}
|
|
927
|
+
</svg>`;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* 将 PRISMA 结构化数据渲染为真实 PNG 图片。
|
|
931
|
+
*
|
|
932
|
+
* @param data PRISMA 结构化数据。
|
|
933
|
+
* @returns PNG 文件内容。
|
|
934
|
+
*/
|
|
935
|
+
function buildPrismaPng(data) {
|
|
936
|
+
const svg = buildPrismaSvg(data);
|
|
937
|
+
return Buffer.from(new Resvg(svg).render().asPng());
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* 基于与 PNG 相同的 SVG 渲染生成 PRISMA PDF,保证英文文案与箭头间距一致。
|
|
941
|
+
*
|
|
942
|
+
* @param data PRISMA 结构化数据。
|
|
943
|
+
* @returns PDF 文件内容。
|
|
944
|
+
*/
|
|
945
|
+
export async function buildPrismaPdf(data) {
|
|
946
|
+
const png = buildPrismaPng(data);
|
|
947
|
+
const pdfDoc = await PDFDocument.create();
|
|
948
|
+
const image = await pdfDoc.embedPng(png);
|
|
949
|
+
// A4,边距内等比缩放,与标准流程图视觉一致
|
|
950
|
+
const pageWidth = 595.28;
|
|
951
|
+
const pageHeight = 841.89;
|
|
952
|
+
const margin = 20;
|
|
953
|
+
const maxW = pageWidth - margin * 2;
|
|
954
|
+
const maxH = pageHeight - margin * 2;
|
|
955
|
+
const scale = Math.min(maxW / image.width, maxH / image.height);
|
|
956
|
+
const drawW = image.width * scale;
|
|
957
|
+
const drawH = image.height * scale;
|
|
958
|
+
const page = pdfDoc.addPage([pageWidth, pageHeight]);
|
|
959
|
+
page.drawImage(image, {
|
|
960
|
+
x: (pageWidth - drawW) / 2,
|
|
961
|
+
y: (pageHeight - drawH) / 2,
|
|
962
|
+
width: drawW,
|
|
963
|
+
height: drawH,
|
|
964
|
+
});
|
|
965
|
+
return Buffer.from(await pdfDoc.save());
|
|
966
|
+
}
|
|
967
|
+
function extractImageBuffer(data) {
|
|
968
|
+
const payload = data && typeof data === "object" && "data" in data
|
|
969
|
+
? data.data
|
|
970
|
+
: data;
|
|
971
|
+
if (!payload || typeof payload !== "object") {
|
|
972
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, "PRISMA 图片响应为空");
|
|
973
|
+
}
|
|
974
|
+
const image = payload.image;
|
|
975
|
+
const fileName = payload.file_name;
|
|
976
|
+
if (typeof image !== "string" || !image.trim()) {
|
|
977
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, "后端尚未保存 PRISMA PNG 图片,请先上传图片");
|
|
978
|
+
}
|
|
979
|
+
const base64 = image.includes(",") ? (image.split(",").pop() ?? "") : image;
|
|
980
|
+
return {
|
|
981
|
+
buffer: Buffer.from(base64, "base64"),
|
|
982
|
+
filename: typeof fileName === "string" && fileName.trim()
|
|
983
|
+
? fileName.trim()
|
|
984
|
+
: "prisma-flow.png",
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* 导出 PRISMA 流程图产物。
|
|
989
|
+
*
|
|
990
|
+
* @param options PRISMA 导出参数。
|
|
991
|
+
* @returns 导出文件信息。
|
|
992
|
+
*/
|
|
993
|
+
export async function exportPrismaFlow(options) {
|
|
994
|
+
await verifyAnalysisPrerequisites(options);
|
|
995
|
+
const exportFormat = parsePrismaExportFormat(options.exportFormat);
|
|
996
|
+
const outputPath = await resolvePrismaExportPath({
|
|
997
|
+
workflowId: options.workflowId,
|
|
998
|
+
configPath: options.configPath,
|
|
999
|
+
output: options.output,
|
|
1000
|
+
title: options.title,
|
|
1001
|
+
format: exportFormat,
|
|
1002
|
+
});
|
|
1003
|
+
const result = await getPrismaFlowData(options);
|
|
1004
|
+
const data = normalizePrismaFlowData(result.data);
|
|
1005
|
+
let buffer = exportFormat === "png"
|
|
1006
|
+
? buildPrismaPng(data)
|
|
1007
|
+
: exportFormat === "pdf"
|
|
1008
|
+
? await buildPrismaPdf(data)
|
|
1009
|
+
: await buildPrismaDocx(data);
|
|
1010
|
+
let upload;
|
|
1011
|
+
if (exportFormat === "png") {
|
|
1012
|
+
upload = await uploadAndVerifyPrismaImage({
|
|
1013
|
+
workflowId: options.workflowId,
|
|
1014
|
+
configPath: options.configPath,
|
|
1015
|
+
buffer,
|
|
1016
|
+
filename: outputPath.split(/[\\/]/).pop() || "prisma-flow.png",
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
1020
|
+
await writeFile(outputPath, buffer);
|
|
1021
|
+
// 默认 PNG:明确告知 Agent 先停在本节点,勿提前弹全文语言选择器
|
|
1022
|
+
const isDefaultPng = exportFormat === "png";
|
|
1023
|
+
return {
|
|
1024
|
+
runnerId: options.workflowId,
|
|
1025
|
+
nodeId: result.nodeId,
|
|
1026
|
+
output: outputPath,
|
|
1027
|
+
filename: outputPath.split(/[\\/]/).pop() || `PRISMA流程图.${exportFormat}`,
|
|
1028
|
+
contentType: exportFormat === "docx"
|
|
1029
|
+
? "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
1030
|
+
: exportFormat === "pdf"
|
|
1031
|
+
? "application/pdf"
|
|
1032
|
+
: "image/png",
|
|
1033
|
+
exportFormat,
|
|
1034
|
+
size: buffer.byteLength,
|
|
1035
|
+
data,
|
|
1036
|
+
upload,
|
|
1037
|
+
...(isDefaultPng
|
|
1038
|
+
? {
|
|
1039
|
+
displayInstruction: PRISMA_IMAGE_DISPLAY_INSTRUCTION,
|
|
1040
|
+
operationOptions: [...PRISMA_OPERATION_OPTIONS],
|
|
1041
|
+
}
|
|
1042
|
+
: {
|
|
1043
|
+
displayInstruction: "已导出 PRISMA 其他格式产物;仍停在本节点,按操作选项以 Markdown 无序列表列出后停顿:\n" +
|
|
1044
|
+
"- 导出其他格式\n- 确认无误,下一步(辅助写作)\n" +
|
|
1045
|
+
"禁止弹全文语言选择器;仅用户点选进入辅助写作后,才必须弹出可点击交互选择器选择 English / 中文。",
|
|
1046
|
+
operationOptions: [...PRISMA_OPERATION_OPTIONS],
|
|
1047
|
+
}),
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* 生成分析图表,并在节点成功后立即获取可展示的产物。
|
|
1052
|
+
*
|
|
1053
|
+
* @param kind 分析图表类型。
|
|
1054
|
+
* @param options 图表生成参数。
|
|
1055
|
+
* @returns 节点执行结果及对应的图表产物。
|
|
1056
|
+
*/
|
|
1057
|
+
async function createAnalysisWithArtifact(kind, options) {
|
|
1058
|
+
const result = await createAnalysis(kind, options);
|
|
1059
|
+
const artifact = await getAnalysis(kind, {
|
|
1060
|
+
workflowId: options.workflowId,
|
|
1061
|
+
nodeId: result.nodeId,
|
|
1062
|
+
configPath: options.configPath,
|
|
1063
|
+
});
|
|
1064
|
+
return withEditablePlotParams(kind, {
|
|
1065
|
+
...result,
|
|
1066
|
+
artifact: await materializeAnalysisArtifacts(kind, artifact.data),
|
|
1067
|
+
displayInstruction: ANALYSIS_ARTIFACT_DISPLAY_INSTRUCTION,
|
|
1068
|
+
}, {
|
|
1069
|
+
workflowId: options.workflowId,
|
|
1070
|
+
nodeId: result.nodeId,
|
|
1071
|
+
configPath: options.configPath,
|
|
1072
|
+
}, result.input);
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* 重新生成分析图表,并在节点成功后立即获取最新产物。
|
|
1076
|
+
*
|
|
1077
|
+
* @param kind 分析图表类型。
|
|
1078
|
+
* @param options 图表重新生成参数。
|
|
1079
|
+
* @returns 重新生成结果及最新图表产物。
|
|
1080
|
+
*/
|
|
1081
|
+
async function rerunAnalysisWithArtifact(kind, options) {
|
|
1082
|
+
const result = await rerunAnalysis(kind, options);
|
|
1083
|
+
const artifact = await getAnalysis(kind, {
|
|
1084
|
+
workflowId: options.workflowId,
|
|
1085
|
+
nodeId: result.nodeId,
|
|
1086
|
+
configPath: options.configPath,
|
|
1087
|
+
});
|
|
1088
|
+
const withParams = await withEditablePlotParams(kind, {
|
|
1089
|
+
...result,
|
|
1090
|
+
artifact: await materializeAnalysisArtifacts(kind, artifact.data),
|
|
1091
|
+
displayInstruction: ANALYSIS_ARTIFACT_DISPLAY_INSTRUCTION,
|
|
1092
|
+
}, {
|
|
1093
|
+
workflowId: options.workflowId,
|
|
1094
|
+
nodeId: result.nodeId,
|
|
1095
|
+
configPath: options.configPath,
|
|
1096
|
+
}, result.input);
|
|
1097
|
+
// 质量评估图:强调只改了选定组
|
|
1098
|
+
if (kind === "assessment" && result.editedPlotType) {
|
|
1099
|
+
const plotLabel = result.editedPlotType === "traffic_light" ? "红绿灯图" : "总结图";
|
|
1100
|
+
const otherLabel = result.editedPlotType === "traffic_light" ? "总结图" : "红绿灯图";
|
|
1101
|
+
return {
|
|
1102
|
+
...withParams,
|
|
1103
|
+
editedPlotType: result.editedPlotType,
|
|
1104
|
+
displayInstruction: ANALYSIS_ARTIFACT_DISPLAY_INSTRUCTION +
|
|
1105
|
+
`本次仅修改了${plotLabel}(editedPlotType=${result.editedPlotType});` +
|
|
1106
|
+
`${otherLabel}参数未改。两份 PDF 可能同节点刷新,禁止声称${otherLabel}也被修改。`,
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
return withParams;
|
|
1110
|
+
}
|
|
1111
|
+
async function getAnalysisWithArtifact(kind, options) {
|
|
1112
|
+
const result = await getAnalysis(kind, options);
|
|
1113
|
+
return withEditablePlotParams(kind, {
|
|
1114
|
+
...result,
|
|
1115
|
+
artifact: await materializeAnalysisArtifacts(kind, result.data),
|
|
1116
|
+
displayInstruction: ANALYSIS_ARTIFACT_DISPLAY_INSTRUCTION,
|
|
1117
|
+
}, options);
|
|
1118
|
+
}
|
|
1119
|
+
export async function runCreateEffectPlot(options) {
|
|
1120
|
+
printOutput(await createAnalysisWithArtifact("effect", options), options.format ?? "markdown");
|
|
1121
|
+
}
|
|
1122
|
+
export async function runGetEffectPlot(options) {
|
|
1123
|
+
printOutput(await getAnalysisWithArtifact("effect", options), options.format ?? "markdown");
|
|
1124
|
+
}
|
|
1125
|
+
export async function runRerunEffectPlot(options) {
|
|
1126
|
+
printOutput(await rerunAnalysisWithArtifact("effect", options), options.format ?? "markdown");
|
|
1127
|
+
}
|
|
1128
|
+
export async function runCreateBiasPlot(options) {
|
|
1129
|
+
printOutput(await createAnalysisWithArtifact("bias", options), options.format ?? "markdown");
|
|
1130
|
+
}
|
|
1131
|
+
export async function runGetBiasPlot(options) {
|
|
1132
|
+
printOutput(await getAnalysisWithArtifact("bias", options), options.format ?? "markdown");
|
|
1133
|
+
}
|
|
1134
|
+
export async function runRerunBiasPlot(options) {
|
|
1135
|
+
printOutput(await rerunAnalysisWithArtifact("bias", options), options.format ?? "markdown");
|
|
1136
|
+
}
|
|
1137
|
+
export async function runCreateAssessmentPlot(options) {
|
|
1138
|
+
printOutput(await createAnalysisWithArtifact("assessment", options), options.format ?? "markdown");
|
|
1139
|
+
}
|
|
1140
|
+
export async function runGetAssessmentPlot(options) {
|
|
1141
|
+
printOutput(await getAnalysisWithArtifact("assessment", options), options.format ?? "markdown");
|
|
1142
|
+
}
|
|
1143
|
+
export async function runRerunAssessmentPlot(options) {
|
|
1144
|
+
printOutput(await rerunAnalysisWithArtifact("assessment", options), options.format ?? "markdown");
|
|
1145
|
+
}
|
|
1146
|
+
export async function runCreatePrismaFlow(options) {
|
|
1147
|
+
printOutput(await exportPrismaFlow(options), options.format ?? "markdown");
|
|
1148
|
+
}
|
|
1149
|
+
/** Exports the editable PRISMA Word artifact as part of the final draft bundle. */
|
|
1150
|
+
export async function exportFinalPrismaWordArtifact(options) {
|
|
1151
|
+
return exportPrismaFlow({ ...options, exportFormat: "docx" });
|
|
1152
|
+
}
|
|
1153
|
+
export async function runUpdatePrismaFlowData(options) {
|
|
1154
|
+
printOutput(await updatePrismaFlowData(options), options.format ?? "markdown");
|
|
1155
|
+
}
|
|
1156
|
+
export async function runGetPrismaImage(options) {
|
|
1157
|
+
printOutput(await getPrismaImage(options), options.format ?? "markdown");
|
|
1158
|
+
}
|
|
1159
|
+
export async function runUploadPrismaImage(options) {
|
|
1160
|
+
printOutput(await uploadPrismaImage(options), options.format ?? "markdown");
|
|
1161
|
+
}
|
|
1162
|
+
export async function runExportPrismaFlow(options) {
|
|
1163
|
+
printOutput(await exportPrismaFlow(options), options.format ?? "markdown");
|
|
1164
|
+
}
|
|
1165
|
+
//# sourceMappingURL=analysis.js.map
|