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,1255 @@
|
|
|
1
|
+
import { basename, dirname, extname, resolve } from "node:path";
|
|
2
|
+
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { ApiClient, createStderrProgress, helixlifeError, helixlifeErrorCode, printOutput, renderListAsMarkdown, } from "../../core/index.js";
|
|
4
|
+
import { META_API_PREFIX, META_ABSTRACT_SCREENING_WAIT_HINT, META_FULLTEXT_SCREENING_WAIT_HINT } from "./constants.js";
|
|
5
|
+
import { requestMeta } from "./api.js";
|
|
6
|
+
import { getMetaNodeOutput, pollMetaNodeStatus } from "./node.js";
|
|
7
|
+
const META_SCREENING_NODE_TYPES = {
|
|
8
|
+
LITERATURE: "literature_retrieve",
|
|
9
|
+
CRITERIA: "generate_sorting_criteria",
|
|
10
|
+
ABSTRACT: "abstract_screening",
|
|
11
|
+
FULLTEXT: "fulltext_screening",
|
|
12
|
+
};
|
|
13
|
+
const MAX_PDF_UPLOAD_SIZE = 15 * 1024 * 1024;
|
|
14
|
+
const ABSTRACT_SCREENING_COLUMNS = [
|
|
15
|
+
{ key: "study", label: "Study" },
|
|
16
|
+
{ key: "author", label: "Author" },
|
|
17
|
+
{ key: "title", label: "Title" },
|
|
18
|
+
{ key: "doi", label: "DOI" },
|
|
19
|
+
{ key: "abstract", label: "Abstract" },
|
|
20
|
+
{ key: "publication_type", label: "Publication Type" },
|
|
21
|
+
{ key: "publication_date", label: "Publication Date" },
|
|
22
|
+
{ key: "pmid", label: "PMID" },
|
|
23
|
+
{ key: "pmc", label: "PMC" },
|
|
24
|
+
{ key: "result", label: "AB & TI Screening" },
|
|
25
|
+
{ key: "reason", label: "Reason" },
|
|
26
|
+
];
|
|
27
|
+
const FULLTEXT_SCREENING_COLUMNS = [
|
|
28
|
+
{ key: "index", label: "序号" },
|
|
29
|
+
{ key: "study", label: "Study" },
|
|
30
|
+
{ key: "author", label: "Author" },
|
|
31
|
+
{ key: "title", label: "Title" },
|
|
32
|
+
{ key: "doi", label: "DOI" },
|
|
33
|
+
{ key: "abstract", label: "Abstract" },
|
|
34
|
+
{ key: "publication_type", label: "Publication Type" },
|
|
35
|
+
{ key: "publication_date", label: "Publication Date" },
|
|
36
|
+
{ key: "pmid", label: "PMID" },
|
|
37
|
+
{ key: "pmc", label: "PMC" },
|
|
38
|
+
{ key: "result", label: "Full Text Screening" },
|
|
39
|
+
{ key: "reason", label: "Reason (Exclude)" },
|
|
40
|
+
];
|
|
41
|
+
const ABSTRACT_SCREENING_DISPLAY_INSTRUCTION = "必须将 tableMarkdown 作为摘要初筛结果的唯一展示源并逐字完整输出全部 11 列和全部返回记录:" +
|
|
42
|
+
"Study / Author / Title / DOI / Abstract / Publication Type / Publication Date / PMID / PMC / AB & TI Screening / Reason。" +
|
|
43
|
+
"禁止从其他字段重建表格,禁止删除、合并或改写任何列和值,禁止摘要、缩写、翻译、截断 Abstract 或使用占位符;" +
|
|
44
|
+
"尤其禁止简化为 Study、PMID、筛选结果、Reason 等子集列。";
|
|
45
|
+
const FULLTEXT_SCREENING_DISPLAY_INSTRUCTION = "必须将 tableMarkdown 作为全文复筛结果的唯一展示源并逐字完整输出全部 12 列和全部返回记录:" +
|
|
46
|
+
"序号 / Study / Author / Title / DOI / Abstract / Publication Type / Publication Date / PMID / PMC / Full Text Screening / Reason (Exclude)。" +
|
|
47
|
+
"禁止从其他字段重建表格,禁止删除、合并或改写任何列和值,禁止摘要、缩写、翻译、截断 Abstract。" +
|
|
48
|
+
"尤其禁止简化为 Study、PMID、Full Text Screening、Reason (Exclude) 四列,或 Study、Full Text Screening、Reason (Exclude) 三列;" +
|
|
49
|
+
"禁止用「全文复筛完成 / 共筛选 N 篇…」等摘要代替完整 tableMarkdown。";
|
|
50
|
+
/** 重新筛选对比表列(与网站「重新筛选完成」弹层一致) */
|
|
51
|
+
const ABSTRACT_RESCREEN_COMPARISON_COLUMNS = [
|
|
52
|
+
{ key: "pmid", label: "PMID" },
|
|
53
|
+
{ key: "study", label: "Study" },
|
|
54
|
+
{ key: "oldResult", label: "原结果" },
|
|
55
|
+
{ key: "newResult", label: "新结果" },
|
|
56
|
+
{ key: "reason", label: "理由" },
|
|
57
|
+
];
|
|
58
|
+
const ABSTRACT_RESCREEN_DISPLAY_INSTRUCTION = "重新筛选完成后,必须将 tableMarkdown 作为对比结果的唯一展示源并逐字完整输出" +
|
|
59
|
+
"(含「重新筛选完成!」标题、汇总句、PMID/Study/原结果/新结果/理由对比表及脚注)。" +
|
|
60
|
+
"禁止用「重新筛选成功」等摘要代替表格,禁止删改任何列或记录。";
|
|
61
|
+
/** 网站脚注中常见英文理由 → 中文 */
|
|
62
|
+
const RESCREEN_REASON_ZH = {
|
|
63
|
+
irrelevant: "不相关",
|
|
64
|
+
"not relevant": "不相关",
|
|
65
|
+
"not-relevant": "不相关",
|
|
66
|
+
};
|
|
67
|
+
function parseJsonObject(raw, optionName) {
|
|
68
|
+
const jsonText = raw?.replace(/^\uFEFF/, "").trim();
|
|
69
|
+
if (!jsonText) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const parsed = JSON.parse(jsonText);
|
|
74
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
75
|
+
throw new Error("not an object");
|
|
76
|
+
}
|
|
77
|
+
return parsed;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `${optionName} 须为合法 JSON 对象`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function parseJsonStringArray(raw, optionName) {
|
|
84
|
+
const jsonText = raw?.replace(/^\uFEFF/, "").trim();
|
|
85
|
+
if (!jsonText) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(jsonText);
|
|
90
|
+
if (!Array.isArray(parsed) ||
|
|
91
|
+
parsed.some((item) => typeof item !== "string")) {
|
|
92
|
+
throw new Error("not a string array");
|
|
93
|
+
}
|
|
94
|
+
return parsed;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `${optionName} 须为合法 JSON 字符串数组`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function parseJsonObjectInput(raw, filePath, optionName, fileOptionName) {
|
|
101
|
+
if (filePath?.trim()) {
|
|
102
|
+
const content = await readFile(filePath.trim(), "utf8");
|
|
103
|
+
return parseJsonObject(content, fileOptionName);
|
|
104
|
+
}
|
|
105
|
+
if (!raw?.trim()) {
|
|
106
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `缺少 ${optionName} 或 ${fileOptionName}`);
|
|
107
|
+
}
|
|
108
|
+
return parseJsonObject(raw, optionName);
|
|
109
|
+
}
|
|
110
|
+
async function parseJsonStringArrayInput(raw, filePath, optionName, fileOptionName) {
|
|
111
|
+
if (filePath?.trim()) {
|
|
112
|
+
const content = await readFile(filePath.trim(), "utf8");
|
|
113
|
+
return parseJsonStringArray(content, fileOptionName);
|
|
114
|
+
}
|
|
115
|
+
if (!raw?.trim()) {
|
|
116
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `缺少 ${optionName} 或 ${fileOptionName}`);
|
|
117
|
+
}
|
|
118
|
+
return parseJsonStringArray(raw, optionName);
|
|
119
|
+
}
|
|
120
|
+
function extractNodeOutput(node) {
|
|
121
|
+
if (node.output &&
|
|
122
|
+
typeof node.output === "object" &&
|
|
123
|
+
"output" in node.output) {
|
|
124
|
+
return node.output.output;
|
|
125
|
+
}
|
|
126
|
+
return node.output ?? {};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Safely narrows an unknown value to a plain record for API response traversal.
|
|
130
|
+
*
|
|
131
|
+
* @param value The unknown value returned by the API or nested response object.
|
|
132
|
+
* @returns The value as a record, or an empty record when it is not object-like.
|
|
133
|
+
*/
|
|
134
|
+
function unwrapRecord(value) {
|
|
135
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
136
|
+
? value
|
|
137
|
+
: {};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Extracts paginated list items from the common Meta response container shapes.
|
|
141
|
+
*
|
|
142
|
+
* @param data The raw list response, possibly wrapped by `data`, `items`, `records`, or similar keys.
|
|
143
|
+
* @returns The list item array used to build a WorkBuddy-friendly table.
|
|
144
|
+
*/
|
|
145
|
+
function extractListItems(data) {
|
|
146
|
+
const root = unwrapRecord(data);
|
|
147
|
+
const nested = unwrapRecord(root.data);
|
|
148
|
+
const payload = Object.keys(nested).length > 0 ? nested : root;
|
|
149
|
+
return Array.isArray(payload.items)
|
|
150
|
+
? payload.items
|
|
151
|
+
: Array.isArray(payload.list)
|
|
152
|
+
? payload.list
|
|
153
|
+
: Array.isArray(payload.records)
|
|
154
|
+
? payload.records
|
|
155
|
+
: Array.isArray(payload.results)
|
|
156
|
+
? payload.results
|
|
157
|
+
: Array.isArray(payload.data)
|
|
158
|
+
? payload.data
|
|
159
|
+
: Array.isArray(data)
|
|
160
|
+
? data
|
|
161
|
+
: [];
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Formats scalar and array fields for markdown table display without dropping values.
|
|
165
|
+
*
|
|
166
|
+
* @param value The raw field value from a screening record.
|
|
167
|
+
* @returns A readable string, joining array values with commas.
|
|
168
|
+
*/
|
|
169
|
+
function formatListField(value) {
|
|
170
|
+
if (Array.isArray(value)) {
|
|
171
|
+
return value
|
|
172
|
+
.map((item) => String(item ?? ""))
|
|
173
|
+
.filter(Boolean)
|
|
174
|
+
.join(", ");
|
|
175
|
+
}
|
|
176
|
+
return value === undefined || value === null ? "" : String(value);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Normalizes publication dates to the website-style `YYYY-MM-DD` display when possible.
|
|
180
|
+
*
|
|
181
|
+
* @param value The raw publication date value, either a timestamp, date string, or empty value.
|
|
182
|
+
* @returns A readable date string, or the original trimmed string when parsing is not possible.
|
|
183
|
+
*/
|
|
184
|
+
function formatPublicationDateForDisplay(value) {
|
|
185
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
186
|
+
return new Date(value * 1000).toISOString().slice(0, 10);
|
|
187
|
+
}
|
|
188
|
+
if (typeof value === "string") {
|
|
189
|
+
const trimmed = value.trim();
|
|
190
|
+
if (/^\d{4}-\d{2}-\d{2}/.test(trimmed)) {
|
|
191
|
+
return trimmed.slice(0, 10);
|
|
192
|
+
}
|
|
193
|
+
const timestamp = Date.parse(trimmed);
|
|
194
|
+
return Number.isNaN(timestamp)
|
|
195
|
+
? trimmed
|
|
196
|
+
: new Date(timestamp).toISOString().slice(0, 10);
|
|
197
|
+
}
|
|
198
|
+
return "";
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Builds the abstract-screening table using the same columns and order as the website.
|
|
202
|
+
*
|
|
203
|
+
* @param data The raw abstract-screening list response.
|
|
204
|
+
* @returns A table descriptor with website column labels and normalized rows.
|
|
205
|
+
*/
|
|
206
|
+
function buildAbstractScreeningTable(data) {
|
|
207
|
+
const rows = extractListItems(data).map((item) => {
|
|
208
|
+
const record = unwrapRecord(item);
|
|
209
|
+
return {
|
|
210
|
+
study: formatListField(record.study),
|
|
211
|
+
author: formatListField(record.author),
|
|
212
|
+
title: formatListField(record.title),
|
|
213
|
+
doi: formatListField(record.doi),
|
|
214
|
+
abstract: formatListField(record.abstract),
|
|
215
|
+
publication_type: formatListField(record.publication_type),
|
|
216
|
+
publication_date: formatPublicationDateForDisplay(record.publication_date),
|
|
217
|
+
pmid: formatListField(record.pmid),
|
|
218
|
+
pmc: formatListField(record.pmc),
|
|
219
|
+
result: formatListField(record.result),
|
|
220
|
+
reason: formatListField(record.reason),
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
return { columns: [...ABSTRACT_SCREENING_COLUMNS], rows };
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* 构建全文复筛展示表;首列序号与 selectionOptions 对齐:`(page-1)*limit+行号`。
|
|
227
|
+
*
|
|
228
|
+
* @param data 全文复筛列表原始响应。
|
|
229
|
+
* @param page 当前页码(1-based)。
|
|
230
|
+
* @param limit 每页条数。
|
|
231
|
+
* @returns 含「序号」及网站列标签的表格描述。
|
|
232
|
+
*/
|
|
233
|
+
function buildFulltextScreeningTable(data, page, limit) {
|
|
234
|
+
const safePage = Math.max(1, page);
|
|
235
|
+
const safeLimit = Math.max(1, limit);
|
|
236
|
+
const rows = extractListItems(data).map((item, i) => {
|
|
237
|
+
const record = unwrapRecord(item);
|
|
238
|
+
return {
|
|
239
|
+
index: (safePage - 1) * safeLimit + i + 1,
|
|
240
|
+
study: formatListField(record.study),
|
|
241
|
+
author: formatListField(record.author),
|
|
242
|
+
title: formatListField(record.title),
|
|
243
|
+
doi: formatListField(record.doi),
|
|
244
|
+
abstract: formatListField(record.abstract),
|
|
245
|
+
publication_type: formatListField(record.publication_type),
|
|
246
|
+
publication_date: formatPublicationDateForDisplay(record.publication_date),
|
|
247
|
+
pmid: formatListField(record.pmid),
|
|
248
|
+
pmc: formatListField(record.pmc),
|
|
249
|
+
result: formatListField(record.result),
|
|
250
|
+
reason: formatListField(record.reason),
|
|
251
|
+
};
|
|
252
|
+
});
|
|
253
|
+
return { columns: [...FULLTEXT_SCREENING_COLUMNS], rows };
|
|
254
|
+
}
|
|
255
|
+
/** Keeps complete internal IDs available for subsequent record actions. */
|
|
256
|
+
function buildScreeningRecordRefs(data) {
|
|
257
|
+
return extractListItems(data).map((item) => {
|
|
258
|
+
const record = unwrapRecord(item);
|
|
259
|
+
return {
|
|
260
|
+
id: formatListField(record.id),
|
|
261
|
+
study: formatListField(record.study),
|
|
262
|
+
title: formatListField(record.title),
|
|
263
|
+
doi: formatListField(record.doi),
|
|
264
|
+
pmid: formatListField(record.pmid),
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* 构建初筛文献选择器选项;序号按分页连续编号:(page-1)*limit+行号。
|
|
270
|
+
*
|
|
271
|
+
* @param recordRefs 当前页 recordRefs。
|
|
272
|
+
* @param page 当前页码(1-based)。
|
|
273
|
+
* @param limit 每页条数。
|
|
274
|
+
* @returns 供 AskQuestion 使用的 selectionOptions。
|
|
275
|
+
*/
|
|
276
|
+
function buildScreeningSelectionOptions(recordRefs, page, limit) {
|
|
277
|
+
const safePage = Math.max(1, page);
|
|
278
|
+
const safeLimit = Math.max(1, limit);
|
|
279
|
+
return recordRefs
|
|
280
|
+
.filter((ref) => ref.id.trim())
|
|
281
|
+
.map((ref, i) => {
|
|
282
|
+
const index = (safePage - 1) * safeLimit + i + 1;
|
|
283
|
+
const studyText = ref.study.trim() || "无 Study";
|
|
284
|
+
const pmidText = ref.pmid.trim() ? `PMID ${ref.pmid.trim()}` : "无 PMID";
|
|
285
|
+
return {
|
|
286
|
+
index,
|
|
287
|
+
// 半角括号/逗号:全角「,」在 AskQuestion 选择器中可能显示为 U+FFFD 乱码
|
|
288
|
+
label: `序号 ${index} (${studyText}, ${pmidText})`,
|
|
289
|
+
value: ref.id,
|
|
290
|
+
pmid: ref.pmid,
|
|
291
|
+
study: ref.study,
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function withScreeningTable(result, table, instruction, recordRefs = [], tableMarkdownOverride, extras) {
|
|
296
|
+
const tableMarkdown = tableMarkdownOverride ?? renderListAsMarkdown(table.rows, table.columns);
|
|
297
|
+
return {
|
|
298
|
+
...result,
|
|
299
|
+
displayInstruction: instruction,
|
|
300
|
+
columns: table.columns,
|
|
301
|
+
rows: table.rows,
|
|
302
|
+
displayColumns: table.columns.map((column) => column.label),
|
|
303
|
+
displayRows: table.rows,
|
|
304
|
+
tableMarkdown,
|
|
305
|
+
recordRefs,
|
|
306
|
+
table,
|
|
307
|
+
...(extras?.selectionOptions ? { selectionOptions: extras.selectionOptions } : {}),
|
|
308
|
+
...(extras?.page != null ? { page: extras.page } : {}),
|
|
309
|
+
...(extras?.limit != null ? { limit: extras.limit } : {}),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* 按 ID 批量读取筛选记录(分页扫描直至全部命中或列表结束)。
|
|
314
|
+
*
|
|
315
|
+
* @param options 工作流、节点、端点与目标 ID 列表。
|
|
316
|
+
* @returns id → 记录映射;未找到的 ID 不会出现在 Map 中。
|
|
317
|
+
*/
|
|
318
|
+
async function loadScreeningRecordsByIds(options) {
|
|
319
|
+
const wanted = new Set(options.ids.filter(Boolean));
|
|
320
|
+
const found = new Map();
|
|
321
|
+
if (wanted.size === 0) {
|
|
322
|
+
return found;
|
|
323
|
+
}
|
|
324
|
+
const limit = 50;
|
|
325
|
+
for (let page = 1;; page += 1) {
|
|
326
|
+
const data = await requestMeta(`${META_API_PREFIX}/${options.endpoint}`, {
|
|
327
|
+
params: {
|
|
328
|
+
run_id: options.workflowId,
|
|
329
|
+
node_id: options.nodeId,
|
|
330
|
+
page,
|
|
331
|
+
limit,
|
|
332
|
+
},
|
|
333
|
+
configPath: options.configPath,
|
|
334
|
+
action: options.action,
|
|
335
|
+
});
|
|
336
|
+
const items = extractListItems(data);
|
|
337
|
+
for (const item of items) {
|
|
338
|
+
const record = unwrapRecord(item);
|
|
339
|
+
const id = String(record.id ?? "");
|
|
340
|
+
if (wanted.has(id)) {
|
|
341
|
+
found.set(id, record);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (found.size >= wanted.size || items.length < limit) {
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return found;
|
|
349
|
+
}
|
|
350
|
+
/** 脚注理由中文化;未知文案原样返回。 */
|
|
351
|
+
function formatRescreenReasonZh(reason) {
|
|
352
|
+
const trimmed = reason.trim();
|
|
353
|
+
if (!trimmed || trimmed === "—") {
|
|
354
|
+
return trimmed;
|
|
355
|
+
}
|
|
356
|
+
return RESCREEN_REASON_ZH[trimmed.toLowerCase()] ?? trimmed;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* 构建与网站一致的重新筛选对比展示(标题 + 汇总 + 表 + 脚注)。
|
|
360
|
+
*
|
|
361
|
+
* @param ids 本次重筛的记录 ID(保持选择顺序)。
|
|
362
|
+
* @param beforeMap 重筛前快照。
|
|
363
|
+
* @param afterMap 重筛后记录。
|
|
364
|
+
* @returns 对比表行、完整 Markdown 与 recordRefs。
|
|
365
|
+
*/
|
|
366
|
+
function buildAbstractRescreenComparison(options) {
|
|
367
|
+
const columns = [...ABSTRACT_RESCREEN_COMPARISON_COLUMNS];
|
|
368
|
+
const rows = [];
|
|
369
|
+
const recordRefs = [];
|
|
370
|
+
const changedSummaries = [];
|
|
371
|
+
for (const id of options.ids) {
|
|
372
|
+
const before = options.beforeMap.get(id);
|
|
373
|
+
const after = options.afterMap.get(id) ?? before;
|
|
374
|
+
if (!before && !after) {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const source = after ?? before ?? {};
|
|
378
|
+
const pmid = formatListField(source.pmid);
|
|
379
|
+
const study = formatListField(source.study);
|
|
380
|
+
const title = formatListField(source.title);
|
|
381
|
+
const oldResult = formatListField(before?.result) || "—";
|
|
382
|
+
const newResult = formatListField(after?.result) || "—";
|
|
383
|
+
const changed = oldResult !== newResult;
|
|
384
|
+
const reasonRaw = changed
|
|
385
|
+
? formatListField(after?.reason) || "—"
|
|
386
|
+
: "—";
|
|
387
|
+
const cells = {
|
|
388
|
+
pmid,
|
|
389
|
+
study,
|
|
390
|
+
oldResult,
|
|
391
|
+
newResult,
|
|
392
|
+
reason: reasonRaw,
|
|
393
|
+
};
|
|
394
|
+
// 变更行加粗,对齐网站高亮
|
|
395
|
+
rows.push(changed
|
|
396
|
+
? Object.fromEntries(Object.entries(cells).map(([key, value]) => [
|
|
397
|
+
key,
|
|
398
|
+
`**${value}**`,
|
|
399
|
+
]))
|
|
400
|
+
: cells);
|
|
401
|
+
recordRefs.push({
|
|
402
|
+
id: formatListField(source.id) || id,
|
|
403
|
+
study,
|
|
404
|
+
title,
|
|
405
|
+
doi: formatListField(source.doi),
|
|
406
|
+
pmid,
|
|
407
|
+
});
|
|
408
|
+
if (changed) {
|
|
409
|
+
const studyLabel = study || "未知文献";
|
|
410
|
+
const titlePart = title ? ` - ${title}` : "";
|
|
411
|
+
const reasonZh = formatRescreenReasonZh(reasonRaw);
|
|
412
|
+
changedSummaries.push(`${studyLabel}${titlePart} 由 ${oldResult} 改为 ${newResult}(${reasonZh})。`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const total = rows.length;
|
|
416
|
+
const changedCount = changedSummaries.length;
|
|
417
|
+
const unchangedCount = total - changedCount;
|
|
418
|
+
const footer = changedCount === 0
|
|
419
|
+
? `全部 ${total} 篇结果保持不变。`
|
|
420
|
+
: `${changedSummaries.join("")}${unchangedCount > 0 ? `其余 ${unchangedCount} 篇保持不变。` : ""}`;
|
|
421
|
+
const tableMarkdown = [
|
|
422
|
+
"重新筛选完成!",
|
|
423
|
+
"",
|
|
424
|
+
`${total} 篇文献中,${changedCount} 篇结果已更新:`,
|
|
425
|
+
"",
|
|
426
|
+
renderListAsMarkdown(rows, columns),
|
|
427
|
+
"",
|
|
428
|
+
footer,
|
|
429
|
+
].join("\n");
|
|
430
|
+
return { columns, rows, tableMarkdown, recordRefs };
|
|
431
|
+
}
|
|
432
|
+
/** 调用筛选结果 `/export` 接口下载产物并落盘。 */
|
|
433
|
+
async function downloadScreeningExportArtifact(options) {
|
|
434
|
+
const client = new ApiClient();
|
|
435
|
+
const response = await client.requestBinary(`${META_API_PREFIX}/${options.endpoint}`, {
|
|
436
|
+
method: "GET",
|
|
437
|
+
params: {
|
|
438
|
+
run_id: options.workflowId,
|
|
439
|
+
node_id: options.nodeId,
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
if (response.buffer.length === 0) {
|
|
443
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, `${options.action}下载内容为空`);
|
|
444
|
+
}
|
|
445
|
+
const output = resolve(options.outputDirectory?.trim() || ".", options.filename);
|
|
446
|
+
await mkdir(dirname(output), { recursive: true });
|
|
447
|
+
await writeFile(output, response.buffer);
|
|
448
|
+
return {
|
|
449
|
+
output,
|
|
450
|
+
filename: options.filename,
|
|
451
|
+
contentType: response.contentType ?? "application/octet-stream",
|
|
452
|
+
size: response.buffer.length,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Converts numeric-like values from SSE payloads into finite numbers.
|
|
457
|
+
*
|
|
458
|
+
* @param value The raw value from the SSE payload.
|
|
459
|
+
* @returns A finite number, or undefined when conversion fails.
|
|
460
|
+
*/
|
|
461
|
+
function toNumber(value) {
|
|
462
|
+
const number = typeof value === "number" ? value : Number(value);
|
|
463
|
+
return Number.isFinite(number) ? number : undefined;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Normalizes screening SSE progress payloads into a consistent progress shape.
|
|
467
|
+
*
|
|
468
|
+
* @param payload The raw SSE payload emitted by the Meta screening endpoint.
|
|
469
|
+
* @returns A normalized progress payload, or undefined when required counters are missing.
|
|
470
|
+
*/
|
|
471
|
+
function normalizeScreeningProgress(payload) {
|
|
472
|
+
const current = toNumber(payload.current);
|
|
473
|
+
const total = toNumber(payload.total);
|
|
474
|
+
if (current === undefined || total === undefined || total <= 0) {
|
|
475
|
+
return undefined;
|
|
476
|
+
}
|
|
477
|
+
return {
|
|
478
|
+
current,
|
|
479
|
+
total,
|
|
480
|
+
percent: Math.ceil((current / total) * 100) || 0,
|
|
481
|
+
items: Array.isArray(payload.items) ? payload.items : undefined,
|
|
482
|
+
isDone: payload.is_done === true,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* 订阅筛选节点 SSE 进度,并把百分比写入 stderr。
|
|
487
|
+
*
|
|
488
|
+
* JSON 模式下 stdout 必须只输出最终结果,因此进度只能写 stderr。
|
|
489
|
+
*/
|
|
490
|
+
function startScreeningProgressSse(options) {
|
|
491
|
+
const client = new ApiClient();
|
|
492
|
+
let lastPercent = -1;
|
|
493
|
+
return client
|
|
494
|
+
.requestSse(`${META_API_PREFIX}/sse/${options.nodeId}/${options.workflowId}`, {
|
|
495
|
+
method: "GET",
|
|
496
|
+
signal: options.signal,
|
|
497
|
+
}, (payload) => {
|
|
498
|
+
const progress = normalizeScreeningProgress(payload);
|
|
499
|
+
if (!progress) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const shouldPrint = progress.percent !== lastPercent &&
|
|
503
|
+
(progress.percent === 100 ||
|
|
504
|
+
progress.percent - lastPercent >= 5 ||
|
|
505
|
+
lastPercent < 0);
|
|
506
|
+
if (!shouldPrint) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
lastPercent = progress.percent;
|
|
510
|
+
process.stderr.write(`${options.label}进度:${progress.current}/${progress.total}(${progress.percent}%)\n`);
|
|
511
|
+
})
|
|
512
|
+
.catch((error) => {
|
|
513
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
process.stderr.write(`${options.label}进度订阅中断,继续等待节点结果...\n`);
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
function splitListField(value) {
|
|
520
|
+
if (typeof value === "string") {
|
|
521
|
+
return value
|
|
522
|
+
.split(/[,,;]/)
|
|
523
|
+
.map((item) => item.trim())
|
|
524
|
+
.filter(Boolean);
|
|
525
|
+
}
|
|
526
|
+
return value;
|
|
527
|
+
}
|
|
528
|
+
function normalizePublicationDate(value) {
|
|
529
|
+
if (typeof value !== "string") {
|
|
530
|
+
return value;
|
|
531
|
+
}
|
|
532
|
+
const trimmed = value.trim();
|
|
533
|
+
if (!trimmed) {
|
|
534
|
+
return undefined;
|
|
535
|
+
}
|
|
536
|
+
const timestamp = /^\d{4}-\d{2}-\d{2}$/.test(trimmed)
|
|
537
|
+
? Date.parse(`${trimmed}T00:00:00Z`)
|
|
538
|
+
: Date.parse(trimmed);
|
|
539
|
+
return Number.isNaN(timestamp) ? value : Math.floor(timestamp / 1000);
|
|
540
|
+
}
|
|
541
|
+
function normalizeScreeningLiteratureInput(input, fallbackId) {
|
|
542
|
+
const allowedKeys = [
|
|
543
|
+
"id",
|
|
544
|
+
"study",
|
|
545
|
+
"author",
|
|
546
|
+
"title",
|
|
547
|
+
"doi",
|
|
548
|
+
"abstract",
|
|
549
|
+
"publication_type",
|
|
550
|
+
"publication_date",
|
|
551
|
+
"pmid",
|
|
552
|
+
"pmc",
|
|
553
|
+
"result",
|
|
554
|
+
"reason",
|
|
555
|
+
];
|
|
556
|
+
const normalized = {};
|
|
557
|
+
for (const key of allowedKeys) {
|
|
558
|
+
if (key in input) {
|
|
559
|
+
normalized[key] = input[key];
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (!("id" in normalized) && fallbackId) {
|
|
563
|
+
normalized.id = fallbackId;
|
|
564
|
+
}
|
|
565
|
+
if ("author" in normalized) {
|
|
566
|
+
normalized.author = splitListField(normalized.author);
|
|
567
|
+
}
|
|
568
|
+
if ("publication_type" in normalized) {
|
|
569
|
+
normalized.publication_type = splitListField(normalized.publication_type);
|
|
570
|
+
}
|
|
571
|
+
if ("publication_date" in normalized) {
|
|
572
|
+
const publicationDate = normalizePublicationDate(normalized.publication_date);
|
|
573
|
+
if (publicationDate === undefined) {
|
|
574
|
+
delete normalized.publication_date;
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
normalized.publication_date = publicationDate;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return normalized;
|
|
581
|
+
}
|
|
582
|
+
async function loadScreeningLiteratureForUpdate(options) {
|
|
583
|
+
const limit = 50;
|
|
584
|
+
for (let page = 1;; page += 1) {
|
|
585
|
+
const data = await requestMeta(`${META_API_PREFIX}/${options.endpoint}`, {
|
|
586
|
+
params: {
|
|
587
|
+
run_id: options.workflowId,
|
|
588
|
+
node_id: options.nodeId,
|
|
589
|
+
page,
|
|
590
|
+
limit,
|
|
591
|
+
},
|
|
592
|
+
configPath: options.configPath,
|
|
593
|
+
action: "读取待编辑筛选文献完整数据",
|
|
594
|
+
});
|
|
595
|
+
const items = extractListItems(data);
|
|
596
|
+
const record = items
|
|
597
|
+
.map((item) => unwrapRecord(item))
|
|
598
|
+
.find((item) => String(item.id ?? "") === options.recordId);
|
|
599
|
+
if (record)
|
|
600
|
+
return record;
|
|
601
|
+
if (items.length < limit)
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "未找到待编辑的筛选文献,已停止更新以避免覆盖丢失原字段");
|
|
605
|
+
}
|
|
606
|
+
async function getWorkflow(workflowId, configPath) {
|
|
607
|
+
return requestMeta(`${META_API_PREFIX}/workflow/runs/${encodeURIComponent(workflowId)}`, {
|
|
608
|
+
configPath,
|
|
609
|
+
action: "查询 Meta 工作流",
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
function findLatestNode(workflow, nodeType) {
|
|
613
|
+
return [...(workflow.nodes ?? [])]
|
|
614
|
+
.reverse()
|
|
615
|
+
.find((node) => node.node_type === nodeType);
|
|
616
|
+
}
|
|
617
|
+
async function resolveNodeId(options) {
|
|
618
|
+
if (options.nodeId?.trim()) {
|
|
619
|
+
return options.nodeId.trim();
|
|
620
|
+
}
|
|
621
|
+
const workflow = await getWorkflow(options.workflowId, options.configPath);
|
|
622
|
+
const node = findLatestNode(workflow, options.nodeType);
|
|
623
|
+
if (!node) {
|
|
624
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `当前工作流中未找到 ${options.label} 节点`);
|
|
625
|
+
}
|
|
626
|
+
return node.id;
|
|
627
|
+
}
|
|
628
|
+
async function getLatestCriteriaInput(options) {
|
|
629
|
+
if (options.criteriaJson?.trim() || options.criteriaFile?.trim()) {
|
|
630
|
+
return parseJsonObjectInput(options.criteriaJson, options.criteriaFile, "--criteria-json", "--criteria-file");
|
|
631
|
+
}
|
|
632
|
+
const workflow = await getWorkflow(options.workflowId, options.configPath);
|
|
633
|
+
const criteriaNode = findLatestNode(workflow, META_SCREENING_NODE_TYPES.CRITERIA);
|
|
634
|
+
const output = criteriaNode?.output;
|
|
635
|
+
if (output && typeof output === "object") {
|
|
636
|
+
return output;
|
|
637
|
+
}
|
|
638
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "当前工作流中未找到可用的纳排标准,请传入 --criteria-json 或 --criteria-file");
|
|
639
|
+
}
|
|
640
|
+
function printScreeningStepResult(result, format) {
|
|
641
|
+
if (format === "json") {
|
|
642
|
+
printOutput({
|
|
643
|
+
runnerId: result.runnerId,
|
|
644
|
+
nodeId: result.nodeId,
|
|
645
|
+
nodeType: result.nodeType,
|
|
646
|
+
status: result.status,
|
|
647
|
+
displayInstruction: result.displayInstruction,
|
|
648
|
+
tableMarkdown: result.tableMarkdown,
|
|
649
|
+
recordRefs: result.recordRefs,
|
|
650
|
+
selectionOptions: result.selectionOptions,
|
|
651
|
+
page: result.page,
|
|
652
|
+
limit: result.limit,
|
|
653
|
+
}, "json");
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (result.tableMarkdown) {
|
|
657
|
+
console.log(result.tableMarkdown);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
console.log(renderListAsMarkdown([
|
|
661
|
+
{
|
|
662
|
+
runnerId: result.runnerId,
|
|
663
|
+
nodeType: result.nodeType,
|
|
664
|
+
status: result.status,
|
|
665
|
+
},
|
|
666
|
+
], [
|
|
667
|
+
{ key: "runnerId", label: "runnerId" },
|
|
668
|
+
{ key: "nodeType", label: "节点类型" },
|
|
669
|
+
{ key: "status", label: "状态" },
|
|
670
|
+
]));
|
|
671
|
+
console.log("\n输出:");
|
|
672
|
+
console.log(typeof result.output === "string"
|
|
673
|
+
? result.output
|
|
674
|
+
: JSON.stringify(result.output, null, 2));
|
|
675
|
+
}
|
|
676
|
+
export async function createAbstractScreening(options) {
|
|
677
|
+
const criteria = await getLatestCriteriaInput(options);
|
|
678
|
+
const literatureNodeId = options.literatureNodeId?.trim() ??
|
|
679
|
+
(await resolveNodeId({
|
|
680
|
+
workflowId: options.workflowId,
|
|
681
|
+
nodeType: META_SCREENING_NODE_TYPES.LITERATURE,
|
|
682
|
+
label: "文献检索",
|
|
683
|
+
configPath: options.configPath,
|
|
684
|
+
}));
|
|
685
|
+
const input = {
|
|
686
|
+
inclusion_criteria: criteria.inclusion_criteria,
|
|
687
|
+
literature_node_id: literatureNodeId,
|
|
688
|
+
};
|
|
689
|
+
// 摘要初筛耗时长,启动前提示用户耐心等待(覆盖通用「接口正在处理中…」)
|
|
690
|
+
process.stderr.write(`${META_ABSTRACT_SCREENING_WAIT_HINT}\n`);
|
|
691
|
+
const progress = createStderrProgress(options.format === "json");
|
|
692
|
+
progress.start("正在进行摘要初筛...");
|
|
693
|
+
try {
|
|
694
|
+
const created = await requestMeta(`${META_API_PREFIX}/abstract-screening-result/execute`, {
|
|
695
|
+
method: "POST",
|
|
696
|
+
body: {
|
|
697
|
+
runner_id: options.workflowId,
|
|
698
|
+
node_type: META_SCREENING_NODE_TYPES.ABSTRACT,
|
|
699
|
+
input,
|
|
700
|
+
},
|
|
701
|
+
configPath: options.configPath,
|
|
702
|
+
action: "摘要初筛",
|
|
703
|
+
});
|
|
704
|
+
if (!created.node_id) {
|
|
705
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, "摘要初筛响应缺少 node_id");
|
|
706
|
+
}
|
|
707
|
+
const sseController = new AbortController();
|
|
708
|
+
const sseProgress = startScreeningProgressSse({
|
|
709
|
+
workflowId: options.workflowId,
|
|
710
|
+
nodeId: created.node_id,
|
|
711
|
+
label: "摘要初筛",
|
|
712
|
+
signal: sseController.signal,
|
|
713
|
+
});
|
|
714
|
+
await pollMetaNodeStatus({
|
|
715
|
+
workflowId: options.workflowId,
|
|
716
|
+
nodeId: created.node_id,
|
|
717
|
+
configPath: options.configPath,
|
|
718
|
+
}).finally(() => {
|
|
719
|
+
sseController.abort();
|
|
720
|
+
});
|
|
721
|
+
await sseProgress;
|
|
722
|
+
const output = await getMetaNodeOutput({
|
|
723
|
+
workflowId: options.workflowId,
|
|
724
|
+
nodeId: created.node_id,
|
|
725
|
+
configPath: options.configPath,
|
|
726
|
+
});
|
|
727
|
+
progress.finish("摘要初筛完成,正在输出结果...");
|
|
728
|
+
const stepResult = {
|
|
729
|
+
runnerId: options.workflowId,
|
|
730
|
+
nodeId: created.node_id,
|
|
731
|
+
nodeType: META_SCREENING_NODE_TYPES.ABSTRACT,
|
|
732
|
+
status: output.data.status,
|
|
733
|
+
input,
|
|
734
|
+
output: extractNodeOutput(output.data),
|
|
735
|
+
node: output.data,
|
|
736
|
+
};
|
|
737
|
+
const listResult = await listAbstractScreening({
|
|
738
|
+
workflowId: options.workflowId,
|
|
739
|
+
nodeId: created.node_id,
|
|
740
|
+
page: 1,
|
|
741
|
+
limit: 10,
|
|
742
|
+
configPath: options.configPath,
|
|
743
|
+
});
|
|
744
|
+
return withScreeningTable(stepResult, listResult.table ?? {
|
|
745
|
+
columns: [...ABSTRACT_SCREENING_COLUMNS],
|
|
746
|
+
rows: [],
|
|
747
|
+
}, listResult.displayInstruction ?? ABSTRACT_SCREENING_DISPLAY_INSTRUCTION, listResult.recordRefs, undefined, {
|
|
748
|
+
selectionOptions: listResult.selectionOptions,
|
|
749
|
+
page: listResult.page,
|
|
750
|
+
limit: listResult.limit,
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
progress.dispose();
|
|
755
|
+
throw error;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
export async function listAbstractScreening(options) {
|
|
759
|
+
const nodeId = await resolveNodeId({
|
|
760
|
+
workflowId: options.workflowId,
|
|
761
|
+
nodeId: options.nodeId,
|
|
762
|
+
nodeType: META_SCREENING_NODE_TYPES.ABSTRACT,
|
|
763
|
+
label: "摘要初筛",
|
|
764
|
+
configPath: options.configPath,
|
|
765
|
+
});
|
|
766
|
+
const page = Math.max(1, Number(options.page ?? 1) || 1);
|
|
767
|
+
const limit = Math.max(1, Number(options.limit ?? 10) || 10);
|
|
768
|
+
const data = await requestMeta(`${META_API_PREFIX}/abstract-screening-result`, {
|
|
769
|
+
params: {
|
|
770
|
+
run_id: options.workflowId,
|
|
771
|
+
node_id: nodeId,
|
|
772
|
+
page,
|
|
773
|
+
limit,
|
|
774
|
+
sort: options.sort,
|
|
775
|
+
order: options.order,
|
|
776
|
+
publication_type: options.publicationType?.join(","),
|
|
777
|
+
},
|
|
778
|
+
configPath: options.configPath,
|
|
779
|
+
action: "查询摘要初筛列表",
|
|
780
|
+
});
|
|
781
|
+
const table = buildAbstractScreeningTable(data);
|
|
782
|
+
const recordRefs = buildScreeningRecordRefs(data);
|
|
783
|
+
const selectionOptions = buildScreeningSelectionOptions(recordRefs, page, limit);
|
|
784
|
+
return withScreeningTable({
|
|
785
|
+
runnerId: options.workflowId,
|
|
786
|
+
nodeId,
|
|
787
|
+
data,
|
|
788
|
+
}, table, `${ABSTRACT_SCREENING_DISPLAY_INSTRUCTION}` +
|
|
789
|
+
"上传/编辑/重筛选记录:原样用本页 selectionOptions(label 展示、value 作 id);禁止自拟或展示 UUID。" +
|
|
790
|
+
"「上传PDF文件」:先弹 title=`请选择要上传 PDF 的文献` 并停轮;选定后仅聊天索要本地路径再停轮(禁止再弹选择器/手动输入);" +
|
|
791
|
+
"收到路径后 upload-pdf;仅上传成功后再 count-pdf 并弹初筛操作选择器。", recordRefs, undefined, { selectionOptions, page, limit });
|
|
792
|
+
}
|
|
793
|
+
export async function updateAbstractScreening(options) {
|
|
794
|
+
const nodeId = await resolveNodeId({
|
|
795
|
+
workflowId: options.workflowId,
|
|
796
|
+
nodeId: options.nodeId,
|
|
797
|
+
nodeType: META_SCREENING_NODE_TYPES.ABSTRACT,
|
|
798
|
+
label: "摘要初筛",
|
|
799
|
+
configPath: options.configPath,
|
|
800
|
+
});
|
|
801
|
+
const patch = await parseJsonObjectInput(options.dataJson, options.dataFile, "--data-json", "--data-file");
|
|
802
|
+
const original = await loadScreeningLiteratureForUpdate({
|
|
803
|
+
workflowId: options.workflowId,
|
|
804
|
+
nodeId,
|
|
805
|
+
endpoint: "abstract-screening-result",
|
|
806
|
+
recordId: options.id,
|
|
807
|
+
configPath: options.configPath,
|
|
808
|
+
});
|
|
809
|
+
const input = normalizeScreeningLiteratureInput({ ...original, ...patch }, options.id);
|
|
810
|
+
const data = await requestMeta(`${META_API_PREFIX}/abstract-screening-result`, {
|
|
811
|
+
method: "PUT",
|
|
812
|
+
params: {
|
|
813
|
+
run_id: options.workflowId,
|
|
814
|
+
node_id: nodeId,
|
|
815
|
+
abstract_screening_id: options.id,
|
|
816
|
+
},
|
|
817
|
+
body: input,
|
|
818
|
+
configPath: options.configPath,
|
|
819
|
+
action: "更新摘要初筛文献",
|
|
820
|
+
});
|
|
821
|
+
return { runnerId: options.workflowId, nodeId, input, data };
|
|
822
|
+
}
|
|
823
|
+
export async function rerunAbstractScreening(options) {
|
|
824
|
+
const nodeId = await resolveNodeId({
|
|
825
|
+
workflowId: options.workflowId,
|
|
826
|
+
nodeId: options.nodeId,
|
|
827
|
+
nodeType: META_SCREENING_NODE_TYPES.ABSTRACT,
|
|
828
|
+
label: "摘要初筛",
|
|
829
|
+
configPath: options.configPath,
|
|
830
|
+
});
|
|
831
|
+
const ids = await parseJsonStringArrayInput(options.idsJson, options.idsFile, "--ids-json", "--ids-file");
|
|
832
|
+
if (ids.length === 0) {
|
|
833
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "重新摘要初筛至少需要一条文献 ID");
|
|
834
|
+
}
|
|
835
|
+
// 重筛前快照,用于构建「原结果 / 新结果」对比表
|
|
836
|
+
const beforeMap = await loadScreeningRecordsByIds({
|
|
837
|
+
workflowId: options.workflowId,
|
|
838
|
+
nodeId,
|
|
839
|
+
endpoint: "abstract-screening-result",
|
|
840
|
+
ids,
|
|
841
|
+
configPath: options.configPath,
|
|
842
|
+
action: "读取重筛前初筛记录",
|
|
843
|
+
});
|
|
844
|
+
const missingBefore = ids.filter((id) => !beforeMap.has(id));
|
|
845
|
+
if (missingBefore.length > 0) {
|
|
846
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `未找到待重新筛选的初筛记录:${missingBefore.join(", ")}`);
|
|
847
|
+
}
|
|
848
|
+
const criteria = options.inclusionCriteria
|
|
849
|
+
? { inclusion_criteria: options.inclusionCriteria }
|
|
850
|
+
: await getLatestCriteriaInput(options);
|
|
851
|
+
const input = {
|
|
852
|
+
inclusion_criteria: criteria.inclusion_criteria,
|
|
853
|
+
abstract_screening_id: ids,
|
|
854
|
+
};
|
|
855
|
+
const data = await requestMeta(`${META_API_PREFIX}/abstract-screening-result/re_screening`, {
|
|
856
|
+
method: "POST",
|
|
857
|
+
params: {
|
|
858
|
+
run_id: options.workflowId,
|
|
859
|
+
node_id: nodeId,
|
|
860
|
+
},
|
|
861
|
+
body: input,
|
|
862
|
+
configPath: options.configPath,
|
|
863
|
+
action: "重新摘要初筛",
|
|
864
|
+
});
|
|
865
|
+
await pollMetaNodeStatus({
|
|
866
|
+
workflowId: options.workflowId,
|
|
867
|
+
nodeId,
|
|
868
|
+
configPath: options.configPath,
|
|
869
|
+
});
|
|
870
|
+
const afterMap = await loadScreeningRecordsByIds({
|
|
871
|
+
workflowId: options.workflowId,
|
|
872
|
+
nodeId,
|
|
873
|
+
endpoint: "abstract-screening-result",
|
|
874
|
+
ids,
|
|
875
|
+
configPath: options.configPath,
|
|
876
|
+
action: "读取重筛后初筛记录",
|
|
877
|
+
});
|
|
878
|
+
const comparison = buildAbstractRescreenComparison({
|
|
879
|
+
ids,
|
|
880
|
+
beforeMap,
|
|
881
|
+
afterMap,
|
|
882
|
+
});
|
|
883
|
+
return withScreeningTable({
|
|
884
|
+
runnerId: options.workflowId,
|
|
885
|
+
nodeId,
|
|
886
|
+
input,
|
|
887
|
+
data,
|
|
888
|
+
}, { columns: comparison.columns, rows: comparison.rows }, ABSTRACT_RESCREEN_DISPLAY_INSTRUCTION, comparison.recordRefs, comparison.tableMarkdown);
|
|
889
|
+
}
|
|
890
|
+
export async function countAbstractPdf(options) {
|
|
891
|
+
const nodeId = await resolveNodeId({
|
|
892
|
+
workflowId: options.workflowId,
|
|
893
|
+
nodeId: options.nodeId,
|
|
894
|
+
nodeType: META_SCREENING_NODE_TYPES.ABSTRACT,
|
|
895
|
+
label: "摘要初筛",
|
|
896
|
+
configPath: options.configPath,
|
|
897
|
+
});
|
|
898
|
+
const data = await requestMeta(`${META_API_PREFIX}/abstract-screening-result/count-pdf`, {
|
|
899
|
+
params: {
|
|
900
|
+
run_id: options.workflowId,
|
|
901
|
+
abstract_node_id: nodeId,
|
|
902
|
+
},
|
|
903
|
+
configPath: options.configPath,
|
|
904
|
+
action: "查询摘要初筛 PDF 数量",
|
|
905
|
+
});
|
|
906
|
+
return { runnerId: options.workflowId, nodeId, data };
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* 为一条摘要初筛记录上传单篇全文 PDF。
|
|
910
|
+
*
|
|
911
|
+
* 请求结构与网站保持一致:工作流、节点和初筛记录 ID 放在 URL query,
|
|
912
|
+
* PDF 放在 multipart/form-data 的 file 字段。多文件串行策略由调用方负责。
|
|
913
|
+
*
|
|
914
|
+
* @param options 工作流、初筛记录 ID、PDF 路径及可选节点配置。
|
|
915
|
+
* @returns 上传接口返回的数据及本次使用的工作流、节点 ID。
|
|
916
|
+
*/
|
|
917
|
+
export async function uploadAbstractPdf(options) {
|
|
918
|
+
const recordId = options.id.trim();
|
|
919
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(recordId)) {
|
|
920
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "摘要初筛结果 ID 必须使用 recordRefs 中的完整 UUID,禁止截断、缩写或重建");
|
|
921
|
+
}
|
|
922
|
+
const filePath = options.file.trim();
|
|
923
|
+
if (extname(filePath).toLowerCase() !== ".pdf") {
|
|
924
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "初筛全文仅支持上传 PDF 文件");
|
|
925
|
+
}
|
|
926
|
+
const fileStats = await stat(filePath);
|
|
927
|
+
if (fileStats.size > MAX_PDF_UPLOAD_SIZE) {
|
|
928
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "PDF 文件不能超过 15MB");
|
|
929
|
+
}
|
|
930
|
+
const nodeId = await resolveNodeId({
|
|
931
|
+
workflowId: options.workflowId,
|
|
932
|
+
nodeId: options.nodeId,
|
|
933
|
+
nodeType: META_SCREENING_NODE_TYPES.ABSTRACT,
|
|
934
|
+
label: "摘要初筛",
|
|
935
|
+
configPath: options.configPath,
|
|
936
|
+
});
|
|
937
|
+
const buffer = await readFile(filePath);
|
|
938
|
+
const form = new FormData();
|
|
939
|
+
form.append("file", new Blob([buffer], { type: "application/pdf" }), basename(filePath));
|
|
940
|
+
const data = await requestMeta(`${META_API_PREFIX}/abstract-screening-result/upload`, {
|
|
941
|
+
method: "POST",
|
|
942
|
+
params: {
|
|
943
|
+
run_id: options.workflowId,
|
|
944
|
+
node_id: nodeId,
|
|
945
|
+
abstract_screening_id: recordId,
|
|
946
|
+
},
|
|
947
|
+
body: form,
|
|
948
|
+
configPath: options.configPath,
|
|
949
|
+
action: "上传全文 PDF",
|
|
950
|
+
});
|
|
951
|
+
return { runnerId: options.workflowId, nodeId, data };
|
|
952
|
+
}
|
|
953
|
+
export async function createFulltextScreening(options) {
|
|
954
|
+
const criteria = await getLatestCriteriaInput(options);
|
|
955
|
+
const abstractNodeId = options.abstractNodeId?.trim() ??
|
|
956
|
+
(await resolveNodeId({
|
|
957
|
+
workflowId: options.workflowId,
|
|
958
|
+
nodeType: META_SCREENING_NODE_TYPES.ABSTRACT,
|
|
959
|
+
label: "摘要初筛",
|
|
960
|
+
configPath: options.configPath,
|
|
961
|
+
}));
|
|
962
|
+
const pdfCountResult = await countAbstractPdf({
|
|
963
|
+
workflowId: options.workflowId,
|
|
964
|
+
nodeId: abstractNodeId,
|
|
965
|
+
configPath: options.configPath,
|
|
966
|
+
});
|
|
967
|
+
const pdfCount = pdfCountResult.data &&
|
|
968
|
+
typeof pdfCountResult.data === "object" &&
|
|
969
|
+
"pdf-count" in pdfCountResult.data
|
|
970
|
+
? Number(pdfCountResult.data["pdf-count"])
|
|
971
|
+
: 0;
|
|
972
|
+
// 初筛进入全文复筛硬门禁:至少 3 篇已上传 PDF
|
|
973
|
+
if (pdfCount < 3) {
|
|
974
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "全文复筛前至少需要三篇已上传 PDF 的文献");
|
|
975
|
+
}
|
|
976
|
+
// 全文复筛耗时长,启动前提示用户耐心等待(覆盖通用「接口正在处理中…」)
|
|
977
|
+
process.stderr.write(`${META_FULLTEXT_SCREENING_WAIT_HINT}\n`);
|
|
978
|
+
const progress = createStderrProgress(options.format === "json");
|
|
979
|
+
progress.start("正在进行全文复筛...");
|
|
980
|
+
try {
|
|
981
|
+
const created = await requestMeta(`${META_API_PREFIX}/fulltext-screening/execute`, {
|
|
982
|
+
method: "POST",
|
|
983
|
+
params: {
|
|
984
|
+
run_id: options.workflowId,
|
|
985
|
+
abstract_node_id: abstractNodeId,
|
|
986
|
+
},
|
|
987
|
+
body: criteria,
|
|
988
|
+
configPath: options.configPath,
|
|
989
|
+
action: "全文复筛",
|
|
990
|
+
});
|
|
991
|
+
if (!created.node_id) {
|
|
992
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, "全文复筛响应缺少 node_id");
|
|
993
|
+
}
|
|
994
|
+
const sseController = new AbortController();
|
|
995
|
+
const sseProgress = startScreeningProgressSse({
|
|
996
|
+
workflowId: options.workflowId,
|
|
997
|
+
nodeId: created.node_id,
|
|
998
|
+
label: "全文复筛",
|
|
999
|
+
signal: sseController.signal,
|
|
1000
|
+
});
|
|
1001
|
+
await pollMetaNodeStatus({
|
|
1002
|
+
workflowId: options.workflowId,
|
|
1003
|
+
nodeId: created.node_id,
|
|
1004
|
+
configPath: options.configPath,
|
|
1005
|
+
}).finally(() => {
|
|
1006
|
+
sseController.abort();
|
|
1007
|
+
});
|
|
1008
|
+
await sseProgress;
|
|
1009
|
+
const output = await getMetaNodeOutput({
|
|
1010
|
+
workflowId: options.workflowId,
|
|
1011
|
+
nodeId: created.node_id,
|
|
1012
|
+
configPath: options.configPath,
|
|
1013
|
+
});
|
|
1014
|
+
progress.finish("全文复筛完成,正在输出结果...");
|
|
1015
|
+
const stepResult = {
|
|
1016
|
+
runnerId: options.workflowId,
|
|
1017
|
+
nodeId: created.node_id,
|
|
1018
|
+
nodeType: META_SCREENING_NODE_TYPES.FULLTEXT,
|
|
1019
|
+
status: output.data.status,
|
|
1020
|
+
input: criteria,
|
|
1021
|
+
output: extractNodeOutput(output.data),
|
|
1022
|
+
node: output.data,
|
|
1023
|
+
};
|
|
1024
|
+
const listResult = await listFulltextScreening({
|
|
1025
|
+
workflowId: options.workflowId,
|
|
1026
|
+
nodeId: created.node_id,
|
|
1027
|
+
page: 1,
|
|
1028
|
+
limit: 10,
|
|
1029
|
+
configPath: options.configPath,
|
|
1030
|
+
});
|
|
1031
|
+
return withScreeningTable(stepResult, listResult.table ?? {
|
|
1032
|
+
columns: [...FULLTEXT_SCREENING_COLUMNS],
|
|
1033
|
+
rows: [],
|
|
1034
|
+
}, listResult.displayInstruction ?? FULLTEXT_SCREENING_DISPLAY_INSTRUCTION, listResult.recordRefs, undefined, {
|
|
1035
|
+
selectionOptions: listResult.selectionOptions,
|
|
1036
|
+
page: listResult.page,
|
|
1037
|
+
limit: listResult.limit,
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
catch (error) {
|
|
1041
|
+
progress.dispose();
|
|
1042
|
+
throw error;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
export async function listFulltextScreening(options) {
|
|
1046
|
+
const nodeId = await resolveNodeId({
|
|
1047
|
+
workflowId: options.workflowId,
|
|
1048
|
+
nodeId: options.nodeId,
|
|
1049
|
+
nodeType: META_SCREENING_NODE_TYPES.FULLTEXT,
|
|
1050
|
+
label: "全文复筛",
|
|
1051
|
+
configPath: options.configPath,
|
|
1052
|
+
});
|
|
1053
|
+
const page = Math.max(1, Number(options.page ?? 1) || 1);
|
|
1054
|
+
const limit = Math.max(1, Number(options.limit ?? 10) || 10);
|
|
1055
|
+
const data = await requestMeta(`${META_API_PREFIX}/fulltext-screening`, {
|
|
1056
|
+
params: {
|
|
1057
|
+
run_id: options.workflowId,
|
|
1058
|
+
node_id: nodeId,
|
|
1059
|
+
page,
|
|
1060
|
+
limit,
|
|
1061
|
+
sort: options.sort,
|
|
1062
|
+
order: options.order,
|
|
1063
|
+
publication_type: options.publicationType?.join(","),
|
|
1064
|
+
},
|
|
1065
|
+
configPath: options.configPath,
|
|
1066
|
+
action: "查询全文复筛列表",
|
|
1067
|
+
});
|
|
1068
|
+
const table = buildFulltextScreeningTable(data, page, limit);
|
|
1069
|
+
const recordRefs = buildScreeningRecordRefs(data);
|
|
1070
|
+
const selectionOptions = buildScreeningSelectionOptions(recordRefs, page, limit);
|
|
1071
|
+
return withScreeningTable({
|
|
1072
|
+
runnerId: options.workflowId,
|
|
1073
|
+
nodeId,
|
|
1074
|
+
data,
|
|
1075
|
+
}, table, `${FULLTEXT_SCREENING_DISPLAY_INSTRUCTION}` +
|
|
1076
|
+
"编辑/重筛选记录:原样用本页 selectionOptions 弹选择器(label 展示、value 作 id);" +
|
|
1077
|
+
"禁止自拟选项或展示 UUID。" +
|
|
1078
|
+
"「编辑文献信息」须展示网站表单全部字段;禁止缺少、简化、翻译表单字段。", recordRefs, undefined, { selectionOptions, page, limit });
|
|
1079
|
+
}
|
|
1080
|
+
function printScreeningActionTableResult(result, format) {
|
|
1081
|
+
if (format === "json") {
|
|
1082
|
+
printOutput({
|
|
1083
|
+
runnerId: result.runnerId,
|
|
1084
|
+
nodeId: result.nodeId,
|
|
1085
|
+
displayInstruction: result.displayInstruction,
|
|
1086
|
+
tableMarkdown: result.tableMarkdown,
|
|
1087
|
+
recordRefs: result.recordRefs,
|
|
1088
|
+
selectionOptions: result.selectionOptions,
|
|
1089
|
+
page: result.page,
|
|
1090
|
+
limit: result.limit,
|
|
1091
|
+
}, "json");
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
console.log(result.tableMarkdown ?? "");
|
|
1095
|
+
}
|
|
1096
|
+
/** 下载初筛/复筛最终产物。 */
|
|
1097
|
+
export async function exportFinalScreeningMarkdownArtifacts(options) {
|
|
1098
|
+
const abstractNodeId = await resolveNodeId({
|
|
1099
|
+
workflowId: options.workflowId,
|
|
1100
|
+
nodeType: META_SCREENING_NODE_TYPES.ABSTRACT,
|
|
1101
|
+
label: "摘要初筛",
|
|
1102
|
+
configPath: options.configPath,
|
|
1103
|
+
});
|
|
1104
|
+
const fulltextNodeId = await resolveNodeId({
|
|
1105
|
+
workflowId: options.workflowId,
|
|
1106
|
+
nodeType: META_SCREENING_NODE_TYPES.FULLTEXT,
|
|
1107
|
+
label: "全文复筛",
|
|
1108
|
+
configPath: options.configPath,
|
|
1109
|
+
});
|
|
1110
|
+
return Promise.all([
|
|
1111
|
+
downloadScreeningExportArtifact({
|
|
1112
|
+
workflowId: options.workflowId,
|
|
1113
|
+
nodeId: abstractNodeId,
|
|
1114
|
+
endpoint: "abstract-screening-result/export",
|
|
1115
|
+
filename: "初筛结果.xlsx",
|
|
1116
|
+
action: "下载初筛结果",
|
|
1117
|
+
outputDirectory: options.output,
|
|
1118
|
+
}),
|
|
1119
|
+
downloadScreeningExportArtifact({
|
|
1120
|
+
workflowId: options.workflowId,
|
|
1121
|
+
nodeId: fulltextNodeId,
|
|
1122
|
+
endpoint: "fulltext-screening/export",
|
|
1123
|
+
filename: "复筛结果.xlsx",
|
|
1124
|
+
action: "下载复筛结果",
|
|
1125
|
+
outputDirectory: options.output,
|
|
1126
|
+
}),
|
|
1127
|
+
]);
|
|
1128
|
+
}
|
|
1129
|
+
export async function updateFulltextScreening(options) {
|
|
1130
|
+
const nodeId = await resolveNodeId({
|
|
1131
|
+
workflowId: options.workflowId,
|
|
1132
|
+
nodeId: options.nodeId,
|
|
1133
|
+
nodeType: META_SCREENING_NODE_TYPES.FULLTEXT,
|
|
1134
|
+
label: "全文复筛",
|
|
1135
|
+
configPath: options.configPath,
|
|
1136
|
+
});
|
|
1137
|
+
const patch = await parseJsonObjectInput(options.dataJson, options.dataFile, "--data-json", "--data-file");
|
|
1138
|
+
const recordId = String(patch.id ?? "").trim();
|
|
1139
|
+
if (!recordId) {
|
|
1140
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "编辑全文复筛文献必须提供 id");
|
|
1141
|
+
}
|
|
1142
|
+
const original = await loadScreeningLiteratureForUpdate({
|
|
1143
|
+
workflowId: options.workflowId,
|
|
1144
|
+
nodeId,
|
|
1145
|
+
endpoint: "fulltext-screening",
|
|
1146
|
+
recordId,
|
|
1147
|
+
configPath: options.configPath,
|
|
1148
|
+
});
|
|
1149
|
+
const input = normalizeScreeningLiteratureInput({ ...original, ...patch });
|
|
1150
|
+
const data = await requestMeta(`${META_API_PREFIX}/fulltext-screening`, {
|
|
1151
|
+
method: "PUT",
|
|
1152
|
+
params: {
|
|
1153
|
+
run_id: options.workflowId,
|
|
1154
|
+
node_id: nodeId,
|
|
1155
|
+
},
|
|
1156
|
+
body: input,
|
|
1157
|
+
configPath: options.configPath,
|
|
1158
|
+
action: "更新全文复筛文献",
|
|
1159
|
+
});
|
|
1160
|
+
return { runnerId: options.workflowId, nodeId, input, data };
|
|
1161
|
+
}
|
|
1162
|
+
export async function rerunFulltextScreening(options) {
|
|
1163
|
+
const nodeId = await resolveNodeId({
|
|
1164
|
+
workflowId: options.workflowId,
|
|
1165
|
+
nodeId: options.nodeId,
|
|
1166
|
+
nodeType: META_SCREENING_NODE_TYPES.FULLTEXT,
|
|
1167
|
+
label: "全文复筛",
|
|
1168
|
+
configPath: options.configPath,
|
|
1169
|
+
});
|
|
1170
|
+
const ids = await parseJsonStringArrayInput(options.idsJson, options.idsFile, "--ids-json", "--ids-file");
|
|
1171
|
+
const input = { fulltext_screening_output_ids: ids };
|
|
1172
|
+
const data = await requestMeta(`${META_API_PREFIX}/fulltext-screening/re-execute-on-selected`, {
|
|
1173
|
+
method: "POST",
|
|
1174
|
+
params: {
|
|
1175
|
+
run_id: options.workflowId,
|
|
1176
|
+
node_id: nodeId,
|
|
1177
|
+
},
|
|
1178
|
+
body: input,
|
|
1179
|
+
configPath: options.configPath,
|
|
1180
|
+
action: "重新全文复筛",
|
|
1181
|
+
});
|
|
1182
|
+
await pollMetaNodeStatus({
|
|
1183
|
+
workflowId: options.workflowId,
|
|
1184
|
+
nodeId,
|
|
1185
|
+
configPath: options.configPath,
|
|
1186
|
+
});
|
|
1187
|
+
return { runnerId: options.workflowId, nodeId, input, data };
|
|
1188
|
+
}
|
|
1189
|
+
export async function checkFulltextScreening(options) {
|
|
1190
|
+
const nodeId = await resolveNodeId({
|
|
1191
|
+
workflowId: options.workflowId,
|
|
1192
|
+
nodeId: options.nodeId,
|
|
1193
|
+
nodeType: META_SCREENING_NODE_TYPES.FULLTEXT,
|
|
1194
|
+
label: "全文复筛",
|
|
1195
|
+
configPath: options.configPath,
|
|
1196
|
+
});
|
|
1197
|
+
const data = await requestMeta(`${META_API_PREFIX}/fulltext-screening/check-before-next`, {
|
|
1198
|
+
params: {
|
|
1199
|
+
run_id: options.workflowId,
|
|
1200
|
+
node_id: nodeId,
|
|
1201
|
+
},
|
|
1202
|
+
configPath: options.configPath,
|
|
1203
|
+
action: "校验全文复筛",
|
|
1204
|
+
});
|
|
1205
|
+
return { runnerId: options.workflowId, nodeId, data };
|
|
1206
|
+
}
|
|
1207
|
+
export async function runCreateAbstractScreening(options) {
|
|
1208
|
+
const result = await createAbstractScreening(options);
|
|
1209
|
+
if ((options.format ?? "markdown") !== "json" && result.tableMarkdown) {
|
|
1210
|
+
console.log(result.tableMarkdown);
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
printScreeningStepResult(result, options.format ?? "markdown");
|
|
1214
|
+
}
|
|
1215
|
+
export async function runListAbstractScreening(options) {
|
|
1216
|
+
const result = await listAbstractScreening(options);
|
|
1217
|
+
printScreeningActionTableResult(result, options.format ?? "markdown");
|
|
1218
|
+
}
|
|
1219
|
+
export async function runUpdateAbstractScreening(options) {
|
|
1220
|
+
const result = await updateAbstractScreening(options);
|
|
1221
|
+
printOutput(result, options.format ?? "markdown");
|
|
1222
|
+
}
|
|
1223
|
+
export async function runRerunAbstractScreening(options) {
|
|
1224
|
+
const result = await rerunAbstractScreening(options);
|
|
1225
|
+
printScreeningActionTableResult(result, options.format ?? "markdown");
|
|
1226
|
+
}
|
|
1227
|
+
export async function runCountAbstractPdf(options) {
|
|
1228
|
+
const result = await countAbstractPdf(options);
|
|
1229
|
+
printOutput(result, options.format ?? "markdown");
|
|
1230
|
+
}
|
|
1231
|
+
export async function runUploadAbstractPdf(options) {
|
|
1232
|
+
const result = await uploadAbstractPdf(options);
|
|
1233
|
+
printOutput(result, options.format ?? "markdown");
|
|
1234
|
+
}
|
|
1235
|
+
export async function runCreateFulltextScreening(options) {
|
|
1236
|
+
const result = await createFulltextScreening(options);
|
|
1237
|
+
printScreeningStepResult(result, options.format ?? "markdown");
|
|
1238
|
+
}
|
|
1239
|
+
export async function runListFulltextScreening(options) {
|
|
1240
|
+
const result = await listFulltextScreening(options);
|
|
1241
|
+
printScreeningActionTableResult(result, options.format ?? "markdown");
|
|
1242
|
+
}
|
|
1243
|
+
export async function runUpdateFulltextScreening(options) {
|
|
1244
|
+
const result = await updateFulltextScreening(options);
|
|
1245
|
+
printOutput(result, options.format ?? "markdown");
|
|
1246
|
+
}
|
|
1247
|
+
export async function runRerunFulltextScreening(options) {
|
|
1248
|
+
const result = await rerunFulltextScreening(options);
|
|
1249
|
+
printOutput(result, options.format ?? "markdown");
|
|
1250
|
+
}
|
|
1251
|
+
export async function runCheckFulltextScreening(options) {
|
|
1252
|
+
const result = await checkFulltextScreening(options);
|
|
1253
|
+
printOutput(result, options.format ?? "markdown");
|
|
1254
|
+
}
|
|
1255
|
+
//# sourceMappingURL=screening.js.map
|