niceeval 0.13.2 → 0.13.3
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/dist/agents/bub.cjs +4 -4
- package/dist/agents/bub.cjs.map +1 -1
- package/dist/agents/bub.d.cts +2 -2
- package/dist/agents/bub.d.mts +2 -2
- package/dist/agents/bub.d.ts +2 -2
- package/dist/agents/claude-code-native.cjs +213 -0
- package/dist/agents/claude-code-native.cjs.map +1 -0
- package/dist/agents/claude-code-native.d.cts +5 -0
- package/dist/agents/claude-code-native.d.mts +5 -0
- package/dist/agents/claude-code-native.d.ts +5 -0
- package/dist/agents/claude-code-native.js +6 -0
- package/dist/agents/claude-code-native.js.map +1 -0
- package/dist/agents/claude-code.cjs +26 -80
- package/dist/agents/claude-code.cjs.map +1 -1
- package/dist/agents/codex-app-server.cjs +314 -0
- package/dist/agents/codex-app-server.cjs.map +1 -0
- package/dist/agents/codex-app-server.d.cts +9 -0
- package/dist/agents/codex-app-server.d.mts +9 -0
- package/dist/agents/codex-app-server.d.ts +9 -0
- package/dist/agents/codex-app-server.js +6 -0
- package/dist/agents/codex-app-server.js.map +1 -0
- package/dist/agents/codex.cjs +19 -68
- package/dist/agents/codex.cjs.map +1 -1
- package/dist/agents/codex.d.cts +3 -3
- package/dist/agents/codex.d.mts +3 -3
- package/dist/agents/codex.d.ts +3 -3
- package/dist/agents/index.cjs +3 -1
- package/dist/agents/index.cjs.map +1 -1
- package/dist/agents/index.d.cts +1 -0
- package/dist/agents/index.d.mts +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.js +84 -82
- package/dist/agents/index.mjs +84 -82
- package/dist/agents/managed-jsonl.cjs +207 -0
- package/dist/agents/managed-jsonl.cjs.map +1 -0
- package/dist/agents/managed-jsonl.d.cts +39 -0
- package/dist/agents/managed-jsonl.d.mts +39 -0
- package/dist/agents/managed-jsonl.d.ts +39 -0
- package/dist/agents/managed-jsonl.js +6 -0
- package/dist/agents/managed-jsonl.js.map +1 -0
- package/dist/agents/managed-process.cjs +31 -0
- package/dist/agents/managed-process.cjs.map +1 -0
- package/dist/agents/managed-process.d.cts +3 -0
- package/dist/agents/managed-process.d.mts +3 -0
- package/dist/agents/managed-process.d.ts +3 -0
- package/dist/agents/managed-process.js +6 -0
- package/dist/agents/managed-process.js.map +1 -0
- package/dist/agents/omp.cjs +18 -1
- package/dist/agents/omp.cjs.map +1 -1
- package/dist/agents/sdk-streams.cjs +11 -4
- package/dist/agents/sdk-streams.cjs.map +1 -1
- package/dist/agents/types.cjs.map +1 -1
- package/dist/agents/types.d.cts +8 -0
- package/dist/agents/types.d.mts +8 -0
- package/dist/agents/types.d.ts +8 -0
- package/dist/analysis/bindings.cjs +74 -19
- package/dist/analysis/bindings.cjs.map +1 -1
- package/dist/analysis/bindings.d.cts +534 -156
- package/dist/analysis/bindings.d.mts +534 -156
- package/dist/analysis/bindings.d.ts +534 -156
- package/dist/analysis/domain-view.cjs.map +1 -1
- package/dist/analysis/domain-view.d.cts +137 -8
- package/dist/analysis/domain-view.d.mts +137 -8
- package/dist/analysis/domain-view.d.ts +137 -8
- package/dist/analysis/index.cjs.map +1 -1
- package/dist/analysis/index.d.cts +1 -1
- package/dist/analysis/index.d.mts +1 -1
- package/dist/analysis/index.d.ts +1 -1
- package/dist/assertions/api.cjs.map +1 -1
- package/dist/assertions/api.d.cts +62 -0
- package/dist/assertions/api.d.mts +62 -0
- package/dist/assertions/api.d.ts +62 -0
- package/dist/assertions/judge.cjs +56 -5
- package/dist/assertions/judge.cjs.map +1 -1
- package/dist/assertions/match.cjs +66 -42
- package/dist/assertions/match.cjs.map +1 -1
- package/dist/assertions/match.d.cts +13 -0
- package/dist/assertions/match.d.mts +13 -0
- package/dist/assertions/match.d.ts +13 -0
- package/dist/assertions/record/attachment.cjs +104 -33
- package/dist/assertions/record/attachment.cjs.map +1 -1
- package/dist/assertions/record/attachment.d.cts +294 -57
- package/dist/assertions/record/attachment.d.mts +294 -57
- package/dist/assertions/record/attachment.d.ts +294 -57
- package/dist/assertions/record/codec.cjs +91 -17
- package/dist/assertions/record/codec.cjs.map +1 -1
- package/dist/assertions/record/codec.d.cts +280 -52
- package/dist/assertions/record/codec.d.mts +280 -52
- package/dist/assertions/record/codec.d.ts +280 -52
- package/dist/assertions/record/index.js +2 -0
- package/dist/assertions/record/model.cjs +16 -0
- package/dist/assertions/record/model.cjs.map +1 -1
- package/dist/assertions/record/model.d.cts +120 -13
- package/dist/assertions/record/model.d.mts +120 -13
- package/dist/assertions/record/model.d.ts +120 -13
- package/dist/assertions/record/model.js +2 -0
- package/dist/assertions/record/producer.cjs +70 -50
- package/dist/assertions/record/producer.cjs.map +1 -1
- package/dist/assertions/record/producer.d.cts +8 -7
- package/dist/assertions/record/producer.d.mts +8 -7
- package/dist/assertions/record/producer.d.ts +8 -7
- package/dist/assertions/runtime.cjs +49 -7
- package/dist/assertions/runtime.cjs.map +1 -1
- package/dist/cli/application.cjs +40 -0
- package/dist/cli/application.cjs.map +1 -0
- package/dist/cli/application.d.cts +129 -0
- package/dist/cli/application.d.mts +129 -0
- package/dist/cli/application.d.ts +129 -0
- package/dist/cli/application.js +24 -0
- package/dist/cli/application.js.map +1 -0
- package/dist/cli/bootstrap.cjs +10 -4
- package/dist/cli/bootstrap.cjs.map +1 -1
- package/dist/cli/node-application.cjs +123 -0
- package/dist/cli/node-application.cjs.map +1 -0
- package/dist/cli/node-application.d.cts +14 -0
- package/dist/cli/node-application.d.mts +14 -0
- package/dist/cli/node-application.d.ts +14 -0
- package/dist/cli/node-application.js +26 -0
- package/dist/cli/node-application.js.map +1 -0
- package/dist/cli/program.cjs +2807 -0
- package/dist/cli/program.cjs.map +1 -0
- package/dist/cli/program.d.cts +352 -0
- package/dist/cli/program.d.mts +352 -0
- package/dist/cli/program.d.ts +352 -0
- package/dist/cli/program.js +36 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/project-configuration.cjs +134 -0
- package/dist/cli/project-configuration.cjs.map +1 -0
- package/dist/cli/project-configuration.d.cts +64 -0
- package/dist/cli/project-configuration.d.mts +64 -0
- package/dist/cli/project-configuration.d.ts +64 -0
- package/dist/cli/project-configuration.js +22 -0
- package/dist/cli/project-configuration.js.map +1 -0
- package/dist/cli/record.cjs +20 -4
- package/dist/cli/record.cjs.map +1 -1
- package/dist/cli/record.d.cts +3 -3
- package/dist/cli/record.d.mts +3 -3
- package/dist/cli/record.d.ts +3 -3
- package/dist/cli.cjs +19 -3130
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -148
- package/dist/cli.d.mts +2 -148
- package/dist/cli.d.ts +2 -148
- package/dist/cli.js +22 -30
- package/dist/context/assert-first.cjs +21 -15
- package/dist/context/assert-first.cjs.map +1 -1
- package/dist/context/assert-first.d.cts +1 -0
- package/dist/context/assert-first.d.mts +1 -0
- package/dist/context/assert-first.d.ts +1 -0
- package/dist/context/attempt-resources.cjs +13 -0
- package/dist/context/attempt-resources.cjs.map +1 -0
- package/dist/context/attempt-resources.d.cts +3 -0
- package/dist/context/attempt-resources.d.mts +3 -0
- package/dist/context/attempt-resources.d.ts +3 -0
- package/dist/context/attempt-resources.js +8 -0
- package/dist/context/attempt-resources.js.map +1 -0
- package/dist/context/session.cjs +4 -3
- package/dist/context/session.cjs.map +1 -1
- package/dist/context/session.d.cts +1 -0
- package/dist/context/session.d.mts +1 -0
- package/dist/context/session.d.ts +1 -0
- package/dist/eval/record/verdict.cjs +3 -2
- package/dist/eval/record/verdict.cjs.map +1 -1
- package/dist/experiment/host/index.cjs +1 -6
- package/dist/experiment/host/index.cjs.map +1 -1
- package/dist/experiment/host/index.d.cts +1 -0
- package/dist/experiment/host/index.d.mts +1 -0
- package/dist/experiment/host/index.d.ts +1 -0
- package/dist/load-config.cjs +13 -4
- package/dist/load-config.cjs.map +1 -1
- package/dist/load-config.d.cts +4 -3
- package/dist/load-config.d.mts +4 -3
- package/dist/load-config.d.ts +4 -3
- package/dist/load-config.js +4 -2
- package/dist/o11y/parsers/codex.cjs +29 -7
- package/dist/o11y/parsers/codex.cjs.map +1 -1
- package/dist/record/codec/core.d.cts +0 -2
- package/dist/record/codec/core.d.mts +0 -2
- package/dist/record/codec/core.d.ts +0 -2
- package/dist/record/codec/identifiers.cjs +2 -3
- package/dist/record/codec/identifiers.cjs.map +1 -1
- package/dist/record/codec/identifiers.d.cts +1 -2
- package/dist/record/codec/identifiers.d.mts +1 -2
- package/dist/record/codec/identifiers.d.ts +1 -2
- package/dist/record/codec/identifiers.js +10 -12
- package/dist/record/codec/index.js +42 -44
- package/dist/record/definition/attachment.cjs +1 -0
- package/dist/record/definition/attachment.cjs.map +1 -1
- package/dist/record/definition/attachment.d.cts +14 -0
- package/dist/record/definition/attachment.d.mts +14 -0
- package/dist/record/definition/attachment.d.ts +14 -0
- package/dist/record/family/assertions-v1.cjs +104 -0
- package/dist/record/family/assertions-v1.cjs.map +1 -0
- package/dist/record/family/assertions-v1.d.cts +2 -0
- package/dist/record/family/assertions-v1.d.mts +2 -0
- package/dist/record/family/assertions-v1.d.ts +2 -0
- package/dist/record/family/assertions-v1.js +6 -0
- package/dist/record/family/assertions-v1.js.map +1 -0
- package/dist/record/family/assertions.cjs +16 -5
- package/dist/record/family/assertions.cjs.map +1 -1
- package/dist/record/family/assertions.d.cts +510 -56
- package/dist/record/family/assertions.d.mts +510 -56
- package/dist/record/family/assertions.d.ts +510 -56
- package/dist/record/family/assertions.js +14 -10
- package/dist/record/family/catalog.cjs +17 -1
- package/dist/record/family/catalog.cjs.map +1 -1
- package/dist/record/family/catalog.d.cts +1292 -104
- package/dist/record/family/catalog.d.mts +1292 -104
- package/dist/record/family/catalog.d.ts +1292 -104
- package/dist/record/family/catalog.js +22 -20
- package/dist/record/family/index.js +144 -138
- package/dist/record/family/observability-v1.cjs +5 -0
- package/dist/record/family/observability-v1.cjs.map +1 -1
- package/dist/record/family/observability.cjs +1 -1
- package/dist/record/family/observability.cjs.map +1 -1
- package/dist/record/host/runtime.cjs +323 -173
- package/dist/record/host/runtime.cjs.map +1 -1
- package/dist/record/host/types.cjs.map +1 -1
- package/dist/record/host/types.d.cts +9 -7
- package/dist/record/host/types.d.mts +9 -7
- package/dist/record/host/types.d.ts +9 -7
- package/dist/record/model/core.cjs.map +1 -1
- package/dist/record/model/core.d.cts +1 -2
- package/dist/record/model/core.d.mts +1 -2
- package/dist/record/model/core.d.ts +1 -2
- package/dist/record/model/definition.cjs +1 -2
- package/dist/record/model/definition.cjs.map +1 -1
- package/dist/record/model/definition.d.cts +1 -7
- package/dist/record/model/definition.d.mts +1 -7
- package/dist/record/model/definition.d.ts +1 -7
- package/dist/record/model/identifiers.cjs +2 -3
- package/dist/record/model/identifiers.cjs.map +1 -1
- package/dist/record/model/identifiers.d.cts +1 -3
- package/dist/record/model/identifiers.d.mts +1 -3
- package/dist/record/model/identifiers.d.ts +1 -3
- package/dist/record/model/identifiers.js +36 -38
- package/dist/record/model/index.js +60 -62
- package/dist/record/platform/node.cjs +17 -7
- package/dist/record/platform/node.cjs.map +1 -1
- package/dist/record/platform/services.cjs.map +1 -1
- package/dist/record/platform/services.d.cts +8 -3
- package/dist/record/platform/services.d.mts +8 -3
- package/dist/record/platform/services.d.ts +8 -3
- package/dist/record/reader/format.cjs +16 -65
- package/dist/record/reader/format.cjs.map +1 -1
- package/dist/record/reader/format.d.cts +5 -9
- package/dist/record/reader/format.d.mts +5 -9
- package/dist/record/reader/format.d.ts +5 -9
- package/dist/record/reader/index.js +10 -8
- package/dist/record/reader/runtime.cjs +63 -32
- package/dist/record/reader/runtime.cjs.map +1 -1
- package/dist/record/reader/runtime.d.cts +40 -4
- package/dist/record/reader/runtime.d.mts +40 -4
- package/dist/record/reader/runtime.d.ts +40 -4
- package/dist/record/reader/runtime.js +6 -4
- package/dist/report/assets/enhance.js +5 -5
- package/dist/report/assets/styles.css +190 -24
- package/dist/report/built-in/result-components.cjs +8 -5
- package/dist/report/built-in/result-components.cjs.map +1 -1
- package/dist/report/built-in/standard.cjs +3 -0
- package/dist/report/built-in/standard.cjs.map +1 -1
- package/dist/report/built-in/standard.d.cts +2 -0
- package/dist/report/built-in/standard.d.mts +2 -0
- package/dist/report/built-in/standard.d.ts +2 -0
- package/dist/report/client-dist/app.js +53 -0
- package/dist/report/components/attempt-detail/compute.cjs +108 -231
- package/dist/report/components/attempt-detail/compute.cjs.map +1 -1
- package/dist/report/components/attempt-detail/compute.d.cts +11 -45
- package/dist/report/components/attempt-detail/compute.d.mts +11 -45
- package/dist/report/components/attempt-detail/compute.d.ts +11 -45
- package/dist/report/components/attempt-detail/compute.js +2 -4
- package/dist/report/components/attempt-detail/content.cjs +4 -4
- package/dist/report/components/attempt-detail/content.cjs.map +1 -1
- package/dist/report/components/attempt-detail/validate.cjs +47 -4
- package/dist/report/components/attempt-detail/validate.cjs.map +1 -1
- package/dist/report/components/entity-lists/compute.cjs +9 -15
- package/dist/report/components/entity-lists/compute.cjs.map +1 -1
- package/dist/report/components/entity-lists/content.cjs +53 -40
- package/dist/report/components/entity-lists/content.cjs.map +1 -1
- package/dist/report/components/entity-lists/index.cjs +1 -3
- package/dist/report/components/entity-lists/index.cjs.map +1 -1
- package/dist/report/components/experiment-detail/compute.cjs +4 -6
- package/dist/report/components/experiment-detail/compute.cjs.map +1 -1
- package/dist/report/components/summaries/index.cjs +5 -1
- package/dist/report/components/summaries/index.cjs.map +1 -1
- package/dist/report/definition/cell.cjs +14 -7
- package/dist/report/definition/cell.cjs.map +1 -1
- package/dist/report/definition/cell.d.cts +4 -0
- package/dist/report/definition/cell.d.mts +4 -0
- package/dist/report/definition/cell.d.ts +4 -0
- package/dist/report/definition/primitives/assertion-evidence.cjs +96 -0
- package/dist/report/definition/primitives/assertion-evidence.cjs.map +1 -0
- package/dist/report/definition/primitives/assertion-evidence.d.cts +11 -0
- package/dist/report/definition/primitives/assertion-evidence.d.mts +11 -0
- package/dist/report/definition/primitives/assertion-evidence.d.ts +11 -0
- package/dist/report/definition/primitives/assertion-evidence.js +6 -0
- package/dist/report/definition/primitives/assertion-evidence.js.map +1 -0
- package/dist/report/definition/primitives/conversation.cjs +7 -0
- package/dist/report/definition/primitives/conversation.cjs.map +1 -1
- package/dist/report/definition/primitives/conversation.d.cts +2 -0
- package/dist/report/definition/primitives/conversation.d.mts +2 -0
- package/dist/report/definition/primitives/conversation.d.ts +2 -0
- package/dist/report/definition/primitives/turn-trace.cjs +53 -3
- package/dist/report/definition/primitives/turn-trace.cjs.map +1 -1
- package/dist/report/definition/primitives.cjs +14 -1
- package/dist/report/definition/primitives.cjs.map +1 -1
- package/dist/report/definition/report.cjs +16 -8
- package/dist/report/definition/report.cjs.map +1 -1
- package/dist/report/definition/report.d.cts +6 -0
- package/dist/report/definition/report.d.mts +6 -0
- package/dist/report/definition/report.d.ts +6 -0
- package/dist/report/definition/table-text.cjs +12 -3
- package/dist/report/definition/table-text.cjs.map +1 -1
- package/dist/report/definition/table-text.d.cts +6 -1
- package/dist/report/definition/table-text.d.mts +6 -1
- package/dist/report/definition/table-text.d.ts +6 -1
- package/dist/report/execution/model.cjs +6 -14
- package/dist/report/execution/model.cjs.map +1 -1
- package/dist/report/execution/model.d.cts +5 -10
- package/dist/report/execution/model.d.mts +5 -10
- package/dist/report/execution/model.d.ts +5 -10
- package/dist/report/execution/model.js +6 -6
- package/dist/report/execution/paths.cjs +0 -11
- package/dist/report/execution/paths.cjs.map +1 -1
- package/dist/report/execution/paths.d.cts +0 -1
- package/dist/report/execution/paths.d.mts +0 -1
- package/dist/report/execution/paths.d.ts +0 -1
- package/dist/report/execution/paths.js +6 -8
- package/dist/report/host/execute.cjs +13 -22
- package/dist/report/host/execute.cjs.map +1 -1
- package/dist/report/host/execute.d.cts +1 -4
- package/dist/report/host/execute.d.mts +1 -4
- package/dist/report/host/execute.d.ts +1 -4
- package/dist/report/host/from-record.cjs +3 -0
- package/dist/report/host/from-record.cjs.map +1 -1
- package/dist/report/host/from-record.d.cts +4 -1
- package/dist/report/host/from-record.d.mts +4 -1
- package/dist/report/host/from-record.d.ts +4 -1
- package/dist/report/host/site-assets.cjs +1 -2
- package/dist/report/host/site-assets.cjs.map +1 -1
- package/dist/report/host/site-assets.d.cts +0 -1
- package/dist/report/host/site-assets.d.mts +0 -1
- package/dist/report/host/site-assets.d.ts +0 -1
- package/dist/report/host/site-assets.js +2 -4
- package/dist/report/host/static.cjs +71 -346
- package/dist/report/host/static.cjs.map +1 -1
- package/dist/report/host/static.d.cts +1 -3
- package/dist/report/host/static.d.mts +1 -3
- package/dist/report/host/static.d.ts +1 -3
- package/dist/report/host/static.js +2 -2
- package/dist/report/host/view-session.cjs.map +1 -1
- package/dist/report/host/view-session.d.cts +1 -1
- package/dist/report/host/view-session.d.mts +1 -1
- package/dist/report/host/view-session.d.ts +1 -1
- package/dist/report/model/locale.cjs +1 -1
- package/dist/report/model/locale.cjs.map +1 -1
- package/dist/report/model/locale.d.cts +1 -1
- package/dist/report/model/locale.d.mts +1 -1
- package/dist/report/model/locale.d.ts +1 -1
- package/dist/report/model/pricing.cjs +2 -2
- package/dist/report/model/pricing.cjs.map +1 -1
- package/dist/report/react/enhance.js +5 -5
- package/dist/report/react/styles.css +190 -24
- package/dist/runner/adoption.cjs +4 -7
- package/dist/runner/adoption.cjs.map +1 -1
- package/dist/runner/attempt-resources.cjs +60 -0
- package/dist/runner/attempt-resources.cjs.map +1 -0
- package/dist/runner/attempt-resources.d.cts +13 -0
- package/dist/runner/attempt-resources.d.mts +13 -0
- package/dist/runner/attempt-resources.d.ts +13 -0
- package/dist/runner/attempt-resources.js +6 -0
- package/dist/runner/attempt-resources.js.map +1 -0
- package/dist/runner/attempt.cjs +37 -3
- package/dist/runner/attempt.cjs.map +1 -1
- package/dist/runner/feedback/failure.cjs +77 -2
- package/dist/runner/feedback/failure.cjs.map +1 -1
- package/dist/runner/feedback/failure.d.cts +2 -1
- package/dist/runner/feedback/failure.d.mts +2 -1
- package/dist/runner/feedback/failure.d.ts +2 -1
- package/dist/runner/project-current.cjs +2 -13
- package/dist/runner/project-current.cjs.map +1 -1
- package/dist/runner/project-current.d.cts +4 -2
- package/dist/runner/project-current.d.mts +4 -2
- package/dist/runner/project-current.d.ts +4 -2
- package/dist/runner/reporters/braintrust.cjs +7 -7
- package/dist/runner/reporters/braintrust.cjs.map +1 -1
- package/dist/runner/reuse-readback.cjs +3 -4
- package/dist/runner/reuse-readback.cjs.map +1 -1
- package/dist/runner/run.cjs +5 -2
- package/dist/runner/run.cjs.map +1 -1
- package/dist/sandbox/backend.cjs +32 -2
- package/dist/sandbox/backend.cjs.map +1 -1
- package/dist/sandbox/backend.d.cts +12 -1
- package/dist/sandbox/backend.d.mts +12 -1
- package/dist/sandbox/backend.d.ts +12 -1
- package/dist/sandbox/backend.js +28 -22
- package/dist/sandbox/docker.cjs +140 -0
- package/dist/sandbox/docker.cjs.map +1 -1
- package/dist/sandbox/docker.d.cts +3 -1
- package/dist/sandbox/docker.d.mts +3 -1
- package/dist/sandbox/docker.d.ts +3 -1
- package/dist/sandbox/e2b.cjs +42 -0
- package/dist/sandbox/e2b.cjs.map +1 -1
- package/dist/sandbox/e2b.d.cts +2 -0
- package/dist/sandbox/e2b.d.mts +2 -0
- package/dist/sandbox/e2b.d.ts +2 -0
- package/dist/sandbox/local.cjs +64 -0
- package/dist/sandbox/local.cjs.map +1 -1
- package/dist/sandbox/local.d.cts +3 -1
- package/dist/sandbox/local.d.mts +3 -1
- package/dist/sandbox/local.d.ts +3 -1
- package/dist/sandbox/managed-process.cjs +49 -0
- package/dist/sandbox/managed-process.cjs.map +1 -0
- package/dist/sandbox/managed-process.d.cts +11 -0
- package/dist/sandbox/managed-process.d.mts +11 -0
- package/dist/sandbox/managed-process.d.ts +11 -0
- package/dist/sandbox/managed-process.js +6 -0
- package/dist/sandbox/managed-process.js.map +1 -0
- package/dist/sandbox/types.cjs.map +1 -1
- package/dist/sandbox/types.d.cts +24 -0
- package/dist/sandbox/types.d.mts +24 -0
- package/dist/sandbox/types.d.ts +24 -0
- package/dist/sandbox/vercel.cjs +1 -0
- package/dist/sandbox/vercel.cjs.map +1 -1
- package/dist/sandbox/vercel.d.cts +1 -0
- package/dist/sandbox/vercel.d.mts +1 -0
- package/dist/sandbox/vercel.d.ts +1 -0
- package/dist/view/server.cjs +2 -37
- package/dist/view/server.cjs.map +1 -1
- package/dist/view/server.d.cts +0 -3
- package/dist/view/server.d.mts +0 -3
- package/dist/view/server.d.ts +0 -3
- package/docs-site/zh/reference/builtin-agents.mdx +7 -7
- package/docs-site/zh/reference/official-adapters.mdx +4 -4
- package/docs-site/zh/reference/report-components.mdx +2 -2
- package/docs-site/zh/tutorials/configuration.mdx +2 -2
- package/docs-site/zh/tutorials/deploy-report-site.mdx +24 -2
- package/docs-site/zh/tutorials/publish-report.mdx +4 -4
- package/docs-site/zh/tutorials/sandbox-agent.mdx +5 -5
- package/docs-site/zh/tutorials/theming.mdx +2 -2
- package/package.json +4 -6
- package/dist/record/migration/index.cjs +0 -5
- package/dist/record/migration/index.cjs.map +0 -1
- package/dist/record/migration/index.d.cts +0 -11
- package/dist/record/migration/index.d.mts +0 -11
- package/dist/record/migration/index.d.ts +0 -11
- package/dist/record/migration/index.js +0 -6
- package/dist/record/migration/index.js.map +0 -1
- package/dist/report/host/site-runtime.cjs +0 -92
- package/dist/report/host/site-runtime.cjs.map +0 -1
- package/dist/report/host/site-runtime.d.cts +0 -9
- package/dist/report/host/site-runtime.d.mts +0 -9
- package/dist/report/host/site-runtime.d.ts +0 -9
- package/dist/report/host/site-runtime.js +0 -10
- package/dist/report/host/site-runtime.js.map +0 -1
- package/dist/view/client-dist/app.css +0 -2
- package/dist/view/client-dist/app.js +0 -49
- package/dist/view/data.cjs +0 -3
- package/dist/view/data.cjs.map +0 -1
- package/dist/view/data.d.cts +0 -9
- package/dist/view/data.d.mts +0 -9
- package/dist/view/data.d.ts +0 -9
- package/dist/view/data.js +0 -4
- package/dist/view/data.js.map +0 -1
- package/dist/view/index.cjs +0 -24
- package/dist/view/index.cjs.map +0 -1
- package/dist/view/index.d.cts +0 -9
- package/dist/view/index.d.mts +0 -9
- package/dist/view/index.d.ts +0 -9
- package/dist/view/index.js +0 -28
- package/dist/view/index.js.map +0 -1
- package/dist/view/report-view-cache.cjs +0 -200
- package/dist/view/report-view-cache.cjs.map +0 -1
- package/dist/view/report-view-cache.d.cts +0 -67
- package/dist/view/report-view-cache.d.mts +0 -67
- package/dist/view/report-view-cache.d.ts +0 -67
- package/dist/view/report-view-cache.js +0 -8
- package/dist/view/report-view-cache.js.map +0 -1
- package/dist/view/shared/types.cjs +0 -3
- package/dist/view/shared/types.cjs.map +0 -1
- package/dist/view/shared/types.d.cts +0 -51
- package/dist/view/shared/types.d.mts +0 -51
- package/dist/view/shared/types.d.ts +0 -51
- package/dist/view/shared/types.js +0 -4
- package/dist/view/shared/types.js.map +0 -1
- package/dist/view/site.cjs +0 -32
- package/dist/view/site.cjs.map +0 -1
- package/dist/view/site.d.cts +0 -24
- package/dist/view/site.d.mts +0 -24
- package/dist/view/site.d.ts +0 -24
- package/dist/view/site.js +0 -8
- package/dist/view/site.js.map +0 -1
- package/dist/view/styles.css +0 -170
- package/dist/view/template.html +0 -17
package/dist/cli.cjs
CHANGED
|
@@ -1,3133 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
exports
|
|
15
|
-
exports.experimentRenameRejectedFromError = experimentRenameRejectedFromError;
|
|
16
|
-
exports.experimentRenameExitCode = experimentRenameExitCode;
|
|
17
|
-
// niceeval CLI 入口。执行 eval 必须以 experiment 为单位;位置参数只在 exp 后筛 eval id 前缀。
|
|
18
|
-
// niceeval check [组|配置] [pattern] 只做发现、选择与 SandboxLayer pure link
|
|
19
|
-
// niceeval exp [组|配置] [pattern] 跑实验
|
|
20
|
-
// niceeval debug <配置> <eval> 只规划一个配对的 Sandbox / Plugin lifecycle
|
|
21
|
-
// niceeval accept @<locator>... 接受多条历史结果并重锚到当前配置
|
|
22
|
-
// niceeval show [selection] 终端渲染固定 Sample 的一个目标 Page
|
|
23
|
-
// niceeval list 只列出发现到的 eval
|
|
24
|
-
// niceeval clean [--record <root>] [--yes] 删除未完成 Run
|
|
25
|
-
// niceeval migrate [--record <root>] [--yes] 显式迁移 Record
|
|
26
|
-
const node_child_process_1 = require("node:child_process");
|
|
27
|
-
const node_crypto_1 = require("node:crypto");
|
|
28
|
-
const node_os_1 = require("node:os");
|
|
29
|
-
const promises_1 = require("node:fs/promises");
|
|
30
|
-
const node_fs_1 = require("node:fs");
|
|
31
|
-
const node_path_1 = require("node:path");
|
|
32
|
-
const node_util_1 = require("node:util");
|
|
33
|
-
const effect_1 = require("effect");
|
|
34
|
-
const attempt_locator_ts_1 = require("./attempt-locator.cjs");
|
|
35
|
-
const aggregate_ts_1 = require("./shared/aggregate.cjs");
|
|
36
|
-
const index_ts_1 = require("./experiment/host/index.cjs");
|
|
37
|
-
const project_current_ts_1 = require("./runner/project-current.cjs");
|
|
38
|
-
const index_ts_2 = require("./record/index.cjs");
|
|
39
|
-
const identifiers_ts_1 = require("./record/codec/identifiers.cjs");
|
|
40
|
-
const attempt_overview_ts_1 = require("./report/built-in/attempt-overview.cjs");
|
|
41
|
-
const run_membership_overview_ts_1 = require("./report/built-in/run-membership-overview.cjs");
|
|
42
|
-
const standard_tsx_1 = require("./report/built-in/standard.cjs");
|
|
43
|
-
const execution_ts_1 = require("./report/built-in/execution.cjs");
|
|
44
|
-
const source_ts_1 = require("./report/built-in/source.cjs");
|
|
45
|
-
const model_ts_1 = require("./report/execution/model.cjs");
|
|
46
|
-
const from_record_ts_1 = require("./report/host/from-record.cjs");
|
|
47
|
-
const execute_ts_1 = require("./report/host/execute.cjs");
|
|
48
|
-
const paths_ts_1 = require("./report/execution/paths.cjs");
|
|
49
|
-
const index_ts_3 = require("./report/host/index.cjs");
|
|
50
|
-
const panel_ts_1 = require("./report/model/panel.cjs");
|
|
51
|
-
const progress_ts_1 = require("./report/host/progress.cjs");
|
|
52
|
-
const static_ts_1 = require("./report/host/static.cjs");
|
|
53
|
-
const node_ts_1 = require("./report/host/node.cjs");
|
|
54
|
-
const record_ts_1 = require("./cli/record.cjs");
|
|
55
|
-
const eval_selection_ts_1 = require("./runner/eval-selection.cjs");
|
|
56
|
-
const registry_ts_1 = require("./sandbox/registry.cjs");
|
|
57
|
-
const cli_commands_ts_1 = require("./sandbox/cli-commands.cjs");
|
|
58
|
-
const link_ts_1 = require("./sandbox/link.cjs");
|
|
59
|
-
const plan_ts_1 = require("./sandbox/plan.cjs");
|
|
60
|
-
const experiment_cleanup_registry_ts_1 = require("./runner/experiment-cleanup-registry.cjs");
|
|
61
|
-
const lock_ts_1 = require("./runner/lock.cjs");
|
|
62
|
-
const shared_state_lease_ts_1 = require("./runner/shared-state-lease.cjs");
|
|
63
|
-
const cleanup_timeout_ts_1 = require("./runner/cleanup-timeout.cjs");
|
|
64
|
-
const report_view_cache_ts_1 = require("./view/report-view-cache.cjs");
|
|
65
|
-
const timeout_ts_1 = require("./runner/timeout.cjs");
|
|
66
|
-
const teardown_registry_ts_1 = require("./runner/teardown-registry.cjs");
|
|
67
|
-
const rename_experiment_ts_1 = require("./runner/rename-experiment.cjs");
|
|
68
|
-
const verdict_ts_1 = require("./shared/verdict.cjs");
|
|
69
|
-
const sandbox_selection_ts_1 = require("./runner/sandbox-selection.cjs");
|
|
70
|
-
const json_ts_1 = require("./runner/reporters/json.cjs");
|
|
71
|
-
const index_ts_4 = require("./runner/feedback/index.cjs");
|
|
72
|
-
const index_ts_5 = require("./i18n/index.cjs");
|
|
73
|
-
const util_ts_1 = require("./util.cjs");
|
|
74
|
-
const session_ts_1 = require("./runner/session.cjs");
|
|
75
|
-
/** A recoverable command-line usage error. Defects deliberately do not enter this channel. */
|
|
76
|
-
class CliUsageError extends effect_1.Data.TaggedError("CliUsageError") {
|
|
77
|
-
}
|
|
78
|
-
exports.CliUsageError = CliUsageError;
|
|
79
|
-
/** A recoverable failure from a concrete CLI-owned boundary (file, process, or dynamic module). */
|
|
80
|
-
class CliOperationError extends effect_1.Data.TaggedError("CliOperationError") {
|
|
81
|
-
}
|
|
82
|
-
exports.CliOperationError = CliOperationError;
|
|
83
|
-
function usageError(message, exitCode = 1) {
|
|
84
|
-
return new CliUsageError({ message, exitCode });
|
|
85
|
-
}
|
|
86
|
-
function cliFailure(operation, cause, exitCode = 1) {
|
|
87
|
-
return cause instanceof CliUsageError || cause instanceof CliOperationError
|
|
88
|
-
? cause
|
|
89
|
-
: new CliOperationError({ operation, cause, exitCode });
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Lift one actual Promise boundary into the application Effect. It is never a
|
|
93
|
-
* private runtime: CLI owns the sole runtime in `cli/bootstrap.ts`.
|
|
94
|
-
*/
|
|
95
|
-
function cliPromise(operation, promise) {
|
|
96
|
-
return effect_1.Effect.tryPromise({
|
|
97
|
-
try: promise,
|
|
98
|
-
catch: (cause) => cliFailure(operation, cause),
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
/** Preserve library effects without running a nested runtime. */
|
|
102
|
-
function cliEffect(operation, effect) {
|
|
103
|
-
return effect.pipe(effect_1.Effect.mapError((cause) => cliFailure(operation, cause)));
|
|
104
|
-
}
|
|
105
|
-
/** Bootstrap owns presentation of typed failures; defects and interruption stay in Cause. */
|
|
106
|
-
function renderCliFailure(failure) {
|
|
107
|
-
if (failure._tag === "CliUsageError")
|
|
108
|
-
return failure.message;
|
|
109
|
-
if (failure.cause instanceof link_ts_1.SandboxLayerLinkError)
|
|
110
|
-
return `${(0, link_ts_1.formatSandboxLayerLinkError)(failure.cause)}\n`;
|
|
111
|
-
if (failure.cause instanceof plan_ts_1.SandboxPhysicalPlanningError)
|
|
112
|
-
return `${(0, plan_ts_1.formatSandboxPhysicalPlanningError)(failure.cause)}\n`;
|
|
113
|
-
const assertionsMessage = runnerAssertionsMessage(failure.cause);
|
|
114
|
-
if (assertionsMessage !== undefined)
|
|
115
|
-
return `error: ${assertionsMessage}\n`;
|
|
116
|
-
if (isReportCliOperation(failure.operation)) {
|
|
117
|
-
const code = failureCode(failure.cause);
|
|
118
|
-
if (code !== undefined) {
|
|
119
|
-
if (failure.cause instanceof node_ts_1.ReportModuleLoadError) {
|
|
120
|
-
return `${code}: ${failure.cause.reason}\n`;
|
|
121
|
-
}
|
|
122
|
-
const reason = stringProperty(failure.cause, "reason");
|
|
123
|
-
const operation = stringProperty(failure.cause, "operation");
|
|
124
|
-
if (reason !== undefined) {
|
|
125
|
-
const stage = operation === undefined ? "" : `${operation}: `;
|
|
126
|
-
const retry = code === "report-view-server-failed" && operation === "listen"
|
|
127
|
-
? "\nChoose another port with --port <port>, or stop the process already listening there."
|
|
128
|
-
: "";
|
|
129
|
-
return `${code}: ${stage}${reason}${retry}\n`;
|
|
130
|
-
}
|
|
131
|
-
return `${code}\n`;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return (0, index_ts_5.t)("cli.error", { error: (0, util_ts_1.formatThrown)(failure.cause) });
|
|
135
|
-
}
|
|
136
|
-
function isReportCliOperation(operation) {
|
|
137
|
-
return operation === "load trusted Report config" ||
|
|
138
|
-
operation === "load trusted Report module" ||
|
|
139
|
-
operation === "load trusted Theme module" ||
|
|
140
|
-
operation === "execute report from Record" ||
|
|
141
|
-
operation === "render Report show output" ||
|
|
142
|
-
operation === "open report view session" ||
|
|
143
|
-
operation === "open report view" ||
|
|
144
|
-
operation === "export static Report";
|
|
145
|
-
}
|
|
146
|
-
function failureCode(value) {
|
|
147
|
-
if (typeof value !== "object" || value === null)
|
|
148
|
-
return undefined;
|
|
149
|
-
const code = Reflect.get(value, "code");
|
|
150
|
-
return typeof code === "string" ? code : undefined;
|
|
151
|
-
}
|
|
152
|
-
function runnerAssertionsMessage(value) {
|
|
153
|
-
if (failureCode(value) !== "runner-record-assertions-invalid" || typeof value !== "object" || value === null) {
|
|
154
|
-
return undefined;
|
|
155
|
-
}
|
|
156
|
-
const issue = Reflect.get(value, "issue");
|
|
157
|
-
if (typeof issue !== "object" || issue === null)
|
|
158
|
-
return undefined;
|
|
159
|
-
const message = Reflect.get(issue, "message");
|
|
160
|
-
return typeof message === "string" && message.length > 0 ? message : undefined;
|
|
161
|
-
}
|
|
162
|
-
// 表驱动的 flag 定义(node:util parseArgs)。--no-x 显式声明,不依赖 Node 20.14+
|
|
163
|
-
// 才支持的 allowNegative,也避免每个 boolean flag 都隐式获得未设计的负向别名。
|
|
164
|
-
// 未知 flag 由 strict 模式报清晰错误,不会静默吞掉后面的位置参数。
|
|
165
|
-
//
|
|
166
|
-
// 每个 flag 的 JSDoc 就是它在 docs-site/zh/reference/cli.mdx flag 表里的说明,由
|
|
167
|
-
// scripts/generate-reference.ts 提取渲染——改 flag 语义时改这里的注释即可,不用碰生成脚本。
|
|
168
|
-
// 负向 flag(no-x)与正向 flag 合并成一行展示,不需要单独写 JSDoc。
|
|
169
|
-
const FLAG_OPTIONS = {
|
|
170
|
-
/** experiment 运行不支持该 flag。要换 agent,请在 `experiments/` 下新增或复制一个配置文件。 */
|
|
171
|
-
agent: { type: "string" },
|
|
172
|
-
/** experiment 运行不支持该 flag。要换模型,请新增或复制一个 experiment 文件并修改 `model`。 */
|
|
173
|
-
model: { type: "string" },
|
|
174
|
-
/** 每个 eval 运行多少次,常用于 pass@N。 */
|
|
175
|
-
attempts: { type: "string" },
|
|
176
|
-
/** 设置同时运行的 eval 数量。 */
|
|
177
|
-
"max-concurrency": { type: "string" },
|
|
178
|
-
/** 设置同时进行的 Sandbox 镜像 lookup/build 数量;与 eval 并发独立,默认 2。 */
|
|
179
|
-
"max-build-concurrency": { type: "string" },
|
|
180
|
-
/** 单个 attempt 的超时时间,单位毫秒。解析链:`--timeout` > experiment > eval(`defineEval({ timeoutMs })`)> `niceeval.config.ts`,默认无上限(四层都没声明就不设 deadline);config 是缺省底而不是覆盖层,写了 config 不会让 eval 自己声明的上限失效。 */
|
|
181
|
-
timeout: { type: "string" },
|
|
182
|
-
/** 整次运行的预算上限(美元)。 */
|
|
183
|
-
budget: { type: "string" },
|
|
184
|
-
/** `exp` 命令专用:跑完留下 failed/errored attempt 的 Sandbox 现场(= `--keep-sandbox=failed`);`--keep-sandbox=all` 连 passed 也留。事后用 `niceeval sandbox list/enter/stop` 查看与销毁。 */
|
|
185
|
-
"keep-sandbox": { type: "boolean" },
|
|
186
|
-
/** `sandbox stop` 专用:销毁全部留存 Sandbox。 */
|
|
187
|
-
all: { type: "boolean" },
|
|
188
|
-
/** `sandbox diff` 专用:只看某个 send 窗口(如 `--window turn2` 或 `--window session2/turn1`);省略输出全部窗口的串联视图。 */
|
|
189
|
-
window: { type: "string" },
|
|
190
|
-
/** `sandbox diff` 专用:只看某个文件的 patch;省略输出该窗口的全部文件。 */
|
|
191
|
-
path: { type: "string" },
|
|
192
|
-
/** `sandbox enter` 专用:shell 退出后让现场保持运行,不送回休眠。 */
|
|
193
|
-
"leave-running": { type: "boolean" },
|
|
194
|
-
/** `sandbox list` 专用:核对强杀(`SIGKILL` / 断电)路径留下的无主沙箱实例(docker + e2b;vercel 无按元数据检索实例的通道,不参与)。只读,不清理;销毁走 `niceeval sandbox prune`。 */
|
|
195
|
-
orphans: { type: "boolean" },
|
|
196
|
-
/** 只运行带有该 tag 的 eval(见 `defineEval` 的 `tags`)。 */
|
|
197
|
-
tag: { type: "string" },
|
|
198
|
-
/** 额外写一份 JUnit XML 报告到指定路径,供 CI 消费。 */
|
|
199
|
-
junit: { type: "string" },
|
|
200
|
-
/** `exp` 运行在 stdout 输出单一有序的 NDJSON 事件流;`exp --dry` 与 `debug` 输出各自的单个 JSON 计划文档。`show` 输出 Host 拥有的内建或自定义单目标机器文档,不形成完整站点或打开第二条取数路径。 */
|
|
201
|
-
json: { type: "boolean" },
|
|
202
|
-
/** `docker profile doctor` 专用:启动受限 DinD 容器并运行内层容器。 */
|
|
203
|
-
smoke: { type: "boolean" },
|
|
204
|
-
/** `view` 命令专用:把结果查看器静态导出到指定目录。 */
|
|
205
|
-
out: { type: "string" },
|
|
206
|
-
/** `view` 命令专用:指定本地服务器监听端口。 */
|
|
207
|
-
port: { type: "string" },
|
|
208
|
-
/** `view` 命令专用:指定监听地址;省略时为 127.0.0.1,只写 `--host` 时为 0.0.0.0。非 loopback 监听无认证或 TLS,会向所有可达客户端暴露报告数据。 */
|
|
209
|
-
host: { type: "string" },
|
|
210
|
-
// 以下旧 show 切片只为实现收敛期间保留解析位置,不属于目标公开 CLI,也不进入参考页。
|
|
211
|
-
/** `show` 专用:按一个 exact Attempt locator 展示已记录的 source snapshot。 */
|
|
212
|
-
source: { type: "boolean" },
|
|
213
|
-
/** `show @<AttemptLocator> --execution`:从公开 Record projection 呈现该 Attempt 的 transcript、tool 与 command evidence。 */
|
|
214
|
-
execution: { type: "boolean" },
|
|
215
|
-
/** `show @<AttemptLocator> --timing[=summary|full]` 专用:从内建 timing Report 读取该 Attempt 的 runner 阶段树。 */
|
|
216
|
-
timing: { type: "boolean" },
|
|
217
|
-
/** `show @<AttemptId> --execution` 专用:JS 正则过滤 retained transcript、tool 与 command evidence。 */
|
|
218
|
-
grep: { type: "string" },
|
|
219
|
-
// --diff 是布尔;--diff=<路径> 在 parseArgs 前预扫成 diffPath(路径必须 = 连写,
|
|
220
|
-
// 空格形式的下一个 token 仍是位置参数 = eval id 前缀,与文档一致)。
|
|
221
|
-
/** 实现收敛占位;目标公开 CLI 通过 Report page 读取 diff 通道。 */
|
|
222
|
-
diff: { type: "boolean" },
|
|
223
|
-
/** 实现收敛占位;目标公开 CLI 不提供跨 Run history。 */
|
|
224
|
-
history: { type: "boolean" },
|
|
225
|
-
/** 实现收敛占位;目标公开 CLI 通过 Report Calculation 呈现用量。 */
|
|
226
|
-
usage: { type: "boolean" },
|
|
227
|
-
/** 实现收敛占位;目标公开 CLI 不提供隐式跨 Run 统计。 */
|
|
228
|
-
stats: { type: "boolean" },
|
|
229
|
-
/** `show` / `view` 命令专用:按完整 ExperimentId 收窄当前项目结果;可重复,不接受前缀或逗号列表。 */
|
|
230
|
-
experiment: { type: "string", multiple: true },
|
|
231
|
-
/** `show` / `view` / `accept` / `sandbox enter|list|stop` 共用:指定实际 Record root;CLI 不补接 `.niceeval/record` 或其它后缀。 */
|
|
232
|
-
record: { type: "string" },
|
|
233
|
-
/** `show` / `view` 可重复传入 `--run`;每次按完整 RunId 增加一个显式 Run,重复 identity 去重。 */
|
|
234
|
-
run: { type: "string", multiple: true },
|
|
235
|
-
/** `show` / `view` 命令专用:内建 `standard` 或受信任的 Report module 路径。 */
|
|
236
|
-
report: { type: "string" },
|
|
237
|
-
/** `show` / `view` 命令专用:内建 Theme 或受信任的闭合 Theme module 路径。 */
|
|
238
|
-
theme: { type: "string" },
|
|
239
|
-
/** `show` / `view` 命令专用:选择报告的初始页;`show` 渲染该页并在尾部附其余页索引,`view` 以它作初始路由。未命中的页 id 按用法错误退出并列出可用页 id。 */
|
|
240
|
-
page: { type: "string" },
|
|
241
|
-
/** `exp` 命令专用:补齐被强杀打断的实验级 teardown——只对选中的实验各执行一次 teardown(新进程语义),不派发 attempt、不跑 setup;没有遗留登记也照常执行。与 eval 前缀位置参数组合是用法错误。 */
|
|
242
|
-
teardown: { type: "boolean" },
|
|
243
|
-
/** `exp --teardown` 专用:显式恢复一个永不自动接管的 sharedState lease;必须同时提供 --owner-token、--confirm-owner-terminated 和 --confirm-remote-quiesced。 */
|
|
244
|
-
"recover-shared-state": { type: "string" },
|
|
245
|
-
/** `--recover-shared-state` 专用:要恢复的不可变 owner token;错误或过期 token 不会修改 lease。 */
|
|
246
|
-
"owner-token": { type: "string" },
|
|
247
|
-
/** `--recover-shared-state` 专用:确认原 owner 已终止。 */
|
|
248
|
-
"confirm-owner-terminated": { type: "boolean" },
|
|
249
|
-
/** `--recover-shared-state` 专用:确认远端外部状态已静止。 */
|
|
250
|
-
"confirm-remote-quiesced": { type: "boolean" },
|
|
251
|
-
/** 只打印本次会匹配到的 eval × 运行配置,不实际执行(人读文本或 `--json` 单文档,见「机器怎么读:--json」)。 */
|
|
252
|
-
dry: { type: "boolean" },
|
|
253
|
-
/** `sandbox prune` 专用:除 orphan 外也销毁 unverified 实例;`exp` 明确拒绝此 flag,重跑失败项或全部项请用 `--rerun` / `--rerun all`。 */
|
|
254
|
-
force: { type: "boolean" },
|
|
255
|
-
/** `exp` 命令专用:重新运行失败项(裸写/failed)或全部项(all),不改变长期指纹。 */
|
|
256
|
-
rerun: { type: "boolean" },
|
|
257
|
-
/** 某个 eval 的一次 attempt 通过后,停止该 eval 剩余的 attempts;省略默认关(`attempts` 默认跑满、测完整通过率)。 */
|
|
258
|
-
"early-exit": { type: "boolean" },
|
|
259
|
-
/** 强制关闭首过即停,即使实验文件里写了 `earlyExit: true`。 */
|
|
260
|
-
"no-early-exit": { type: "boolean" },
|
|
261
|
-
/** `view` 命令专用:启动后自动打开浏览器(默认行为)。 */
|
|
262
|
-
open: { type: "boolean" },
|
|
263
|
-
"no-open": { type: "boolean" },
|
|
264
|
-
/** `clean` / `migrate` 专用:确认不可逆 maintenance 动作或在 clean Git restore point 下执行 migration。 */
|
|
265
|
-
yes: { type: "boolean" },
|
|
266
|
-
/** 打印用法说明并退出。 */
|
|
267
|
-
help: { type: "boolean", short: "h" },
|
|
268
|
-
/** 打印 niceeval 的版本号并退出。 */
|
|
269
|
-
version: { type: "boolean", short: "v" },
|
|
270
|
-
};
|
|
271
|
-
function numberFlag(name, raw) {
|
|
272
|
-
if (raw === undefined)
|
|
273
|
-
return undefined;
|
|
274
|
-
const n = Number(raw);
|
|
275
|
-
if (!Number.isFinite(n)) {
|
|
276
|
-
throw usageError((0, index_ts_5.t)("cli.flag.invalidNumber", { flag: name, value: raw }));
|
|
277
|
-
}
|
|
278
|
-
return n;
|
|
279
|
-
}
|
|
280
|
-
const CLI_COMMANDS = ["check", "exp", "debug", "accept", "show", "list", "view", "clean", "migrate", "init", "run", "sandbox", "session", "docker"];
|
|
281
|
-
function isCliCommand(candidate) {
|
|
282
|
-
return CLI_COMMANDS.some((command) => command === candidate);
|
|
283
|
-
}
|
|
284
|
-
function parseArgs(argv) {
|
|
285
|
-
if (argv[0] === "--")
|
|
286
|
-
argv = argv.slice(1);
|
|
287
|
-
if (argv.some((arg) => arg === "--strict" || arg.startsWith("--strict="))) {
|
|
288
|
-
throw usageError((0, index_ts_5.t)("cli.flag.strictRemoved"));
|
|
289
|
-
}
|
|
290
|
-
// --diff=<路径> 预扫:diff 本体是布尔(裸 --diff = 文件级摘要),路径只接受 = 连写。
|
|
291
|
-
let diffPath;
|
|
292
|
-
// --keep-sandbox[=failed|all] 预扫:本体是布尔(裸 = failed 档),档位只接受 = 连写。
|
|
293
|
-
let keepSandboxTier;
|
|
294
|
-
// --timing[=summary|full] 预扫:node:util 的单个 option 不支持 boolean|string 联合,
|
|
295
|
-
// 所以 mode 在严格 parseArgs 前提取,再把两种形式统一成布尔 --timing。
|
|
296
|
-
let timingMode;
|
|
297
|
-
// --source[=full|<path>] 预扫:本体是布尔,值只接受 = 连写,避免吞掉 eval 前缀位置参数。
|
|
298
|
-
let sourceValue;
|
|
299
|
-
let rerunMode;
|
|
300
|
-
// boolean|string 联合 flag 的空格写法先归一:--rerun all → --rerun=all;
|
|
301
|
-
// --host 的裸写则补成默认地址,保持 `--host <地址>` 的普通 string flag 形态。
|
|
302
|
-
{
|
|
303
|
-
const normalized = [];
|
|
304
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
305
|
-
const arg = argv[index];
|
|
306
|
-
const next = argv[index + 1];
|
|
307
|
-
if (arg === "--rerun" && (next === "failed" || next === "all")) {
|
|
308
|
-
normalized.push(`--rerun=${next}`);
|
|
309
|
-
index += 1;
|
|
310
|
-
}
|
|
311
|
-
else if (arg === "--host" && (next === undefined || next.startsWith("-"))) {
|
|
312
|
-
normalized.push("--host=0.0.0.0");
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
normalized.push(arg);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
argv = normalized;
|
|
319
|
-
}
|
|
320
|
-
argv = argv.map((arg) => {
|
|
321
|
-
if (arg.startsWith("--host=")) {
|
|
322
|
-
const value = arg.slice("--host=".length);
|
|
323
|
-
if (value.length === 0) {
|
|
324
|
-
throw usageError("--host=<address> requires a non-empty address, or use bare --host.\n");
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
if (arg.startsWith("--source=")) {
|
|
328
|
-
const value = arg.slice("--source=".length);
|
|
329
|
-
if (value.length === 0) {
|
|
330
|
-
throw usageError("--source=<path> requires a non-empty captured source path, or use bare --source.\n");
|
|
331
|
-
}
|
|
332
|
-
sourceValue = value;
|
|
333
|
-
return "--source";
|
|
334
|
-
}
|
|
335
|
-
if (arg.startsWith("--diff=")) {
|
|
336
|
-
const path = arg.slice("--diff=".length);
|
|
337
|
-
if (path)
|
|
338
|
-
diffPath = path;
|
|
339
|
-
return "--diff";
|
|
340
|
-
}
|
|
341
|
-
if (arg.startsWith("--keep-sandbox=")) {
|
|
342
|
-
const tier = arg.slice("--keep-sandbox=".length);
|
|
343
|
-
if (tier !== "failed" && tier !== "all") {
|
|
344
|
-
throw usageError(`--keep-sandbox only accepts "failed" (default) or "all", got "${tier}".\n`);
|
|
345
|
-
}
|
|
346
|
-
keepSandboxTier = tier;
|
|
347
|
-
return "--keep-sandbox";
|
|
348
|
-
}
|
|
349
|
-
if (arg.startsWith("--timing=")) {
|
|
350
|
-
const mode = arg.slice("--timing=".length);
|
|
351
|
-
if (mode !== "summary" && mode !== "full") {
|
|
352
|
-
throw usageError(`--timing only accepts "summary" (default) or "full", got "${mode}".\n`);
|
|
353
|
-
}
|
|
354
|
-
timingMode = mode;
|
|
355
|
-
return "--timing";
|
|
356
|
-
}
|
|
357
|
-
if (arg.startsWith("--rerun=")) {
|
|
358
|
-
const mode = arg.slice("--rerun=".length);
|
|
359
|
-
if (mode !== "failed" && mode !== "all") {
|
|
360
|
-
throw usageError(`--rerun only accepts "failed" (default) or "all", got "${mode}".\n`);
|
|
361
|
-
}
|
|
362
|
-
rerunMode = mode;
|
|
363
|
-
return "--rerun";
|
|
364
|
-
}
|
|
365
|
-
// `--output` 整个删除(见 docs/feature/experiments/cli.md 与 memory/exp-output-two-forms-ruling.md):
|
|
366
|
-
// beta 不留别名,任何取值(裸 flag 或 `--output=value`)都按用法错误拒绝,不静默吞掉、也不
|
|
367
|
-
// 落到 node:util parseArgs 的通用「unknown option」文案——给出专门的 error:/fix: 两行,
|
|
368
|
-
// 指向唯一还存在的两条路径:不加 flag 跑人读文本,机器面用 `--json`。
|
|
369
|
-
if (arg === "--output" || arg.startsWith("--output=")) {
|
|
370
|
-
throw usageError((0, index_ts_5.t)("cli.flag.outputRemoved"));
|
|
371
|
-
}
|
|
372
|
-
return arg;
|
|
373
|
-
});
|
|
374
|
-
let values;
|
|
375
|
-
let rawPositionals;
|
|
376
|
-
let providedOptions;
|
|
377
|
-
try {
|
|
378
|
-
const parsed = (0, node_util_1.parseArgs)({
|
|
379
|
-
args: argv,
|
|
380
|
-
options: FLAG_OPTIONS,
|
|
381
|
-
allowPositionals: true,
|
|
382
|
-
strict: true,
|
|
383
|
-
tokens: true,
|
|
384
|
-
});
|
|
385
|
-
values = parsed.values;
|
|
386
|
-
rawPositionals = parsed.positionals;
|
|
387
|
-
providedOptions = parsed.tokens
|
|
388
|
-
.filter((token) => token.kind === "option")
|
|
389
|
-
.map((token) => token.name);
|
|
390
|
-
}
|
|
391
|
-
catch (e) {
|
|
392
|
-
if (e instanceof CliUsageError)
|
|
393
|
-
throw e;
|
|
394
|
-
throw usageError((0, index_ts_5.t)("cli.flag.parseError", { message: e instanceof Error ? e.message : String(e) }));
|
|
395
|
-
}
|
|
396
|
-
// 第一个位置参数必须是已知命令;其余是 eval id 前缀 / view 输入。
|
|
397
|
-
// 裸 eval id 早已不再是运行入口,所以不识别的首 token 应当就地报用法错误,
|
|
398
|
-
// 不应先装载项目 config / eval 再偶然以其它错误退出。
|
|
399
|
-
let command = "run";
|
|
400
|
-
let positionals = rawPositionals;
|
|
401
|
-
if (rawPositionals.length > 0) {
|
|
402
|
-
const candidate = rawPositionals[0];
|
|
403
|
-
if (!isCliCommand(candidate)) {
|
|
404
|
-
throw usageError((0, index_ts_5.t)("cli.command.unknown", { command: candidate }));
|
|
405
|
-
}
|
|
406
|
-
command = candidate;
|
|
407
|
-
positionals = rawPositionals.slice(1);
|
|
408
|
-
}
|
|
409
|
-
const flags = {
|
|
410
|
-
agent: values.agent,
|
|
411
|
-
model: values.model,
|
|
412
|
-
attempts: numberFlag("attempts", values.attempts),
|
|
413
|
-
maxConcurrency: numberFlag("max-concurrency", values["max-concurrency"]),
|
|
414
|
-
maxBuildConcurrency: numberFlag("max-build-concurrency", values["max-build-concurrency"]),
|
|
415
|
-
timeout: numberFlag("timeout", values.timeout),
|
|
416
|
-
budget: numberFlag("budget", values.budget),
|
|
417
|
-
tag: values.tag,
|
|
418
|
-
junit: values.junit,
|
|
419
|
-
json: values.json === true,
|
|
420
|
-
smoke: values.smoke === true,
|
|
421
|
-
out: values.out,
|
|
422
|
-
port: numberFlag("port", values.port),
|
|
423
|
-
host: values.host,
|
|
424
|
-
dry: values.dry === true,
|
|
425
|
-
force: values.force === true,
|
|
426
|
-
rerun: values.rerun === true ? (rerunMode ?? "failed") : undefined,
|
|
427
|
-
earlyExit: values["no-early-exit"] === true ? false : values["early-exit"] === true ? true : undefined,
|
|
428
|
-
open: values["no-open"] === true ? false : values.open === true ? true : undefined,
|
|
429
|
-
help: values.help === true,
|
|
430
|
-
version: values.version === true,
|
|
431
|
-
yes: values.yes === true,
|
|
432
|
-
source: values.source === true ? (sourceValue ?? true) : undefined,
|
|
433
|
-
execution: values.execution === true,
|
|
434
|
-
diff: values.diff === true && diffPath === undefined,
|
|
435
|
-
diffPath,
|
|
436
|
-
timing: values.timing === true ? (timingMode ?? "summary") : undefined,
|
|
437
|
-
grep: values.grep,
|
|
438
|
-
keepSandbox: values["keep-sandbox"] === true ? (keepSandboxTier ?? "failed") : undefined,
|
|
439
|
-
all: values.all === true,
|
|
440
|
-
window: values.window,
|
|
441
|
-
sandboxPath: values.path,
|
|
442
|
-
leaveRunning: values["leave-running"] === true,
|
|
443
|
-
history: values.history === true,
|
|
444
|
-
usage: values.usage === true,
|
|
445
|
-
stats: values.stats === true,
|
|
446
|
-
experiment: values.experiment,
|
|
447
|
-
record: values.record,
|
|
448
|
-
run: values.run,
|
|
449
|
-
report: values.report,
|
|
450
|
-
page: values.page,
|
|
451
|
-
theme: values.theme,
|
|
452
|
-
orphans: values.orphans === true,
|
|
453
|
-
teardown: values.teardown === true,
|
|
454
|
-
recoverSharedState: values["recover-shared-state"],
|
|
455
|
-
ownerToken: values["owner-token"],
|
|
456
|
-
confirmOwnerTerminated: values["confirm-owner-terminated"] === true,
|
|
457
|
-
confirmRemoteQuiesced: values["confirm-remote-quiesced"] === true,
|
|
458
|
-
};
|
|
459
|
-
return { command, positionals, flags, providedOptions };
|
|
460
|
-
}
|
|
461
|
-
function projectCurrentDryPlan(input) {
|
|
462
|
-
return Object.freeze({
|
|
463
|
-
slots: Object.freeze(input.slots.map(projectCurrentDryPlanSlot)),
|
|
464
|
-
readbacks: Object.freeze([...input.readbacks]),
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
function projectCurrentDryPlanSlot(slot) {
|
|
468
|
-
return Object.freeze({
|
|
469
|
-
runId: String(slot.runId),
|
|
470
|
-
slotId: String(slot.slotId),
|
|
471
|
-
experimentId: slot.experimentId,
|
|
472
|
-
evalId: slot.evalId,
|
|
473
|
-
attempt: slot.attempt,
|
|
474
|
-
state: slot.state === "reuse" ? "reused" : "gap",
|
|
475
|
-
comparisons: Object.freeze(slot.comparisons.map((comparison) => Object.freeze({
|
|
476
|
-
attachment: comparison.attachment,
|
|
477
|
-
recordedClaim: comparison.recordedClaim,
|
|
478
|
-
sourceState: comparison.sourceState,
|
|
479
|
-
result: comparison.result,
|
|
480
|
-
reason: comparison.reason,
|
|
481
|
-
}))),
|
|
482
|
-
...(slot.state === "gap" ? { reason: slot.reason, scope: slot.scope } : {}),
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
function currentDryPlanRows(plan) {
|
|
486
|
-
const rows = new Map();
|
|
487
|
-
const rowFor = (experimentId, evalId) => {
|
|
488
|
-
const key = JSON.stringify([experimentId, evalId]);
|
|
489
|
-
const existing = rows.get(key);
|
|
490
|
-
if (existing !== undefined)
|
|
491
|
-
return existing;
|
|
492
|
-
const created = { experimentId, evalId, slots: [], readbacks: [] };
|
|
493
|
-
rows.set(key, created);
|
|
494
|
-
return created;
|
|
495
|
-
};
|
|
496
|
-
for (const slot of plan.slots)
|
|
497
|
-
rowFor(slot.experimentId, slot.evalId).slots.push(slot);
|
|
498
|
-
for (const readback of plan.readbacks) {
|
|
499
|
-
rowFor(readback.target.experimentId, readback.target.evalId).readbacks.push(readback);
|
|
500
|
-
}
|
|
501
|
-
return Object.freeze([...rows.values()].map((row) => Object.freeze({
|
|
502
|
-
experimentId: row.experimentId,
|
|
503
|
-
evalId: row.evalId,
|
|
504
|
-
slots: Object.freeze([...row.slots].sort((left, right) => left.attempt - right.attempt)),
|
|
505
|
-
readbacks: Object.freeze([...row.readbacks].sort((left, right) => left.target.attempt - right.target.attempt)),
|
|
506
|
-
})).sort((left, right) => left.experimentId.localeCompare(right.experimentId) || left.evalId.localeCompare(right.evalId)));
|
|
507
|
-
}
|
|
508
|
-
function renderCurrentDryPlan(input) {
|
|
509
|
-
const reused = input.rows.reduce((total, row) => total + row.slots.filter((slot) => slot.state === "reused").length, 0);
|
|
510
|
-
const lines = [
|
|
511
|
-
`plan: ${input.totalAttempts} attempts · ${input.evals} evals × ${input.configs} configs · runs ${input.attempts}`,
|
|
512
|
-
...(reused === 0 ? [] : [`reuse: ${reused}/${input.totalAttempts} exact current Record attempts`]),
|
|
513
|
-
`plugins: ${input.pluginAudit.occurrences.length} lifecycle occurrences`,
|
|
514
|
-
];
|
|
515
|
-
for (const row of input.rows) {
|
|
516
|
-
const reusedAttempts = row.slots.filter((slot) => slot.state === "reused").map((slot) => slot.attempt);
|
|
517
|
-
const gaps = row.slots.filter((slot) => slot.state === "gap");
|
|
518
|
-
const hasIdentityGap = gaps.some((slot) => slot.reason === "identity-mismatch");
|
|
519
|
-
const parts = [
|
|
520
|
-
...(row.locked ? ["locked"] : []),
|
|
521
|
-
...(reusedAttempts.length === 0 ? [] : [`reused ${reusedAttempts.join(",")}`]),
|
|
522
|
-
...gaps.map((slot) => `gap ${slot.attempt}:${slot.reason ?? "unknown"}`),
|
|
523
|
-
];
|
|
524
|
-
lines.push(`${row.experimentId} ${row.evalId} ${parts.join(" · ") || "no slots"}`);
|
|
525
|
-
for (const readback of row.readbacks) {
|
|
526
|
-
const verdict = readback.state === "reused"
|
|
527
|
-
? readback.verdict
|
|
528
|
-
: readback.verdict.state === "available"
|
|
529
|
-
? readback.verdict.value
|
|
530
|
-
: readback.verdict.state;
|
|
531
|
-
const locator = readback.source.locator;
|
|
532
|
-
lines.push(` source ${locator} · ${readback.state} · verdict ${verdict}`);
|
|
533
|
-
if (hasIdentityGap && readback.state === "prior") {
|
|
534
|
-
lines.push(` accept: niceeval accept ${locator}`);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
return `${lines.join("\n")}\n`;
|
|
539
|
-
}
|
|
540
|
-
function renderCurrentDryPlanJson(input) {
|
|
541
|
-
const reused = input.rows.reduce((total, row) => total + row.slots.filter((slot) => slot.state === "reused").length, 0);
|
|
542
|
-
return `${JSON.stringify({
|
|
543
|
-
format: "niceeval.current-reuse-plan/v1",
|
|
544
|
-
schemaVersion: 1,
|
|
545
|
-
total: input.totalAttempts,
|
|
546
|
-
evals: input.evals,
|
|
547
|
-
configs: input.configs,
|
|
548
|
-
attempts: input.attempts,
|
|
549
|
-
reused,
|
|
550
|
-
matrix: input.rows,
|
|
551
|
-
plugins: input.pluginAudit.occurrences,
|
|
552
|
-
})}\n`;
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Fold the invocation's real fresh results and current Record readbacks at the
|
|
556
|
-
* same eval identity. This intentionally projects only identity + terminal
|
|
557
|
-
* verdict; a reused readback is never recreated as an EvalResult.
|
|
558
|
-
*/
|
|
559
|
-
function foldInvocationEvalStats(summary) {
|
|
560
|
-
const terminals = [
|
|
561
|
-
...summary.results.map((result) => Object.freeze({
|
|
562
|
-
identity: `${result.experimentId ?? ""}|${result.id}`,
|
|
563
|
-
verdict: result.verdict,
|
|
564
|
-
})),
|
|
565
|
-
...summary.reusedAttempts.map((readback) => Object.freeze({
|
|
566
|
-
identity: `${readback.target.experimentId}|${readback.target.evalId}`,
|
|
567
|
-
verdict: readback.source.evaluationKind !== "score"
|
|
568
|
-
? readback.verdict
|
|
569
|
-
: readback.score.state === "applicable"
|
|
570
|
-
&& readback.score.attachment.state === "available"
|
|
571
|
-
&& readback.score.attachment.value.state === "complete"
|
|
572
|
-
? "passed"
|
|
573
|
-
: "errored",
|
|
574
|
-
})),
|
|
575
|
-
];
|
|
576
|
-
return (0, verdict_ts_1.evalLevelStats)(terminals, (terminal) => terminal.identity);
|
|
577
|
-
}
|
|
578
|
-
function firstViewerOnlyFlag(flags) {
|
|
579
|
-
const SHOW = "show";
|
|
580
|
-
const BOTH = "show / view";
|
|
581
|
-
const VIEW = "view";
|
|
582
|
-
if (flags.source)
|
|
583
|
-
return { flag: "--source", command: SHOW };
|
|
584
|
-
if (flags.execution)
|
|
585
|
-
return { flag: "--execution", command: SHOW };
|
|
586
|
-
if (flags.timing !== undefined)
|
|
587
|
-
return { flag: "--timing", command: SHOW };
|
|
588
|
-
if (flags.grep !== undefined)
|
|
589
|
-
return { flag: "--grep", command: SHOW };
|
|
590
|
-
if (flags.diff || flags.diffPath !== undefined)
|
|
591
|
-
return { flag: "--diff", command: SHOW };
|
|
592
|
-
if (flags.history)
|
|
593
|
-
return { flag: "--history", command: SHOW };
|
|
594
|
-
if (flags.usage)
|
|
595
|
-
return { flag: "--usage", command: SHOW };
|
|
596
|
-
if (flags.stats)
|
|
597
|
-
return { flag: "--stats", command: SHOW };
|
|
598
|
-
if (flags.experiment !== undefined)
|
|
599
|
-
return { flag: "--experiment", command: BOTH };
|
|
600
|
-
if (flags.report !== undefined)
|
|
601
|
-
return { flag: "--report", command: BOTH };
|
|
602
|
-
if (flags.theme !== undefined)
|
|
603
|
-
return { flag: "--theme", command: VIEW };
|
|
604
|
-
if (flags.page !== undefined)
|
|
605
|
-
return { flag: "--page", command: BOTH };
|
|
606
|
-
if (flags.run !== undefined)
|
|
607
|
-
return { flag: "--run", command: VIEW };
|
|
608
|
-
if (flags.out !== undefined)
|
|
609
|
-
return { flag: "--out", command: VIEW };
|
|
610
|
-
if (flags.port !== undefined)
|
|
611
|
-
return { flag: "--port", command: VIEW };
|
|
612
|
-
if (flags.open !== undefined)
|
|
613
|
-
return { flag: "--open", command: VIEW };
|
|
614
|
-
return undefined;
|
|
615
|
-
}
|
|
616
|
-
/** `clean` and `migrate` operate on a current Record root with explicit confirmation. */
|
|
617
|
-
function firstRecordMaintenanceUnsupportedFlag(flags) {
|
|
618
|
-
const unsupported = [
|
|
619
|
-
["--agent", flags.agent],
|
|
620
|
-
["--model", flags.model],
|
|
621
|
-
["--attempts", flags.attempts],
|
|
622
|
-
["--max-concurrency", flags.maxConcurrency],
|
|
623
|
-
["--max-build-concurrency", flags.maxBuildConcurrency],
|
|
624
|
-
["--timeout", flags.timeout],
|
|
625
|
-
["--budget", flags.budget],
|
|
626
|
-
["--tag", flags.tag],
|
|
627
|
-
["--junit", flags.junit],
|
|
628
|
-
["--json", flags.json],
|
|
629
|
-
["--smoke", flags.smoke],
|
|
630
|
-
["--dry", flags.dry],
|
|
631
|
-
["--force", flags.force],
|
|
632
|
-
["--rerun", flags.rerun],
|
|
633
|
-
["--early-exit/--no-early-exit", flags.earlyExit],
|
|
634
|
-
["--open/--no-open", flags.open],
|
|
635
|
-
["--out", flags.out],
|
|
636
|
-
["--port", flags.port],
|
|
637
|
-
["--host", flags.host],
|
|
638
|
-
["--source", flags.source],
|
|
639
|
-
["--execution", flags.execution],
|
|
640
|
-
["--diff", flags.diff || flags.diffPath !== undefined],
|
|
641
|
-
["--grep", flags.grep],
|
|
642
|
-
["--timing", flags.timing],
|
|
643
|
-
["--keep-sandbox", flags.keepSandbox],
|
|
644
|
-
["--all", flags.all],
|
|
645
|
-
["--window", flags.window],
|
|
646
|
-
["--path", flags.sandboxPath],
|
|
647
|
-
["--leave-running", flags.leaveRunning],
|
|
648
|
-
["--history", flags.history],
|
|
649
|
-
["--usage", flags.usage],
|
|
650
|
-
["--stats", flags.stats],
|
|
651
|
-
["--experiment", flags.experiment],
|
|
652
|
-
["--run", flags.run],
|
|
653
|
-
["--report", flags.report],
|
|
654
|
-
["--page", flags.page],
|
|
655
|
-
["--theme", flags.theme],
|
|
656
|
-
["--orphans", flags.orphans],
|
|
657
|
-
["--teardown", flags.teardown],
|
|
658
|
-
["--recover-shared-state", flags.recoverSharedState],
|
|
659
|
-
["--owner-token", flags.ownerToken],
|
|
660
|
-
["--confirm-owner-terminated", flags.confirmOwnerTerminated],
|
|
661
|
-
["--confirm-remote-quiesced", flags.confirmRemoteQuiesced],
|
|
662
|
-
];
|
|
663
|
-
const bad = unsupported.find(([flag, value]) => {
|
|
664
|
-
if (flag === "--open/--no-open" || flag === "--early-exit/--no-early-exit") {
|
|
665
|
-
return value !== undefined;
|
|
666
|
-
}
|
|
667
|
-
return value !== undefined && value !== false && (!Array.isArray(value) || value.length > 0);
|
|
668
|
-
});
|
|
669
|
-
return bad?.[0];
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* `accept` 接受一个或多个精确 locator;它不是 `exp` 的另一种选择器,也不派发 attempt。
|
|
673
|
-
* `--record` 是唯一允许的附加 flag,用来接受发布根或其它显式记录根里的结果。
|
|
674
|
-
*/
|
|
675
|
-
function parseAcceptLocators(positionals, flags) {
|
|
676
|
-
if (positionals.length === 0 || positionals.some((locator) => !/^@[^@\s]+$/.test(locator))) {
|
|
677
|
-
throw usageError((0, index_ts_5.t)("cli.accept.usage"));
|
|
678
|
-
}
|
|
679
|
-
if (new Set(positionals).size !== positionals.length) {
|
|
680
|
-
throw usageError("niceeval accept rejects duplicate locators.\n");
|
|
681
|
-
}
|
|
682
|
-
// `parseArgs` 已经负责未知 flag;这里拒绝那些虽为全局已知、但会改变运行/查看语义的 flag。
|
|
683
|
-
// `--record` 是 accept 的唯一范围输入,help/version 在 main() 更早处理。
|
|
684
|
-
const unsupported = [
|
|
685
|
-
["--agent", flags.agent],
|
|
686
|
-
["--model", flags.model],
|
|
687
|
-
["--attempts", flags.attempts],
|
|
688
|
-
["--max-concurrency", flags.maxConcurrency],
|
|
689
|
-
["--max-build-concurrency", flags.maxBuildConcurrency],
|
|
690
|
-
["--timeout", flags.timeout],
|
|
691
|
-
["--budget", flags.budget],
|
|
692
|
-
["--tag", flags.tag],
|
|
693
|
-
["--junit", flags.junit],
|
|
694
|
-
["--json", flags.json],
|
|
695
|
-
["--dry", flags.dry],
|
|
696
|
-
["--force", flags.force],
|
|
697
|
-
["--rerun", flags.rerun],
|
|
698
|
-
["--early-exit/--no-early-exit", flags.earlyExit],
|
|
699
|
-
["--open/--no-open", flags.open],
|
|
700
|
-
["--source", flags.source],
|
|
701
|
-
["--execution", flags.execution],
|
|
702
|
-
["--diff", flags.diff || flags.diffPath !== undefined],
|
|
703
|
-
["--grep", flags.grep],
|
|
704
|
-
["--timing", flags.timing],
|
|
705
|
-
["--keep-sandbox", flags.keepSandbox],
|
|
706
|
-
["--all", flags.all],
|
|
707
|
-
["--window", flags.window],
|
|
708
|
-
["--path", flags.sandboxPath],
|
|
709
|
-
["--leave-running", flags.leaveRunning],
|
|
710
|
-
["--history", flags.history],
|
|
711
|
-
["--usage", flags.usage],
|
|
712
|
-
["--stats", flags.stats],
|
|
713
|
-
["--experiment", flags.experiment],
|
|
714
|
-
["--run", flags.run],
|
|
715
|
-
["--report", flags.report],
|
|
716
|
-
["--page", flags.page],
|
|
717
|
-
["--theme", flags.theme],
|
|
718
|
-
["--orphans", flags.orphans],
|
|
719
|
-
["--teardown", flags.teardown],
|
|
720
|
-
["--recover-shared-state", flags.recoverSharedState],
|
|
721
|
-
["--owner-token", flags.ownerToken],
|
|
722
|
-
["--confirm-owner-terminated", flags.confirmOwnerTerminated],
|
|
723
|
-
["--confirm-remote-quiesced", flags.confirmRemoteQuiesced],
|
|
724
|
-
];
|
|
725
|
-
const bad = unsupported.find(([flag, value]) => {
|
|
726
|
-
// `--no-open` / `--no-early-exit` are represented as false, but are still
|
|
727
|
-
// explicit flags and therefore invalid on this command.
|
|
728
|
-
if (flag === "--open/--no-open" || flag === "--early-exit/--no-early-exit")
|
|
729
|
-
return value !== undefined;
|
|
730
|
-
return value !== undefined && value !== false && (!Array.isArray(value) || value.length > 0);
|
|
731
|
-
});
|
|
732
|
-
if (bad) {
|
|
733
|
-
throw usageError((0, index_ts_5.t)("cli.accept.flagUnsupported", { flag: bad[0] }));
|
|
734
|
-
}
|
|
735
|
-
return [...positionals];
|
|
736
|
-
}
|
|
737
|
-
/** 调用 acceptance core;CLI 只负责 cwd/记录根边界、输出与退出码,不重建结果或启动 runner。 */
|
|
738
|
-
function runAcceptCommand(cwd, locators, recordRoot) {
|
|
739
|
-
return effect_1.Effect.gen(function* () {
|
|
740
|
-
const results = yield* cliEffect("accept locators", index_ts_1.experimentHost.accept({
|
|
741
|
-
cwd,
|
|
742
|
-
locators,
|
|
743
|
-
...(recordRoot !== undefined ? { recordRoot } : {}),
|
|
744
|
-
}));
|
|
745
|
-
yield* effect_1.Effect.sync(() => {
|
|
746
|
-
for (const result of results) {
|
|
747
|
-
process.stdout.write((0, index_ts_5.t)("cli.accept.done", {
|
|
748
|
-
runId: result.runId,
|
|
749
|
-
sourceLocator: result.sourceLocator,
|
|
750
|
-
locator: result.locator,
|
|
751
|
-
fingerprint: result.fingerprint ?? "—",
|
|
752
|
-
}));
|
|
753
|
-
}
|
|
754
|
-
});
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
// ── exp rename:CLI 契约与纯解析/格式化 ──────────────────────────────────────
|
|
758
|
-
// 核心在 src/runner/rename-experiment.ts(planExperimentRename / renameExperiment,
|
|
759
|
-
// 资格门与写入都在那里,见 docs/source-map.md 与 docs/feature/experiments/rename.md);
|
|
760
|
-
// CLI 只做:解析 `exp rename <oldId> <newId>`、递选项进核心、把核心返回的计划/结果按
|
|
761
|
-
// 形态渲染。稳定 reason、计划与结果文档都是核心导出的契约,CLI 不在渲染层重建资格算法。
|
|
762
|
-
exports.EXPERIMENT_RENAME_FORMAT = "niceeval.experimentRename";
|
|
763
|
-
exports.EXPERIMENT_RENAME_SCHEMA_VERSION = 1;
|
|
764
|
-
const EXPERIMENT_RENAME_REASON_MESSAGE = {
|
|
765
|
-
"source-empty": "cli.rename.error.sourceEmpty",
|
|
766
|
-
"target-not-found": "cli.rename.error.targetNotFound",
|
|
767
|
-
"target-has-results": "cli.rename.error.targetHasResults",
|
|
768
|
-
"source-unreadable": "cli.rename.error.sourceUnreadable",
|
|
769
|
-
"artifact-unavailable": "cli.rename.error.artifactUnavailable",
|
|
770
|
-
"nothing-to-migrate": "cli.rename.error.nothingToMigrate",
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
771
15
|
};
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
return { ok: false, kind: "usage" };
|
|
779
|
-
}
|
|
780
|
-
return { ok: true, oldId: args[0], newId: args[1] };
|
|
781
|
-
}
|
|
782
|
-
/**
|
|
783
|
-
* exp rename 只允许 --dry / --json;返回第一个被误用的 flag,没有误用返回 undefined。
|
|
784
|
-
* 与 accept 同一套纪律:node:util parseArgs 已经报未知 flag,这里拒绝那些全局已知、
|
|
785
|
-
* 但会改变运行/查看语义的 flag。
|
|
786
|
-
*/
|
|
787
|
-
function firstExperimentRenameUnsupportedFlag(flags) {
|
|
788
|
-
const unsupported = [
|
|
789
|
-
["--agent", flags.agent],
|
|
790
|
-
["--model", flags.model],
|
|
791
|
-
["--attempts", flags.attempts],
|
|
792
|
-
["--max-concurrency", flags.maxConcurrency],
|
|
793
|
-
["--max-build-concurrency", flags.maxBuildConcurrency],
|
|
794
|
-
["--timeout", flags.timeout],
|
|
795
|
-
["--budget", flags.budget],
|
|
796
|
-
["--tag", flags.tag],
|
|
797
|
-
["--junit", flags.junit],
|
|
798
|
-
["--force", flags.force],
|
|
799
|
-
["--rerun", flags.rerun],
|
|
800
|
-
["--early-exit/--no-early-exit", flags.earlyExit],
|
|
801
|
-
["--open/--no-open", flags.open],
|
|
802
|
-
["--source", flags.source],
|
|
803
|
-
["--execution", flags.execution],
|
|
804
|
-
["--diff", flags.diff || flags.diffPath !== undefined],
|
|
805
|
-
["--grep", flags.grep],
|
|
806
|
-
["--timing", flags.timing],
|
|
807
|
-
["--keep-sandbox", flags.keepSandbox],
|
|
808
|
-
["--all", flags.all],
|
|
809
|
-
["--window", flags.window],
|
|
810
|
-
["--path", flags.sandboxPath],
|
|
811
|
-
["--leave-running", flags.leaveRunning],
|
|
812
|
-
["--history", flags.history],
|
|
813
|
-
["--usage", flags.usage],
|
|
814
|
-
["--stats", flags.stats],
|
|
815
|
-
["--experiment", flags.experiment],
|
|
816
|
-
["--record", flags.record],
|
|
817
|
-
["--run", flags.run],
|
|
818
|
-
["--report", flags.report],
|
|
819
|
-
["--page", flags.page],
|
|
820
|
-
["--theme", flags.theme],
|
|
821
|
-
["--orphans", flags.orphans],
|
|
822
|
-
["--teardown", flags.teardown],
|
|
823
|
-
["--recover-shared-state", flags.recoverSharedState],
|
|
824
|
-
["--owner-token", flags.ownerToken],
|
|
825
|
-
["--confirm-owner-terminated", flags.confirmOwnerTerminated],
|
|
826
|
-
["--confirm-remote-quiesced", flags.confirmRemoteQuiesced],
|
|
827
|
-
["--out", flags.out],
|
|
828
|
-
["--port", flags.port],
|
|
829
|
-
["--host", flags.host],
|
|
830
|
-
];
|
|
831
|
-
const bad = unsupported.find(([flag, value]) => {
|
|
832
|
-
// `--no-open` / `--no-early-exit` 表现为 false,但仍是显式 flag,在 rename 上同样非法。
|
|
833
|
-
if (flag === "--open/--no-open" || flag === "--early-exit/--no-early-exit")
|
|
834
|
-
return value !== undefined;
|
|
835
|
-
return value !== undefined && value !== false && (!Array.isArray(value) || value.length > 0);
|
|
836
|
-
});
|
|
837
|
-
return bad?.[0];
|
|
838
|
-
}
|
|
839
|
-
/** 人读面:核心计划 → 预览文本。逐条列出迁移与排除;有 `blocked` 时点名阻断原因。 */
|
|
840
|
-
function renderExperimentRenamePlanHuman(plan) {
|
|
841
|
-
const lines = [(0, index_ts_5.t)("cli.rename.previewHeader", { oldId: plan.oldId, newId: plan.newId })];
|
|
842
|
-
if (plan.blocked !== undefined) {
|
|
843
|
-
lines.push((0, index_ts_5.t)("cli.rename.blocked", { reason: plan.blocked.reason }));
|
|
844
|
-
lines.push(...renderExperimentRenameBlockedDetail(plan.blocked));
|
|
845
|
-
}
|
|
846
|
-
if (plan.migrations.length > 0) {
|
|
847
|
-
lines.push((0, index_ts_5.t)("cli.rename.migratingHeader", { count: plan.migrations.length }));
|
|
848
|
-
for (const entry of plan.migrations) {
|
|
849
|
-
lines.push((0, index_ts_5.t)("cli.rename.migratingRow", {
|
|
850
|
-
evalId: entry.evalId,
|
|
851
|
-
sourceLocator: entry.sourceLocator,
|
|
852
|
-
newId: plan.newId,
|
|
853
|
-
}));
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
if (plan.excluded.length > 0) {
|
|
857
|
-
lines.push((0, index_ts_5.t)("cli.rename.excludedHeader", { count: plan.excluded.length }));
|
|
858
|
-
for (const entry of plan.excluded) {
|
|
859
|
-
lines.push((0, index_ts_5.t)("cli.rename.excludedRow", { evalId: entry.evalId, reason: entry.reason }));
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
return `${lines.join("\n")}\n`;
|
|
863
|
-
}
|
|
864
|
-
/** 拒绝的人读面:先给 reason 专属文案(点名旧 id、新 id、受影响 eval 与下一步),再补冲突清单。 */
|
|
865
|
-
function renderExperimentRenameRejectedHuman(rejected) {
|
|
866
|
-
const lines = [
|
|
867
|
-
(0, index_ts_5.t)(EXPERIMENT_RENAME_REASON_MESSAGE[rejected.reason], {
|
|
868
|
-
oldId: rejected.oldId,
|
|
869
|
-
newId: rejected.newId,
|
|
870
|
-
evalId: rejected.evalId ?? "",
|
|
871
|
-
}),
|
|
872
|
-
];
|
|
873
|
-
lines.push(...renderExperimentRenameRejectedDetail(rejected));
|
|
874
|
-
return `${lines.join("\n")}\n`;
|
|
875
|
-
}
|
|
876
|
-
/** 成功的人读面:新 snapshot 路径 + 逐条新 locator。 */
|
|
877
|
-
function renderExperimentRenameDoneHuman(done) {
|
|
878
|
-
const lines = [
|
|
879
|
-
(0, index_ts_5.t)("cli.rename.doneHeader", { oldId: done.oldId, newId: done.newId, count: done.migrated.length }),
|
|
880
|
-
(0, index_ts_5.t)("cli.rename.snapshotPath", { path: done.snapshotPath }),
|
|
881
|
-
];
|
|
882
|
-
for (const entry of done.migrated) {
|
|
883
|
-
lines.push((0, index_ts_5.t)("cli.rename.doneRow", {
|
|
884
|
-
evalId: entry.evalId,
|
|
885
|
-
sourceLocator: entry.sourceLocator,
|
|
886
|
-
locator: entry.locator,
|
|
887
|
-
}));
|
|
888
|
-
}
|
|
889
|
-
return `${lines.join("\n")}\n`;
|
|
890
|
-
}
|
|
891
|
-
/** 阻断的逐条事实:冲突 eval 逐行列出,并保留底层读取详情。 */
|
|
892
|
-
function renderExperimentRenameBlockedDetail(blocked) {
|
|
893
|
-
const lines = [];
|
|
894
|
-
for (const evalId of blocked.conflictingEvals ?? []) {
|
|
895
|
-
lines.push(` ${evalId}`);
|
|
896
|
-
}
|
|
897
|
-
if (blocked.detail !== undefined)
|
|
898
|
-
lines.push(` ${blocked.detail}`);
|
|
899
|
-
return lines;
|
|
900
|
-
}
|
|
901
|
-
function renderExperimentRenameRejectedDetail(rejected) {
|
|
902
|
-
const lines = [];
|
|
903
|
-
const conflicting = rejected.conflictingEvals ?? [];
|
|
904
|
-
if (conflicting.length > 0) {
|
|
905
|
-
lines.push((0, index_ts_5.t)("cli.rename.conflicting", { evals: conflicting.join(", ") }));
|
|
906
|
-
}
|
|
907
|
-
return lines;
|
|
908
|
-
}
|
|
909
|
-
/** 机器面:单份 JSON 文档,不混入人读文本(见 rename.md「命令」)。 */
|
|
910
|
-
function renderExperimentRenameJson(document) {
|
|
911
|
-
return `${JSON.stringify({
|
|
912
|
-
format: exports.EXPERIMENT_RENAME_FORMAT,
|
|
913
|
-
schemaVersion: exports.EXPERIMENT_RENAME_SCHEMA_VERSION,
|
|
914
|
-
...document,
|
|
915
|
-
})}\n`;
|
|
916
|
-
}
|
|
917
|
-
/** 把 renameExperiment 抛出的资格错误投影成 rejected 文档(JSON 与拒绝人读共用)。 */
|
|
918
|
-
function experimentRenameRejectedFromError(error) {
|
|
919
|
-
const plan = error.plan;
|
|
920
|
-
const blocked = plan?.blocked;
|
|
921
|
-
return {
|
|
922
|
-
status: "rejected",
|
|
923
|
-
oldId: plan?.oldId ?? "",
|
|
924
|
-
newId: plan?.newId ?? "",
|
|
925
|
-
reason: error.reason,
|
|
926
|
-
...(blocked?.evalId === undefined ? {} : { evalId: blocked.evalId }),
|
|
927
|
-
...(blocked?.conflictingEvals === undefined ? {} : { conflictingEvals: blocked.conflictingEvals }),
|
|
928
|
-
...(blocked?.detail === undefined ? {} : { detail: blocked.detail }),
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
/** 退出码:可迁移预览与成功执行为 0;blocked 预览 / rejected 整批零写入按失败退出。 */
|
|
932
|
-
function experimentRenameExitCode(document) {
|
|
933
|
-
if (document.status === "done")
|
|
934
|
-
return 0;
|
|
935
|
-
if (document.status === "plan")
|
|
936
|
-
return document.blocked === undefined ? 0 : 1;
|
|
937
|
-
return 1;
|
|
938
|
-
}
|
|
939
|
-
/**
|
|
940
|
-
* exp rename 的 CLI 执行:解析 → 核心 → 渲染 → 退出码。核心契约见
|
|
941
|
-
* src/runner/rename-experiment.ts(planExperimentRename / renameExperiment)。
|
|
942
|
-
* renameExperiment 对资格失败抛 ExperimentRenameError,CLI 把它投影成 rejected 文档。
|
|
943
|
-
*/
|
|
944
|
-
function runExperimentRenameCommand(cwd, args, flags) {
|
|
945
|
-
return effect_1.Effect.gen(function* () {
|
|
946
|
-
const parsed = parseExperimentRenamePositionals(args);
|
|
947
|
-
if (!parsed.ok) {
|
|
948
|
-
yield* effect_1.Effect.sync(() => process.stderr.write((0, index_ts_5.t)("cli.rename.usage")));
|
|
949
|
-
return 1;
|
|
950
|
-
}
|
|
951
|
-
const unsupported = firstExperimentRenameUnsupportedFlag(flags);
|
|
952
|
-
if (unsupported !== undefined) {
|
|
953
|
-
yield* effect_1.Effect.sync(() => process.stderr.write((0, index_ts_5.t)("cli.rename.flagUnsupported", { flag: unsupported })));
|
|
954
|
-
return 1;
|
|
955
|
-
}
|
|
956
|
-
const { oldId, newId } = parsed;
|
|
957
|
-
const mod = (yield* cliPromise("load experiment rename command", () => __niceevalDynamicImport("./runner/rename-experiment.cjs")));
|
|
958
|
-
if (flags.dry) {
|
|
959
|
-
const plan = yield* cliEffect("plan experiment rename", mod.planExperimentRename({ cwd, oldId, newId }));
|
|
960
|
-
yield* effect_1.Effect.sync(() => {
|
|
961
|
-
if (flags.json)
|
|
962
|
-
process.stdout.write(renderExperimentRenameJson(plan));
|
|
963
|
-
else
|
|
964
|
-
process.stdout.write(renderExperimentRenamePlanHuman(plan));
|
|
965
|
-
});
|
|
966
|
-
return experimentRenameExitCode(plan);
|
|
967
|
-
}
|
|
968
|
-
const outcome = yield* effect_1.Effect.either(cliEffect("rename experiment", mod.renameExperiment({ cwd, oldId, newId })));
|
|
969
|
-
if (effect_1.Either.isRight(outcome)) {
|
|
970
|
-
yield* effect_1.Effect.sync(() => {
|
|
971
|
-
if (flags.json)
|
|
972
|
-
process.stdout.write(renderExperimentRenameJson(outcome.right));
|
|
973
|
-
else
|
|
974
|
-
process.stdout.write(renderExperimentRenameDoneHuman(outcome.right));
|
|
975
|
-
});
|
|
976
|
-
return 0;
|
|
977
|
-
}
|
|
978
|
-
// ExperimentRenameError 携带稳定 reason 与 rejected plan;其它异常按通用失败兜底。
|
|
979
|
-
const error = outcome.left.cause;
|
|
980
|
-
yield* effect_1.Effect.sync(() => {
|
|
981
|
-
if (error instanceof rename_experiment_ts_1.ExperimentRenameError) {
|
|
982
|
-
const rejected = experimentRenameRejectedFromError(error);
|
|
983
|
-
if (flags.json)
|
|
984
|
-
process.stdout.write(renderExperimentRenameJson(rejected));
|
|
985
|
-
else
|
|
986
|
-
process.stdout.write(renderExperimentRenameRejectedHuman(rejected));
|
|
987
|
-
}
|
|
988
|
-
else {
|
|
989
|
-
process.stderr.write((0, index_ts_5.t)("cli.rename.failed", { error: error instanceof Error ? error.message : String(error) }));
|
|
990
|
-
}
|
|
991
|
-
});
|
|
992
|
-
return 1;
|
|
993
|
-
});
|
|
994
|
-
}
|
|
995
|
-
/** 加载 cwd/.env(不覆盖已有环境变量)。 */
|
|
996
|
-
function loadDotenv(cwd) {
|
|
997
|
-
const path = (0, node_path_1.join)(cwd, ".env");
|
|
998
|
-
if (!(0, node_fs_1.existsSync)(path))
|
|
999
|
-
return effect_1.Effect.void;
|
|
1000
|
-
return cliPromise("read .env", () => (0, promises_1.readFile)(path, "utf-8")).pipe(effect_1.Effect.tap((raw) => effect_1.Effect.sync(() => {
|
|
1001
|
-
for (const line of raw.split("\n")) {
|
|
1002
|
-
const entry = line.trim();
|
|
1003
|
-
if (!entry || entry.startsWith("#"))
|
|
1004
|
-
continue;
|
|
1005
|
-
const eq = entry.indexOf("=");
|
|
1006
|
-
if (eq === -1)
|
|
1007
|
-
continue;
|
|
1008
|
-
const key = entry.slice(0, eq).trim();
|
|
1009
|
-
let value = entry.slice(eq + 1).trim();
|
|
1010
|
-
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
1011
|
-
value = value.slice(1, -1);
|
|
1012
|
-
}
|
|
1013
|
-
if (process.env[key] === undefined)
|
|
1014
|
-
process.env[key] = value;
|
|
1015
|
-
}
|
|
1016
|
-
})), effect_1.Effect.asVoid);
|
|
1017
|
-
}
|
|
1018
|
-
function loadConfig(cwd) {
|
|
1019
|
-
return effect_1.Effect.gen(function* () {
|
|
1020
|
-
const { loadConfigFile } = (yield* cliPromise("load config module", () => __niceevalDynamicImport("./load-config.cjs")));
|
|
1021
|
-
return yield* cliPromise("load NiceEval config", () => loadConfigFile(cwd));
|
|
1022
|
-
});
|
|
1023
|
-
}
|
|
1024
|
-
// AGENTS.md/CLAUDE.md 托管区块:告诉在这个项目里干活的 coding agent「niceeval 不在你的训练数据里,
|
|
1025
|
-
// 先读随包文档,跑完只经公开 show 面诊断」。随包只发中文准绳版文档(英文站是手工同步、可能滞后,
|
|
1026
|
-
// 不进包,见 package.json 的 files);init 时写入/刷新;标记之外的用户内容永不触碰。
|
|
1027
|
-
const AGENT_RULES_BEGIN = "<!-- BEGIN:niceeval-agent-rules -->";
|
|
1028
|
-
const AGENT_RULES_END = "<!-- END:niceeval-agent-rules -->";
|
|
1029
|
-
const AGENT_RULES_CONTENT = [
|
|
1030
|
-
"# niceeval is NOT in your training data",
|
|
1031
|
-
"",
|
|
1032
|
-
"Its APIs and conventions may differ from anything you have seen. Start with",
|
|
1033
|
-
"`node_modules/niceeval/INDEX.md`, then read the task-specific bundled guides it points",
|
|
1034
|
-
"to before writing any eval, experiment, adapter, or niceeval config. That index and",
|
|
1035
|
-
"the bundled Chinese docs are the authoritative version matching this installation.",
|
|
1036
|
-
"After a run, use this repository's package-manager invocation of `niceeval show` for",
|
|
1037
|
-
"diagnosis (`pnpm --silent exec niceeval show` in a pnpm project). Pick an `@<locator>`",
|
|
1038
|
-
"from the compact index, then show that locator for an overview, or add",
|
|
1039
|
-
"`--source` / `--execution` / `--timing` / `--diff` / `--json` for evidence.",
|
|
1040
|
-
"When diagnosing an existing run, do not inspect raw `.niceeval` files or treat the current",
|
|
1041
|
-
"`evals/` or `agents/` source as evidence of what happened in that run. If `niceeval show`",
|
|
1042
|
-
"cannot expose the evidence you need, report that product gap. Reading source remains",
|
|
1043
|
-
"appropriate when the task is to author or modify that source.",
|
|
1044
|
-
].join("\n");
|
|
1045
|
-
// 优先复用已有的 AGENTS.md;项目只有 CLAUDE.md(没有 AGENTS.md)时改写 CLAUDE.md 本身,
|
|
1046
|
-
// 不再另建一份重复文件;两者都没有则新建 AGENTS.md。CLAUDE.md 是指向 AGENTS.md 的符号链接时,
|
|
1047
|
-
// existsSync 会 follow 链接——目标存在则直接算作「AGENTS.md 已存在」,写入的还是同一份内容,
|
|
1048
|
-
// 不会产生分裂;目标不存在(悬空链接)则落回新建 AGENTS.md,写入后链接自然生效。
|
|
1049
|
-
function resolveAgentDocPath(cwd) {
|
|
1050
|
-
const agentsPath = (0, node_path_1.join)(cwd, "AGENTS.md");
|
|
1051
|
-
if ((0, node_fs_1.existsSync)(agentsPath))
|
|
1052
|
-
return agentsPath;
|
|
1053
|
-
const claudePath = (0, node_path_1.join)(cwd, "CLAUDE.md");
|
|
1054
|
-
if ((0, node_fs_1.existsSync)(claudePath))
|
|
1055
|
-
return claudePath;
|
|
1056
|
-
return agentsPath;
|
|
1057
|
-
}
|
|
1058
|
-
// init 提示用:从 cwd 向上找最近的 package.json,判断宿主是否 ESM 形态。装载本身不挑形态
|
|
1059
|
-
// (bin 注册了 tsx 的 ESM+CJS 双 hook,exports 全出口带 require 条件,见 docs/cli.md
|
|
1060
|
-
// 「装载用户 .ts」),但 CJS 编译面下 config / eval 文件用不了顶层 await,ESM 仍是推荐形态;
|
|
1061
|
-
// 找不到 package.json 或解析失败按非 ESM 处理(tsx/Node 的缺省语义就是 CJS)。只提示,
|
|
1062
|
-
// 不改用户的 package.json。
|
|
1063
|
-
function hostPrefersEsm(cwd) {
|
|
1064
|
-
let dir = (0, node_path_1.resolve)(cwd);
|
|
1065
|
-
while (true) {
|
|
1066
|
-
const pkgPath = (0, node_path_1.join)(dir, "package.json");
|
|
1067
|
-
if ((0, node_fs_1.existsSync)(pkgPath)) {
|
|
1068
|
-
try {
|
|
1069
|
-
const pkg = JSON.parse((0, node_fs_1.readFileSync)(pkgPath, "utf-8"));
|
|
1070
|
-
return pkg.type === "module";
|
|
1071
|
-
}
|
|
1072
|
-
catch {
|
|
1073
|
-
return false;
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
const parent = (0, node_path_1.dirname)(dir);
|
|
1077
|
-
if (parent === dir)
|
|
1078
|
-
return false;
|
|
1079
|
-
dir = parent;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
function initProject(cwd) {
|
|
1083
|
-
return effect_1.Effect.gen(function* () {
|
|
1084
|
-
yield* cliPromise("create eval directory", () => (0, promises_1.mkdir)((0, node_path_1.join)(cwd, "evals"), { recursive: true }));
|
|
1085
|
-
const configPath = (0, node_path_1.join)(cwd, "niceeval.config.ts");
|
|
1086
|
-
if (!(0, node_fs_1.existsSync)(configPath)) {
|
|
1087
|
-
yield* cliPromise("write initial config", () => (0, promises_1.writeFile)(configPath, [
|
|
1088
|
-
'import { defineConfig } from "niceeval";',
|
|
1089
|
-
"",
|
|
1090
|
-
"export default defineConfig({",
|
|
1091
|
-
" // Add experiments/ with defineExperiment(...) to run evals.",
|
|
1092
|
-
" //",
|
|
1093
|
-
" // Judge Facts require an Eval declaration: judge: true. Configure the model here or",
|
|
1094
|
-
" // on an Experiment. A consumed Fact without a model or key becomes unavailable and",
|
|
1095
|
-
" // makes that Attempt errored. Any OpenAI-compatible /chat/completions service works;",
|
|
1096
|
-
" // the key is read from OPENAI_API_KEY unless apiKeyEnv says otherwise.",
|
|
1097
|
-
' // judge: { model: "gpt-5.4-mini" },',
|
|
1098
|
-
"});",
|
|
1099
|
-
"",
|
|
1100
|
-
].join("\n"), "utf-8"));
|
|
1101
|
-
}
|
|
1102
|
-
const agentDocPath = resolveAgentDocPath(cwd);
|
|
1103
|
-
const existing = (0, node_fs_1.existsSync)(agentDocPath)
|
|
1104
|
-
? yield* cliPromise("read agent instructions", () => (0, promises_1.readFile)(agentDocPath, "utf-8"))
|
|
1105
|
-
: "";
|
|
1106
|
-
const next = (0, util_ts_1.upsertManagedBlock)(existing, AGENT_RULES_BEGIN, AGENT_RULES_END, AGENT_RULES_CONTENT);
|
|
1107
|
-
if (next !== existing)
|
|
1108
|
-
yield* cliPromise("write agent instructions", () => (0, promises_1.writeFile)(agentDocPath, next, "utf-8"));
|
|
1109
|
-
});
|
|
1110
|
-
}
|
|
1111
|
-
function openBrowser(url) {
|
|
1112
|
-
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
1113
|
-
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
1114
|
-
return cliPromise("open browser", () => new Promise((resolveOpen) => {
|
|
1115
|
-
let done = false;
|
|
1116
|
-
const finish = (ok) => {
|
|
1117
|
-
if (done)
|
|
1118
|
-
return;
|
|
1119
|
-
done = true;
|
|
1120
|
-
clearTimeout(timer);
|
|
1121
|
-
resolveOpen(ok);
|
|
1122
|
-
};
|
|
1123
|
-
const child = (0, node_child_process_1.spawn)(command, args, { detached: true, stdio: "ignore" });
|
|
1124
|
-
const timer = setTimeout(() => finish(true), 1500);
|
|
1125
|
-
child.once("error", () => finish(false));
|
|
1126
|
-
child.once("exit", (code) => finish(code === 0));
|
|
1127
|
-
child.unref();
|
|
1128
|
-
}));
|
|
1129
|
-
}
|
|
1130
|
-
/**
|
|
1131
|
-
* run 结束后把 coordinator 累计的诊断折成 `InvocationCompletion`(见 docs/feature/experiments/cli.md
|
|
1132
|
-
* 「运行完成状态不只看 verdict 计数」)。只读已经真实发生过的诊断,不额外发明信号:
|
|
1133
|
-
* - `"interrupted"` 诊断只在 run.ts 判定为真·中断(Effect exit 真实标记中断,不是「signal 被
|
|
1134
|
-
* abort 过」这种更弱的信号)时才会出现,见 `runner/run.ts` 的 `reportInterrupted()` 调用点。
|
|
1135
|
-
* - `"budget-exhausted:<experimentId>"` 诊断的 `count` 就是该 experiment 因预算耗尽未派发的
|
|
1136
|
-
* attempt 数(见 `runner/feedback/reducer.ts` 对 `budget-exhausted` 事件的注释),跨
|
|
1137
|
-
* experiment 求和得到 `unstarted`。
|
|
1138
|
-
* - `"reporter-error:<reporter>"` 诊断转成 `ReporterError[]`;`required` 字段来自事件自带的
|
|
1139
|
-
* `data.required`,直接反映这个 reporter 注册时的真实 required/best-effort 分类(见上面
|
|
1140
|
-
* 构造 `reporters: ReporterRegistration[]` 的地方——`--junit` 为 `required: true`,
|
|
1141
|
-
* `config.reporters` 恒 `false`),不是一个统一写死的占位值。
|
|
1142
|
-
* - `earlyExitUnstarted` 从反馈状态的 attempt:early-exit 计数派生(减去 fail-fast 的那部分——
|
|
1143
|
-
* 那是「未完整覆盖」,进 unstarted,不是「省下的重复验证」)。
|
|
1144
|
-
*/
|
|
1145
|
-
function assembleInvocationCompletion(state) {
|
|
1146
|
-
let unstarted = 0;
|
|
1147
|
-
let failFastSkipped = 0;
|
|
1148
|
-
let haltedSkipped = 0;
|
|
1149
|
-
let interrupted = false;
|
|
1150
|
-
const reporterErrors = [];
|
|
1151
|
-
for (const d of state.diagnostics) {
|
|
1152
|
-
// 归类按**稳定词法** `code`,不按 `key`:`key` 里编着折叠身份(experimentId / evalId /
|
|
1153
|
-
// reporter 名),拿它做前缀匹配会在「把身份从 key 里摘出去」时静默失配——记账悄悄归零,
|
|
1154
|
-
// 没有任何测试或类型会报警。`code` 省略时回落到 key 的首段:缺省 key 恒是
|
|
1155
|
-
// `${code}:${identity}`(见 sink.ts 的 DiagnosticInput),首段即 code。
|
|
1156
|
-
const code = d.code ?? d.key.split(":", 1)[0];
|
|
1157
|
-
if (code === "interrupted") {
|
|
1158
|
-
interrupted = true;
|
|
1159
|
-
}
|
|
1160
|
-
else if (code === "budget-exhausted") {
|
|
1161
|
-
unstarted += d.count;
|
|
1162
|
-
}
|
|
1163
|
-
else if (code === "fail-fast") {
|
|
1164
|
-
// run 级 fail-fast 造成的未派发同样计入 unstarted(结论落 incomplete,见
|
|
1165
|
-
// docs/feature/experiments/architecture.md「Completion 与退出」)。
|
|
1166
|
-
unstarted += d.count;
|
|
1167
|
-
failFastSkipped += d.count;
|
|
1168
|
-
}
|
|
1169
|
-
else if (code === "dispatch-halted") {
|
|
1170
|
-
// 止损闸停派发造成的未派发(见 docs/feature/error-classification/architecture.md
|
|
1171
|
-
// 「记账」)。这条诊断的 count 是「同一死因被声明了几次」(重复声明折叠),不是未派发数——
|
|
1172
|
-
// 未派发数由 emitter 累计后写在 data.unstarted 里(与 budget-exhausted 同一口径)。
|
|
1173
|
-
const halted = typeof d.data?.unstarted === "number" ? d.data.unstarted : 0;
|
|
1174
|
-
unstarted += halted;
|
|
1175
|
-
haltedSkipped += halted;
|
|
1176
|
-
}
|
|
1177
|
-
else if (code === "reporter-error") {
|
|
1178
|
-
// required 决定这条错误是否写进 InvocationCompletion.reporterErrors 并让 completion 非 complete
|
|
1179
|
-
// (见 docs/cli.md「required reporter」);best-effort reporter 的失败只保留为 diagnostic。
|
|
1180
|
-
if (d.data?.required !== true)
|
|
1181
|
-
continue;
|
|
1182
|
-
reporterErrors.push({
|
|
1183
|
-
reporter: typeof d.data?.reporter === "string" ? d.data.reporter : d.key.slice("reporter-error:".length),
|
|
1184
|
-
required: true,
|
|
1185
|
-
message: d.message,
|
|
1186
|
-
});
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
// 中断造成的未派发(仍在 queued 的 attempt)同样计入 unstarted(见 docs/feature/experiments/
|
|
1190
|
-
// architecture.md「Completion 与退出」:budget 耗尽、fail-fast 或中断造成的未派发都不伪装成全绿)。
|
|
1191
|
-
if (interrupted)
|
|
1192
|
-
unstarted += state.queued;
|
|
1193
|
-
// attempt:early-exit 计数含 fail-fast 与止损闸的未派发(反馈层同一事件驱动计数守恒);
|
|
1194
|
-
// 「省下的重复验证」= 总数减去那两部分。
|
|
1195
|
-
const earlyExitUnstarted = Math.max(0, state.earlyExitSkipped - failFastSkipped - haltedSkipped);
|
|
1196
|
-
const status = interrupted
|
|
1197
|
-
? "interrupted"
|
|
1198
|
-
: unstarted > 0 || reporterErrors.length > 0
|
|
1199
|
-
? "incomplete"
|
|
1200
|
-
: "complete";
|
|
1201
|
-
return { status, unstarted, earlyExitUnstarted, reporterErrors };
|
|
1202
|
-
}
|
|
1203
|
-
/** package.json 的 version 字段;-v/--version 直接回显这个号。 */
|
|
1204
|
-
function packageVersion() {
|
|
1205
|
-
return cliPromise("read package version", () => (0, promises_1.readFile)(new URL("../package.json", __niceevalImportMetaUrl), "utf-8")).pipe(effect_1.Effect.flatMap((raw) => effect_1.Effect.try({
|
|
1206
|
-
try: () => JSON.parse(raw).version,
|
|
1207
|
-
catch: (cause) => cliFailure("decode package version", cause),
|
|
1208
|
-
})));
|
|
1209
|
-
}
|
|
1210
|
-
function writeStdout(text) {
|
|
1211
|
-
return effect_1.Effect.sync(() => {
|
|
1212
|
-
process.stdout.write(text);
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1215
|
-
function writeStderr(text) {
|
|
1216
|
-
return effect_1.Effect.sync(() => {
|
|
1217
|
-
process.stderr.write(text);
|
|
1218
|
-
});
|
|
1219
|
-
}
|
|
1220
|
-
/** Idempotent application-level sweep for resources owned by this invocation. */
|
|
1221
|
-
function releaseCliResources() {
|
|
1222
|
-
// Shared-state leases are never part of this exit sweep. A normal Runner
|
|
1223
|
-
// lifecycle releases one only after every pool/provider/Experiment cleanup
|
|
1224
|
-
// succeeds; a failed cleanup intentionally leaves explicit recovery work.
|
|
1225
|
-
return cliPromise("stop remaining sandboxes", () => (0, registry_ts_1.stopAllSandboxes)()).pipe(effect_1.Effect.ignore, effect_1.Effect.zipRight((0, experiment_cleanup_registry_ts_1.drainExperimentTeardowns)().pipe(effect_1.Effect.ignore)), effect_1.Effect.zipRight((0, lock_ts_1.drainHeldCaseLocksEffect)().pipe(effect_1.Effect.ignore)));
|
|
1226
|
-
}
|
|
1227
|
-
function errorMessage(cause) {
|
|
1228
|
-
return cause instanceof Error ? cause.message : String(cause);
|
|
1229
|
-
}
|
|
1230
|
-
function runDockerCommand(positionals, flags) {
|
|
1231
|
-
return effect_1.Effect.gen(function* () {
|
|
1232
|
-
const { runDockerProfileCommand } = (yield* cliPromise("load Docker profile command", () => __niceevalDynamicImport("./sandbox/docker-profile/cli.cjs")));
|
|
1233
|
-
return yield* cliPromise("run Docker profile command", () => runDockerProfileCommand(positionals, { json: flags.json, smoke: flags.smoke }));
|
|
1234
|
-
});
|
|
1235
|
-
}
|
|
1236
|
-
function runSandboxCliCommand(cwd, positionals, flags) {
|
|
1237
|
-
return cliEffect("run sandbox command", (0, cli_commands_ts_1.runSandboxCommandEffect)(cwd, [...positionals], {
|
|
1238
|
-
all: flags.all,
|
|
1239
|
-
window: flags.window,
|
|
1240
|
-
path: flags.sandboxPath,
|
|
1241
|
-
leaveRunning: flags.leaveRunning,
|
|
1242
|
-
run: flags.record,
|
|
1243
|
-
orphans: flags.orphans,
|
|
1244
|
-
force: flags.force,
|
|
1245
|
-
}));
|
|
1246
|
-
}
|
|
1247
|
-
function runSessionCommand(cwd, positionals, flags) {
|
|
1248
|
-
return effect_1.Effect.gen(function* () {
|
|
1249
|
-
const niceevalRoot = (0, node_path_1.resolve)(cwd, ".niceeval");
|
|
1250
|
-
const subcommand = positionals[0] ?? "list";
|
|
1251
|
-
if (subcommand === "list") {
|
|
1252
|
-
if (positionals.length > 2) {
|
|
1253
|
-
yield* writeStderr("niceeval session list accepts at most one experiment prefix.\n");
|
|
1254
|
-
return 1;
|
|
1255
|
-
}
|
|
1256
|
-
const outcome = yield* effect_1.Effect.either(cliEffect("list sessions", (0, session_ts_1.listSessions)(niceevalRoot, {
|
|
1257
|
-
all: flags.all,
|
|
1258
|
-
...(positionals[1] === undefined ? {} : { selector: positionals[1] }),
|
|
1259
|
-
})));
|
|
1260
|
-
if (effect_1.Either.isLeft(outcome)) {
|
|
1261
|
-
yield* writeStderr(`${errorMessage(outcome.left.cause)}\n`);
|
|
1262
|
-
return 1;
|
|
1263
|
-
}
|
|
1264
|
-
yield* writeStdout(flags.json
|
|
1265
|
-
? `${JSON.stringify(outcome.right)}\n`
|
|
1266
|
-
: (0, session_ts_1.renderSessionListText)(outcome.right, Date.now(), flags.all));
|
|
1267
|
-
return 0;
|
|
1268
|
-
}
|
|
1269
|
-
if (subcommand === "show") {
|
|
1270
|
-
if (positionals.length !== 2 || flags.all) {
|
|
1271
|
-
yield* writeStderr("Usage: niceeval session show <sessionId> [--json]\n");
|
|
1272
|
-
return 1;
|
|
1273
|
-
}
|
|
1274
|
-
const outcome = yield* effect_1.Effect.either(cliEffect("show session", (0, session_ts_1.showSession)(niceevalRoot, positionals[1])));
|
|
1275
|
-
if (effect_1.Either.isLeft(outcome)) {
|
|
1276
|
-
yield* writeStderr(`${errorMessage(outcome.left.cause)}\n`);
|
|
1277
|
-
return 1;
|
|
1278
|
-
}
|
|
1279
|
-
yield* writeStdout(flags.json
|
|
1280
|
-
? `${JSON.stringify(outcome.right)}\n`
|
|
1281
|
-
: (0, session_ts_1.renderSessionShowText)(outcome.right));
|
|
1282
|
-
return 0;
|
|
1283
|
-
}
|
|
1284
|
-
yield* writeStderr("Usage: niceeval session list [--all] [<experiment-prefix>] [--json]\n" +
|
|
1285
|
-
" niceeval session show <sessionId> [--json]\n");
|
|
1286
|
-
return 1;
|
|
1287
|
-
});
|
|
1288
|
-
}
|
|
1289
|
-
function runRecordMaintenanceCommand(cwd, command, positionals, flags) {
|
|
1290
|
-
return effect_1.Effect.gen(function* () {
|
|
1291
|
-
if (positionals.length > 0) {
|
|
1292
|
-
yield* writeStderr(`niceeval ${command} does not accept positional arguments.\n`);
|
|
1293
|
-
return 1;
|
|
1294
|
-
}
|
|
1295
|
-
const unsupported = firstRecordMaintenanceUnsupportedFlag(flags);
|
|
1296
|
-
if (unsupported !== undefined) {
|
|
1297
|
-
yield* writeStderr(`niceeval ${command} does not accept ${unsupported}.\n`);
|
|
1298
|
-
return 1;
|
|
1299
|
-
}
|
|
1300
|
-
const result = yield* cliEffect("run Record maintenance", (0, record_ts_1.runRecordCliCommand)({
|
|
1301
|
-
command,
|
|
1302
|
-
cwd,
|
|
1303
|
-
...(flags.record === undefined ? {} : { record: flags.record }),
|
|
1304
|
-
yes: flags.yes,
|
|
1305
|
-
}));
|
|
1306
|
-
if (result.stdout !== "")
|
|
1307
|
-
yield* writeStdout(result.stdout);
|
|
1308
|
-
if (result.stderr !== "")
|
|
1309
|
-
yield* writeStderr(result.stderr);
|
|
1310
|
-
return result.exitCode;
|
|
1311
|
-
});
|
|
1312
|
-
}
|
|
1313
|
-
function agentRunFromExperiment(experiment, selectedEvalIds, overrides = {}) {
|
|
1314
|
-
return {
|
|
1315
|
-
agent: experiment.agent,
|
|
1316
|
-
model: experiment.model,
|
|
1317
|
-
reasoningEffort: experiment.reasoningEffort,
|
|
1318
|
-
flags: experiment.flags ?? {},
|
|
1319
|
-
plugins: experiment.plugins,
|
|
1320
|
-
attempts: overrides.attempts ?? experiment.attempts ?? 1,
|
|
1321
|
-
earlyExit: overrides.earlyExit ?? experiment.earlyExit ?? false,
|
|
1322
|
-
sandbox: experiment.sandbox,
|
|
1323
|
-
sandboxReuse: experiment.sandboxReuse,
|
|
1324
|
-
...(experiment.sharedState === undefined ? {} : { sharedState: experiment.sharedState }),
|
|
1325
|
-
judge: experiment.judge,
|
|
1326
|
-
...(0, timeout_ts_1.resolveRunTimeout)(overrides.timeout, experiment.timeoutMs),
|
|
1327
|
-
budget: overrides.budget ?? experiment.budget,
|
|
1328
|
-
selectedEvalIds,
|
|
1329
|
-
experimentId: experiment.id,
|
|
1330
|
-
experimentBaseDir: experiment.baseDir,
|
|
1331
|
-
experimentSourcePath: experiment.sourcePath,
|
|
1332
|
-
description: experiment.description,
|
|
1333
|
-
labels: experiment.labels,
|
|
1334
|
-
maxConcurrency: experiment.maxConcurrency,
|
|
1335
|
-
setup: experiment.setup,
|
|
1336
|
-
teardown: experiment.teardown,
|
|
1337
|
-
classifyFailure: experiment.classifyFailure,
|
|
1338
|
-
};
|
|
1339
|
-
}
|
|
1340
|
-
function hasSharedStateRecoveryOptions(flags) {
|
|
1341
|
-
return flags.recoverSharedState !== undefined ||
|
|
1342
|
-
flags.ownerToken !== undefined ||
|
|
1343
|
-
flags.confirmOwnerTerminated ||
|
|
1344
|
-
flags.confirmRemoteQuiesced;
|
|
1345
|
-
}
|
|
1346
|
-
function renderSharedStateRecoveryTarget(record) {
|
|
1347
|
-
return (0, index_ts_5.t)("cli.exp.sharedStateRecoveryTarget", {
|
|
1348
|
-
key: record.key,
|
|
1349
|
-
experimentId: record.experimentId,
|
|
1350
|
-
ownerToken: record.ownerToken,
|
|
1351
|
-
host: record.host,
|
|
1352
|
-
pid: record.pid,
|
|
1353
|
-
processIdentity: record.processIdentity,
|
|
1354
|
-
heartbeatAt: record.heartbeatAt,
|
|
1355
|
-
});
|
|
1356
|
-
}
|
|
1357
|
-
function experimentTeardownOf(experiment) {
|
|
1358
|
-
return typeof experiment.teardown === "function" ? experiment.teardown : undefined;
|
|
1359
|
-
}
|
|
1360
|
-
/**
|
|
1361
|
-
* A legacy free generation can exist from an interrupted pre-fix recovery:
|
|
1362
|
-
* it has durable owner evidence but may still have its old teardown entry.
|
|
1363
|
-
* Only the entry keyed by that exact immutable owner is eligible for removal.
|
|
1364
|
-
*
|
|
1365
|
-
* Claimed recovery takes the same path before publishing free. A failed or
|
|
1366
|
-
* contested claim therefore leaves the recovery generation closed rather than
|
|
1367
|
-
* letting a waiter enter while the old teardown could still be replayed.
|
|
1368
|
-
*/
|
|
1369
|
-
function inspectExactRecoveryTeardownRegistrationEffect(input) {
|
|
1370
|
-
const { niceevalRoot, record, currentHost } = input;
|
|
1371
|
-
const id = (0, teardown_registry_ts_1.teardownEntryId)(record.experimentId, record.pid);
|
|
1372
|
-
return effect_1.Effect.gen(function* () {
|
|
1373
|
-
const registration = yield* (0, teardown_registry_ts_1.readExactTeardownRegistrationEffect)(niceevalRoot, record.experimentId, record.pid);
|
|
1374
|
-
// A holder can die before its registration is durably written. There is
|
|
1375
|
-
// then no old teardown obligation to clear, but no unrelated entry may be
|
|
1376
|
-
// inferred from the current Experiment declaration.
|
|
1377
|
-
if (registration === undefined)
|
|
1378
|
-
return false;
|
|
1379
|
-
if (registration.host !== record.host) {
|
|
1380
|
-
return yield* effect_1.Effect.fail(new Error(`teardown registration ${JSON.stringify(id)} belongs to host ${JSON.stringify(registration.host)}, ` +
|
|
1381
|
-
`not immutable sharedState owner host ${JSON.stringify(record.host)}`));
|
|
1382
|
-
}
|
|
1383
|
-
// The registration does not carry a process identity itself, so recovery
|
|
1384
|
-
// compares it to the immutable sharedState owner. This shares the lease
|
|
1385
|
-
// terminal boundary: an exact local Z/X/x owner is stopped; an unreadable
|
|
1386
|
-
// or malformed identity probe fails closed instead of treating PID
|
|
1387
|
-
// existence as proof that cleanup can still run.
|
|
1388
|
-
const terminated = yield* (0, teardown_registry_ts_1.isExactTeardownRegistrationOwnerTerminatedEffect)({
|
|
1389
|
-
registration,
|
|
1390
|
-
owner: record,
|
|
1391
|
-
currentHost,
|
|
1392
|
-
});
|
|
1393
|
-
if (!terminated) {
|
|
1394
|
-
return yield* effect_1.Effect.fail(new Error(`teardown registration ${JSON.stringify(id)} still belongs to a live exact sharedState owner; refusing to clear it`));
|
|
1395
|
-
}
|
|
1396
|
-
return true;
|
|
1397
|
-
});
|
|
1398
|
-
}
|
|
1399
|
-
function clearExactRecoveryTeardownRegistrationEffect(input) {
|
|
1400
|
-
const { niceevalRoot, record, currentHost, expectedRegistration } = input;
|
|
1401
|
-
const id = (0, teardown_registry_ts_1.teardownEntryId)(record.experimentId, record.pid);
|
|
1402
|
-
return effect_1.Effect.gen(function* () {
|
|
1403
|
-
const present = yield* inspectExactRecoveryTeardownRegistrationEffect({
|
|
1404
|
-
niceevalRoot,
|
|
1405
|
-
record,
|
|
1406
|
-
currentHost,
|
|
1407
|
-
});
|
|
1408
|
-
if (!present) {
|
|
1409
|
-
if (expectedRegistration) {
|
|
1410
|
-
return yield* effect_1.Effect.fail(new Error(`teardown registration ${JSON.stringify(id)} disappeared before this recovery could clear it`));
|
|
1411
|
-
}
|
|
1412
|
-
return;
|
|
1413
|
-
}
|
|
1414
|
-
if (!expectedRegistration) {
|
|
1415
|
-
return yield* effect_1.Effect.fail(new Error(`teardown registration ${JSON.stringify(id)} appeared after this recovery was claimed`));
|
|
1416
|
-
}
|
|
1417
|
-
const removed = yield* (0, teardown_registry_ts_1.removeTeardownRegistrationIfPresentEffect)(niceevalRoot, id);
|
|
1418
|
-
if (!removed) {
|
|
1419
|
-
return yield* effect_1.Effect.fail(new Error(`teardown registration ${JSON.stringify(id)} changed before this recovery could clear it`));
|
|
1420
|
-
}
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
function renderDebugPlanJson(input) {
|
|
1424
|
-
return `${JSON.stringify({
|
|
1425
|
-
format: "niceeval.debug-plan/v1",
|
|
1426
|
-
schemaVersion: 1,
|
|
1427
|
-
experimentId: input.experimentId,
|
|
1428
|
-
evalId: input.evalId,
|
|
1429
|
-
commandPlan: input.commandPlan,
|
|
1430
|
-
})}\n`;
|
|
1431
|
-
}
|
|
1432
|
-
function runDebugCommand(cwd, positionals, flags) {
|
|
1433
|
-
return effect_1.Effect.gen(function* () {
|
|
1434
|
-
if (positionals.length !== 2) {
|
|
1435
|
-
yield* writeStderr((0, index_ts_5.t)("cli.debug.usage"));
|
|
1436
|
-
return 1;
|
|
1437
|
-
}
|
|
1438
|
-
const [experimentSelector, evalSelector] = positionals;
|
|
1439
|
-
const result = yield* cliEffect("debug Experiment lifecycle", index_ts_1.experimentHost.debug({
|
|
1440
|
-
cwd,
|
|
1441
|
-
experimentSelector,
|
|
1442
|
-
evalSelector,
|
|
1443
|
-
}));
|
|
1444
|
-
switch (result.status) {
|
|
1445
|
-
case "experiment-no-match":
|
|
1446
|
-
yield* writeStderr((0, index_ts_5.t)("cli.debug.experimentNoMatch", {
|
|
1447
|
-
selector: result.selector,
|
|
1448
|
-
candidates: result.candidates.join(", ") || (0, index_ts_5.t)("cli.none"),
|
|
1449
|
-
}));
|
|
1450
|
-
return 1;
|
|
1451
|
-
case "experiment-ambiguous":
|
|
1452
|
-
yield* writeStderr((0, index_ts_5.t)("cli.debug.experimentAmbiguous", {
|
|
1453
|
-
selector: result.selector,
|
|
1454
|
-
candidates: result.candidates.join(", "),
|
|
1455
|
-
}));
|
|
1456
|
-
return 1;
|
|
1457
|
-
case "eval-no-match":
|
|
1458
|
-
yield* writeStderr((0, index_ts_5.t)("cli.debug.evalNoMatch", {
|
|
1459
|
-
selector: result.selector,
|
|
1460
|
-
experimentId: result.experimentId,
|
|
1461
|
-
candidates: result.candidates.join(", ") || (0, index_ts_5.t)("cli.none"),
|
|
1462
|
-
}));
|
|
1463
|
-
return 1;
|
|
1464
|
-
case "eval-ambiguous":
|
|
1465
|
-
yield* writeStderr((0, index_ts_5.t)("cli.debug.evalAmbiguous", {
|
|
1466
|
-
selector: result.selector,
|
|
1467
|
-
experimentId: result.experimentId,
|
|
1468
|
-
candidates: result.candidates.join(", "),
|
|
1469
|
-
}));
|
|
1470
|
-
return 1;
|
|
1471
|
-
case "planned":
|
|
1472
|
-
yield* writeStdout(flags.json
|
|
1473
|
-
? renderDebugPlanJson(result)
|
|
1474
|
-
: (0, index_ts_4.renderHumanCommandPlan)(result.commandPlan, {
|
|
1475
|
-
isTTY: process.stdout.isTTY,
|
|
1476
|
-
noColor: process.env.NO_COLOR,
|
|
1477
|
-
width: process.stdout.columns,
|
|
1478
|
-
}));
|
|
1479
|
-
return 0;
|
|
1480
|
-
}
|
|
1481
|
-
});
|
|
1482
|
-
}
|
|
1483
|
-
function runEvaluationCommand(cwd, command, positionals, flags, interruption) {
|
|
1484
|
-
return effect_1.Effect.gen(function* () {
|
|
1485
|
-
// Explicit recovery is intentionally human-only. It has no receipt or
|
|
1486
|
-
// NDJSON contract, so accepting --json would give machine callers an
|
|
1487
|
-
// apparently successful but unparseable interface.
|
|
1488
|
-
if (command === "exp" && flags.recoverSharedState !== undefined && flags.json) {
|
|
1489
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryJsonUnsupported"));
|
|
1490
|
-
return 1;
|
|
1491
|
-
}
|
|
1492
|
-
// Keep project-local coordination (sessions, locks, kept sandboxes) separate
|
|
1493
|
-
// from the portable Record root selected by `--record` below.
|
|
1494
|
-
const coordinationRoot = (0, node_path_1.resolve)(cwd, ".niceeval");
|
|
1495
|
-
const config = yield* loadConfig(cwd);
|
|
1496
|
-
const maxBuildConcurrency = flags.maxBuildConcurrency ?? config.maxBuildConcurrency ?? 2;
|
|
1497
|
-
if (!Number.isInteger(maxBuildConcurrency) || maxBuildConcurrency <= 0) {
|
|
1498
|
-
yield* writeStderr(`maxBuildConcurrency must be a positive integer, got ${maxBuildConcurrency}.\n`);
|
|
1499
|
-
return 1;
|
|
1500
|
-
}
|
|
1501
|
-
let evals = Object.freeze([]);
|
|
1502
|
-
if (command === "list") {
|
|
1503
|
-
const listed = yield* cliEffect("list evals", index_ts_1.experimentHost.list({
|
|
1504
|
-
cwd,
|
|
1505
|
-
...(flags.tag === undefined ? {} : { tag: flags.tag }),
|
|
1506
|
-
}));
|
|
1507
|
-
evals = listed.evals;
|
|
1508
|
-
yield* writeStdout((0, index_ts_5.t)("cli.list.header", { count: evals.length }));
|
|
1509
|
-
for (const evalDefinition of evals) {
|
|
1510
|
-
yield* writeStdout(` ${evalDefinition.id}${evalDefinition.description ? ` — ${evalDefinition.description}` : ""}\n`);
|
|
1511
|
-
}
|
|
1512
|
-
return 0;
|
|
1513
|
-
}
|
|
1514
|
-
const agentRuns = [];
|
|
1515
|
-
let experimentSelection = (0, index_ts_5.t)("cli.all");
|
|
1516
|
-
let availableExperimentPaths = (0, index_ts_5.t)("cli.none");
|
|
1517
|
-
if (command === "exp" || command === "check") {
|
|
1518
|
-
if (hasSharedStateRecoveryOptions(flags) && (command !== "exp" || !flags.teardown)) {
|
|
1519
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryFlags"));
|
|
1520
|
-
return 1;
|
|
1521
|
-
}
|
|
1522
|
-
if (flags.agent || flags.model) {
|
|
1523
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.agentModelFlagUnsupported"));
|
|
1524
|
-
return 1;
|
|
1525
|
-
}
|
|
1526
|
-
if (flags.force) {
|
|
1527
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.forceUnsupported"));
|
|
1528
|
-
return 1;
|
|
1529
|
-
}
|
|
1530
|
-
// `--record` selects the fact root for `exp`; `check` has no Record
|
|
1531
|
-
// reader or writer, so accepting it there would silently discard input.
|
|
1532
|
-
if (command === "check" && flags.record !== undefined) {
|
|
1533
|
-
yield* writeStderr((0, index_ts_5.t)("cli.check.recordUnsupported"));
|
|
1534
|
-
return 1;
|
|
1535
|
-
}
|
|
1536
|
-
const viewerFlag = firstViewerOnlyFlag(flags);
|
|
1537
|
-
if (viewerFlag) {
|
|
1538
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.viewerFlagUnsupported", { flag: viewerFlag.flag, command: viewerFlag.command }));
|
|
1539
|
-
return 1;
|
|
1540
|
-
}
|
|
1541
|
-
if (command === "exp" && positionals[0] === "list") {
|
|
1542
|
-
if (positionals.length > 2) {
|
|
1543
|
-
yield* writeStderr("niceeval exp list accepts at most one experiment prefix.\n");
|
|
1544
|
-
return 1;
|
|
1545
|
-
}
|
|
1546
|
-
const selector = positionals[1];
|
|
1547
|
-
const listed = yield* cliEffect("list experiments", index_ts_1.experimentHost.list({
|
|
1548
|
-
cwd,
|
|
1549
|
-
...(flags.tag === undefined ? {} : { tag: flags.tag }),
|
|
1550
|
-
...(selector === undefined ? {} : { selector }),
|
|
1551
|
-
}));
|
|
1552
|
-
if (listed.selections.length === 0 && selector !== undefined) {
|
|
1553
|
-
yield* writeStderr((0, index_ts_5.t)("cli.experiment.noMatch", {
|
|
1554
|
-
arg: selector,
|
|
1555
|
-
experiments: (0, aggregate_ts_1.browsableExperimentPaths)(listed.experimentIds).join(", ") || (0, index_ts_5.t)("cli.none"),
|
|
1556
|
-
}));
|
|
1557
|
-
return 1;
|
|
1558
|
-
}
|
|
1559
|
-
const rows = listed.selections.map(({ experiment, selectedEvalIds }) => {
|
|
1560
|
-
return {
|
|
1561
|
-
experimentId: experiment.id,
|
|
1562
|
-
...(experiment.description !== undefined ? { description: experiment.description } : {}),
|
|
1563
|
-
agent: experiment.agent.name,
|
|
1564
|
-
...(experiment.model !== undefined ? { model: experiment.model } : {}),
|
|
1565
|
-
attempts: experiment.attempts,
|
|
1566
|
-
evalCount: selectedEvalIds.length,
|
|
1567
|
-
labels: { ...experiment.labels },
|
|
1568
|
-
selectedEvalIds,
|
|
1569
|
-
};
|
|
1570
|
-
});
|
|
1571
|
-
if (flags.json) {
|
|
1572
|
-
yield* writeStdout(`${JSON.stringify({ format: "niceeval.experiments", schemaVersion: 1, experiments: rows })}\n`);
|
|
1573
|
-
}
|
|
1574
|
-
else {
|
|
1575
|
-
for (const row of rows) {
|
|
1576
|
-
yield* writeStdout([
|
|
1577
|
-
row.experimentId,
|
|
1578
|
-
row.description ?? "—",
|
|
1579
|
-
row.agent,
|
|
1580
|
-
row.model ?? "—",
|
|
1581
|
-
`attempts=${row.attempts}`,
|
|
1582
|
-
`evals=${row.evalCount}`,
|
|
1583
|
-
`labels=${JSON.stringify(row.labels)}`,
|
|
1584
|
-
].join("\t") + "\n");
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
return 0;
|
|
1588
|
-
}
|
|
1589
|
-
const expArg = positionals[0];
|
|
1590
|
-
const extraPatterns = positionals.slice(1);
|
|
1591
|
-
experimentSelection = positionals.join(" ") || (0, index_ts_5.t)("cli.all");
|
|
1592
|
-
const listed = yield* cliEffect("select experiments", index_ts_1.experimentHost.list({
|
|
1593
|
-
cwd,
|
|
1594
|
-
...(flags.tag === undefined ? {} : { tag: flags.tag }),
|
|
1595
|
-
...(expArg === undefined ? {} : { selector: expArg }),
|
|
1596
|
-
...(extraPatterns.length === 0 ? {} : { evalPatterns: extraPatterns }),
|
|
1597
|
-
}));
|
|
1598
|
-
evals = listed.evals;
|
|
1599
|
-
const selected = listed.selections;
|
|
1600
|
-
availableExperimentPaths = (0, aggregate_ts_1.browsableExperimentPaths)(listed.experimentIds).join(", ") || (0, index_ts_5.t)("cli.none");
|
|
1601
|
-
if (selected.length === 0) {
|
|
1602
|
-
yield* writeStderr((0, index_ts_5.t)("cli.experiment.noMatch", {
|
|
1603
|
-
arg: expArg ?? (0, index_ts_5.t)("cli.all"),
|
|
1604
|
-
experiments: availableExperimentPaths,
|
|
1605
|
-
}));
|
|
1606
|
-
if (expArg === "show" || expArg === "view") {
|
|
1607
|
-
yield* writeStderr((0, index_ts_5.t)("cli.experiment.viewerCommandHint", {
|
|
1608
|
-
command: expArg,
|
|
1609
|
-
args: extraPatterns.length > 0 ? ` ${extraPatterns.join(" ")}` : "",
|
|
1610
|
-
}));
|
|
1611
|
-
}
|
|
1612
|
-
return 1;
|
|
1613
|
-
}
|
|
1614
|
-
const reminder = yield* (0, cli_commands_ts_1.keptSandboxReminderEffect)(cwd).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(undefined)));
|
|
1615
|
-
if (reminder)
|
|
1616
|
-
yield* writeStderr(reminder);
|
|
1617
|
-
const orphans = yield* (0, cli_commands_ts_1.orphanReminderEffect)(cwd).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(undefined)));
|
|
1618
|
-
if (orphans)
|
|
1619
|
-
yield* writeStderr(orphans);
|
|
1620
|
-
const teardownReminder = yield* (0, teardown_registry_ts_1.orphanedTeardownReminderEffect)(coordinationRoot, new Set(selected
|
|
1621
|
-
.filter(({ experiment }) => experimentTeardownOf(experiment) !== undefined)
|
|
1622
|
-
.map(({ experiment }) => experiment.id)), (0, node_os_1.hostname)()).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(undefined)));
|
|
1623
|
-
if (teardownReminder)
|
|
1624
|
-
yield* writeStderr(teardownReminder);
|
|
1625
|
-
if (flags.teardown) {
|
|
1626
|
-
if (extraPatterns.length > 0) {
|
|
1627
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.teardownNoEvalPatterns"));
|
|
1628
|
-
return 1;
|
|
1629
|
-
}
|
|
1630
|
-
const niceevalRootForTeardown = coordinationRoot;
|
|
1631
|
-
let selectedForTeardown = selected;
|
|
1632
|
-
let explicitRecovery;
|
|
1633
|
-
if (flags.recoverSharedState !== undefined) {
|
|
1634
|
-
// The input key is the recovery authority. Read its immutable
|
|
1635
|
-
// evidence before considering today's Experiment declaration: an
|
|
1636
|
-
// author may have renamed or removed sharedState after the holder
|
|
1637
|
-
// crashed, but must still have a public recovery path for that old
|
|
1638
|
-
// external state.
|
|
1639
|
-
const inspected = yield* effect_1.Effect.either((0, shared_state_lease_ts_1.readSharedStateLeaseRecoveryTargetEffect)(niceevalRootForTeardown, flags.recoverSharedState));
|
|
1640
|
-
if (effect_1.Either.isLeft(inspected)) {
|
|
1641
|
-
yield* writeStderr(`sharedState recovery inspection failed: ${errorMessage(inspected.left)}\n`);
|
|
1642
|
-
return 1;
|
|
1643
|
-
}
|
|
1644
|
-
if (inspected.right === undefined) {
|
|
1645
|
-
yield* writeStderr(`No recoverable sharedState ownership evidence exists for ${JSON.stringify(flags.recoverSharedState)}.\n`);
|
|
1646
|
-
return 1;
|
|
1647
|
-
}
|
|
1648
|
-
yield* writeStderr(renderSharedStateRecoveryTarget(inspected.right));
|
|
1649
|
-
if (selected.length !== 1) {
|
|
1650
|
-
yield* writeStderr("sharedState recovery requires an experiment selector that resolves to exactly one Experiment.\n");
|
|
1651
|
-
return 1;
|
|
1652
|
-
}
|
|
1653
|
-
const target = selected[0];
|
|
1654
|
-
if (inspected.right.experimentId !== target.experiment.id) {
|
|
1655
|
-
yield* writeStderr(`sharedState key ${JSON.stringify(flags.recoverSharedState)} belongs to experiment ${inspected.right.experimentId}, not ${target.experiment.id}; refusing recovery.\n`);
|
|
1656
|
-
return 1;
|
|
1657
|
-
}
|
|
1658
|
-
// Inspection intentionally precedes confirmation validation. The
|
|
1659
|
-
// immutable generation head retains the exact owner evidence even
|
|
1660
|
-
// after a crashed recovery attempt; this public command is the only
|
|
1661
|
-
// supported way to reveal it, never private-file manipulation.
|
|
1662
|
-
if (flags.ownerToken === undefined ||
|
|
1663
|
-
!flags.confirmOwnerTerminated ||
|
|
1664
|
-
!flags.confirmRemoteQuiesced) {
|
|
1665
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryFlags"));
|
|
1666
|
-
return 1;
|
|
1667
|
-
}
|
|
1668
|
-
// Inspection is read-only and remains available without a teardown.
|
|
1669
|
-
// Only the uniquely selected target can reach this point. Validate
|
|
1670
|
-
// its teardown immediately before claiming a recovery generation.
|
|
1671
|
-
if (experimentTeardownOf(target.experiment) === undefined) {
|
|
1672
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryTeardownRequired", {
|
|
1673
|
-
experimentId: target.experiment.id,
|
|
1674
|
-
}));
|
|
1675
|
-
return 1;
|
|
1676
|
-
}
|
|
1677
|
-
const begun = yield* effect_1.Effect.either((0, shared_state_lease_ts_1.beginExplicitSharedStateRecoveryEffect)({
|
|
1678
|
-
niceevalRoot: niceevalRootForTeardown,
|
|
1679
|
-
key: flags.recoverSharedState,
|
|
1680
|
-
ownerToken: flags.ownerToken,
|
|
1681
|
-
localHost: (0, node_os_1.hostname)(),
|
|
1682
|
-
confirmOwnerTerminated: flags.confirmOwnerTerminated,
|
|
1683
|
-
confirmRemoteQuiesced: flags.confirmRemoteQuiesced,
|
|
1684
|
-
}));
|
|
1685
|
-
if (effect_1.Either.isLeft(begun)) {
|
|
1686
|
-
yield* writeStderr(`sharedState recovery refused: ${errorMessage(begun.left)}\n`);
|
|
1687
|
-
return 1;
|
|
1688
|
-
}
|
|
1689
|
-
if (begun.right._tag === "AlreadyReleased") {
|
|
1690
|
-
const registrationWasPresent = yield* effect_1.Effect.either(inspectExactRecoveryTeardownRegistrationEffect({
|
|
1691
|
-
niceevalRoot: niceevalRootForTeardown,
|
|
1692
|
-
record: begun.right.record,
|
|
1693
|
-
currentHost: (0, node_os_1.hostname)(),
|
|
1694
|
-
}));
|
|
1695
|
-
if (effect_1.Either.isLeft(registrationWasPresent)) {
|
|
1696
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryAlreadyReleasedRegistrationFailed", {
|
|
1697
|
-
key: begun.right.record.key,
|
|
1698
|
-
message: errorMessage(registrationWasPresent.left),
|
|
1699
|
-
}));
|
|
1700
|
-
return 1;
|
|
1701
|
-
}
|
|
1702
|
-
const cleared = yield* effect_1.Effect.either(clearExactRecoveryTeardownRegistrationEffect({
|
|
1703
|
-
niceevalRoot: niceevalRootForTeardown,
|
|
1704
|
-
record: begun.right.record,
|
|
1705
|
-
currentHost: (0, node_os_1.hostname)(),
|
|
1706
|
-
expectedRegistration: registrationWasPresent.right,
|
|
1707
|
-
}));
|
|
1708
|
-
if (effect_1.Either.isLeft(cleared)) {
|
|
1709
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryAlreadyReleasedRegistrationFailed", {
|
|
1710
|
-
key: begun.right.record.key,
|
|
1711
|
-
message: errorMessage(cleared.left),
|
|
1712
|
-
}));
|
|
1713
|
-
return 1;
|
|
1714
|
-
}
|
|
1715
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryAlreadyReleased", { key: begun.right.record.key }));
|
|
1716
|
-
yield* writeStderr((0, index_ts_5.t)("runner.sharedStateExplicitRecovered", {
|
|
1717
|
-
key: begun.right.record.key,
|
|
1718
|
-
experimentId: begun.right.record.experimentId,
|
|
1719
|
-
ownerToken: begun.right.record.ownerToken,
|
|
1720
|
-
}));
|
|
1721
|
-
return 0;
|
|
1722
|
-
}
|
|
1723
|
-
if (begun.right.record.experimentId !== target.experiment.id) {
|
|
1724
|
-
yield* writeStderr(`sharedState key ${JSON.stringify(flags.recoverSharedState)} changed Experiment ownership during recovery; refusing teardown.\n`);
|
|
1725
|
-
return 1;
|
|
1726
|
-
}
|
|
1727
|
-
const registrationWasPresent = yield* effect_1.Effect.either(inspectExactRecoveryTeardownRegistrationEffect({
|
|
1728
|
-
niceevalRoot: niceevalRootForTeardown,
|
|
1729
|
-
record: begun.right.record,
|
|
1730
|
-
currentHost: (0, node_os_1.hostname)(),
|
|
1731
|
-
}));
|
|
1732
|
-
if (effect_1.Either.isLeft(registrationWasPresent)) {
|
|
1733
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryRegistrationFailed", {
|
|
1734
|
-
key: begun.right.record.key,
|
|
1735
|
-
message: errorMessage(registrationWasPresent.left),
|
|
1736
|
-
}));
|
|
1737
|
-
return 1;
|
|
1738
|
-
}
|
|
1739
|
-
explicitRecovery = {
|
|
1740
|
-
key: begun.right.record.key,
|
|
1741
|
-
ownerToken: begun.right.record.ownerToken,
|
|
1742
|
-
recoveryId: begun.right.recoveryId,
|
|
1743
|
-
experimentId: begun.right.record.experimentId,
|
|
1744
|
-
record: begun.right.record,
|
|
1745
|
-
registrationWasPresent: registrationWasPresent.right,
|
|
1746
|
-
};
|
|
1747
|
-
selectedForTeardown = [target];
|
|
1748
|
-
}
|
|
1749
|
-
else if (hasSharedStateRecoveryOptions(flags)) {
|
|
1750
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryFlags"));
|
|
1751
|
-
return 1;
|
|
1752
|
-
}
|
|
1753
|
-
let anyFailed = false;
|
|
1754
|
-
for (const { experiment, selectedEvalIds } of selectedForTeardown) {
|
|
1755
|
-
const teardown = experimentTeardownOf(experiment);
|
|
1756
|
-
if (teardown === undefined) {
|
|
1757
|
-
// Discovery normally reaches this branch only for an absent
|
|
1758
|
-
// teardown, because defineExperiment rejects non-functions. Keep
|
|
1759
|
-
// the CLI boundary equally strict for dynamically supplied
|
|
1760
|
-
// definitions: a truthy or null-ish impostor must never be
|
|
1761
|
-
// treated as a completed recovery hook.
|
|
1762
|
-
if (experiment.teardown !== undefined) {
|
|
1763
|
-
anyFailed = true;
|
|
1764
|
-
yield* writeStderr((0, index_ts_5.t)("define.experimentTeardownNotFunction"));
|
|
1765
|
-
}
|
|
1766
|
-
continue;
|
|
1767
|
-
}
|
|
1768
|
-
const ctx = {
|
|
1769
|
-
experimentId: experiment.id,
|
|
1770
|
-
selectedEvalIds,
|
|
1771
|
-
signal: new AbortController().signal,
|
|
1772
|
-
progress: () => { },
|
|
1773
|
-
diagnostic: (input) => process.stderr.write(`${input.message}\n`),
|
|
1774
|
-
};
|
|
1775
|
-
if (explicitRecovery !== undefined) {
|
|
1776
|
-
// The exact registration was inspected after the immutable
|
|
1777
|
-
// recovery marker was claimed. Do not re-scan by current
|
|
1778
|
-
// Experiment identity: its sharedState declaration can have
|
|
1779
|
-
// changed, and broad scans could claim another run's cleanup.
|
|
1780
|
-
const outcome = yield* effect_1.Effect.either(cliEffect("run experiment teardown", (0, cleanup_timeout_ts_1.cleanupCallback)(() => teardown(ctx))));
|
|
1781
|
-
if (effect_1.Either.isRight(outcome)) {
|
|
1782
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.teardownDone", { experimentId: experiment.id }));
|
|
1783
|
-
}
|
|
1784
|
-
else {
|
|
1785
|
-
anyFailed = true;
|
|
1786
|
-
const error = outcome.left.cause;
|
|
1787
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.teardownFailed", {
|
|
1788
|
-
experimentId: experiment.id,
|
|
1789
|
-
message: error instanceof Error ? error.message : String(error),
|
|
1790
|
-
}));
|
|
1791
|
-
}
|
|
1792
|
-
continue;
|
|
1793
|
-
}
|
|
1794
|
-
const registrations = yield* (0, teardown_registry_ts_1.readTeardownRegistrationsEffect)(niceevalRootForTeardown).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed([])));
|
|
1795
|
-
const matching = registrations.filter(({ entry }) => entry.experimentId === experiment.id);
|
|
1796
|
-
const orphaned = matching.filter(({ entry }) => (0, teardown_registry_ts_1.isOrphanedTeardownRegistration)(entry, (0, node_os_1.hostname)()));
|
|
1797
|
-
const executions = effect_1.Effect.all(orphaned.map(({ id }) => (0, teardown_registry_ts_1.removeTeardownRegistrationIfPresentEffect)(niceevalRootForTeardown, id).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(false)), effect_1.Effect.map((claimed) => claimed ? id : undefined))), { concurrency: "unbounded" }).pipe(effect_1.Effect.map((claimed) => matching.length === 0 ? [undefined] : claimed.filter((id) => id !== undefined)));
|
|
1798
|
-
for (const _ of yield* executions) {
|
|
1799
|
-
const outcome = yield* effect_1.Effect.either(cliEffect("run experiment teardown", (0, cleanup_timeout_ts_1.cleanupCallback)(() => teardown(ctx))));
|
|
1800
|
-
if (effect_1.Either.isRight(outcome)) {
|
|
1801
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.teardownDone", { experimentId: experiment.id }));
|
|
1802
|
-
}
|
|
1803
|
-
else {
|
|
1804
|
-
anyFailed = true;
|
|
1805
|
-
const error = outcome.left.cause;
|
|
1806
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.teardownFailed", {
|
|
1807
|
-
experimentId: experiment.id,
|
|
1808
|
-
message: error instanceof Error ? error.message : String(error),
|
|
1809
|
-
}));
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
if (!anyFailed && explicitRecovery !== undefined) {
|
|
1814
|
-
// A waiter may enter as soon as `complete...` publishes free. The
|
|
1815
|
-
// old durable teardown obligation must therefore be removed first.
|
|
1816
|
-
// Any read/claim failure leaves the immutable recovery marker
|
|
1817
|
-
// closed, so an operator can inspect and retry instead of replaying
|
|
1818
|
-
// old teardown after a new owner starts.
|
|
1819
|
-
const cleared = yield* effect_1.Effect.either(clearExactRecoveryTeardownRegistrationEffect({
|
|
1820
|
-
niceevalRoot: niceevalRootForTeardown,
|
|
1821
|
-
record: explicitRecovery.record,
|
|
1822
|
-
currentHost: (0, node_os_1.hostname)(),
|
|
1823
|
-
expectedRegistration: explicitRecovery.registrationWasPresent,
|
|
1824
|
-
}));
|
|
1825
|
-
if (effect_1.Either.isLeft(cleared)) {
|
|
1826
|
-
yield* writeStderr((0, index_ts_5.t)("cli.exp.sharedStateRecoveryRegistrationFailed", {
|
|
1827
|
-
key: explicitRecovery.key,
|
|
1828
|
-
message: errorMessage(cleared.left),
|
|
1829
|
-
}));
|
|
1830
|
-
return 1;
|
|
1831
|
-
}
|
|
1832
|
-
const completed = yield* effect_1.Effect.either((0, shared_state_lease_ts_1.completeExplicitSharedStateRecoveryEffect)({
|
|
1833
|
-
niceevalRoot: niceevalRootForTeardown,
|
|
1834
|
-
key: explicitRecovery.key,
|
|
1835
|
-
ownerToken: explicitRecovery.ownerToken,
|
|
1836
|
-
recoveryId: explicitRecovery.recoveryId,
|
|
1837
|
-
localHost: (0, node_os_1.hostname)(),
|
|
1838
|
-
}));
|
|
1839
|
-
if (effect_1.Either.isLeft(completed)) {
|
|
1840
|
-
yield* writeStderr(`sharedState recovery could not release its exact owner token: ${errorMessage(completed.left)}\n`);
|
|
1841
|
-
return 1;
|
|
1842
|
-
}
|
|
1843
|
-
yield* writeStderr((0, index_ts_5.t)("runner.sharedStateExplicitRecovered", {
|
|
1844
|
-
key: explicitRecovery.key,
|
|
1845
|
-
experimentId: explicitRecovery.experimentId,
|
|
1846
|
-
ownerToken: explicitRecovery.ownerToken,
|
|
1847
|
-
}));
|
|
1848
|
-
}
|
|
1849
|
-
return anyFailed ? 1 : 0;
|
|
1850
|
-
}
|
|
1851
|
-
const experimentScopeIds = new Set();
|
|
1852
|
-
for (const { experiment, selectedEvalIds, selectorEvalIds } of selected) {
|
|
1853
|
-
for (const evalId of selectorEvalIds)
|
|
1854
|
-
experimentScopeIds.add(evalId);
|
|
1855
|
-
agentRuns.push(agentRunFromExperiment(experiment, selectedEvalIds, flags));
|
|
1856
|
-
}
|
|
1857
|
-
for (const pattern of extraPatterns) {
|
|
1858
|
-
const matches = (0, aggregate_ts_1.evalPrefixPredicate)([pattern]);
|
|
1859
|
-
if ([...experimentScopeIds].some((id) => matches(id)))
|
|
1860
|
-
continue;
|
|
1861
|
-
yield* writeStderr((0, index_ts_5.t)("cli.experiment.noEvalPrefixMatch", {
|
|
1862
|
-
pattern,
|
|
1863
|
-
selection: expArg ?? (0, index_ts_5.t)("cli.all"),
|
|
1864
|
-
}));
|
|
1865
|
-
return 1;
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
else {
|
|
1869
|
-
const listed = yield* cliEffect("list experiments", index_ts_1.experimentHost.list({
|
|
1870
|
-
cwd,
|
|
1871
|
-
...(flags.tag === undefined ? {} : { tag: flags.tag }),
|
|
1872
|
-
}));
|
|
1873
|
-
const selectedByPattern = yield* effect_1.Effect.all(positionals.map((selector) => cliEffect("select experiments", index_ts_1.experimentHost.list({
|
|
1874
|
-
cwd,
|
|
1875
|
-
...(flags.tag === undefined ? {} : { tag: flags.tag }),
|
|
1876
|
-
selector,
|
|
1877
|
-
}))));
|
|
1878
|
-
const asExp = [...new Map(selectedByPattern
|
|
1879
|
-
.flatMap((selection) => selection.selections)
|
|
1880
|
-
.map(({ experiment }) => [experiment.id, experiment])).values()];
|
|
1881
|
-
yield* writeStderr((0, index_ts_5.t)("cli.run.experimentRequired"));
|
|
1882
|
-
if (asExp.length > 0) {
|
|
1883
|
-
yield* writeStderr((0, index_ts_5.t)("cli.run.experimentRequiredHint", {
|
|
1884
|
-
pattern: positionals[0] ?? "",
|
|
1885
|
-
kind: asExp.length > 1 ? (0, index_ts_5.t)("cli.experimentGroup") : "",
|
|
1886
|
-
}));
|
|
1887
|
-
}
|
|
1888
|
-
else {
|
|
1889
|
-
yield* writeStderr((0, index_ts_5.t)("cli.run.experimentRequiredKnown", {
|
|
1890
|
-
experiments: listed.experimentIds.join(", ") || (0, index_ts_5.t)("cli.none"),
|
|
1891
|
-
}));
|
|
1892
|
-
}
|
|
1893
|
-
return 1;
|
|
1894
|
-
}
|
|
1895
|
-
const outputForm = (0, index_ts_4.resolveOutputForm)({ json: flags.json, isTTY: process.stderr.isTTY === true });
|
|
1896
|
-
const matchedByRun = agentRuns.map((run) => (0, eval_selection_ts_1.selectedEvalsForRun)(evals, run));
|
|
1897
|
-
const totalAttempts = agentRuns.reduce((sum, run, index) => sum + matchedByRun[index].length * run.attempts, 0);
|
|
1898
|
-
const uniqueEvalIds = new Set(matchedByRun.flat().map((evalDefinition) => evalDefinition.id));
|
|
1899
|
-
if (totalAttempts === 0) {
|
|
1900
|
-
yield* writeStderr((0, index_ts_5.t)("cli.experiment.noEvalsSelected", {
|
|
1901
|
-
selection: experimentSelection,
|
|
1902
|
-
experiments: availableExperimentPaths,
|
|
1903
|
-
}));
|
|
1904
|
-
return 1;
|
|
1905
|
-
}
|
|
1906
|
-
if (command === "check") {
|
|
1907
|
-
yield* cliEffect("link run sandboxes", (0, sandbox_selection_ts_1.linkRunSandboxes)(evals, agentRuns));
|
|
1908
|
-
const pairCount = matchedByRun.reduce((sum, selected) => sum + selected.length, 0);
|
|
1909
|
-
yield* writeStdout(`Sandbox layers linked: ${pairCount} pair${pairCount === 1 ? "" : "s"}.\n`);
|
|
1910
|
-
return 0;
|
|
1911
|
-
}
|
|
1912
|
-
const recordTarget = yield* effect_1.Effect.try({
|
|
1913
|
-
try: () => parseActualRecordRoot(cwd, flags.record),
|
|
1914
|
-
catch: (cause) => cliFailure("parse experiment Record root", cause),
|
|
1915
|
-
});
|
|
1916
|
-
const experimentPlan = yield* cliEffect("plan Experiment", index_ts_1.experimentHost.plan({
|
|
1917
|
-
evals,
|
|
1918
|
-
agentRuns,
|
|
1919
|
-
config,
|
|
1920
|
-
recordRoot: recordTarget.root,
|
|
1921
|
-
...(flags.rerun === undefined ? {} : { rerun: flags.rerun }),
|
|
1922
|
-
...(flags.keepSandbox === undefined ? {} : { keepSandbox: flags.keepSandbox }),
|
|
1923
|
-
...(flags.dry ? { previewStartedAt: Date.now() } : {}),
|
|
1924
|
-
}));
|
|
1925
|
-
const targetPlan = experimentPlan.target;
|
|
1926
|
-
if (flags.dry) {
|
|
1927
|
-
const current = experimentPlan.current;
|
|
1928
|
-
if (current === undefined) {
|
|
1929
|
-
return yield* effect_1.Effect.fail(cliFailure("preview Experiment", new Error("Experiment Host did not return the requested dry plan.")));
|
|
1930
|
-
}
|
|
1931
|
-
const currentPlan = projectCurrentDryPlan(current);
|
|
1932
|
-
const rows = currentDryPlanRows(currentPlan);
|
|
1933
|
-
const now = Date.now();
|
|
1934
|
-
const lockedFlags = yield* effect_1.Effect.all(rows.map((row) => (0, lock_ts_1.readCaseLockEffect)(coordinationRoot, row.experimentId, row.evalId).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(undefined)), effect_1.Effect.map((lock) => lock !== undefined && !(0, lock_ts_1.isCaseLockExpired)(lock, now)))), { concurrency: "unbounded" });
|
|
1935
|
-
const evalGroupsByEvalId = new Map(evals.flatMap((evalDef) => evalDef.evalGroup === undefined ? [] : [[evalDef.id, {
|
|
1936
|
-
...evalDef.evalGroup,
|
|
1937
|
-
index: evalDef.evalGroup.evalIds.indexOf(evalDef.id),
|
|
1938
|
-
}]]));
|
|
1939
|
-
const rowsWithLocks = Object.freeze(rows.map((row, index) => {
|
|
1940
|
-
const evalGroup = evalGroupsByEvalId.get(row.evalId);
|
|
1941
|
-
return Object.freeze({
|
|
1942
|
-
...row,
|
|
1943
|
-
...(evalGroup === undefined ? {} : {
|
|
1944
|
-
evalGroupId: evalGroup.id,
|
|
1945
|
-
evalGroupIndex: evalGroup.index,
|
|
1946
|
-
}),
|
|
1947
|
-
...(lockedFlags[index] ? { locked: true } : {}),
|
|
1948
|
-
});
|
|
1949
|
-
}));
|
|
1950
|
-
const occurrenceAudits = new Map();
|
|
1951
|
-
for (const pair of targetPlan.preparedPairsByKey.values()) {
|
|
1952
|
-
for (const occurrence of pair.plugin.occurrences) {
|
|
1953
|
-
occurrenceAudits.set(JSON.stringify(occurrence.audit), occurrence.audit);
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
const pluginAudit = Object.freeze({
|
|
1957
|
-
occurrences: Object.freeze([...occurrenceAudits.values()]),
|
|
1958
|
-
});
|
|
1959
|
-
const input = {
|
|
1960
|
-
totalAttempts,
|
|
1961
|
-
evals: uniqueEvalIds.size,
|
|
1962
|
-
configs: agentRuns.length,
|
|
1963
|
-
attempts: Math.max(1, ...agentRuns.map((run) => run.attempts)),
|
|
1964
|
-
rows: rowsWithLocks,
|
|
1965
|
-
pluginAudit,
|
|
1966
|
-
};
|
|
1967
|
-
if (outputForm === "json") {
|
|
1968
|
-
yield* writeStdout(renderCurrentDryPlanJson(input));
|
|
1969
|
-
}
|
|
1970
|
-
else {
|
|
1971
|
-
yield* writeStdout(renderCurrentDryPlan(input));
|
|
1972
|
-
}
|
|
1973
|
-
return 0;
|
|
1974
|
-
}
|
|
1975
|
-
const maxConcurrency = flags.maxConcurrency ?? config.maxConcurrency ?? (0, sandbox_selection_ts_1.recommendedConcurrencyForPreparedPairs)([...targetPlan.preparedPairsByKey.values()]);
|
|
1976
|
-
const experimentConcurrency = {};
|
|
1977
|
-
for (const run of agentRuns) {
|
|
1978
|
-
if (run.experimentId !== undefined && run.maxConcurrency !== undefined)
|
|
1979
|
-
experimentConcurrency[run.experimentId] = run.maxConcurrency;
|
|
1980
|
-
}
|
|
1981
|
-
const plan = {
|
|
1982
|
-
shape: { evals: uniqueEvalIds.size, configs: agentRuns.length, totalAttempts, maxConcurrency },
|
|
1983
|
-
...(Object.keys(experimentConcurrency).length === 0 ? {} : { experimentConcurrency }),
|
|
1984
|
-
};
|
|
1985
|
-
const io = (0, index_ts_4.createNodeFeedbackIO)();
|
|
1986
|
-
const commandLabel = ["niceeval", command, ...positionals].join(" ").trim();
|
|
1987
|
-
const renderer = outputForm === "human"
|
|
1988
|
-
? (0, index_ts_4.createHumanRenderer)({ io, command: commandLabel })
|
|
1989
|
-
: (0, index_ts_4.createJsonRenderer)({ io });
|
|
1990
|
-
const sessionTracker = new session_ts_1.SessionTracker(coordinationRoot);
|
|
1991
|
-
const coordinator = (0, index_ts_4.createFeedbackCoordinator)({
|
|
1992
|
-
profile: outputForm,
|
|
1993
|
-
renderer,
|
|
1994
|
-
io,
|
|
1995
|
-
onEvent: (event, state) => sessionTracker.onFeedback(event, state),
|
|
1996
|
-
});
|
|
1997
|
-
let resourcesReleased = false;
|
|
1998
|
-
const releaseResources = effect_1.Effect.suspend(() => {
|
|
1999
|
-
if (resourcesReleased)
|
|
2000
|
-
return effect_1.Effect.void;
|
|
2001
|
-
resourcesReleased = true;
|
|
2002
|
-
return releaseCliResources();
|
|
2003
|
-
});
|
|
2004
|
-
let sessionClosed = false;
|
|
2005
|
-
const closeSession = (input) => sessionTracker.close(input).pipe(effect_1.Effect.tap(() => effect_1.Effect.sync(() => { sessionClosed = true; })));
|
|
2006
|
-
yield* effect_1.Effect.addFinalizer(() => releaseResources);
|
|
2007
|
-
yield* effect_1.Effect.addFinalizer(() => sessionClosed
|
|
2008
|
-
? effect_1.Effect.void
|
|
2009
|
-
: closeSession({ status: "incomplete" }).pipe(effect_1.Effect.ignore));
|
|
2010
|
-
yield* effect_1.Effect.addFinalizer(() => cliPromise("stop dynamic feedback", () => coordinator.stopDynamic()).pipe(effect_1.Effect.ignore));
|
|
2011
|
-
yield* effect_1.Effect.acquireRelease(effect_1.Effect.sync(() => (0, index_ts_4.createInputGuard)({
|
|
2012
|
-
stdin: (0, index_ts_4.createNodeInputGuardStdin)(),
|
|
2013
|
-
stderrIsTTY: io.stderr.isTTY,
|
|
2014
|
-
coordinator,
|
|
2015
|
-
onInterrupt: () => process.emit("SIGINT"),
|
|
2016
|
-
})), (inputGuard) => effect_1.Effect.sync(() => inputGuard.stop()));
|
|
2017
|
-
let invocationSummary;
|
|
2018
|
-
const reporters = [];
|
|
2019
|
-
if (flags.junit)
|
|
2020
|
-
reporters.push({ reporter: (0, json_ts_1.JUnit)(flags.junit), name: "junit", required: true, target: flags.junit });
|
|
2021
|
-
(config.reporters ?? []).forEach((reporter, index) => {
|
|
2022
|
-
reporters.push({ reporter, name: `config-reporter-${index}`, required: false });
|
|
2023
|
-
});
|
|
2024
|
-
reporters.push({
|
|
2025
|
-
name: "cli-summary",
|
|
2026
|
-
required: false,
|
|
2027
|
-
reporter: {
|
|
2028
|
-
onInvocationComplete(summary) {
|
|
2029
|
-
invocationSummary = summary;
|
|
2030
|
-
},
|
|
2031
|
-
},
|
|
2032
|
-
});
|
|
2033
|
-
// This synchronous hand-off and the following Experiment Host yield have no
|
|
2034
|
-
// asynchronous gap. A first signal therefore either interrupts the root
|
|
2035
|
-
// before dispatch begins, or aborts this Invocation so it can close its
|
|
2036
|
-
// durable interrupted receipt before the CLI returns.
|
|
2037
|
-
const ownsGracefulDispatch = yield* effect_1.Effect.sync(() => interruption?.enterGracefulDispatch() ?? true);
|
|
2038
|
-
if (!ownsGracefulDispatch)
|
|
2039
|
-
yield* effect_1.Effect.interrupt;
|
|
2040
|
-
// Runner remains Effect-native. During graceful dispatch the application
|
|
2041
|
-
// edge translates SIGINT into this Invocation signal, letting dispatch
|
|
2042
|
-
// settle and the receipt close before the process exits with 130.
|
|
2043
|
-
const receipt = yield* cliEffect("run Experiment", index_ts_1.experimentHost.run({
|
|
2044
|
-
config,
|
|
2045
|
-
evals,
|
|
2046
|
-
agentRuns,
|
|
2047
|
-
reporters,
|
|
2048
|
-
maxConcurrency,
|
|
2049
|
-
maxBuildConcurrency,
|
|
2050
|
-
keepSandbox: flags.keepSandbox,
|
|
2051
|
-
rerun: flags.rerun,
|
|
2052
|
-
coordinationRoot,
|
|
2053
|
-
recordRoot: recordTarget.root,
|
|
2054
|
-
session: sessionTracker,
|
|
2055
|
-
onCurrentRecordReusePlan: (current) => effect_1.Effect.sync(() => coordinator.start({
|
|
2056
|
-
...plan,
|
|
2057
|
-
shape: { ...plan.shape, runIds: current.runIds },
|
|
2058
|
-
reused: current.reused,
|
|
2059
|
-
...(current.reusedFailures.length === 0 ? {} : { reusedFailures: current.reusedFailures }),
|
|
2060
|
-
})),
|
|
2061
|
-
...(interruption === undefined ? {} : { signal: interruption.invocationSignal }),
|
|
2062
|
-
}));
|
|
2063
|
-
yield* releaseResources;
|
|
2064
|
-
const summary = invocationSummary;
|
|
2065
|
-
if (summary === undefined) {
|
|
2066
|
-
return yield* effect_1.Effect.fail(cliFailure("collect invocation summary", new Error("Runner completed without an invocation summary.")));
|
|
2067
|
-
}
|
|
2068
|
-
const completion = assembleInvocationCompletion(coordinator.state);
|
|
2069
|
-
yield* closeSession({ status: completion.status, completion, receipt, completedAt: receipt.completedAt });
|
|
2070
|
-
yield* cliPromise("finish feedback", () => coordinator.finish({ summary, completion, receipt }));
|
|
2071
|
-
const foldedStats = foldInvocationEvalStats(summary);
|
|
2072
|
-
return (0, index_ts_4.computeExitCode)({ ...summary, failed: foldedStats.failed, errored: foldedStats.errored }, completion);
|
|
2073
|
-
});
|
|
2074
|
-
}
|
|
2075
|
-
/** Resolve the CLI's one portable Record target without treating `.niceeval` as Record data. */
|
|
2076
|
-
function parseActualRecordRoot(cwd, rootText) {
|
|
2077
|
-
if (rootText !== undefined && rootText.trim() === "") {
|
|
2078
|
-
throw usageError("--record requires an actual Record root directory.\n");
|
|
2079
|
-
}
|
|
2080
|
-
const rootPath = (0, node_path_1.resolve)(cwd, rootText ?? ".niceeval/record");
|
|
2081
|
-
const root = (0, index_ts_2.makeRecordRoot)(rootPath);
|
|
2082
|
-
if (effect_1.Either.isLeft(root)) {
|
|
2083
|
-
throw usageError(`Invalid --record root: ${root.left.code}.\n`);
|
|
2084
|
-
}
|
|
2085
|
-
return Object.freeze({ root: root.right, rootPath });
|
|
2086
|
-
}
|
|
2087
|
-
/**
|
|
2088
|
-
* The CLI validates selection identity before Record I/O. It never turns a
|
|
2089
|
-
* prefix, a path, or a directory entry into a Run / Experiment selection.
|
|
2090
|
-
*/
|
|
2091
|
-
function parseReportCliRequest(input) {
|
|
2092
|
-
const unsupported = reportUnsupportedFlag(input.command, input.flags);
|
|
2093
|
-
if (unsupported !== undefined) {
|
|
2094
|
-
throw usageError(`niceeval ${input.command} does not accept ${unsupported}.\n`);
|
|
2095
|
-
}
|
|
2096
|
-
const runs = input.flags.run ?? [];
|
|
2097
|
-
const { root, rootPath } = parseActualRecordRoot(input.cwd, input.flags.record);
|
|
2098
|
-
const page = parseReportRoute(input.flags.page);
|
|
2099
|
-
const evidenceReports = [
|
|
2100
|
-
input.flags.source !== undefined ? "--source" : undefined,
|
|
2101
|
-
input.flags.execution ? "--execution" : undefined,
|
|
2102
|
-
input.flags.timing !== undefined ? "--timing" : undefined,
|
|
2103
|
-
].filter((flag) => flag !== undefined);
|
|
2104
|
-
if (evidenceReports.length > 1) {
|
|
2105
|
-
throw usageError(`niceeval show chooses one evidence Report at a time; remove all but one of ${evidenceReports.join(", ")}.\n`);
|
|
2106
|
-
}
|
|
2107
|
-
if (!input.flags.execution && input.flags.grep !== undefined) {
|
|
2108
|
-
throw usageError("niceeval show --grep only combines with --execution.\n");
|
|
2109
|
-
}
|
|
2110
|
-
if (input.flags.execution) {
|
|
2111
|
-
const report = (0, execution_ts_1.executionEvidenceReport)(executionEvidenceOptions(input.flags));
|
|
2112
|
-
if (input.positionals.length !== 1) {
|
|
2113
|
-
throw usageError("niceeval show --execution requires exactly one current Record Attempt locator.\n");
|
|
2114
|
-
}
|
|
2115
|
-
if (runs.length > 0 || input.flags.experiment !== undefined) {
|
|
2116
|
-
throw usageError("niceeval show --execution uses its Attempt locator as the only selection; remove --run and --experiment.\n");
|
|
2117
|
-
}
|
|
2118
|
-
if (input.flags.report !== undefined) {
|
|
2119
|
-
throw usageError("niceeval show --execution selects its built-in execution Report; remove --report.\n");
|
|
2120
|
-
}
|
|
2121
|
-
const locator = input.positionals[0];
|
|
2122
|
-
if (locator === undefined) {
|
|
2123
|
-
throw usageError("niceeval show --execution requires one current Record Attempt locator.\n");
|
|
2124
|
-
}
|
|
2125
|
-
const parsedLocator = parseCurrentAttemptLocator(locator);
|
|
2126
|
-
return Object.freeze({
|
|
2127
|
-
command: input.command,
|
|
2128
|
-
cwd: input.cwd,
|
|
2129
|
-
root,
|
|
2130
|
-
rootPath,
|
|
2131
|
-
target: Object.freeze({ kind: "attempt", locator: parsedLocator }),
|
|
2132
|
-
reportSelection: Object.freeze({ kind: "fixed", report }),
|
|
2133
|
-
themeSelection: themeSelection(input.cwd, input.flags.theme),
|
|
2134
|
-
...(page === undefined ? {} : { page }),
|
|
2135
|
-
});
|
|
2136
|
-
}
|
|
2137
|
-
if (input.flags.timing !== undefined) {
|
|
2138
|
-
if (input.positionals.length !== 1) {
|
|
2139
|
-
throw usageError("niceeval show --timing requires exactly one current Record Attempt locator.\n");
|
|
2140
|
-
}
|
|
2141
|
-
if (runs.length > 0 || input.flags.experiment !== undefined) {
|
|
2142
|
-
throw usageError("niceeval show --timing uses its Attempt locator as the only selection; remove --run and --experiment.\n");
|
|
2143
|
-
}
|
|
2144
|
-
if (input.flags.report !== undefined) {
|
|
2145
|
-
throw usageError("niceeval show --timing selects its built-in timing Report; remove --report.\n");
|
|
2146
|
-
}
|
|
2147
|
-
const locator = input.positionals[0];
|
|
2148
|
-
if (locator === undefined) {
|
|
2149
|
-
throw usageError("niceeval show --timing requires one current Record Attempt locator.\n");
|
|
2150
|
-
}
|
|
2151
|
-
const parsedLocator = parseCurrentAttemptLocator(locator);
|
|
2152
|
-
return Object.freeze({
|
|
2153
|
-
command: input.command,
|
|
2154
|
-
cwd: input.cwd,
|
|
2155
|
-
root,
|
|
2156
|
-
rootPath,
|
|
2157
|
-
target: Object.freeze({ kind: "attempt", locator: parsedLocator }),
|
|
2158
|
-
reportSelection: Object.freeze({
|
|
2159
|
-
kind: "fixed",
|
|
2160
|
-
report: (0, execution_ts_1.timingEvidenceReport)({ mode: input.flags.timing }),
|
|
2161
|
-
}),
|
|
2162
|
-
themeSelection: themeSelection(input.cwd, input.flags.theme),
|
|
2163
|
-
...(page === undefined ? {} : { page }),
|
|
2164
|
-
});
|
|
2165
|
-
}
|
|
2166
|
-
if (input.flags.source !== undefined) {
|
|
2167
|
-
if (input.positionals.length !== 1) {
|
|
2168
|
-
throw usageError("niceeval show --source requires exactly one current Record Attempt locator.\n");
|
|
2169
|
-
}
|
|
2170
|
-
if (runs.length > 0 || input.flags.experiment !== undefined) {
|
|
2171
|
-
throw usageError("niceeval show --source uses its Attempt locator as the only selection; remove --run and --experiment.\n");
|
|
2172
|
-
}
|
|
2173
|
-
if (input.flags.report !== undefined) {
|
|
2174
|
-
throw usageError("niceeval show --source selects its built-in source Report; remove --report.\n");
|
|
2175
|
-
}
|
|
2176
|
-
const locator = input.positionals[0];
|
|
2177
|
-
if (locator === undefined) {
|
|
2178
|
-
throw usageError("niceeval show --source requires one current Record Attempt locator.\n");
|
|
2179
|
-
}
|
|
2180
|
-
const parsedLocator = parseCurrentAttemptLocator(locator);
|
|
2181
|
-
const report = (0, source_ts_1.sourceEvidenceReport)({
|
|
2182
|
-
...(typeof input.flags.source === "string" ? { file: input.flags.source } : {}),
|
|
2183
|
-
});
|
|
2184
|
-
return Object.freeze({
|
|
2185
|
-
command: input.command,
|
|
2186
|
-
cwd: input.cwd,
|
|
2187
|
-
root,
|
|
2188
|
-
rootPath,
|
|
2189
|
-
target: Object.freeze({ kind: "attempt", locator: parsedLocator }),
|
|
2190
|
-
reportSelection: Object.freeze({ kind: "fixed", report }),
|
|
2191
|
-
themeSelection: themeSelection(input.cwd, input.flags.theme),
|
|
2192
|
-
...(page === undefined ? {} : { page }),
|
|
2193
|
-
});
|
|
2194
|
-
}
|
|
2195
|
-
if (input.positionals.length > 0) {
|
|
2196
|
-
if (input.positionals.length !== 1) {
|
|
2197
|
-
throw usageError(`niceeval ${input.command} accepts one exact Attempt locator, the default current-project selection, or --run.\n`);
|
|
2198
|
-
}
|
|
2199
|
-
if (runs.length > 0 || input.flags.experiment !== undefined) {
|
|
2200
|
-
throw usageError(`niceeval ${input.command} uses its Attempt locator as the only selection; remove --run and --experiment.\n`);
|
|
2201
|
-
}
|
|
2202
|
-
const locator = input.positionals[0];
|
|
2203
|
-
if (locator === undefined || !locator.startsWith("@")) {
|
|
2204
|
-
throw usageError(`niceeval ${input.command} selects Record data only with a canonical @1<12-character-body> locator, --run, or no selector for current-project results.\n`);
|
|
2205
|
-
}
|
|
2206
|
-
const parsedLocator = parseCurrentAttemptLocator(locator);
|
|
2207
|
-
return Object.freeze({
|
|
2208
|
-
command: input.command,
|
|
2209
|
-
cwd: input.cwd,
|
|
2210
|
-
root,
|
|
2211
|
-
rootPath,
|
|
2212
|
-
target: Object.freeze({ kind: "attempt", locator: parsedLocator }),
|
|
2213
|
-
reportSelection: input.flags.report === undefined
|
|
2214
|
-
? Object.freeze({ kind: "fixed", report: attempt_overview_ts_1.defaultAttemptOverviewReport })
|
|
2215
|
-
: reportSelection(input.cwd, input.flags.report),
|
|
2216
|
-
themeSelection: themeSelection(input.cwd, input.flags.theme),
|
|
2217
|
-
...(page === undefined ? {} : { page }),
|
|
2218
|
-
});
|
|
2219
|
-
}
|
|
2220
|
-
if (runs.length > 0 && input.flags.experiment !== undefined) {
|
|
2221
|
-
throw usageError("--experiment narrows the default current-project selection; it cannot combine with explicit --run.\n");
|
|
2222
|
-
}
|
|
2223
|
-
const report = runs.length > 0 && input.flags.report === undefined
|
|
2224
|
-
? Object.freeze({ kind: "fixed", report: run_membership_overview_ts_1.defaultRunMembershipOverviewReport })
|
|
2225
|
-
: reportSelection(input.cwd, input.flags.report);
|
|
2226
|
-
const theme = themeSelection(input.cwd, input.flags.theme);
|
|
2227
|
-
const target = runs.length > 0
|
|
2228
|
-
? Object.freeze({ kind: "selection", selection: explicitSelection(runs) })
|
|
2229
|
-
: Object.freeze({
|
|
2230
|
-
kind: "project-current",
|
|
2231
|
-
...(input.flags.experiment === undefined
|
|
2232
|
-
? {}
|
|
2233
|
-
: { experimentSelectors: uniqueExperimentSelectors(input.flags.experiment) }),
|
|
2234
|
-
});
|
|
2235
|
-
return Object.freeze({
|
|
2236
|
-
command: input.command,
|
|
2237
|
-
cwd: input.cwd,
|
|
2238
|
-
root,
|
|
2239
|
-
rootPath,
|
|
2240
|
-
target,
|
|
2241
|
-
reportSelection: report,
|
|
2242
|
-
themeSelection: theme,
|
|
2243
|
-
...(page === undefined ? {} : { page }),
|
|
2244
|
-
});
|
|
2245
|
-
}
|
|
2246
|
-
function executionEvidenceOptions(flags) {
|
|
2247
|
-
if (flags.grep === undefined)
|
|
2248
|
-
return Object.freeze({});
|
|
2249
|
-
try {
|
|
2250
|
-
new RegExp(flags.grep);
|
|
2251
|
-
return Object.freeze({ grep: flags.grep });
|
|
2252
|
-
}
|
|
2253
|
-
catch {
|
|
2254
|
-
throw usageError(`--grep "${flags.grep}" is not a valid JavaScript regular expression.\n`);
|
|
2255
|
-
}
|
|
2256
|
-
}
|
|
2257
|
-
function reportUnsupportedFlag(command, flags) {
|
|
2258
|
-
const unsupported = [
|
|
2259
|
-
["--agent", flags.agent],
|
|
2260
|
-
["--model", flags.model],
|
|
2261
|
-
["--attempts", flags.attempts],
|
|
2262
|
-
["--max-concurrency", flags.maxConcurrency],
|
|
2263
|
-
["--max-build-concurrency", flags.maxBuildConcurrency],
|
|
2264
|
-
["--timeout", flags.timeout],
|
|
2265
|
-
["--budget", flags.budget],
|
|
2266
|
-
["--tag", flags.tag],
|
|
2267
|
-
["--junit", flags.junit],
|
|
2268
|
-
["--json", command === "view" && flags.json],
|
|
2269
|
-
["--smoke", flags.smoke],
|
|
2270
|
-
["--dry", flags.dry],
|
|
2271
|
-
["--force", flags.force],
|
|
2272
|
-
["--rerun", flags.rerun],
|
|
2273
|
-
["--early-exit/--no-early-exit", flags.earlyExit],
|
|
2274
|
-
["--open/--no-open", command === "show" ? flags.open : undefined],
|
|
2275
|
-
["--out", command === "show" ? flags.out : undefined],
|
|
2276
|
-
["--port", command === "show" ? flags.port : undefined],
|
|
2277
|
-
["--host", command === "show" ? flags.host : undefined],
|
|
2278
|
-
["--source", command === "show" ? undefined : flags.source],
|
|
2279
|
-
["--execution", command === "show" ? undefined : flags.execution],
|
|
2280
|
-
["--diff", flags.diff || flags.diffPath !== undefined],
|
|
2281
|
-
["--grep", command === "show" ? undefined : flags.grep],
|
|
2282
|
-
["--timing", command === "show" ? undefined : flags.timing],
|
|
2283
|
-
["--keep-sandbox", flags.keepSandbox],
|
|
2284
|
-
["--all", flags.all],
|
|
2285
|
-
["--window", flags.window],
|
|
2286
|
-
["--path", flags.sandboxPath],
|
|
2287
|
-
["--leave-running", flags.leaveRunning],
|
|
2288
|
-
["--history", flags.history],
|
|
2289
|
-
["--usage", flags.usage],
|
|
2290
|
-
["--stats", flags.stats],
|
|
2291
|
-
["--orphans", flags.orphans],
|
|
2292
|
-
["--teardown", flags.teardown],
|
|
2293
|
-
["--recover-shared-state", flags.recoverSharedState],
|
|
2294
|
-
["--owner-token", flags.ownerToken],
|
|
2295
|
-
["--confirm-owner-terminated", flags.confirmOwnerTerminated],
|
|
2296
|
-
["--confirm-remote-quiesced", flags.confirmRemoteQuiesced],
|
|
2297
|
-
["--yes", flags.yes],
|
|
2298
|
-
];
|
|
2299
|
-
const found = unsupported.find(([, value]) => value !== undefined && value !== false && (!Array.isArray(value) || value.length > 0));
|
|
2300
|
-
return found?.[0];
|
|
2301
|
-
}
|
|
2302
|
-
function parseReportRoute(value) {
|
|
2303
|
-
if (value === undefined)
|
|
2304
|
-
return undefined;
|
|
2305
|
-
const issue = (0, paths_ts_1.validateReportRoute)(value);
|
|
2306
|
-
if (issue !== undefined) {
|
|
2307
|
-
throw usageError(`Invalid --page route "${value}": ${issue.reason}.\n`);
|
|
2308
|
-
}
|
|
2309
|
-
return value;
|
|
2310
|
-
}
|
|
2311
|
-
function reportSelection(cwd, value) {
|
|
2312
|
-
if (value === undefined)
|
|
2313
|
-
return Object.freeze({ kind: "config" });
|
|
2314
|
-
if (value === "standard")
|
|
2315
|
-
return Object.freeze({ kind: "built-in", name: "standard" });
|
|
2316
|
-
if (isTrustedModulePath(value)) {
|
|
2317
|
-
return Object.freeze({ kind: "module", path: (0, node_ts_1.resolveTrustedModulePath)(cwd, value) });
|
|
2318
|
-
}
|
|
2319
|
-
throw usageError("--report accepts built-in standard or an explicit trusted module path.\n");
|
|
2320
|
-
}
|
|
2321
|
-
function themeSelection(cwd, value) {
|
|
2322
|
-
if (value === undefined)
|
|
2323
|
-
return Object.freeze({ kind: "config" });
|
|
2324
|
-
if (value === "basalt" || value === "chalk") {
|
|
2325
|
-
return Object.freeze({ kind: "built-in", name: value });
|
|
2326
|
-
}
|
|
2327
|
-
if (isTrustedModulePath(value)) {
|
|
2328
|
-
return Object.freeze({ kind: "module", path: (0, node_ts_1.resolveTrustedModulePath)(cwd, value) });
|
|
2329
|
-
}
|
|
2330
|
-
throw usageError("--theme accepts built-in basalt/chalk or an explicit trusted module path.\n");
|
|
2331
|
-
}
|
|
2332
|
-
function isTrustedModulePath(value) {
|
|
2333
|
-
return value.startsWith("./") || value.startsWith("../") || (0, node_path_1.isAbsolute)(value);
|
|
2334
|
-
}
|
|
2335
|
-
function explicitSelection(values) {
|
|
2336
|
-
const runIds = uniqueExactRunIds(values);
|
|
2337
|
-
const [first, ...rest] = runIds;
|
|
2338
|
-
if (first === undefined) {
|
|
2339
|
-
throw usageError("niceeval show/view requires one or more --run <run-id> values.\n");
|
|
2340
|
-
}
|
|
2341
|
-
const nonEmptyRunIds = [first, ...rest];
|
|
2342
|
-
return Object.freeze({
|
|
2343
|
-
policy: "explicit-runs",
|
|
2344
|
-
runIds: nonEmptyRunIds,
|
|
2345
|
-
});
|
|
2346
|
-
}
|
|
2347
|
-
function uniqueExactRunIds(values) {
|
|
2348
|
-
const result = [];
|
|
2349
|
-
const seen = new Set();
|
|
2350
|
-
for (const value of values) {
|
|
2351
|
-
const decoded = effect_1.Schema.decodeUnknownEither(index_ts_2.RunIdSchema)(value);
|
|
2352
|
-
if (effect_1.Either.isLeft(decoded)) {
|
|
2353
|
-
throw usageError(`Invalid --run value "${value}": expected one exact portable RunId.\n`);
|
|
2354
|
-
}
|
|
2355
|
-
if (!seen.has(decoded.right)) {
|
|
2356
|
-
seen.add(decoded.right);
|
|
2357
|
-
result.push(decoded.right);
|
|
2358
|
-
}
|
|
2359
|
-
}
|
|
2360
|
-
return Object.freeze(result);
|
|
2361
|
-
}
|
|
2362
|
-
function uniqueExperimentSelectors(values) {
|
|
2363
|
-
const result = [];
|
|
2364
|
-
const seen = new Set();
|
|
2365
|
-
for (const value of values) {
|
|
2366
|
-
const decoded = effect_1.Schema.decodeUnknownEither(identifiers_ts_1.ExperimentIdSchema)(value);
|
|
2367
|
-
if (effect_1.Either.isLeft(decoded)) {
|
|
2368
|
-
throw usageError(`Invalid --experiment value "${value}": expected an Experiment selector.\n`);
|
|
2369
|
-
}
|
|
2370
|
-
if (!seen.has(decoded.right)) {
|
|
2371
|
-
seen.add(decoded.right);
|
|
2372
|
-
result.push(decoded.right);
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2375
|
-
return Object.freeze(result);
|
|
2376
|
-
}
|
|
2377
|
-
function selectedExperimentIds(slots) {
|
|
2378
|
-
return Object.freeze([...new Set(slots.map((slot) => slot.experimentId))]
|
|
2379
|
-
.sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right))));
|
|
2380
|
-
}
|
|
2381
|
-
/** Current Record locators use the strict scheme-1 short alias. */
|
|
2382
|
-
function parseCurrentAttemptLocator(value) {
|
|
2383
|
-
const parsed = (0, attempt_locator_ts_1.parseAttemptLocator)(value);
|
|
2384
|
-
if (!parsed.valid) {
|
|
2385
|
-
throw usageError(`Invalid Attempt locator "${value}": expected @1 followed by 12 canonical uppercase Crockford characters.\n`);
|
|
2386
|
-
}
|
|
2387
|
-
return parsed.locator;
|
|
2388
|
-
}
|
|
2389
|
-
/**
|
|
2390
|
-
* Config is deliberately fresh-read for every view rebuild even when CLI flags
|
|
2391
|
-
* override its current Report or Theme. It is both a trusted boundary and a
|
|
2392
|
-
* live input: an invalid config must not leave a stale hidden module graph.
|
|
2393
|
-
*/
|
|
2394
|
-
function loadCliReportInputs(request) {
|
|
2395
|
-
return effect_1.Effect.gen(function* () {
|
|
2396
|
-
const projectCurrent = request.target.kind === "project-current"
|
|
2397
|
-
? yield* cliEffect("load current project target", (0, project_current_ts_1.loadProjectCurrent)(request.cwd, {
|
|
2398
|
-
...(request.target.experimentSelectors === undefined
|
|
2399
|
-
? {}
|
|
2400
|
-
: { experiments: request.target.experimentSelectors }),
|
|
2401
|
-
freshImport: true,
|
|
2402
|
-
}))
|
|
2403
|
-
: undefined;
|
|
2404
|
-
const configured = yield* cliEffect("load trusted Report config", (0, node_ts_1.loadTrustedReportConfig)(request.cwd));
|
|
2405
|
-
const selectedReport = yield* reportFromSelection(request.reportSelection, configured.report, configured.watchInputs);
|
|
2406
|
-
const selectedTheme = yield* themeFromSelection(request.themeSelection, configured.theme);
|
|
2407
|
-
const projectCurrentSelection = projectCurrent === undefined
|
|
2408
|
-
? undefined
|
|
2409
|
-
: Object.freeze({
|
|
2410
|
-
policy: "project-current",
|
|
2411
|
-
currentSlots: projectCurrent.currentSlots,
|
|
2412
|
-
...(request.target.kind === "project-current" && request.target.experimentSelectors !== undefined
|
|
2413
|
-
? { experimentIds: selectedExperimentIds(projectCurrent.currentSlots) }
|
|
2414
|
-
: {}),
|
|
2415
|
-
});
|
|
2416
|
-
const sourceWatchInputs = uniqueWatchInputs([
|
|
2417
|
-
...(projectCurrent?.watchInputs ?? []),
|
|
2418
|
-
...configured.watchInputs,
|
|
2419
|
-
...selectedReport.watchInputs,
|
|
2420
|
-
...selectedTheme.watchInputs,
|
|
2421
|
-
]);
|
|
2422
|
-
return Object.freeze({
|
|
2423
|
-
report: selectedReport.value,
|
|
2424
|
-
theme: selectedTheme.value,
|
|
2425
|
-
reportWatchInputs: uniqueWatchInputs(selectedReport.watchInputs),
|
|
2426
|
-
sourceWatchInputs,
|
|
2427
|
-
...(projectCurrentSelection === undefined ? {} : { projectCurrentSelection }),
|
|
2428
|
-
watchInputs: uniqueWatchInputs([
|
|
2429
|
-
request.rootPath,
|
|
2430
|
-
...sourceWatchInputs,
|
|
2431
|
-
]),
|
|
2432
|
-
});
|
|
2433
|
-
});
|
|
2434
|
-
}
|
|
2435
|
-
function reportFromSelection(selection, configured, configuredWatchInputs) {
|
|
2436
|
-
switch (selection.kind) {
|
|
2437
|
-
case "fixed":
|
|
2438
|
-
return effect_1.Effect.succeed(Object.freeze({ value: selection.report, watchInputs: Object.freeze([]) }));
|
|
2439
|
-
case "config":
|
|
2440
|
-
return effect_1.Effect.succeed(Object.freeze({
|
|
2441
|
-
value: configured ?? standard_tsx_1.standard,
|
|
2442
|
-
watchInputs: configured === undefined ? Object.freeze([]) : configuredWatchInputs,
|
|
2443
|
-
}));
|
|
2444
|
-
case "built-in":
|
|
2445
|
-
return effect_1.Effect.succeed(Object.freeze({ value: standard_tsx_1.standard, watchInputs: Object.freeze([]) }));
|
|
2446
|
-
case "module":
|
|
2447
|
-
return cliEffect("load trusted Report module", (0, node_ts_1.loadTrustedReportModule)(selection.path)).pipe(effect_1.Effect.map((loaded) => Object.freeze({ value: loaded.report, watchInputs: loaded.watchInputs })));
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
function themeFromSelection(selection, configured) {
|
|
2451
|
-
switch (selection.kind) {
|
|
2452
|
-
case "config":
|
|
2453
|
-
return effect_1.Effect.succeed(Object.freeze({ value: configured ?? node_ts_1.basalt, watchInputs: Object.freeze([]) }));
|
|
2454
|
-
case "built-in":
|
|
2455
|
-
return effect_1.Effect.succeed(Object.freeze({
|
|
2456
|
-
value: selection.name === "chalk" ? node_ts_1.chalk : node_ts_1.basalt,
|
|
2457
|
-
watchInputs: Object.freeze([]),
|
|
2458
|
-
}));
|
|
2459
|
-
case "module":
|
|
2460
|
-
return cliEffect("load trusted Theme module", (0, node_ts_1.loadTrustedThemeModule)(selection.path)).pipe(effect_1.Effect.map((loaded) => Object.freeze({ value: loaded.theme, watchInputs: loaded.watchInputs })));
|
|
2461
|
-
}
|
|
2462
|
-
}
|
|
2463
|
-
function uniqueWatchInputs(paths) {
|
|
2464
|
-
return Object.freeze([...new Set(paths.map((path) => (0, node_path_1.resolve)(path)))].sort());
|
|
2465
|
-
}
|
|
2466
|
-
function buildCliReportSite(request, inputs) {
|
|
2467
|
-
const revision = request.target.kind === "attempt"
|
|
2468
|
-
? (0, from_record_ts_1.buildReportSiteFromRecord)({
|
|
2469
|
-
root: request.root,
|
|
2470
|
-
locator: request.target.locator,
|
|
2471
|
-
report: inputs.report,
|
|
2472
|
-
theme: inputs.theme,
|
|
2473
|
-
})
|
|
2474
|
-
: (0, from_record_ts_1.buildReportSiteFromRecord)({
|
|
2475
|
-
root: request.root,
|
|
2476
|
-
selection: request.target.kind === "selection"
|
|
2477
|
-
? request.target.selection
|
|
2478
|
-
: inputs.projectCurrentSelection,
|
|
2479
|
-
report: inputs.report,
|
|
2480
|
-
theme: inputs.theme,
|
|
2481
|
-
});
|
|
2482
|
-
return revision.pipe(effect_1.Effect.mapError(reportExecutionFailure));
|
|
2483
|
-
}
|
|
2484
|
-
function reportExecutionFailure(error) {
|
|
2485
|
-
switch (failureCode(error)) {
|
|
2486
|
-
case "sample-run-not-found":
|
|
2487
|
-
return usageError(`Run "${stringProperty(error, "runId") ?? "unknown"}" was not found in the selected Record.\n`);
|
|
2488
|
-
case "sample-run-invalid":
|
|
2489
|
-
return usageError(`Run "${stringProperty(error, "runId") ?? "unknown"}" is not a valid published Record Run.\n`);
|
|
2490
|
-
case "sample-selection-invalid":
|
|
2491
|
-
return usageError(`Invalid Record analysis selection: ${stringProperty(error, "field") ?? "selection"}.\n`);
|
|
2492
|
-
case "sample-attempt-not-found":
|
|
2493
|
-
return usageError(`Attempt "${stringProperty(error, "attemptId") ?? "unknown"}" was not found in the selected Record.\n`);
|
|
2494
|
-
case "sample-attempt-ambiguous":
|
|
2495
|
-
return usageError(`Attempt "${stringProperty(error, "attemptId") ?? "unknown"}" is ambiguous in the selected Record.\n`);
|
|
2496
|
-
case "sample-attempt-locator-not-found":
|
|
2497
|
-
return usageError(`Attempt locator "${stringProperty(error, "locator") ?? "unknown"}" was not found in the selected Record.\n`);
|
|
2498
|
-
case "sample-attempt-locator-ambiguous":
|
|
2499
|
-
return usageError(`Attempt locator "${stringProperty(error, "locator") ?? "unknown"}" is ambiguous in the selected Record.\n`);
|
|
2500
|
-
case "record-migration-required":
|
|
2501
|
-
return usageError("record-migration-required\nRun: niceeval migrate\n");
|
|
2502
|
-
case "record-bootstrap-invalid":
|
|
2503
|
-
return usageError("record-bootstrap-invalid\nPass --record <actual-record-root> or create a current NiceEval Record.\n");
|
|
2504
|
-
case "record-format-unsupported":
|
|
2505
|
-
return usageError("record-format-unsupported\nThis Record was created by a NiceEval version that is no longer supported. Remove .niceeval (for example, rm -rf .niceeval) and rerun the evaluation.\n");
|
|
2506
|
-
case "record-migration-interrupted":
|
|
2507
|
-
return usageError("record-migration-interrupted\nRestore the Record from Git or a backup before retrying.\n");
|
|
2508
|
-
case "report-route-invalid":
|
|
2509
|
-
return usageError(`Unknown Report route "${stringProperty(error, "route") ?? "unknown"}".\n`);
|
|
2510
|
-
case "report-build-budget-exceeded":
|
|
2511
|
-
return usageError(reportBuildBudgetExceededMessage(error));
|
|
2512
|
-
case "report-site-execution-problem":
|
|
2513
|
-
return usageError(reportSiteExecutionProblemMessage(error));
|
|
2514
|
-
default:
|
|
2515
|
-
return cliFailure("execute report from Record", error);
|
|
2516
|
-
}
|
|
2517
|
-
}
|
|
2518
|
-
function automaticRecordMigrationFailure(error) {
|
|
2519
|
-
const code = failureCode(error);
|
|
2520
|
-
if (code === "record-auto-migration-git-save-required") {
|
|
2521
|
-
return usageError("record-auto-migration-git-save-required\n" +
|
|
2522
|
-
"Save every portable Record byte first (for example: git add .niceeval/record && git commit), then retry the same command.\n");
|
|
2523
|
-
}
|
|
2524
|
-
if (code === "record-maintenance-busy" || code === "record-writer-busy") {
|
|
2525
|
-
return usageError(`${code}\nClose the command using this Record, then retry.\n`);
|
|
2526
|
-
}
|
|
2527
|
-
if (code === "record-format-unsupported") {
|
|
2528
|
-
return usageError("record-format-unsupported\nUse the NiceEval version that wrote this future or unknown Record format.\n");
|
|
2529
|
-
}
|
|
2530
|
-
if (code === "record-migration-recovery-required" || code === "record-migration-interrupted") {
|
|
2531
|
-
return usageError(`${code}\nRestore and verify the complete Record from Git before retrying.\n`);
|
|
2532
|
-
}
|
|
2533
|
-
return cliFailure("automatically migrate Record", error);
|
|
2534
|
-
}
|
|
2535
|
-
/** Preserve the actionable dimension and target from the Host's closed budget error. */
|
|
2536
|
-
function reportBuildBudgetExceededMessage(error) {
|
|
2537
|
-
const budget = stringProperty(error, "budget");
|
|
2538
|
-
const maximum = finiteNumberProperty(error, "maximum");
|
|
2539
|
-
const observedAtLeast = finiteNumberProperty(error, "observedAtLeast");
|
|
2540
|
-
if (budget === undefined || maximum === undefined || observedAtLeast === undefined) {
|
|
2541
|
-
return "report-build-budget-exceeded\n";
|
|
2542
|
-
}
|
|
2543
|
-
const pageId = stringProperty(error, "pageId");
|
|
2544
|
-
const route = stringProperty(error, "route");
|
|
2545
|
-
const target = pageId === undefined && route === undefined
|
|
2546
|
-
? ""
|
|
2547
|
-
: ` (${[
|
|
2548
|
-
...(pageId === undefined ? [] : [`page ${JSON.stringify(pageId)}`]),
|
|
2549
|
-
...(route === undefined ? [] : [`route ${JSON.stringify(route)}`]),
|
|
2550
|
-
].join(", ")})`;
|
|
2551
|
-
return `report-build-budget-exceeded: ${budget} observed at least ${observedAtLeast}; maximum ${maximum}${target}\n`;
|
|
2552
|
-
}
|
|
2553
|
-
const REPORT_CLI_PROBLEMS_MAX = 20;
|
|
2554
|
-
const REPORT_CLI_PROBLEM_TEXT_MAX = 1024;
|
|
2555
|
-
/** Keep the concrete closed-problem reason while bounding author-controlled diagnostics. */
|
|
2556
|
-
function reportSiteExecutionProblemMessage(error) {
|
|
2557
|
-
if (typeof error !== "object" || error === null)
|
|
2558
|
-
return "report-site-execution-problem\n";
|
|
2559
|
-
const problems = Reflect.get(error, "problems");
|
|
2560
|
-
if (!Array.isArray(problems))
|
|
2561
|
-
return "report-site-execution-problem\n";
|
|
2562
|
-
const lines = problems
|
|
2563
|
-
.slice(0, REPORT_CLI_PROBLEMS_MAX)
|
|
2564
|
-
.flatMap((problem) => {
|
|
2565
|
-
const code = boundedReportCliProblemText(stringProperty(problem, "code"));
|
|
2566
|
-
const summary = boundedReportCliProblemText(stringProperty(problem, "summary"));
|
|
2567
|
-
if (code === undefined && summary === undefined)
|
|
2568
|
-
return [];
|
|
2569
|
-
if (code === undefined)
|
|
2570
|
-
return [summary];
|
|
2571
|
-
return [summary === undefined ? code : `${code}: ${summary}`];
|
|
2572
|
-
});
|
|
2573
|
-
const omitted = Math.max(0, problems.length - REPORT_CLI_PROBLEMS_MAX);
|
|
2574
|
-
return [
|
|
2575
|
-
"report-site-execution-problem",
|
|
2576
|
-
...lines,
|
|
2577
|
-
...(omitted === 0 ? [] : [`${omitted} additional Report problem(s) omitted.`]),
|
|
2578
|
-
"",
|
|
2579
|
-
].join("\n");
|
|
2580
|
-
}
|
|
2581
|
-
function boundedReportCliProblemText(value) {
|
|
2582
|
-
if (value === undefined)
|
|
2583
|
-
return undefined;
|
|
2584
|
-
const bounded = value
|
|
2585
|
-
.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ")
|
|
2586
|
-
.slice(0, REPORT_CLI_PROBLEM_TEXT_MAX)
|
|
2587
|
-
.trim();
|
|
2588
|
-
return bounded === "" ? undefined : bounded;
|
|
2589
|
-
}
|
|
2590
|
-
function stringProperty(value, key) {
|
|
2591
|
-
if (typeof value !== "object" || value === null)
|
|
2592
|
-
return undefined;
|
|
2593
|
-
const candidate = Reflect.get(value, key);
|
|
2594
|
-
return typeof candidate === "string" ? candidate : undefined;
|
|
2595
|
-
}
|
|
2596
|
-
function finiteNumberProperty(value, key) {
|
|
2597
|
-
if (typeof value !== "object" || value === null)
|
|
2598
|
-
return undefined;
|
|
2599
|
-
const candidate = Reflect.get(value, key);
|
|
2600
|
-
return typeof candidate === "number" && Number.isFinite(candidate) ? candidate : undefined;
|
|
2601
|
-
}
|
|
2602
|
-
function requireKnownReportPage(revision, page) {
|
|
2603
|
-
const routes = (0, model_ts_1.closedSiteRevisionData)(revision).routes;
|
|
2604
|
-
if (page === undefined || routes.includes(page)) {
|
|
2605
|
-
return effect_1.Effect.void;
|
|
2606
|
-
}
|
|
2607
|
-
const available = [...routes].sort().join(", ");
|
|
2608
|
-
return effect_1.Effect.fail(usageError(`Unknown Report route "${page}". Available routes: ${available || "none"}.\n`));
|
|
2609
|
-
}
|
|
2610
|
-
const cliReportConsole = Object.freeze({
|
|
2611
|
-
write: (text) => effect_1.Effect.try({
|
|
2612
|
-
try: () => {
|
|
2613
|
-
process.stdout.write(text);
|
|
2614
|
-
},
|
|
2615
|
-
catch: () => Object.freeze({
|
|
2616
|
-
code: "report-console-write-failed",
|
|
2617
|
-
operation: "write",
|
|
2618
|
-
}),
|
|
2619
|
-
}),
|
|
2620
|
-
});
|
|
2621
|
-
const reportHostPhaseLabels = Object.freeze({
|
|
2622
|
-
"record-open": "Open Record",
|
|
2623
|
-
"selection": "Select runs and attempts",
|
|
2624
|
-
"sample-open": "Open analysis Sample",
|
|
2625
|
-
"report-execution": "Execute Report and read requested facts",
|
|
2626
|
-
});
|
|
2627
|
-
function formatReportProgressDuration(durationMs) {
|
|
2628
|
-
if (durationMs < 1000)
|
|
2629
|
-
return `${Math.round(durationMs)}ms`;
|
|
2630
|
-
return `${(durationMs / 1000).toFixed(1)}s`;
|
|
2631
|
-
}
|
|
2632
|
-
function writeReportProgress(text) {
|
|
2633
|
-
try {
|
|
2634
|
-
process.stderr.write(text);
|
|
2635
|
-
}
|
|
2636
|
-
catch {
|
|
2637
|
-
// Progress is diagnostic only; it must never alter Report execution.
|
|
2638
|
-
}
|
|
2639
|
-
}
|
|
2640
|
-
const cliProgressFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
2641
|
-
/**
|
|
2642
|
-
* Keeps interactive progress on one terminal line, while preserving plain
|
|
2643
|
-
* line-oriented diagnostics when stderr is redirected to a file or CI log.
|
|
2644
|
-
*/
|
|
2645
|
-
class CliReportProgress {
|
|
2646
|
-
active;
|
|
2647
|
-
start(label) {
|
|
2648
|
-
if (!process.stderr.isTTY) {
|
|
2649
|
-
writeReportProgress(`⠋ ${label}…\n`);
|
|
2650
|
-
return () => undefined;
|
|
2651
|
-
}
|
|
2652
|
-
if (this.active !== undefined)
|
|
2653
|
-
clearInterval(this.active.timer);
|
|
2654
|
-
let frame = 0;
|
|
2655
|
-
const render = () => writeReportProgress(`\r\u001b[2K${cliProgressFrames[frame++ % cliProgressFrames.length]} ${label}…`);
|
|
2656
|
-
render();
|
|
2657
|
-
const timer = setInterval(render, 80);
|
|
2658
|
-
const active = { label, timer };
|
|
2659
|
-
this.active = active;
|
|
2660
|
-
return () => {
|
|
2661
|
-
if (this.active !== active)
|
|
2662
|
-
return;
|
|
2663
|
-
clearInterval(timer);
|
|
2664
|
-
this.active = undefined;
|
|
2665
|
-
};
|
|
2666
|
-
}
|
|
2667
|
-
complete(label, text) {
|
|
2668
|
-
if (process.stderr.isTTY) {
|
|
2669
|
-
if (this.active?.label === label) {
|
|
2670
|
-
clearInterval(this.active.timer);
|
|
2671
|
-
this.active = undefined;
|
|
2672
|
-
}
|
|
2673
|
-
writeReportProgress(`\r\u001b[2K${text}\n`);
|
|
2674
|
-
return;
|
|
2675
|
-
}
|
|
2676
|
-
writeReportProgress(`${text}\n`);
|
|
2677
|
-
}
|
|
2678
|
-
}
|
|
2679
|
-
function makeCliReportProgress(rootPath, progress) {
|
|
2680
|
-
return Object.freeze({
|
|
2681
|
-
report: (event) => {
|
|
2682
|
-
const label = event.phase === "record-open"
|
|
2683
|
-
? `${reportHostPhaseLabels[event.phase]} (${rootPath})`
|
|
2684
|
-
: reportHostPhaseLabels[event.phase];
|
|
2685
|
-
if (event.type === "start") {
|
|
2686
|
-
progress.start(label);
|
|
2687
|
-
return;
|
|
2688
|
-
}
|
|
2689
|
-
const marker = event.outcome === "success" ? "✓" : "×";
|
|
2690
|
-
const outcome = event.outcome === "success" ? "" : ` · ${event.outcome}`;
|
|
2691
|
-
progress.complete(label, `${marker} ${label} (${formatReportProgressDuration(event.durationMs)}${outcome})`);
|
|
2692
|
-
},
|
|
2693
|
-
});
|
|
2694
|
-
}
|
|
2695
|
-
function cliPhaseOutcome(exit) {
|
|
2696
|
-
if (effect_1.Exit.isSuccess(exit))
|
|
2697
|
-
return "success";
|
|
2698
|
-
if (effect_1.Cause.isInterruptedOnly(exit.cause))
|
|
2699
|
-
return "interrupted";
|
|
2700
|
-
return effect_1.Cause.defects(exit.cause).pipe((defects) => defects.length > 0) ? "defect" : "failure";
|
|
2701
|
-
}
|
|
2702
|
-
function withCliReportPhase(progress, label, effect) {
|
|
2703
|
-
return effect_1.Effect.gen(function* () {
|
|
2704
|
-
const startedAt = yield* effect_1.Clock.currentTimeNanos;
|
|
2705
|
-
const stopProgress = yield* effect_1.Effect.sync(() => progress.start(label));
|
|
2706
|
-
return yield* effect.pipe(effect_1.Effect.onExit((exit) => effect_1.Effect.gen(function* () {
|
|
2707
|
-
const completedAt = yield* effect_1.Clock.currentTimeNanos;
|
|
2708
|
-
const durationMs = Math.max(0, Number(completedAt - startedAt) / 1000000);
|
|
2709
|
-
const outcome = cliPhaseOutcome(exit);
|
|
2710
|
-
const marker = outcome === "success" ? "✓" : "×";
|
|
2711
|
-
const detail = outcome === "success" ? "" : ` · ${outcome}`;
|
|
2712
|
-
yield* effect_1.Effect.sync(() => {
|
|
2713
|
-
stopProgress();
|
|
2714
|
-
progress.complete(label, `${marker} ${label} (${formatReportProgressDuration(durationMs)}${detail})`);
|
|
2715
|
-
});
|
|
2716
|
-
})));
|
|
2717
|
-
});
|
|
2718
|
-
}
|
|
2719
|
-
function runShowCommand(cwd, positionals, flags) {
|
|
2720
|
-
return effect_1.Effect.gen(function* () {
|
|
2721
|
-
const progress = new CliReportProgress();
|
|
2722
|
-
const request = yield* effect_1.Effect.try({
|
|
2723
|
-
try: () => parseReportCliRequest({ command: "show", cwd, positionals, flags }),
|
|
2724
|
-
catch: (cause) => cliFailure("parse show arguments", cause),
|
|
2725
|
-
});
|
|
2726
|
-
const inputs = yield* withCliReportPhase(progress, "Load project, config, Report, and Theme", loadCliReportInputs(request));
|
|
2727
|
-
const textProjection = flags.json
|
|
2728
|
-
? undefined
|
|
2729
|
-
: (0, panel_ts_1.panelCapabilityOf)({
|
|
2730
|
-
isTTY: process.stdout.isTTY,
|
|
2731
|
-
width: process.stdout.columns,
|
|
2732
|
-
});
|
|
2733
|
-
const show = request.target.kind === "attempt"
|
|
2734
|
-
? index_ts_3.reportHost.show({
|
|
2735
|
-
root: request.root,
|
|
2736
|
-
locator: request.target.locator,
|
|
2737
|
-
report: inputs.report,
|
|
2738
|
-
...(request.page === undefined ? {} : { route: request.page }),
|
|
2739
|
-
...(textProjection === undefined ? {} : { textProjection: { width: textProjection.width, panelMode: textProjection.mode } }),
|
|
2740
|
-
format: flags.json ? "json" : "text",
|
|
2741
|
-
})
|
|
2742
|
-
: index_ts_3.reportHost.show({
|
|
2743
|
-
root: request.root,
|
|
2744
|
-
selection: request.target.kind === "selection"
|
|
2745
|
-
? request.target.selection
|
|
2746
|
-
: inputs.projectCurrentSelection,
|
|
2747
|
-
report: inputs.report,
|
|
2748
|
-
...(request.page === undefined ? {} : { route: request.page }),
|
|
2749
|
-
...(textProjection === undefined ? {} : { textProjection: { width: textProjection.width, panelMode: textProjection.mode } }),
|
|
2750
|
-
format: flags.json ? "json" : "text",
|
|
2751
|
-
});
|
|
2752
|
-
const output = yield* show.pipe(effect_1.Effect.provideService(progress_ts_1.ReportHostProgressObserver, makeCliReportProgress(request.rootPath, progress)), effect_1.Effect.mapError(reportExecutionFailure));
|
|
2753
|
-
yield* cliReportConsole.write(output).pipe(effect_1.Effect.mapError((error) => cliFailure("render Report show output", error)));
|
|
2754
|
-
return 0;
|
|
2755
|
-
});
|
|
2756
|
-
}
|
|
2757
|
-
function runViewCommand(cwd, positionals, flags) {
|
|
2758
|
-
return effect_1.Effect.gen(function* () {
|
|
2759
|
-
const progress = new CliReportProgress();
|
|
2760
|
-
const request = yield* effect_1.Effect.try({
|
|
2761
|
-
try: () => parseReportCliRequest({ command: "view", cwd, positionals, flags }),
|
|
2762
|
-
catch: (cause) => cliFailure("parse view arguments", cause),
|
|
2763
|
-
});
|
|
2764
|
-
if (flags.out !== undefined) {
|
|
2765
|
-
if (flags.out.trim() === "") {
|
|
2766
|
-
return yield* effect_1.Effect.fail(usageError("--out requires a target directory.\n"));
|
|
2767
|
-
}
|
|
2768
|
-
if (flags.port !== undefined || flags.host !== undefined || flags.open === true) {
|
|
2769
|
-
return yield* effect_1.Effect.fail(usageError("view --out does not start a server; remove --port, --host, and --open.\n"));
|
|
2770
|
-
}
|
|
2771
|
-
if (request.page !== undefined) {
|
|
2772
|
-
return yield* effect_1.Effect.fail(usageError("view --out does not accept --page.\n"));
|
|
2773
|
-
}
|
|
2774
|
-
const inputs = yield* withCliReportPhase(progress, "Load project, config, Report, and Theme", loadCliReportInputs(request));
|
|
2775
|
-
const revision = yield* buildCliReportSite(request, inputs).pipe(effect_1.Effect.provideService(progress_ts_1.ReportHostProgressObserver, makeCliReportProgress(request.rootPath, progress)));
|
|
2776
|
-
const receipt = yield* withCliReportPhase(progress, "Export static report site", index_ts_3.reportHost.export({
|
|
2777
|
-
revision,
|
|
2778
|
-
out: (0, node_path_1.resolve)(cwd, flags.out),
|
|
2779
|
-
}).pipe(effect_1.Effect.provideService(static_ts_1.ReportFileSystem, (0, node_ts_1.makeNodeReportFileSystem)()), effect_1.Effect.mapError((error) => staticExportFailure(error, (0, node_path_1.resolve)(cwd, flags.out)))));
|
|
2780
|
-
yield* writeStdout(`Exported static report site: ${receipt.out}\n`);
|
|
2781
|
-
return 0;
|
|
2782
|
-
}
|
|
2783
|
-
const initialInputs = yield* withCliReportPhase(progress, "Load project, config, Report, and Theme", loadCliReportInputs(request));
|
|
2784
|
-
const initial = yield* buildCliReportSite(request, initialInputs).pipe(effect_1.Effect.provideService(progress_ts_1.ReportHostProgressObserver, makeCliReportProgress(request.rootPath, progress)));
|
|
2785
|
-
yield* requireKnownReportPage(initial, request.page);
|
|
2786
|
-
const buildCache = yield* makeReportViewBuildCache(request, initialInputs, initial);
|
|
2787
|
-
const { host, port } = yield* effect_1.Effect.try({
|
|
2788
|
-
try: () => ({ host: viewHost(flags.host), port: viewPort(flags.port) }),
|
|
2789
|
-
catch: (cause) => cliFailure("parse view server arguments", cause),
|
|
2790
|
-
});
|
|
2791
|
-
const server = yield* withCliReportPhase(progress, "Start HTTP server and file watcher", index_ts_3.reportHost.serve({
|
|
2792
|
-
url: `http://${host.includes(":") ? `[${host}]` : host}:${port}/`,
|
|
2793
|
-
watchInputs: initialInputs.watchInputs,
|
|
2794
|
-
initial: effect_1.Effect.succeed(initial),
|
|
2795
|
-
rebuild: () => rebuildReportView(request, buildCache),
|
|
2796
|
-
host,
|
|
2797
|
-
port,
|
|
2798
|
-
}).pipe(effect_1.Effect.mapError((error) => cliFailure("open report view", error))));
|
|
2799
|
-
const urls = server.urls.map((url) => request.page === undefined ? url : new URL(request.page, url).toString());
|
|
2800
|
-
const url = urls[0];
|
|
2801
|
-
if (!isLoopbackViewHost(host)) {
|
|
2802
|
-
yield* writeStderr("Warning: niceeval view is listening beyond loopback without authentication or TLS; " +
|
|
2803
|
-
"every reachable client can read report data, execution JSON, and downloads.\n");
|
|
2804
|
-
}
|
|
2805
|
-
yield* writeStdout(`niceeval view — open in a browser:\n${urls.join("\n")}\n`);
|
|
2806
|
-
if (flags.open !== false) {
|
|
2807
|
-
yield* openBrowser(url).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(false)));
|
|
2808
|
-
}
|
|
2809
|
-
return yield* effect_1.Effect.never;
|
|
2810
|
-
});
|
|
2811
|
-
}
|
|
2812
|
-
function rebuildReportView(request, cache) {
|
|
2813
|
-
return effect_1.Effect.gen(function* () {
|
|
2814
|
-
const inputs = yield* loadCliReportInputs(request);
|
|
2815
|
-
const [snapshotIdentity, sourceFingerprint, reportInputFingerprint, themeCss] = yield* effect_1.Effect.all([
|
|
2816
|
-
readReportSnapshotIdentity(request, inputs),
|
|
2817
|
-
fingerprintReportSourceInputs(inputs.sourceWatchInputs),
|
|
2818
|
-
fingerprintReportSourceInputs(inputs.reportWatchInputs),
|
|
2819
|
-
closeReportThemeStylesheet(inputs.theme),
|
|
2820
|
-
]);
|
|
2821
|
-
const pageIdentity = reportPageCacheIdentity(request, inputs, {
|
|
2822
|
-
snapshotIdentity,
|
|
2823
|
-
reportInputs: reportInputFingerprint,
|
|
2824
|
-
});
|
|
2825
|
-
const themeIdentity = reportCacheBytesIdentity(themeCss);
|
|
2826
|
-
const cachedPages = cache.pageBytes;
|
|
2827
|
-
const reusePages = cachedPages !== undefined &&
|
|
2828
|
-
snapshotIdentity === cache.snapshotIdentity &&
|
|
2829
|
-
pageIdentity !== undefined && pageIdentity === cache.pageIdentity;
|
|
2830
|
-
const exactNoop = reusePages && themeIdentity === cache.themeIdentity &&
|
|
2831
|
-
sameReportInputFingerprint(sourceFingerprint, cache.sourceFingerprint);
|
|
2832
|
-
let revision;
|
|
2833
|
-
let pageBytes;
|
|
2834
|
-
if (exactNoop) {
|
|
2835
|
-
revision = cache.revision;
|
|
2836
|
-
pageBytes = cachedPages;
|
|
2837
|
-
}
|
|
2838
|
-
else if (reusePages && themeIdentity !== cache.themeIdentity) {
|
|
2839
|
-
const rethemed = (0, report_view_cache_ts_1.rethemeClosedReportPageBytes)(cachedPages, themeCss, static_ts_1.signClosedSiteRevision);
|
|
2840
|
-
if (rethemed === undefined) {
|
|
2841
|
-
revision = yield* buildCliReportSite(request, inputs);
|
|
2842
|
-
pageBytes = (0, report_view_cache_ts_1.closeReportPageBytes)(revision, {
|
|
2843
|
-
selectionIdentity: reportSelectionCacheIdentity(request, inputs),
|
|
2844
|
-
themeIdentity,
|
|
2845
|
-
});
|
|
2846
|
-
}
|
|
2847
|
-
else {
|
|
2848
|
-
revision = rethemed;
|
|
2849
|
-
pageBytes = (0, report_view_cache_ts_1.closeReportPageBytes)(revision, {
|
|
2850
|
-
selectionIdentity: reportSelectionCacheIdentity(request, inputs),
|
|
2851
|
-
themeIdentity,
|
|
2852
|
-
});
|
|
2853
|
-
}
|
|
2854
|
-
}
|
|
2855
|
-
else {
|
|
2856
|
-
revision = yield* buildCliReportSite(request, inputs);
|
|
2857
|
-
pageBytes = (0, report_view_cache_ts_1.closeReportPageBytes)(revision, {
|
|
2858
|
-
selectionIdentity: reportSelectionCacheIdentity(request, inputs),
|
|
2859
|
-
themeIdentity,
|
|
2860
|
-
});
|
|
2861
|
-
}
|
|
2862
|
-
yield* requireKnownReportPage(revision, request.page);
|
|
2863
|
-
cache.revision = revision;
|
|
2864
|
-
cache.pageBytes = pageBytes;
|
|
2865
|
-
cache.watchInputs = inputs.watchInputs;
|
|
2866
|
-
cache.sourceFingerprint = sourceFingerprint;
|
|
2867
|
-
cache.snapshotIdentity = pageBytes?.sampleIdentity ?? snapshotIdentity;
|
|
2868
|
-
cache.pageIdentity = pageBytes === undefined
|
|
2869
|
-
? undefined
|
|
2870
|
-
: reportPageCacheIdentity(request, inputs, {
|
|
2871
|
-
snapshotIdentity: pageBytes.sampleIdentity,
|
|
2872
|
-
reportInputs: reportInputFingerprint,
|
|
2873
|
-
});
|
|
2874
|
-
cache.themeIdentity = themeIdentity;
|
|
2875
|
-
return Object.freeze({
|
|
2876
|
-
kind: "site",
|
|
2877
|
-
site: revision,
|
|
2878
|
-
watchInputs: inputs.watchInputs,
|
|
2879
|
-
});
|
|
2880
|
-
}).pipe(effect_1.Effect.catchAll((failure) => {
|
|
2881
|
-
const problem = reportViewRebuildFailure(failure);
|
|
2882
|
-
return writeStderr(`view rebuild failed: ${problem.summary}\n`).pipe(effect_1.Effect.zipRight(effect_1.Effect.fail(problem)));
|
|
2883
|
-
}));
|
|
2884
|
-
}
|
|
2885
|
-
function makeReportViewBuildCache(request, inputs, revision) {
|
|
2886
|
-
return effect_1.Effect.gen(function* () {
|
|
2887
|
-
const [sourceFingerprint, reportInputFingerprint, themeCss] = yield* effect_1.Effect.all([
|
|
2888
|
-
fingerprintReportSourceInputs(inputs.sourceWatchInputs),
|
|
2889
|
-
fingerprintReportSourceInputs(inputs.reportWatchInputs),
|
|
2890
|
-
closeReportThemeStylesheet(inputs.theme),
|
|
2891
|
-
]);
|
|
2892
|
-
const themeIdentity = reportCacheBytesIdentity(themeCss);
|
|
2893
|
-
const selectionIdentity = reportSelectionCacheIdentity(request, inputs);
|
|
2894
|
-
const pageBytes = (0, report_view_cache_ts_1.closeReportPageBytes)(revision, { selectionIdentity, themeIdentity });
|
|
2895
|
-
const pageIdentity = reportPageCacheIdentity(request, inputs, {
|
|
2896
|
-
snapshotIdentity: pageBytes?.sampleIdentity ?? "",
|
|
2897
|
-
reportInputs: reportInputFingerprint,
|
|
2898
|
-
});
|
|
2899
|
-
return {
|
|
2900
|
-
revision,
|
|
2901
|
-
...(pageBytes === undefined ? {} : { pageBytes }),
|
|
2902
|
-
watchInputs: inputs.watchInputs,
|
|
2903
|
-
sourceFingerprint,
|
|
2904
|
-
snapshotIdentity: pageBytes?.sampleIdentity ?? "",
|
|
2905
|
-
...(pageBytes === undefined || pageIdentity === undefined ? {} : { pageIdentity }),
|
|
2906
|
-
themeIdentity,
|
|
2907
|
-
};
|
|
2908
|
-
});
|
|
2909
|
-
}
|
|
2910
|
-
function sameReportInputFingerprint(left, right) {
|
|
2911
|
-
return left.complete && right.complete && left.digest === right.digest;
|
|
2912
|
-
}
|
|
2913
|
-
function reportPageCacheIdentity(request, inputs, input) {
|
|
2914
|
-
if (input.snapshotIdentity.length === 0 || !input.reportInputs.complete)
|
|
2915
|
-
return undefined;
|
|
2916
|
-
return (0, node_crypto_1.createHash)("sha256").update(JSON.stringify([
|
|
2917
|
-
"niceeval.report-page-bytes/v1",
|
|
2918
|
-
static_ts_1.REPORT_STATIC_RENDERER,
|
|
2919
|
-
input.snapshotIdentity,
|
|
2920
|
-
input.reportInputs.digest,
|
|
2921
|
-
(0, execute_ts_1.reportDefinitionIdentity)(inputs.report),
|
|
2922
|
-
reportSelectionCacheIdentity(request, inputs),
|
|
2923
|
-
])).digest("hex");
|
|
2924
|
-
}
|
|
2925
|
-
function reportCacheBytesIdentity(bytes) {
|
|
2926
|
-
return (0, node_crypto_1.createHash)("sha256").update(bytes).digest("hex");
|
|
2927
|
-
}
|
|
2928
|
-
/** Delegates the exact Theme stylesheet closure to the static Host. */
|
|
2929
|
-
function closeReportThemeStylesheet(theme) {
|
|
2930
|
-
return effect_1.Effect.try({
|
|
2931
|
-
try: () => (0, static_ts_1.closeStaticThemeStylesheet)(theme),
|
|
2932
|
-
catch: (cause) => cliFailure("close Report theme stylesheet", cause),
|
|
2933
|
-
});
|
|
2934
|
-
}
|
|
2935
|
-
/**
|
|
2936
|
-
* The Host owns Record selection, project-current narrowing, and semantic
|
|
2937
|
-
* snapshot construction. This probe deliberately never opens a Sample.
|
|
2938
|
-
*/
|
|
2939
|
-
function readReportSnapshotIdentity(request, inputs) {
|
|
2940
|
-
const target = request.target.kind === "attempt"
|
|
2941
|
-
? Object.freeze({ root: request.root, locator: request.target.locator })
|
|
2942
|
-
: Object.freeze({
|
|
2943
|
-
root: request.root,
|
|
2944
|
-
selection: request.target.kind === "selection"
|
|
2945
|
-
? request.target.selection
|
|
2946
|
-
: inputs.projectCurrentSelection,
|
|
2947
|
-
});
|
|
2948
|
-
return (0, from_record_ts_1.reportSnapshotIdentityFromRecord)(target).pipe(effect_1.Effect.mapError(reportExecutionFailure));
|
|
2949
|
-
}
|
|
2950
|
-
function reportSelectionCacheIdentity(request, inputs) {
|
|
2951
|
-
const target = request.target.kind === "project-current"
|
|
2952
|
-
? inputs.projectCurrentSelection
|
|
2953
|
-
: request.target;
|
|
2954
|
-
return (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(target)).digest("hex");
|
|
2955
|
-
}
|
|
2956
|
-
/**
|
|
2957
|
-
* A source-watch signal is only a hint. This digest deliberately excludes the
|
|
2958
|
-
* Record root: selected Record equivalence comes only from the Host-owned
|
|
2959
|
-
* semantic snapshot probe. Links and special files make the result incomplete,
|
|
2960
|
-
* so unprovable input can never produce an exact no-op; a missing optional
|
|
2961
|
-
* path is a complete, watchable source-state observation.
|
|
2962
|
-
*/
|
|
2963
|
-
function fingerprintReportSourceInputs(paths) {
|
|
2964
|
-
return effect_1.Effect.tryPromise({
|
|
2965
|
-
try: async () => {
|
|
2966
|
-
const hash = (0, node_crypto_1.createHash)("sha256");
|
|
2967
|
-
const visited = new Set();
|
|
2968
|
-
let complete = true;
|
|
2969
|
-
const visit = async (path) => {
|
|
2970
|
-
const absolute = (0, node_path_1.resolve)(path);
|
|
2971
|
-
if (visited.has(absolute))
|
|
2972
|
-
return;
|
|
2973
|
-
visited.add(absolute);
|
|
2974
|
-
let info;
|
|
2975
|
-
try {
|
|
2976
|
-
info = await (0, promises_1.lstat)(absolute);
|
|
2977
|
-
}
|
|
2978
|
-
catch (cause) {
|
|
2979
|
-
if (typeof cause === "object" && cause !== null && Reflect.get(cause, "code") === "ENOENT") {
|
|
2980
|
-
// An absent optional config/module entry is itself a complete
|
|
2981
|
-
// source-state observation. A later creation emits a watcher
|
|
2982
|
-
// signal and changes this digest.
|
|
2983
|
-
hash.update(`missing\0${absolute}\0`);
|
|
2984
|
-
return;
|
|
2985
|
-
}
|
|
2986
|
-
throw cause;
|
|
2987
|
-
}
|
|
2988
|
-
hash.update(`path\0${absolute}\0`);
|
|
2989
|
-
if (info.isSymbolicLink()) {
|
|
2990
|
-
complete = false;
|
|
2991
|
-
hash.update(`link\0${await (0, promises_1.readlink)(absolute)}\0`);
|
|
2992
|
-
return;
|
|
2993
|
-
}
|
|
2994
|
-
if (info.isFile()) {
|
|
2995
|
-
hash.update("file\0");
|
|
2996
|
-
hash.update(await (0, promises_1.readFile)(absolute));
|
|
2997
|
-
hash.update("\0");
|
|
2998
|
-
return;
|
|
2999
|
-
}
|
|
3000
|
-
if (!info.isDirectory()) {
|
|
3001
|
-
complete = false;
|
|
3002
|
-
hash.update(`opaque\0${info.mode}\0`);
|
|
3003
|
-
return;
|
|
3004
|
-
}
|
|
3005
|
-
hash.update("directory\0");
|
|
3006
|
-
const names = (await (0, promises_1.readdir)(absolute)).sort();
|
|
3007
|
-
for (const name of names)
|
|
3008
|
-
yieldName(hash, name);
|
|
3009
|
-
for (const name of names)
|
|
3010
|
-
await visit((0, node_path_1.join)(absolute, name));
|
|
3011
|
-
};
|
|
3012
|
-
for (const path of [...new Set(paths.map((entry) => (0, node_path_1.resolve)(entry)))].sort()) {
|
|
3013
|
-
await visit(path);
|
|
3014
|
-
}
|
|
3015
|
-
return Object.freeze({ digest: hash.digest("hex"), complete });
|
|
3016
|
-
},
|
|
3017
|
-
catch: (cause) => cliFailure("fingerprint Report source inputs", cause),
|
|
3018
|
-
});
|
|
3019
|
-
}
|
|
3020
|
-
function yieldName(hash, name) {
|
|
3021
|
-
hash.update(`entry\0${name}\0`);
|
|
3022
|
-
}
|
|
3023
|
-
function staticExportFailure(error, out) {
|
|
3024
|
-
if (failureCode(error) === "report-export-target-exists") {
|
|
3025
|
-
return usageError(`report-export-target-exists\nRemove ${out} before retrying.\n`);
|
|
3026
|
-
}
|
|
3027
|
-
return cliFailure("export static Report", error);
|
|
3028
|
-
}
|
|
3029
|
-
function viewHost(value) {
|
|
3030
|
-
const host = (value ?? "127.0.0.1").trim();
|
|
3031
|
-
if (host.length === 0)
|
|
3032
|
-
throw usageError("--host requires a non-empty address.\n");
|
|
3033
|
-
return host;
|
|
3034
|
-
}
|
|
3035
|
-
function isLoopbackViewHost(host) {
|
|
3036
|
-
return host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
3037
|
-
}
|
|
3038
|
-
function viewPort(value) {
|
|
3039
|
-
const port = value ?? 0;
|
|
3040
|
-
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
3041
|
-
throw usageError(`--port must be an integer from 0 through 65535, got ${port}.\n`);
|
|
3042
|
-
}
|
|
3043
|
-
return port;
|
|
3044
|
-
}
|
|
3045
|
-
function reportViewRebuildFailure(failure) {
|
|
3046
|
-
const summary = failure._tag === "CliUsageError"
|
|
3047
|
-
? failure.message
|
|
3048
|
-
: failure.cause instanceof node_ts_1.ReportModuleLoadError
|
|
3049
|
-
? `${failure.cause.code}: ${failure.cause.reason}`
|
|
3050
|
-
: failureCode(failure.cause) ?? "Report rebuild failed";
|
|
3051
|
-
return Object.freeze({ summary });
|
|
3052
|
-
}
|
|
3053
|
-
const cliProgram = (interruption) => effect_1.Effect.gen(function* () {
|
|
3054
|
-
const cwd = process.cwd();
|
|
3055
|
-
const { command, positionals, flags, providedOptions } = yield* effect_1.Effect.try({
|
|
3056
|
-
try: () => parseArgs(process.argv.slice(2)),
|
|
3057
|
-
catch: (cause) => cliFailure("parse CLI arguments", cause),
|
|
3058
|
-
});
|
|
3059
|
-
// Session / Docker profile queries must remain config-free read paths.
|
|
3060
|
-
if (command !== "session" && command !== "docker") {
|
|
3061
|
-
yield* loadDotenv(cwd);
|
|
3062
|
-
}
|
|
3063
|
-
if (flags.help) {
|
|
3064
|
-
yield* writeStdout((0, index_ts_5.t)("cli.help"));
|
|
3065
|
-
return 0;
|
|
3066
|
-
}
|
|
3067
|
-
if (command === "exp" && positionals.length === 1 && positionals[0] === "help") {
|
|
3068
|
-
yield* writeStdout((0, index_ts_5.t)("cli.help"));
|
|
3069
|
-
return 0;
|
|
3070
|
-
}
|
|
3071
|
-
if (flags.version) {
|
|
3072
|
-
yield* writeStdout((yield* packageVersion()) + "\n");
|
|
3073
|
-
return 0;
|
|
3074
|
-
}
|
|
3075
|
-
if (command === "show" || command === "view" || command === "exp") {
|
|
3076
|
-
const migrated = yield* (0, record_ts_1.ensureAutomaticRecordMigration)({
|
|
3077
|
-
cwd,
|
|
3078
|
-
...(flags.record === undefined ? {} : { record: flags.record }),
|
|
3079
|
-
}).pipe(effect_1.Effect.mapError(automaticRecordMigrationFailure));
|
|
3080
|
-
if (migrated !== null) {
|
|
3081
|
-
yield* writeStderr(`Record automatically migrated to schemaVersion ${migrated.targetSchemaVersion}; restore commit ${migrated.restoreCommit}.\n`);
|
|
3082
|
-
}
|
|
3083
|
-
}
|
|
3084
|
-
if (command === "debug") {
|
|
3085
|
-
const unsupported = providedOptions.find((name) => name !== "json" && name !== "help" && name !== "version");
|
|
3086
|
-
if (unsupported !== undefined) {
|
|
3087
|
-
yield* writeStderr((0, index_ts_5.t)("cli.debug.flagUnsupported", { flag: `--${unsupported}` }));
|
|
3088
|
-
return 1;
|
|
3089
|
-
}
|
|
3090
|
-
return yield* runDebugCommand(cwd, positionals, flags);
|
|
3091
|
-
}
|
|
3092
|
-
if (command === "docker")
|
|
3093
|
-
return yield* runDockerCommand(positionals, flags);
|
|
3094
|
-
if (command === "accept") {
|
|
3095
|
-
const locators = yield* effect_1.Effect.try({
|
|
3096
|
-
try: () => parseAcceptLocators(positionals, flags),
|
|
3097
|
-
catch: (cause) => cliFailure("parse acceptance locators", cause),
|
|
3098
|
-
});
|
|
3099
|
-
const outcome = yield* effect_1.Effect.either(runAcceptCommand(cwd, locators, flags.record));
|
|
3100
|
-
if (effect_1.Either.isLeft(outcome)) {
|
|
3101
|
-
yield* writeStderr((0, index_ts_5.t)("cli.accept.failed", { error: errorMessage(outcome.left.cause) }));
|
|
3102
|
-
return 1;
|
|
3103
|
-
}
|
|
3104
|
-
return 0;
|
|
3105
|
-
}
|
|
3106
|
-
if (command === "view")
|
|
3107
|
-
return yield* runViewCommand(cwd, positionals, flags);
|
|
3108
|
-
if (command === "sandbox")
|
|
3109
|
-
return yield* runSandboxCliCommand(cwd, positionals, flags);
|
|
3110
|
-
if (command === "session")
|
|
3111
|
-
return yield* runSessionCommand(cwd, positionals, flags);
|
|
3112
|
-
if (command === "show")
|
|
3113
|
-
return yield* runShowCommand(cwd, positionals, flags);
|
|
3114
|
-
if (command === "clean" || command === "migrate") {
|
|
3115
|
-
return yield* runRecordMaintenanceCommand(cwd, command, positionals, flags);
|
|
3116
|
-
}
|
|
3117
|
-
if (command === "init") {
|
|
3118
|
-
yield* initProject(cwd);
|
|
3119
|
-
yield* writeStdout((0, index_ts_5.t)("cli.init.done"));
|
|
3120
|
-
if (!hostPrefersEsm(cwd))
|
|
3121
|
-
yield* writeStdout((0, index_ts_5.t)("cli.init.esmHint"));
|
|
3122
|
-
return 0;
|
|
3123
|
-
}
|
|
3124
|
-
if (command === "exp" && positionals[0] === "rename") {
|
|
3125
|
-
return yield* runExperimentRenameCommand(cwd, positionals.slice(1), flags);
|
|
3126
|
-
}
|
|
3127
|
-
return yield* runEvaluationCommand(cwd, command, positionals, flags, interruption);
|
|
3128
|
-
}).pipe(
|
|
3129
|
-
// Every command boundary reports a typed CLI failure; defects and
|
|
3130
|
-
// interruption remain in the Cause channel for bootstrap to own.
|
|
3131
|
-
effect_1.Effect.mapError((cause) => cliFailure("run CLI command", cause)));
|
|
3132
|
-
exports.cliProgram = cliProgram;
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.FLAG_OPTIONS = void 0;
|
|
18
|
+
// The CLI surface deliberately contains no Node boundary.
|
|
19
|
+
__exportStar(require("./cli/application.cjs"), exports);
|
|
20
|
+
var program_ts_1 = require("./cli/program.cjs");
|
|
21
|
+
Object.defineProperty(exports, "FLAG_OPTIONS", { enumerable: true, get: function () { return program_ts_1.FLAG_OPTIONS; } });
|
|
3133
22
|
//# sourceMappingURL=cli.cjs.map
|