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,1248 @@
|
|
|
1
|
+
import { dirname, join, resolve } from "node:path";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { ApiClient, createStderrProgress, helixlifeError, helixlifeErrorCode, printOutput, renderListAsMarkdown, } from "../../core/index.js";
|
|
5
|
+
import { requestMeta } from "./api.js";
|
|
6
|
+
import { META_API_PREFIX } from "./constants.js";
|
|
7
|
+
import { getMetaNodeOutput, pollMetaNodeStatus } from "./node.js";
|
|
8
|
+
const META_EXTRACTION_NODE_TYPES = {
|
|
9
|
+
FULLTEXT: "fulltext_screening",
|
|
10
|
+
QUALITY: "quality_assessment",
|
|
11
|
+
BASELINE: "baseline_information",
|
|
12
|
+
OUTCOME: "outcome_indicator_extraction",
|
|
13
|
+
};
|
|
14
|
+
const BASELINE_INFORMATION_COLUMNS = [
|
|
15
|
+
{ key: "study", label: "Study" },
|
|
16
|
+
{ key: "region", label: "Region" },
|
|
17
|
+
{ key: "number_of_participants", label: "No. of participants" },
|
|
18
|
+
{ key: "sex", label: "Sex" },
|
|
19
|
+
{ key: "age", label: "Age" },
|
|
20
|
+
{ key: "population", label: "Population" },
|
|
21
|
+
{ key: "intervention_exposure", label: "Intervention / Exposure" },
|
|
22
|
+
{ key: "comparator_context", label: "Comparator / Context" },
|
|
23
|
+
{ key: "outcome", label: "Outcomes" },
|
|
24
|
+
{ key: "study_design", label: "Study Design" },
|
|
25
|
+
];
|
|
26
|
+
const QUALITY_ASSESSMENT_COLUMNS = [
|
|
27
|
+
{ key: "study", label: "Study" },
|
|
28
|
+
{ key: "d_one", label: "D1" },
|
|
29
|
+
{ key: "d_two", label: "D2" },
|
|
30
|
+
{ key: "d_three", label: "D3" },
|
|
31
|
+
{ key: "d_four", label: "D4" },
|
|
32
|
+
{ key: "d_five", label: "D5" },
|
|
33
|
+
{ key: "overall", label: "Overall" },
|
|
34
|
+
];
|
|
35
|
+
/** 强制按网站 10 列横向表展示基线资料,禁止删空列或重建缩略表。 */
|
|
36
|
+
const BASELINE_DISPLAY_INSTRUCTION = "必须将 tableMarkdown 作为基线资料结果的唯一展示源并逐字完整输出全部 10 列和全部返回记录:" +
|
|
37
|
+
"Study | Region | No. of participants | Sex | Age | Population | Intervention / Exposure | Comparator / Context | Outcomes | Study Design。" +
|
|
38
|
+
"禁止从 data、rows、displayRows、table 或 output 重建表格;禁止删除、合并、改名或改序任何列;" +
|
|
39
|
+
"禁止因单元格为空、Not reported 或整列为空而省略 Age、Comparator / Context 等列;空列也必须保留。" +
|
|
40
|
+
"禁止用「基线资料提取成功」等摘要代替完整 tableMarkdown,禁止缩写、翻译或改成纵向 item/content 表。" +
|
|
41
|
+
" When the user chooses「编辑内容」or「选择文献重新抽提」, first single-select record(s) by Study using baselineRecordRefs (keep id internal);" +
|
|
42
|
+
" for「编辑内容」, then show editable fields with current values, then prompt once for free-text「手动输入」; never invent id from Study text alone.";
|
|
43
|
+
const OUTCOME_PARENT_COLUMNS = [
|
|
44
|
+
{ key: "index", label: "序号" },
|
|
45
|
+
{ key: "study", label: "Study" },
|
|
46
|
+
{ key: "pmid", label: "Pmid" },
|
|
47
|
+
];
|
|
48
|
+
const OUTCOME_DETAIL_COLUMNS = [
|
|
49
|
+
{ key: "index", label: "序号" },
|
|
50
|
+
{ key: "outcome_name", label: "Outcome Name" },
|
|
51
|
+
{ key: "detection_time_point", label: "Detection Time Point" },
|
|
52
|
+
{ key: "group_name", label: "Group Name" },
|
|
53
|
+
{ key: "sample_size", label: "Sample Size" },
|
|
54
|
+
{ key: "outcome_type", label: "Outcome Type" },
|
|
55
|
+
{ key: "value_type_1", label: "Value Type" },
|
|
56
|
+
{ key: "value_1", label: "Value" },
|
|
57
|
+
{ key: "value_type_2", label: "Value Type" },
|
|
58
|
+
{ key: "value_2", label: "Value" },
|
|
59
|
+
{ key: "value_type_3", label: "Value Type" },
|
|
60
|
+
{ key: "value_3", label: "Value" },
|
|
61
|
+
];
|
|
62
|
+
/** 强制按网站「结局数据」父子表展示;完整表经 artifact 落盘,避免 Shell 截断 stdout。 */
|
|
63
|
+
const OUTCOME_DISPLAY_INSTRUCTION = "完整结局表已写入 artifact.output:必须先用 Read 读取该文件全文,再把全文原样贴进回复(这是唯一展示源;禁止只扫 JSON 就回复)。" +
|
|
64
|
+
"JSON 故意不内嵌超长 tableMarkdown,以免 Shell stdout 出现 [truncated] 后被改成摘要。" +
|
|
65
|
+
"结构必须与网站一致:先父表(序号 / Study / Pmid),再每个研究下各自的 Markdown 子表(首列序号 + 11 列:" +
|
|
66
|
+
"Outcome Name / Detection Time Point / Group Name / Sample Size / Outcome Type / Value Type / Value ×3);" +
|
|
67
|
+
"g1/g2 在同一单元格内上下堆叠(
 换行实体须原样保留)。" +
|
|
68
|
+
"【交付失败·禁止只贴父表】若回复只有研究列表父表、没有各研究下含 Outcome Name 等列的子表,视为未展示结局数据,须立刻补贴 artifact 全文;" +
|
|
69
|
+
"禁止声称「完整结局数据表已展示」;禁止用 outcomeParentSelectionOptions 自行拼父表冒充完整表;" +
|
|
70
|
+
"禁止迷你表、文字汇总、单元格 ... 截断、以「太长」省略子表。" +
|
|
71
|
+
" When the user chooses「编辑内容」or「删除结局指标」: " +
|
|
72
|
+
"(1) first single-select parent Study+Pmid via outcomeParentSelectionOptions (label already includes 序号; keep ids internal); " +
|
|
73
|
+
"(2) then show ONLY that parent's detailTables sub-table (with 序号) and pop AskQuestion from outcomeChildSelectionOptions (≤10/page, add「查看下一页结局」when more; never ask for typed 序号); " +
|
|
74
|
+
"(3) for「编辑内容」, then show the full website edit form fields with current values once: " +
|
|
75
|
+
"outcome_name, detection_time_point, and for both g1 and g2 separately: group_name, sample_size, outcome_type (only g1 editable; g2 synced readonly), " +
|
|
76
|
+
"plus Value type/Value slots driven by Outcome Type — Binary→Events; Continuity→SD then Mean; Survival→HR then 95%Lower CI then 95%Upper CI; list every side's values (empty as (空)). " +
|
|
77
|
+
"Never flatten all Study—Outcome Name into one selector; never merge g1/g2 into a single summary cell; " +
|
|
78
|
+
"never always show 3 Value slots regardless of type; then prompt once for free-text「手动输入」.";
|
|
79
|
+
/**
|
|
80
|
+
* 将结局 tableMarkdown 写入系统临时目录,返回 artifact 路径。
|
|
81
|
+
*
|
|
82
|
+
* @param tableMarkdown 完整结局 Markdown 表。
|
|
83
|
+
* @returns 落盘产物描述。
|
|
84
|
+
*/
|
|
85
|
+
async function writeOutcomeTableArtifact(tableMarkdown) {
|
|
86
|
+
const dir = join(tmpdir(), "helixlife-meta-outcome");
|
|
87
|
+
await mkdir(dir, { recursive: true });
|
|
88
|
+
const filename = "结局数据.md";
|
|
89
|
+
const outputPath = join(dir, filename);
|
|
90
|
+
const buffer = Buffer.from(tableMarkdown, "utf8");
|
|
91
|
+
await writeFile(outputPath, buffer);
|
|
92
|
+
return {
|
|
93
|
+
output: outputPath,
|
|
94
|
+
filename,
|
|
95
|
+
contentType: "text/markdown; charset=utf-8",
|
|
96
|
+
size: buffer.length,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function parseJsonObject(raw, optionName) {
|
|
100
|
+
const jsonText = raw?.replace(/^\uFEFF/, "").trim();
|
|
101
|
+
if (!jsonText) {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const parsed = JSON.parse(jsonText);
|
|
106
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
107
|
+
throw new Error("not an object");
|
|
108
|
+
}
|
|
109
|
+
return parsed;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `${optionName} 须为合法 JSON 对象`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function parseJsonStringArray(raw, optionName) {
|
|
116
|
+
const jsonText = raw?.replace(/^\uFEFF/, "").trim();
|
|
117
|
+
if (!jsonText) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const parsed = JSON.parse(jsonText);
|
|
122
|
+
if (!Array.isArray(parsed) ||
|
|
123
|
+
parsed.some((item) => typeof item !== "string")) {
|
|
124
|
+
throw new Error("not a string array");
|
|
125
|
+
}
|
|
126
|
+
return parsed;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `${optionName} 须为合法 JSON 字符串数组`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function parseJsonObjectInput(raw, filePath, optionName, fileOptionName) {
|
|
133
|
+
if (filePath?.trim()) {
|
|
134
|
+
const content = await readFile(filePath.trim(), "utf8");
|
|
135
|
+
return parseJsonObject(content, fileOptionName);
|
|
136
|
+
}
|
|
137
|
+
if (!raw?.trim()) {
|
|
138
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `缺少 ${optionName} 或 ${fileOptionName}`);
|
|
139
|
+
}
|
|
140
|
+
return parseJsonObject(raw, optionName);
|
|
141
|
+
}
|
|
142
|
+
async function parseJsonStringArrayInput(raw, filePath, optionName, fileOptionName) {
|
|
143
|
+
if (filePath?.trim()) {
|
|
144
|
+
const content = await readFile(filePath.trim(), "utf8");
|
|
145
|
+
return parseJsonStringArray(content, fileOptionName);
|
|
146
|
+
}
|
|
147
|
+
if (!raw?.trim()) {
|
|
148
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `缺少 ${optionName} 或 ${fileOptionName}`);
|
|
149
|
+
}
|
|
150
|
+
return parseJsonStringArray(raw, optionName);
|
|
151
|
+
}
|
|
152
|
+
function extractNodeOutput(node) {
|
|
153
|
+
if (node.output &&
|
|
154
|
+
typeof node.output === "object" &&
|
|
155
|
+
"output" in node.output) {
|
|
156
|
+
return node.output.output;
|
|
157
|
+
}
|
|
158
|
+
return node.output ?? {};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Safely narrows an unknown API value into a record for list traversal.
|
|
162
|
+
*
|
|
163
|
+
* @param value The raw value returned by the API.
|
|
164
|
+
* @returns The value as a record, or an empty record when it is not object-like.
|
|
165
|
+
*/
|
|
166
|
+
function unwrapRecord(value) {
|
|
167
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
168
|
+
? value
|
|
169
|
+
: {};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Extracts rows from the common paginated response shapes used by Meta list APIs.
|
|
173
|
+
*
|
|
174
|
+
* @param data The raw list response.
|
|
175
|
+
* @returns The extracted list rows.
|
|
176
|
+
*/
|
|
177
|
+
function extractListItems(data) {
|
|
178
|
+
const root = unwrapRecord(data);
|
|
179
|
+
const nested = unwrapRecord(root.data);
|
|
180
|
+
const payload = Object.keys(nested).length > 0 ? nested : root;
|
|
181
|
+
return Array.isArray(payload.items)
|
|
182
|
+
? payload.items
|
|
183
|
+
: Array.isArray(payload.list)
|
|
184
|
+
? payload.list
|
|
185
|
+
: Array.isArray(payload.records)
|
|
186
|
+
? payload.records
|
|
187
|
+
: Array.isArray(payload.results)
|
|
188
|
+
? payload.results
|
|
189
|
+
: Array.isArray(payload.data)
|
|
190
|
+
? payload.data
|
|
191
|
+
: Array.isArray(data)
|
|
192
|
+
? data
|
|
193
|
+
: [];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Formats scalar and array fields for table display without dropping values.
|
|
197
|
+
*
|
|
198
|
+
* @param value The raw field value from a baseline record.
|
|
199
|
+
* @returns A readable string, joining arrays with commas.
|
|
200
|
+
*/
|
|
201
|
+
function formatListField(value) {
|
|
202
|
+
if (Array.isArray(value)) {
|
|
203
|
+
return value
|
|
204
|
+
.map((item) => String(item ?? ""))
|
|
205
|
+
.filter(Boolean)
|
|
206
|
+
.join(", ");
|
|
207
|
+
}
|
|
208
|
+
return value === undefined || value === null ? "" : String(value);
|
|
209
|
+
}
|
|
210
|
+
/** 调用抽提结果 `/export` 接口下载产物并落盘。 */
|
|
211
|
+
async function downloadExtractionExportArtifact(options) {
|
|
212
|
+
const client = new ApiClient();
|
|
213
|
+
const response = await client.requestBinary(`${META_API_PREFIX}/${options.endpoint}`, {
|
|
214
|
+
method: "GET",
|
|
215
|
+
params: {
|
|
216
|
+
run_id: options.workflowId,
|
|
217
|
+
node_id: options.nodeId,
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
if (response.buffer.length === 0) {
|
|
221
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, `${options.action}下载内容为空`);
|
|
222
|
+
}
|
|
223
|
+
const output = resolve(options.outputDirectory?.trim() || ".", options.filename);
|
|
224
|
+
await mkdir(dirname(output), { recursive: true });
|
|
225
|
+
await writeFile(output, response.buffer);
|
|
226
|
+
return {
|
|
227
|
+
output,
|
|
228
|
+
filename: options.filename,
|
|
229
|
+
contentType: response.contentType ?? "application/octet-stream",
|
|
230
|
+
size: response.buffer.length,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Builds the baseline information table using the same columns as the website.
|
|
235
|
+
*
|
|
236
|
+
* @param data The raw baseline information list response.
|
|
237
|
+
* @returns A table descriptor with website column labels and normalized rows.
|
|
238
|
+
*/
|
|
239
|
+
function buildBaselineInformationTable(data) {
|
|
240
|
+
const rows = extractListItems(data).map((item) => {
|
|
241
|
+
const record = unwrapRecord(item);
|
|
242
|
+
return {
|
|
243
|
+
study: formatListField(record.study),
|
|
244
|
+
region: formatListField(record.region),
|
|
245
|
+
number_of_participants: formatListField(record.number_of_participants),
|
|
246
|
+
sex: formatListField(record.sex),
|
|
247
|
+
age: formatListField(record.age),
|
|
248
|
+
population: formatListField(record.population),
|
|
249
|
+
intervention_exposure: formatListField(record.intervention_exposure),
|
|
250
|
+
comparator_context: formatListField(record.comparator_context),
|
|
251
|
+
outcome: formatListField(record.outcome),
|
|
252
|
+
study_design: formatListField(record.study_design),
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
return { columns: [...BASELINE_INFORMATION_COLUMNS], rows };
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 为基线资料结果附加 tableMarkdown 等展示字段;Agent 须以 tableMarkdown 为唯一展示源。
|
|
259
|
+
*
|
|
260
|
+
* @param result 待附加表格的命令结果。
|
|
261
|
+
* @param table 规范化后的基线表(固定 10 列)。
|
|
262
|
+
* @param baselineRecordRefs 编辑/重抽用的内部定位(不对用户展示 id)。
|
|
263
|
+
* @returns 带 displayInstruction / tableMarkdown 的结果。
|
|
264
|
+
*/
|
|
265
|
+
function withBaselineTable(result, table, baselineRecordRefs = []) {
|
|
266
|
+
const tableMarkdown = renderListAsMarkdown(table.rows, table.columns);
|
|
267
|
+
return {
|
|
268
|
+
...result,
|
|
269
|
+
displayInstruction: BASELINE_DISPLAY_INSTRUCTION,
|
|
270
|
+
columns: table.columns,
|
|
271
|
+
rows: table.rows,
|
|
272
|
+
displayColumns: table.columns.map((column) => column.label),
|
|
273
|
+
displayRows: table.rows,
|
|
274
|
+
tableMarkdown,
|
|
275
|
+
baselineRecordRefs,
|
|
276
|
+
table,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* 输出基线资料结果;JSON 暴露 tableMarkdown + baselineRecordRefs(编辑定位),不暴露 rows。
|
|
281
|
+
*
|
|
282
|
+
* @param result create/list 返回的基线结果。
|
|
283
|
+
* @param format 输出格式。
|
|
284
|
+
*/
|
|
285
|
+
function printBaselineResult(result, format) {
|
|
286
|
+
if (format === "json") {
|
|
287
|
+
printOutput({
|
|
288
|
+
runnerId: result.runnerId,
|
|
289
|
+
nodeId: result.nodeId,
|
|
290
|
+
...(result && "nodeType" in result
|
|
291
|
+
? { nodeType: result.nodeType, status: result.status }
|
|
292
|
+
: {}),
|
|
293
|
+
displayInstruction: result.displayInstruction,
|
|
294
|
+
tableMarkdown: result.tableMarkdown,
|
|
295
|
+
baselineRecordRefs: result.baselineRecordRefs,
|
|
296
|
+
}, "json");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
console.log(result.tableMarkdown ?? "");
|
|
300
|
+
}
|
|
301
|
+
/** 基线资料编辑/重抽定位,与展示表分离。 */
|
|
302
|
+
function buildBaselineRecordRefs(data) {
|
|
303
|
+
return extractListItems(data).map((item) => {
|
|
304
|
+
const record = unwrapRecord(item);
|
|
305
|
+
return {
|
|
306
|
+
id: formatListField(record.id),
|
|
307
|
+
study: formatListField(record.study),
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* 构建结局父表(序号 + Study / Pmid),序号与父级选择器对齐。
|
|
313
|
+
*
|
|
314
|
+
* @param data 结局列表原始响应。
|
|
315
|
+
* @returns 每行对应一篇研究的父表描述。
|
|
316
|
+
*/
|
|
317
|
+
function buildOutcomeParentTable(data) {
|
|
318
|
+
const rows = extractListItems(data).map((item, index) => {
|
|
319
|
+
const record = unwrapRecord(item);
|
|
320
|
+
return {
|
|
321
|
+
index: String(index + 1),
|
|
322
|
+
study: formatListField(record.study),
|
|
323
|
+
pmid: formatListField(record.pmid),
|
|
324
|
+
};
|
|
325
|
+
});
|
|
326
|
+
return { columns: [...OUTCOME_PARENT_COLUMNS], rows };
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Extracts value rows from an outcome group payload without dropping any value slots.
|
|
330
|
+
*
|
|
331
|
+
* @param value The raw `g1_data` or `g2_data` value.
|
|
332
|
+
* @returns Normalized value rows.
|
|
333
|
+
*/
|
|
334
|
+
function extractOutcomeValueRows(value) {
|
|
335
|
+
if (!Array.isArray(value)) {
|
|
336
|
+
return [];
|
|
337
|
+
}
|
|
338
|
+
return value.map((item) => unwrapRecord(item));
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* 单元格内 g1/g2 换行:写入 HTML 数字实体 
(U+2028)。
|
|
342
|
+
* - 不能写裸 U+2028/\\n:会拆掉 Markdown 管道表
|
|
343
|
+
* - 不能写 <br> / HTML <table>:聊天端会剥掉标签(BinaryBinary 或整表空白)
|
|
344
|
+
* 实体留在 Markdown 源码中不拆表,渲染解码后浏览器在单元格内换行。
|
|
345
|
+
*/
|
|
346
|
+
const OUTCOME_CELL_LINE_BREAK = "
";
|
|
347
|
+
/**
|
|
348
|
+
* 将 g1/g2 按网站子表样式上下堆叠到同一单元格;空侧保留 `/`。
|
|
349
|
+
* 两侧皆空时只输出单个 `/`。
|
|
350
|
+
*
|
|
351
|
+
* @param first g1 侧字段值。
|
|
352
|
+
* @param second g2 侧字段值。
|
|
353
|
+
* @returns 用换行实体连接的单元格文本(或单个 `/`)。
|
|
354
|
+
*/
|
|
355
|
+
function formatOutcomeGroupPair(first, second) {
|
|
356
|
+
const firstValue = formatListField(first);
|
|
357
|
+
const secondValue = formatListField(second);
|
|
358
|
+
if (!firstValue && !secondValue) {
|
|
359
|
+
return "/";
|
|
360
|
+
}
|
|
361
|
+
return `${firstValue || "/"}${OUTCOME_CELL_LINE_BREAK}${secondValue || "/"}`;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* 按网站子表写入结局行:单元格内 g1/g2 上下堆叠;同一结局多 chunk 共用同一序号。
|
|
365
|
+
*
|
|
366
|
+
* @param rows 目标行数组。
|
|
367
|
+
* @param outcome 单条结局记录。
|
|
368
|
+
* @param childIndex 该父级下的子结局序号(1-based)。
|
|
369
|
+
*/
|
|
370
|
+
function pushOutcomeRows(rows, outcome, childIndex) {
|
|
371
|
+
const g1Data = extractOutcomeValueRows(outcome.g1_data);
|
|
372
|
+
const g2Data = extractOutcomeValueRows(outcome.g2_data);
|
|
373
|
+
const chunkCount = Math.max(1, Math.ceil(Math.max(g1Data.length, g2Data.length) / 3));
|
|
374
|
+
for (let chunkIndex = 0; chunkIndex < chunkCount; chunkIndex += 1) {
|
|
375
|
+
const valueCells = {};
|
|
376
|
+
for (let slot = 0; slot < 3; slot += 1) {
|
|
377
|
+
const dataIndex = chunkIndex * 3 + slot;
|
|
378
|
+
valueCells[`value_type_${slot + 1}`] = formatOutcomeGroupPair(g1Data[dataIndex]?.value_type, g2Data[dataIndex]?.value_type);
|
|
379
|
+
valueCells[`value_${slot + 1}`] = formatOutcomeGroupPair(g1Data[dataIndex]?.value, g2Data[dataIndex]?.value);
|
|
380
|
+
}
|
|
381
|
+
rows.push({
|
|
382
|
+
// 多 chunk 时仅首行展示序号,避免选择器与表行一一错位
|
|
383
|
+
index: chunkIndex === 0 ? String(childIndex) : "",
|
|
384
|
+
outcome_name: formatListField(outcome.outcome_name),
|
|
385
|
+
detection_time_point: formatListField(outcome.detection_time_point),
|
|
386
|
+
group_name: formatOutcomeGroupPair(outcome.g1_group_name, outcome.g2_group_name),
|
|
387
|
+
sample_size: formatOutcomeGroupPair(outcome.g1_sample_size, outcome.g2_sample_size),
|
|
388
|
+
outcome_type: formatOutcomeGroupPair(outcome.g1_outcome_type, outcome.g2_outcome_type),
|
|
389
|
+
...valueCells,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* 按父级研究拆分子表;首列序号与该父级下子选择器对齐。
|
|
395
|
+
*
|
|
396
|
+
* @param data 结局列表原始响应。
|
|
397
|
+
* @returns 按 Study/PMID 分组的子表。
|
|
398
|
+
*/
|
|
399
|
+
function buildOutcomeDetailTables(data) {
|
|
400
|
+
return extractListItems(data).map((item, index) => {
|
|
401
|
+
const record = unwrapRecord(item);
|
|
402
|
+
const outcomes = Array.isArray(record.outcomes) ? record.outcomes : [];
|
|
403
|
+
const rows = [];
|
|
404
|
+
outcomes.forEach((outcomeItem, outcomeIndex) => {
|
|
405
|
+
const outcome = unwrapRecord(outcomeItem);
|
|
406
|
+
pushOutcomeRows(rows, outcome, outcomeIndex + 1);
|
|
407
|
+
});
|
|
408
|
+
const study = formatListField(record.study) || `Study ${index + 1}`;
|
|
409
|
+
const pmid = formatListField(record.pmid);
|
|
410
|
+
return {
|
|
411
|
+
title: pmid ? `${study} (PMID ${pmid})` : study,
|
|
412
|
+
columns: [...OUTCOME_DETAIL_COLUMNS],
|
|
413
|
+
rows,
|
|
414
|
+
parentIndex: index + 1,
|
|
415
|
+
};
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* 构建父级 Study+Pmid 选择器选项(含序号)。
|
|
420
|
+
*
|
|
421
|
+
* @param data 结局列表原始响应。
|
|
422
|
+
* @returns AskQuestion 用父级选项。
|
|
423
|
+
*/
|
|
424
|
+
function buildOutcomeParentSelectionOptions(data) {
|
|
425
|
+
return extractListItems(data).map((item, index) => {
|
|
426
|
+
const record = unwrapRecord(item);
|
|
427
|
+
const study = formatListField(record.study) || "无 Study";
|
|
428
|
+
const pmid = formatListField(record.pmid);
|
|
429
|
+
const pmidText = pmid ? `PMID ${pmid}` : "无 PMID";
|
|
430
|
+
const parentIndex = index + 1;
|
|
431
|
+
return {
|
|
432
|
+
index: parentIndex,
|
|
433
|
+
// 半角括号/逗号:全角「,」在 AskQuestion 选择器中可能显示为乱码
|
|
434
|
+
label: `序号 ${parentIndex} (${study}, ${pmidText})`,
|
|
435
|
+
value: String(parentIndex),
|
|
436
|
+
study: formatListField(record.study),
|
|
437
|
+
pmid,
|
|
438
|
+
};
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* 构建子结局选择器选项;按父级序号分组,label 含子表序号。
|
|
443
|
+
*
|
|
444
|
+
* @param outcomeRecordRefs 已带 parentIndex/childIndex 的结局定位。
|
|
445
|
+
* @returns AskQuestion 用子结局选项。
|
|
446
|
+
*/
|
|
447
|
+
function buildOutcomeChildSelectionOptions(outcomeRecordRefs) {
|
|
448
|
+
return outcomeRecordRefs
|
|
449
|
+
.filter((ref) => ref.id.trim())
|
|
450
|
+
.map((ref) => {
|
|
451
|
+
const outcomeName = ref.outcomeName.trim() || "无 Outcome Name";
|
|
452
|
+
return {
|
|
453
|
+
index: ref.childIndex,
|
|
454
|
+
label: `序号 ${ref.childIndex} (${outcomeName})`,
|
|
455
|
+
value: ref.id,
|
|
456
|
+
parentIndex: ref.parentIndex,
|
|
457
|
+
study: ref.study,
|
|
458
|
+
pmid: ref.pmid,
|
|
459
|
+
outcomeName: ref.outcomeName,
|
|
460
|
+
};
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
/** 保留完整结局 id,并附带父/子序号供两级选择器定位。 */
|
|
464
|
+
function buildOutcomeRecordRefs(data) {
|
|
465
|
+
return extractListItems(data).flatMap((item, parentOffset) => {
|
|
466
|
+
const parent = unwrapRecord(item);
|
|
467
|
+
const outcomes = Array.isArray(parent.outcomes) ? parent.outcomes : [];
|
|
468
|
+
const parentIndex = parentOffset + 1;
|
|
469
|
+
return outcomes.map((outcomeItem, outcomeOffset) => {
|
|
470
|
+
const outcome = unwrapRecord(outcomeItem);
|
|
471
|
+
return {
|
|
472
|
+
id: formatListField(outcome.id),
|
|
473
|
+
study: formatListField(parent.study),
|
|
474
|
+
pmid: formatListField(parent.pmid),
|
|
475
|
+
outcomeName: formatListField(outcome.outcome_name),
|
|
476
|
+
detectionTimePoint: formatListField(outcome.detection_time_point),
|
|
477
|
+
g1GroupName: formatListField(outcome.g1_group_name),
|
|
478
|
+
g2GroupName: formatListField(outcome.g2_group_name),
|
|
479
|
+
parentIndex,
|
|
480
|
+
childIndex: outcomeOffset + 1,
|
|
481
|
+
};
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* 为命令结果附加网站风格的结局父表与子表(供 WorkBuddy 原样展示)。
|
|
487
|
+
*
|
|
488
|
+
* @param result 待 enrichment 的命令结果。
|
|
489
|
+
* @param table 父表(序号 / Study / Pmid)。
|
|
490
|
+
* @param detailTables 各研究下带序号的子表。
|
|
491
|
+
* @param outcomeRecordRefs 编辑/删除用的内部定位(不对用户展示 id)。
|
|
492
|
+
* @param outcomeParentSelectionOptions 父级 Study+Pmid 选择器选项。
|
|
493
|
+
* @param outcomeChildSelectionOptions 子结局选择器选项(按 parentIndex 过滤)。
|
|
494
|
+
* @returns 带 tableMarkdown / displayInstruction 的结果。
|
|
495
|
+
*/
|
|
496
|
+
function withOutcomeTables(result, table, detailTables, outcomeRecordRefs = [], outcomeParentSelectionOptions = [], outcomeChildSelectionOptions = []) {
|
|
497
|
+
// 对齐网站「结局数据」:父表 + 子表均用 Markdown;单元格内换行靠 
 实体
|
|
498
|
+
const detailSections = detailTables.flatMap((detailTable, _index, all) => {
|
|
499
|
+
const section = [""];
|
|
500
|
+
if (all.length > 1) {
|
|
501
|
+
section.push(`#### ${detailTable.title}`, "");
|
|
502
|
+
}
|
|
503
|
+
section.push(renderListAsMarkdown(detailTable.rows, detailTable.columns));
|
|
504
|
+
return section;
|
|
505
|
+
});
|
|
506
|
+
const tableMarkdown = [
|
|
507
|
+
"结局数据",
|
|
508
|
+
"",
|
|
509
|
+
renderListAsMarkdown(table.rows, table.columns),
|
|
510
|
+
...detailSections,
|
|
511
|
+
].join("\n");
|
|
512
|
+
return {
|
|
513
|
+
...result,
|
|
514
|
+
displayInstruction: OUTCOME_DISPLAY_INSTRUCTION,
|
|
515
|
+
columns: table.columns,
|
|
516
|
+
rows: table.rows,
|
|
517
|
+
displayColumns: table.columns.map((column) => column.label),
|
|
518
|
+
displayRows: table.rows,
|
|
519
|
+
tableMarkdown,
|
|
520
|
+
outcomeRecordRefs,
|
|
521
|
+
outcomeParentSelectionOptions,
|
|
522
|
+
outcomeChildSelectionOptions,
|
|
523
|
+
table,
|
|
524
|
+
detailTables,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
function pickFields(input, allowedKeys) {
|
|
528
|
+
const picked = {};
|
|
529
|
+
for (const key of allowedKeys) {
|
|
530
|
+
if (key in input) {
|
|
531
|
+
picked[key] = input[key];
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return picked;
|
|
535
|
+
}
|
|
536
|
+
function isEmptyObject(value) {
|
|
537
|
+
return (typeof value === "object" &&
|
|
538
|
+
value !== null &&
|
|
539
|
+
!Array.isArray(value) &&
|
|
540
|
+
Object.keys(value).length === 0);
|
|
541
|
+
}
|
|
542
|
+
function filterOutcomeDataRows(value) {
|
|
543
|
+
if (!Array.isArray(value)) {
|
|
544
|
+
return value;
|
|
545
|
+
}
|
|
546
|
+
return value.filter((item) => {
|
|
547
|
+
if (isEmptyObject(item)) {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
if (!item || typeof item !== "object") {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
const row = item;
|
|
554
|
+
return Boolean(row.value_type || row.value);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
function buildQualityAssessmentTable(data) {
|
|
558
|
+
const rows = extractListItems(data).map((item) => {
|
|
559
|
+
const source = unwrapRecord(item);
|
|
560
|
+
return Object.fromEntries(QUALITY_ASSESSMENT_COLUMNS.map((column) => [
|
|
561
|
+
column.key,
|
|
562
|
+
formatListField(source[column.key]),
|
|
563
|
+
]));
|
|
564
|
+
});
|
|
565
|
+
return { columns: [...QUALITY_ASSESSMENT_COLUMNS], rows };
|
|
566
|
+
}
|
|
567
|
+
/** 质量评估编辑用内部定位,与展示表分离。 */
|
|
568
|
+
function buildQualityRecordRefs(data) {
|
|
569
|
+
return extractListItems(data).map((item) => {
|
|
570
|
+
const record = unwrapRecord(item);
|
|
571
|
+
return {
|
|
572
|
+
id: formatListField(record.id),
|
|
573
|
+
study: formatListField(record.study),
|
|
574
|
+
};
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
const OUTCOME_DATA_CONFIG = {
|
|
578
|
+
Binary: ["Events"],
|
|
579
|
+
Continuity: ["SD", "Mean"],
|
|
580
|
+
Survival: ["HR", "95%Lower CI", "95%Upper CI"],
|
|
581
|
+
};
|
|
582
|
+
/**
|
|
583
|
+
* Rebuilds outcome value slots exactly as the website edit form does.
|
|
584
|
+
*
|
|
585
|
+
* @param value Existing group values from the selected outcome record.
|
|
586
|
+
* @param outcomeType The first group's selected outcome type.
|
|
587
|
+
* @returns Fixed value slots for a known type, or filtered original rows otherwise.
|
|
588
|
+
*/
|
|
589
|
+
function normalizeOutcomeDataRows(value, outcomeType) {
|
|
590
|
+
const slots = OUTCOME_DATA_CONFIG[outcomeType];
|
|
591
|
+
if (!slots) {
|
|
592
|
+
return filterOutcomeDataRows(value);
|
|
593
|
+
}
|
|
594
|
+
const existing = Array.isArray(value)
|
|
595
|
+
? value.map((item) => unwrapRecord(item))
|
|
596
|
+
: [];
|
|
597
|
+
return slots.map((valueType, index) => ({
|
|
598
|
+
value_type: valueType,
|
|
599
|
+
value: existing[index]?.value ?? "",
|
|
600
|
+
}));
|
|
601
|
+
}
|
|
602
|
+
function normalizeQualityInput(input) {
|
|
603
|
+
return pickFields(input, [
|
|
604
|
+
"id",
|
|
605
|
+
"study",
|
|
606
|
+
"d_one",
|
|
607
|
+
"d_two",
|
|
608
|
+
"d_three",
|
|
609
|
+
"d_four",
|
|
610
|
+
"d_five",
|
|
611
|
+
"overall",
|
|
612
|
+
]);
|
|
613
|
+
}
|
|
614
|
+
function normalizeBaselineInput(input) {
|
|
615
|
+
return pickFields(input, [
|
|
616
|
+
"id",
|
|
617
|
+
"region",
|
|
618
|
+
"number_of_participants",
|
|
619
|
+
"sex",
|
|
620
|
+
"age",
|
|
621
|
+
"population",
|
|
622
|
+
"intervention_exposure",
|
|
623
|
+
"comparator_context",
|
|
624
|
+
"outcome",
|
|
625
|
+
"study",
|
|
626
|
+
"study_design",
|
|
627
|
+
]);
|
|
628
|
+
}
|
|
629
|
+
function normalizeOutcomeInput(input) {
|
|
630
|
+
const normalized = pickFields(input, [
|
|
631
|
+
"id",
|
|
632
|
+
"outcome_name",
|
|
633
|
+
"detection_time_point",
|
|
634
|
+
"g1_group_name",
|
|
635
|
+
"g1_sample_size",
|
|
636
|
+
"g1_outcome_type",
|
|
637
|
+
"g1_data",
|
|
638
|
+
"g2_group_name",
|
|
639
|
+
"g2_sample_size",
|
|
640
|
+
"g2_outcome_type",
|
|
641
|
+
"g2_data",
|
|
642
|
+
]);
|
|
643
|
+
const outcomeType = String(normalized.g1_outcome_type ?? "");
|
|
644
|
+
if (outcomeType) {
|
|
645
|
+
normalized.g2_outcome_type = outcomeType;
|
|
646
|
+
}
|
|
647
|
+
if ("g1_data" in normalized || outcomeType) {
|
|
648
|
+
normalized.g1_data = normalizeOutcomeDataRows(normalized.g1_data, outcomeType);
|
|
649
|
+
}
|
|
650
|
+
if ("g2_data" in normalized || outcomeType) {
|
|
651
|
+
normalized.g2_data = normalizeOutcomeDataRows(normalized.g2_data, outcomeType);
|
|
652
|
+
}
|
|
653
|
+
return normalized;
|
|
654
|
+
}
|
|
655
|
+
async function loadExtractionRecordForUpdate(options) {
|
|
656
|
+
const limit = 50;
|
|
657
|
+
for (let page = 1;; page += 1) {
|
|
658
|
+
const data = await requestMeta(`${META_API_PREFIX}/${options.endpoint}`, {
|
|
659
|
+
params: {
|
|
660
|
+
run_id: options.workflowId,
|
|
661
|
+
node_id: options.nodeId,
|
|
662
|
+
page,
|
|
663
|
+
limit,
|
|
664
|
+
scale_type: options.scaleType,
|
|
665
|
+
},
|
|
666
|
+
configPath: options.configPath,
|
|
667
|
+
action: "读取待编辑抽提记录完整数据",
|
|
668
|
+
});
|
|
669
|
+
const items = extractListItems(data);
|
|
670
|
+
for (const item of items) {
|
|
671
|
+
const record = unwrapRecord(item);
|
|
672
|
+
if (String(record.id ?? "") === options.recordId)
|
|
673
|
+
return record;
|
|
674
|
+
if (options.nestedOutcomes && Array.isArray(record.outcomes)) {
|
|
675
|
+
const outcome = record.outcomes
|
|
676
|
+
.map((value) => unwrapRecord(value))
|
|
677
|
+
.find((value) => String(value.id ?? "") === options.recordId);
|
|
678
|
+
if (outcome)
|
|
679
|
+
return outcome;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (items.length < limit)
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "未找到待编辑的抽提记录,已停止更新以避免覆盖丢失原字段");
|
|
686
|
+
}
|
|
687
|
+
async function getWorkflow(workflowId, configPath) {
|
|
688
|
+
return requestMeta(`${META_API_PREFIX}/workflow/runs/${encodeURIComponent(workflowId)}`, {
|
|
689
|
+
configPath,
|
|
690
|
+
action: "查询 Meta 工作流",
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
function findLatestNode(workflow, nodeType) {
|
|
694
|
+
return [...(workflow.nodes ?? [])]
|
|
695
|
+
.reverse()
|
|
696
|
+
.find((node) => node.node_type === nodeType);
|
|
697
|
+
}
|
|
698
|
+
async function resolveNodeId(options) {
|
|
699
|
+
if (options.nodeId?.trim()) {
|
|
700
|
+
return options.nodeId.trim();
|
|
701
|
+
}
|
|
702
|
+
const workflow = await getWorkflow(options.workflowId, options.configPath);
|
|
703
|
+
const node = findLatestNode(workflow, options.nodeType);
|
|
704
|
+
if (!node) {
|
|
705
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, `当前工作流中未找到 ${options.label} 节点`);
|
|
706
|
+
}
|
|
707
|
+
return node.id;
|
|
708
|
+
}
|
|
709
|
+
async function runExtractionExecute(options) {
|
|
710
|
+
const progress = createStderrProgress(options.format === "json");
|
|
711
|
+
progress.start(options.progressMessage);
|
|
712
|
+
try {
|
|
713
|
+
const created = await requestMeta(`${META_API_PREFIX}/${options.endpoint}`, {
|
|
714
|
+
method: "POST",
|
|
715
|
+
params: {
|
|
716
|
+
run_id: options.workflowId,
|
|
717
|
+
...options.params,
|
|
718
|
+
},
|
|
719
|
+
configPath: options.configPath,
|
|
720
|
+
action: options.progressMessage,
|
|
721
|
+
});
|
|
722
|
+
if (!created.node_id) {
|
|
723
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_RESPONSE, `${options.progressMessage}响应缺少 node_id`);
|
|
724
|
+
}
|
|
725
|
+
await pollMetaNodeStatus({
|
|
726
|
+
workflowId: options.workflowId,
|
|
727
|
+
nodeId: created.node_id,
|
|
728
|
+
configPath: options.configPath,
|
|
729
|
+
});
|
|
730
|
+
const output = await getMetaNodeOutput({
|
|
731
|
+
workflowId: options.workflowId,
|
|
732
|
+
nodeId: created.node_id,
|
|
733
|
+
configPath: options.configPath,
|
|
734
|
+
});
|
|
735
|
+
progress.finish("Meta 节点执行完成,正在输出结果...");
|
|
736
|
+
return {
|
|
737
|
+
runnerId: options.workflowId,
|
|
738
|
+
nodeId: created.node_id,
|
|
739
|
+
nodeType: options.nodeType,
|
|
740
|
+
status: output.data.status,
|
|
741
|
+
input: options.input,
|
|
742
|
+
output: extractNodeOutput(output.data),
|
|
743
|
+
node: output.data,
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
catch (error) {
|
|
747
|
+
progress.dispose();
|
|
748
|
+
throw error;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function printStepResult(result, format) {
|
|
752
|
+
if (format === "json") {
|
|
753
|
+
printOutput(result, "json");
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
if (result.table) {
|
|
757
|
+
console.log(renderListAsMarkdown(result.table.rows, result.table.columns));
|
|
758
|
+
for (const detailTable of result.detailTables ?? []) {
|
|
759
|
+
console.log(`\n${detailTable.title}`);
|
|
760
|
+
console.log(renderListAsMarkdown(detailTable.rows, detailTable.columns));
|
|
761
|
+
}
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
console.log(renderListAsMarkdown([
|
|
765
|
+
{
|
|
766
|
+
runnerId: result.runnerId,
|
|
767
|
+
nodeType: result.nodeType,
|
|
768
|
+
status: result.status,
|
|
769
|
+
},
|
|
770
|
+
], [
|
|
771
|
+
{ key: "runnerId", label: "runnerId" },
|
|
772
|
+
{ key: "nodeType", label: "节点类型" },
|
|
773
|
+
{ key: "status", label: "状态" },
|
|
774
|
+
]));
|
|
775
|
+
console.log("\n输出:");
|
|
776
|
+
console.log(JSON.stringify(result.output, null, 2));
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* 进入数据抽提前校验全文复筛(对齐网站复筛结果「下一步」的 check-before-next)。
|
|
780
|
+
* Include 不足等业务错误会直接抛出,阻止创建质量评估节点。
|
|
781
|
+
*/
|
|
782
|
+
async function verifyFulltextBeforeExtraction(options) {
|
|
783
|
+
const fulltextNodeId = options.fulltextNodeId?.trim() ??
|
|
784
|
+
(await resolveNodeId({
|
|
785
|
+
workflowId: options.workflowId,
|
|
786
|
+
nodeType: META_EXTRACTION_NODE_TYPES.FULLTEXT,
|
|
787
|
+
label: "全文复筛",
|
|
788
|
+
configPath: options.configPath,
|
|
789
|
+
}));
|
|
790
|
+
await requestMeta(`${META_API_PREFIX}/fulltext-screening/check-before-next`, {
|
|
791
|
+
params: {
|
|
792
|
+
run_id: options.workflowId,
|
|
793
|
+
node_id: fulltextNodeId,
|
|
794
|
+
},
|
|
795
|
+
configPath: options.configPath,
|
|
796
|
+
action: "校验全文复筛",
|
|
797
|
+
});
|
|
798
|
+
return fulltextNodeId;
|
|
799
|
+
}
|
|
800
|
+
export async function createQuality(options) {
|
|
801
|
+
const scaleType = options.scaleType ?? "rob2";
|
|
802
|
+
// 与网站 screening.tsx onNextStep(case 1) 一致:先 check,再进数据抽提。
|
|
803
|
+
const fulltextNodeId = await verifyFulltextBeforeExtraction(options);
|
|
804
|
+
return runExtractionExecute({
|
|
805
|
+
workflowId: options.workflowId,
|
|
806
|
+
nodeType: META_EXTRACTION_NODE_TYPES.QUALITY,
|
|
807
|
+
endpoint: "quality-assessment/execute",
|
|
808
|
+
params: { scale_type: scaleType, fulltext_node_id: fulltextNodeId },
|
|
809
|
+
input: { scale_type: scaleType, fulltext_node_id: fulltextNodeId },
|
|
810
|
+
progressMessage: "正在生成质量评估...",
|
|
811
|
+
format: options.format,
|
|
812
|
+
configPath: options.configPath,
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
export async function listQuality(options) {
|
|
816
|
+
const scaleType = options.scaleType ?? "rob2";
|
|
817
|
+
const nodeId = await resolveNodeId({
|
|
818
|
+
workflowId: options.workflowId,
|
|
819
|
+
nodeId: options.nodeId,
|
|
820
|
+
nodeType: META_EXTRACTION_NODE_TYPES.QUALITY,
|
|
821
|
+
label: "质量评估",
|
|
822
|
+
configPath: options.configPath,
|
|
823
|
+
});
|
|
824
|
+
const data = await requestMeta(`${META_API_PREFIX}/quality-assessment`, {
|
|
825
|
+
params: {
|
|
826
|
+
run_id: options.workflowId,
|
|
827
|
+
node_id: nodeId,
|
|
828
|
+
page: options.page ?? 1,
|
|
829
|
+
limit: options.limit ?? 10,
|
|
830
|
+
scale_type: scaleType,
|
|
831
|
+
},
|
|
832
|
+
configPath: options.configPath,
|
|
833
|
+
action: "查询质量评估结果",
|
|
834
|
+
});
|
|
835
|
+
const table = buildQualityAssessmentTable(data);
|
|
836
|
+
const tableMarkdown = renderListAsMarkdown(table.rows, table.columns);
|
|
837
|
+
return {
|
|
838
|
+
runnerId: options.workflowId,
|
|
839
|
+
nodeId,
|
|
840
|
+
data,
|
|
841
|
+
displayInstruction: "Render the quality assessment result using columns and rows/displayRows as a table. Render every returned row as an actual table row; when 10 rows are returned, all 10 must be visibly rendered. Never collapse or replace rows with '[and N more...]', ellipses, placeholders, summaries, or expandable content. Preserve the defined column order and every field value. " +
|
|
842
|
+
"When the user chooses「编辑内容」, first single-select a record by Study using qualityRecordRefs (keep id internal), then show editable fields with current values, then prompt once for free-text「手动输入」; never offer dimension pickers or suggested values like Low/Some concerns/High.",
|
|
843
|
+
columns: table.columns,
|
|
844
|
+
rows: table.rows,
|
|
845
|
+
displayColumns: table.columns.map((column) => column.label),
|
|
846
|
+
displayRows: table.rows,
|
|
847
|
+
tableMarkdown,
|
|
848
|
+
table,
|
|
849
|
+
qualityRecordRefs: buildQualityRecordRefs(data),
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
export async function updateQuality(options) {
|
|
853
|
+
const scaleType = options.scaleType ?? "rob2";
|
|
854
|
+
const nodeId = await resolveNodeId({
|
|
855
|
+
workflowId: options.workflowId,
|
|
856
|
+
nodeId: options.nodeId,
|
|
857
|
+
nodeType: META_EXTRACTION_NODE_TYPES.QUALITY,
|
|
858
|
+
label: "质量评估",
|
|
859
|
+
configPath: options.configPath,
|
|
860
|
+
});
|
|
861
|
+
const patch = await parseJsonObjectInput(options.dataJson, options.dataFile, "--data-json", "--data-file");
|
|
862
|
+
const recordId = String(patch.id ?? "").trim();
|
|
863
|
+
if (!recordId)
|
|
864
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "编辑质量评估必须提供 id");
|
|
865
|
+
const original = await loadExtractionRecordForUpdate({
|
|
866
|
+
workflowId: options.workflowId,
|
|
867
|
+
nodeId,
|
|
868
|
+
endpoint: "quality-assessment",
|
|
869
|
+
recordId,
|
|
870
|
+
scaleType,
|
|
871
|
+
configPath: options.configPath,
|
|
872
|
+
});
|
|
873
|
+
const input = normalizeQualityInput({ ...original, ...patch });
|
|
874
|
+
const data = await requestMeta(`${META_API_PREFIX}/quality-assessment`, {
|
|
875
|
+
method: "PUT",
|
|
876
|
+
params: {
|
|
877
|
+
run_id: options.workflowId,
|
|
878
|
+
node_id: nodeId,
|
|
879
|
+
scale_type: scaleType,
|
|
880
|
+
},
|
|
881
|
+
body: input,
|
|
882
|
+
configPath: options.configPath,
|
|
883
|
+
action: "更新质量评估",
|
|
884
|
+
});
|
|
885
|
+
return { runnerId: options.workflowId, nodeId, input, data };
|
|
886
|
+
}
|
|
887
|
+
export async function rerunQuality(options) {
|
|
888
|
+
const scaleType = options.scaleType ?? "rob2";
|
|
889
|
+
const nodeId = await resolveNodeId({
|
|
890
|
+
workflowId: options.workflowId,
|
|
891
|
+
nodeId: options.nodeId,
|
|
892
|
+
nodeType: META_EXTRACTION_NODE_TYPES.QUALITY,
|
|
893
|
+
label: "质量评估",
|
|
894
|
+
configPath: options.configPath,
|
|
895
|
+
});
|
|
896
|
+
const ids = await parseJsonStringArrayInput(options.idsJson, options.idsFile, "--ids-json", "--ids-file");
|
|
897
|
+
const input = { output_ids: ids };
|
|
898
|
+
const data = await requestMeta(`${META_API_PREFIX}/quality-assessment/re-execute-on-selected`, {
|
|
899
|
+
method: "POST",
|
|
900
|
+
params: {
|
|
901
|
+
run_id: options.workflowId,
|
|
902
|
+
node_id: nodeId,
|
|
903
|
+
scale_type: scaleType,
|
|
904
|
+
},
|
|
905
|
+
body: input,
|
|
906
|
+
configPath: options.configPath,
|
|
907
|
+
action: "重新质量评估",
|
|
908
|
+
});
|
|
909
|
+
await pollMetaNodeStatus({
|
|
910
|
+
workflowId: options.workflowId,
|
|
911
|
+
nodeId,
|
|
912
|
+
configPath: options.configPath,
|
|
913
|
+
});
|
|
914
|
+
return { runnerId: options.workflowId, nodeId, input, data };
|
|
915
|
+
}
|
|
916
|
+
export async function verifyQuality(options) {
|
|
917
|
+
const nodeId = await resolveNodeId({
|
|
918
|
+
workflowId: options.workflowId,
|
|
919
|
+
nodeId: options.nodeId,
|
|
920
|
+
nodeType: META_EXTRACTION_NODE_TYPES.QUALITY,
|
|
921
|
+
label: "质量评估",
|
|
922
|
+
configPath: options.configPath,
|
|
923
|
+
});
|
|
924
|
+
const data = await requestMeta(`${META_API_PREFIX}/quality-assessment/verify-for-plot`, {
|
|
925
|
+
params: { run_id: options.workflowId, node_id: nodeId },
|
|
926
|
+
configPath: options.configPath,
|
|
927
|
+
action: "校验质量评估绘图数据",
|
|
928
|
+
});
|
|
929
|
+
return { runnerId: options.workflowId, nodeId, data };
|
|
930
|
+
}
|
|
931
|
+
export async function createBaseline(options) {
|
|
932
|
+
const fulltextNodeId = options.fulltextNodeId?.trim() ??
|
|
933
|
+
(await resolveNodeId({
|
|
934
|
+
workflowId: options.workflowId,
|
|
935
|
+
nodeType: META_EXTRACTION_NODE_TYPES.FULLTEXT,
|
|
936
|
+
label: "全文复筛",
|
|
937
|
+
configPath: options.configPath,
|
|
938
|
+
}));
|
|
939
|
+
const result = await runExtractionExecute({
|
|
940
|
+
workflowId: options.workflowId,
|
|
941
|
+
nodeType: META_EXTRACTION_NODE_TYPES.BASELINE,
|
|
942
|
+
endpoint: "baseline-information/execute",
|
|
943
|
+
params: { fulltext_node_id: fulltextNodeId },
|
|
944
|
+
input: { fulltext_node_id: fulltextNodeId },
|
|
945
|
+
progressMessage: "正在生成基线资料...",
|
|
946
|
+
format: options.format,
|
|
947
|
+
configPath: options.configPath,
|
|
948
|
+
});
|
|
949
|
+
const listResult = await listBaseline({
|
|
950
|
+
workflowId: options.workflowId,
|
|
951
|
+
nodeId: result.nodeId,
|
|
952
|
+
page: 1,
|
|
953
|
+
limit: 10,
|
|
954
|
+
configPath: options.configPath,
|
|
955
|
+
});
|
|
956
|
+
return withBaselineTable(result, listResult.table ?? {
|
|
957
|
+
columns: [...BASELINE_INFORMATION_COLUMNS],
|
|
958
|
+
rows: [],
|
|
959
|
+
}, listResult.baselineRecordRefs ?? []);
|
|
960
|
+
}
|
|
961
|
+
export async function listBaseline(options) {
|
|
962
|
+
const nodeId = await resolveNodeId({
|
|
963
|
+
workflowId: options.workflowId,
|
|
964
|
+
nodeId: options.nodeId,
|
|
965
|
+
nodeType: META_EXTRACTION_NODE_TYPES.BASELINE,
|
|
966
|
+
label: "基线资料",
|
|
967
|
+
configPath: options.configPath,
|
|
968
|
+
});
|
|
969
|
+
const data = await requestMeta(`${META_API_PREFIX}/baseline-information`, {
|
|
970
|
+
params: {
|
|
971
|
+
run_id: options.workflowId,
|
|
972
|
+
node_id: nodeId,
|
|
973
|
+
page: options.page,
|
|
974
|
+
limit: options.limit,
|
|
975
|
+
},
|
|
976
|
+
configPath: options.configPath,
|
|
977
|
+
action: "查询基线资料列表",
|
|
978
|
+
});
|
|
979
|
+
return withBaselineTable({ runnerId: options.workflowId, nodeId, data }, buildBaselineInformationTable(data), buildBaselineRecordRefs(data));
|
|
980
|
+
}
|
|
981
|
+
export async function updateBaseline(options) {
|
|
982
|
+
const nodeId = await resolveNodeId({
|
|
983
|
+
workflowId: options.workflowId,
|
|
984
|
+
nodeId: options.nodeId,
|
|
985
|
+
nodeType: META_EXTRACTION_NODE_TYPES.BASELINE,
|
|
986
|
+
label: "基线资料",
|
|
987
|
+
configPath: options.configPath,
|
|
988
|
+
});
|
|
989
|
+
const patch = await parseJsonObjectInput(options.dataJson, options.dataFile, "--data-json", "--data-file");
|
|
990
|
+
const recordId = String(patch.id ?? "").trim();
|
|
991
|
+
if (!recordId)
|
|
992
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "编辑基线资料必须提供 id");
|
|
993
|
+
const original = await loadExtractionRecordForUpdate({
|
|
994
|
+
workflowId: options.workflowId,
|
|
995
|
+
nodeId,
|
|
996
|
+
endpoint: "baseline-information",
|
|
997
|
+
recordId,
|
|
998
|
+
configPath: options.configPath,
|
|
999
|
+
});
|
|
1000
|
+
const input = normalizeBaselineInput({ ...original, ...patch });
|
|
1001
|
+
const data = await requestMeta(`${META_API_PREFIX}/baseline-information`, {
|
|
1002
|
+
method: "PUT",
|
|
1003
|
+
params: { run_id: options.workflowId, node_id: nodeId },
|
|
1004
|
+
body: input,
|
|
1005
|
+
configPath: options.configPath,
|
|
1006
|
+
action: "更新基线资料",
|
|
1007
|
+
});
|
|
1008
|
+
return { runnerId: options.workflowId, nodeId, input, data };
|
|
1009
|
+
}
|
|
1010
|
+
export async function rerunBaseline(options) {
|
|
1011
|
+
const nodeId = await resolveNodeId({
|
|
1012
|
+
workflowId: options.workflowId,
|
|
1013
|
+
nodeId: options.nodeId,
|
|
1014
|
+
nodeType: META_EXTRACTION_NODE_TYPES.BASELINE,
|
|
1015
|
+
label: "基线资料",
|
|
1016
|
+
configPath: options.configPath,
|
|
1017
|
+
});
|
|
1018
|
+
const ids = await parseJsonStringArrayInput(options.idsJson, options.idsFile, "--ids-json", "--ids-file");
|
|
1019
|
+
const input = { output_ids: ids };
|
|
1020
|
+
const data = await requestMeta(`${META_API_PREFIX}/baseline-information/re-execute-on-selected`, {
|
|
1021
|
+
method: "POST",
|
|
1022
|
+
params: { run_id: options.workflowId, node_id: nodeId },
|
|
1023
|
+
body: input,
|
|
1024
|
+
configPath: options.configPath,
|
|
1025
|
+
action: "重新抽提基线资料",
|
|
1026
|
+
});
|
|
1027
|
+
await pollMetaNodeStatus({
|
|
1028
|
+
workflowId: options.workflowId,
|
|
1029
|
+
nodeId,
|
|
1030
|
+
configPath: options.configPath,
|
|
1031
|
+
});
|
|
1032
|
+
return { runnerId: options.workflowId, nodeId, input, data };
|
|
1033
|
+
}
|
|
1034
|
+
export async function createOutcome(options) {
|
|
1035
|
+
const fulltextNodeId = options.fulltextNodeId?.trim() ??
|
|
1036
|
+
(await resolveNodeId({
|
|
1037
|
+
workflowId: options.workflowId,
|
|
1038
|
+
nodeType: META_EXTRACTION_NODE_TYPES.FULLTEXT,
|
|
1039
|
+
label: "全文复筛",
|
|
1040
|
+
configPath: options.configPath,
|
|
1041
|
+
}));
|
|
1042
|
+
const result = await runExtractionExecute({
|
|
1043
|
+
workflowId: options.workflowId,
|
|
1044
|
+
nodeType: META_EXTRACTION_NODE_TYPES.OUTCOME,
|
|
1045
|
+
endpoint: "outcome-indicator-extraction/execute",
|
|
1046
|
+
params: { fulltext_node_id: fulltextNodeId },
|
|
1047
|
+
input: { fulltext_node_id: fulltextNodeId },
|
|
1048
|
+
progressMessage: "正在生成结局指标...",
|
|
1049
|
+
format: options.format,
|
|
1050
|
+
configPath: options.configPath,
|
|
1051
|
+
});
|
|
1052
|
+
const listResult = await listOutcome({
|
|
1053
|
+
workflowId: options.workflowId,
|
|
1054
|
+
nodeId: result.nodeId,
|
|
1055
|
+
page: 1,
|
|
1056
|
+
limit: 10,
|
|
1057
|
+
configPath: options.configPath,
|
|
1058
|
+
});
|
|
1059
|
+
return withOutcomeTables(result, listResult.table ?? { columns: [...OUTCOME_PARENT_COLUMNS], rows: [] }, listResult.detailTables ?? [], listResult.outcomeRecordRefs, listResult.outcomeParentSelectionOptions, listResult.outcomeChildSelectionOptions);
|
|
1060
|
+
}
|
|
1061
|
+
export async function listOutcome(options) {
|
|
1062
|
+
const nodeId = await resolveNodeId({
|
|
1063
|
+
workflowId: options.workflowId,
|
|
1064
|
+
nodeId: options.nodeId,
|
|
1065
|
+
nodeType: META_EXTRACTION_NODE_TYPES.OUTCOME,
|
|
1066
|
+
label: "结局指标",
|
|
1067
|
+
configPath: options.configPath,
|
|
1068
|
+
});
|
|
1069
|
+
const data = await requestMeta(`${META_API_PREFIX}/outcome-indicator-extraction`, {
|
|
1070
|
+
params: {
|
|
1071
|
+
run_id: options.workflowId,
|
|
1072
|
+
node_id: nodeId,
|
|
1073
|
+
page: options.page ?? 1,
|
|
1074
|
+
limit: options.limit ?? 10,
|
|
1075
|
+
},
|
|
1076
|
+
configPath: options.configPath,
|
|
1077
|
+
action: "查询结局指标",
|
|
1078
|
+
});
|
|
1079
|
+
const outcomeRecordRefs = buildOutcomeRecordRefs(data);
|
|
1080
|
+
return withOutcomeTables({ runnerId: options.workflowId, nodeId, data }, buildOutcomeParentTable(data), buildOutcomeDetailTables(data), outcomeRecordRefs, buildOutcomeParentSelectionOptions(data), buildOutcomeChildSelectionOptions(outcomeRecordRefs));
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* 输出结局指标结果:JSON 只返回 artifact 路径(完整表落盘),避免 Shell 截断超长 stdout。
|
|
1084
|
+
*
|
|
1085
|
+
* @param result 结局 create/list 结果。
|
|
1086
|
+
* @param format 输出格式。
|
|
1087
|
+
*/
|
|
1088
|
+
async function printOutcomeResult(result, format) {
|
|
1089
|
+
const tableMarkdown = result.tableMarkdown ?? "";
|
|
1090
|
+
if (format === "json") {
|
|
1091
|
+
const artifact = await writeOutcomeTableArtifact(tableMarkdown);
|
|
1092
|
+
// artifact 置前;故意不内嵌 tableMarkdown,防止宿主 Shell 截断后 Agent 改摘要
|
|
1093
|
+
printOutput({
|
|
1094
|
+
displayInstruction: result.displayInstruction ?? OUTCOME_DISPLAY_INSTRUCTION,
|
|
1095
|
+
// 自检:回复必须同时含父表 + 全部研究子表;仅父表 = 交付失败
|
|
1096
|
+
displayRequires: [
|
|
1097
|
+
"Read artifact.output 全文",
|
|
1098
|
+
"原样粘贴父表(序号/Study/Pmid)",
|
|
1099
|
+
"原样粘贴每个研究下的子表(含 Outcome Name 等 11 列)",
|
|
1100
|
+
],
|
|
1101
|
+
artifact,
|
|
1102
|
+
outcomeRecordRefs: result.outcomeRecordRefs,
|
|
1103
|
+
outcomeParentSelectionOptions: result.outcomeParentSelectionOptions,
|
|
1104
|
+
outcomeChildSelectionOptions: result.outcomeChildSelectionOptions,
|
|
1105
|
+
// 子表结构化数据:编辑时按 parentIndex 取单表展示(含序号);列表展示仍以 artifact 为准
|
|
1106
|
+
detailTables: result.detailTables,
|
|
1107
|
+
runnerId: result.runnerId,
|
|
1108
|
+
nodeId: result.nodeId,
|
|
1109
|
+
...(result && "nodeType" in result ? { nodeType: result.nodeType, status: result.status } : {}),
|
|
1110
|
+
}, "json");
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
console.log(tableMarkdown);
|
|
1114
|
+
}
|
|
1115
|
+
/** 下载质量评估/结局指标最终产物。 */
|
|
1116
|
+
export async function exportFinalExtractionMarkdownArtifacts(options) {
|
|
1117
|
+
const qualityNodeId = await resolveNodeId({
|
|
1118
|
+
workflowId: options.workflowId,
|
|
1119
|
+
nodeType: META_EXTRACTION_NODE_TYPES.QUALITY,
|
|
1120
|
+
label: "质量评估",
|
|
1121
|
+
configPath: options.configPath,
|
|
1122
|
+
});
|
|
1123
|
+
const outcomeNodeId = await resolveNodeId({
|
|
1124
|
+
workflowId: options.workflowId,
|
|
1125
|
+
nodeType: META_EXTRACTION_NODE_TYPES.OUTCOME,
|
|
1126
|
+
label: "结局指标",
|
|
1127
|
+
configPath: options.configPath,
|
|
1128
|
+
});
|
|
1129
|
+
return Promise.all([
|
|
1130
|
+
downloadExtractionExportArtifact({
|
|
1131
|
+
workflowId: options.workflowId,
|
|
1132
|
+
nodeId: qualityNodeId,
|
|
1133
|
+
endpoint: "quality-assessment/plot-data/export",
|
|
1134
|
+
filename: "质量评估.xlsx",
|
|
1135
|
+
action: "下载质量评估结果",
|
|
1136
|
+
outputDirectory: options.output,
|
|
1137
|
+
}),
|
|
1138
|
+
downloadExtractionExportArtifact({
|
|
1139
|
+
workflowId: options.workflowId,
|
|
1140
|
+
nodeId: outcomeNodeId,
|
|
1141
|
+
endpoint: "outcome-indicator-extraction/plot-data/export",
|
|
1142
|
+
filename: "结局指标.xlsx",
|
|
1143
|
+
action: "下载结局指标结果",
|
|
1144
|
+
outputDirectory: options.output,
|
|
1145
|
+
}),
|
|
1146
|
+
]);
|
|
1147
|
+
}
|
|
1148
|
+
export async function updateOutcome(options) {
|
|
1149
|
+
const nodeId = await resolveNodeId({
|
|
1150
|
+
workflowId: options.workflowId,
|
|
1151
|
+
nodeId: options.nodeId,
|
|
1152
|
+
nodeType: META_EXTRACTION_NODE_TYPES.OUTCOME,
|
|
1153
|
+
label: "结局指标",
|
|
1154
|
+
configPath: options.configPath,
|
|
1155
|
+
});
|
|
1156
|
+
const patch = await parseJsonObjectInput(options.dataJson, options.dataFile, "--data-json", "--data-file");
|
|
1157
|
+
const recordId = String(patch.id ?? "").trim();
|
|
1158
|
+
if (!recordId)
|
|
1159
|
+
throw new helixlifeError(helixlifeErrorCode.INVALID_ARGUMENT, "编辑结局指标必须提供 id");
|
|
1160
|
+
const original = await loadExtractionRecordForUpdate({
|
|
1161
|
+
workflowId: options.workflowId,
|
|
1162
|
+
nodeId,
|
|
1163
|
+
endpoint: "outcome-indicator-extraction",
|
|
1164
|
+
recordId,
|
|
1165
|
+
nestedOutcomes: true,
|
|
1166
|
+
configPath: options.configPath,
|
|
1167
|
+
});
|
|
1168
|
+
const input = normalizeOutcomeInput({ ...original, ...patch });
|
|
1169
|
+
const data = await requestMeta(`${META_API_PREFIX}/outcome-indicator-extraction`, {
|
|
1170
|
+
method: "PUT",
|
|
1171
|
+
params: { run_id: options.workflowId, node_id: nodeId },
|
|
1172
|
+
body: input,
|
|
1173
|
+
configPath: options.configPath,
|
|
1174
|
+
action: "更新结局指标",
|
|
1175
|
+
});
|
|
1176
|
+
return { runnerId: options.workflowId, nodeId, input, data };
|
|
1177
|
+
}
|
|
1178
|
+
export async function deleteOutcome(options) {
|
|
1179
|
+
const data = await requestMeta(`${META_API_PREFIX}/outcome-indicator-extraction/${encodeURIComponent(options.outcomeId)}`, {
|
|
1180
|
+
method: "DELETE",
|
|
1181
|
+
configPath: options.configPath,
|
|
1182
|
+
action: "删除结局指标",
|
|
1183
|
+
});
|
|
1184
|
+
return { runnerId: options.workflowId, data };
|
|
1185
|
+
}
|
|
1186
|
+
export async function verifyOutcome(options) {
|
|
1187
|
+
const nodeId = await resolveNodeId({
|
|
1188
|
+
workflowId: options.workflowId,
|
|
1189
|
+
nodeId: options.nodeId,
|
|
1190
|
+
nodeType: META_EXTRACTION_NODE_TYPES.OUTCOME,
|
|
1191
|
+
label: "结局指标",
|
|
1192
|
+
configPath: options.configPath,
|
|
1193
|
+
});
|
|
1194
|
+
const data = await requestMeta(`${META_API_PREFIX}/outcome-indicator-extraction/verify-for-plot`, {
|
|
1195
|
+
params: { run_id: options.workflowId, node_id: nodeId },
|
|
1196
|
+
configPath: options.configPath,
|
|
1197
|
+
action: "校验结局指标绘图数据",
|
|
1198
|
+
});
|
|
1199
|
+
return { runnerId: options.workflowId, nodeId, data };
|
|
1200
|
+
}
|
|
1201
|
+
export async function runCreateQuality(options) {
|
|
1202
|
+
printStepResult(await createQuality(options), options.format ?? "markdown");
|
|
1203
|
+
}
|
|
1204
|
+
export async function runListQuality(options) {
|
|
1205
|
+
const result = await listQuality(options);
|
|
1206
|
+
if ((options.format ?? "markdown") !== "json" && result.table) {
|
|
1207
|
+
console.log(renderListAsMarkdown(result.table.rows, result.table.columns));
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
printOutput(result, options.format ?? "markdown");
|
|
1211
|
+
}
|
|
1212
|
+
export async function runUpdateQuality(options) {
|
|
1213
|
+
printOutput(await updateQuality(options), options.format ?? "markdown");
|
|
1214
|
+
}
|
|
1215
|
+
export async function runRerunQuality(options) {
|
|
1216
|
+
printOutput(await rerunQuality(options), options.format ?? "markdown");
|
|
1217
|
+
}
|
|
1218
|
+
export async function runVerifyQuality(options) {
|
|
1219
|
+
printOutput(await verifyQuality(options), options.format ?? "markdown");
|
|
1220
|
+
}
|
|
1221
|
+
export async function runCreateBaseline(options) {
|
|
1222
|
+
printBaselineResult(await createBaseline(options), options.format ?? "markdown");
|
|
1223
|
+
}
|
|
1224
|
+
export async function runListBaseline(options) {
|
|
1225
|
+
printBaselineResult(await listBaseline(options), options.format ?? "markdown");
|
|
1226
|
+
}
|
|
1227
|
+
export async function runUpdateBaseline(options) {
|
|
1228
|
+
printOutput(await updateBaseline(options), options.format ?? "markdown");
|
|
1229
|
+
}
|
|
1230
|
+
export async function runRerunBaseline(options) {
|
|
1231
|
+
printOutput(await rerunBaseline(options), options.format ?? "markdown");
|
|
1232
|
+
}
|
|
1233
|
+
export async function runCreateOutcome(options) {
|
|
1234
|
+
await printOutcomeResult(await createOutcome(options), options.format ?? "markdown");
|
|
1235
|
+
}
|
|
1236
|
+
export async function runListOutcome(options) {
|
|
1237
|
+
await printOutcomeResult(await listOutcome(options), options.format ?? "markdown");
|
|
1238
|
+
}
|
|
1239
|
+
export async function runUpdateOutcome(options) {
|
|
1240
|
+
printOutput(await updateOutcome(options), options.format ?? "markdown");
|
|
1241
|
+
}
|
|
1242
|
+
export async function runDeleteOutcome(options) {
|
|
1243
|
+
printOutput(await deleteOutcome(options), options.format ?? "markdown");
|
|
1244
|
+
}
|
|
1245
|
+
export async function runVerifyOutcome(options) {
|
|
1246
|
+
printOutput(await verifyOutcome(options), options.format ?? "markdown");
|
|
1247
|
+
}
|
|
1248
|
+
//# sourceMappingURL=extraction.js.map
|