@aiwg/cli 2026.10.1 → 2026.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/agentic/code/providers/capability-matrix.yaml +270 -0
- package/agentic/code/providers/model-capabilities.v1.json +3183 -24
- package/bin/aiwg.mjs +135 -47
- package/dist/src/a2a/codecs.js +9 -0
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +57 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +107 -15
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +285 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +65 -0
- package/dist/src/artifacts/index-refresh.js +576 -0
- package/dist/src/artifacts/index-worker.mjs +9 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/repair.js +9 -4
- package/dist/src/artifacts/stats.js +5 -2
- package/dist/src/artifacts/types.js +30 -10
- package/dist/src/artifacts/watcher.js +1 -1
- package/dist/src/auth/credential-store.js +2 -1
- package/dist/src/catalog/cli.d.mts +5 -0
- package/dist/src/channel/manager.d.mts +20 -0
- package/dist/src/channel/manager.mjs +78 -12
- package/dist/src/cli/child-process-result.js +40 -0
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/env.js +38 -16
- package/dist/src/cli/flag-parsing.js +236 -0
- package/dist/src/cli/handlers/artifacts.js +3 -0
- package/dist/src/cli/handlers/auth.js +3 -0
- package/dist/src/cli/handlers/decision.js +218 -5
- package/dist/src/cli/handlers/diagnose.js +13 -5
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/feedback.js +14 -5
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/init.js +248 -30
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/mc.js +23 -0
- package/dist/src/cli/handlers/models.js +169 -10
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/ralph.js +3 -0
- package/dist/src/cli/handlers/refresh.js +28 -0
- package/dist/src/cli/handlers/regenerate.js +14 -0
- package/dist/src/cli/handlers/resource-versions.js +3 -0
- package/dist/src/cli/handlers/rules.js +195 -0
- package/dist/src/cli/handlers/run.js +4 -0
- package/dist/src/cli/handlers/runtime-info.js +25 -11
- package/dist/src/cli/handlers/script-runner.js +19 -8
- package/dist/src/cli/handlers/session.js +406 -62
- package/dist/src/cli/handlers/sessions.js +243 -76
- package/dist/src/cli/handlers/setup-manifest.js +474 -79
- package/dist/src/cli/handlers/setup.js +180 -39
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +60 -3
- package/dist/src/cli/handlers/team.js +18 -8
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +307 -55
- package/dist/src/cli/handlers/utilities.js +125 -26
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/prompt-utils.js +72 -0
- package/dist/src/cli/provider-resolution.js +11 -0
- package/dist/src/cli/router.js +15 -1
- package/dist/src/cli/services/deployment-verification.js +19 -9
- package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
- package/dist/src/cli/skill-usage.js +2 -3
- package/dist/src/config/aiwg-config.js +349 -35
- package/dist/src/config/cli.js +256 -23
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
- package/dist/src/config/gitignore.js +50 -0
- package/dist/src/config/index-graph-overrides.js +36 -0
- package/dist/src/config/project-artifacts-health.d.mts +37 -0
- package/dist/src/config/project-artifacts-health.mjs +14 -3
- package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
- package/dist/src/config/user-config-dir.d.mts +9 -0
- package/dist/src/config/user-config.js +38 -11
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/adapters/jev.js +75 -27
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +14 -7
- package/dist/src/decision/ensemble/aggregate.js +23 -10
- package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
- package/dist/src/decision/entry.js +25 -6
- package/dist/src/decision/graph-live-qualification.js +1 -1
- package/dist/src/decision/graph-run.js +9 -0
- package/dist/src/decision/heldout/collector.js +15 -14
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/index.js +1 -0
- package/dist/src/decision/interop.js +60 -8
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +942 -0
- package/dist/src/decision/offload-activation.js +137 -0
- package/dist/src/decision/offload.js +550 -0
- package/dist/src/decision/qualification/load.js +4 -6
- package/dist/src/decision/savings.js +28 -0
- package/dist/src/decision/validate.js +6 -3
- package/dist/src/effects/dispatch-verifier.js +252 -0
- package/dist/src/effects/index.js +1 -0
- package/dist/src/effects/types.js +5 -1
- package/dist/src/effects/verifiers/git.js +2 -1
- package/dist/src/effects/verifiers/tracker.js +26 -14
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-local-remove.js +36 -1
- package/dist/src/features/installer.js +2 -2
- package/dist/src/flow/graph-metadata.js +5 -0
- package/dist/src/gates/providers/loader.js +64 -18
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +15 -3
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +95 -5
- package/dist/src/issues/cli.js +4 -4
- package/dist/src/issues/local.js +24 -5
- package/dist/src/jobs/runner.js +1 -1
- package/dist/src/lint/cli.js +27 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/lint/reporters.js +25 -1
- package/dist/src/lint/rule-activation.js +43 -0
- package/dist/src/lint/runner.js +137 -62
- package/dist/src/mcp/adapters/codex-runtime.js +366 -56
- package/dist/src/mcp/claude-install.d.mts +4 -0
- package/dist/src/mcp/claude-install.mjs +41 -0
- package/dist/src/mcp/cli.d.mts +1 -0
- package/dist/src/mcp/cli.mjs +620 -85
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-file.mjs +279 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/config-layers.mjs +348 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/credentials.mjs +484 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +17 -27
- package/dist/src/mcp/helpers.mjs +109 -4
- package/dist/src/mcp/muse-native-config.d.mts +13 -0
- package/dist/src/mcp/muse-native-config.mjs +1 -0
- package/dist/src/mcp/omp-config.d.mts +4 -0
- package/dist/src/mcp/omp-config.mjs +50 -29
- package/dist/src/mcp/profiles.js +97 -23
- package/dist/src/mcp/profiles.mjs +99 -27
- package/dist/src/mcp/registry.js +248 -131
- package/dist/src/mcp/registry.mjs +169 -100
- package/dist/src/mcp/server-config.d.mts +3 -0
- package/dist/src/mcp/server-config.mjs +89 -0
- package/dist/src/mcp/server.mjs +127 -82
- package/dist/src/mcp/test-simple.mjs +2 -1
- package/dist/src/mcp/toml-editor.d.mts +4 -0
- package/dist/src/mcp/toml-string.d.mts +1 -0
- package/dist/src/mcp/toml-string.mjs +9 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tool-filters.mjs +357 -0
- package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
- package/dist/src/mcp/tools/decision.mjs +22 -3
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +50 -2
- package/dist/src/mcp/tools/subsystems.mjs +39 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +2856 -0
- package/dist/src/mcp/tools/worker-shell-control.mjs +289 -0
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
- package/dist/src/memory/project-registry.js +2 -2
- package/dist/src/mission-protocol/codecs.js +647 -1
- package/dist/src/mission-protocol/types.js +110 -0
- package/dist/src/mission-protocol/worker-status.js +2 -0
- package/dist/src/models/config-loader.js +76 -14
- package/dist/src/models/grok-build-models.js +93 -41
- package/dist/src/models/model-capabilities.v1.json +3183 -24
- package/dist/src/models/model-discovery.js +931 -67
- package/dist/src/models/provider-policy.js +719 -11
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/qualification-digest.mjs +17 -0
- package/dist/src/models/resolver.js +261 -0
- package/dist/src/models/worker-direct-readiness.js +550 -0
- package/dist/src/models/worker-dispatch-offer.js +169 -0
- package/dist/src/models/worker-health-readiness.js +144 -0
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +986 -0
- package/dist/src/models/wrapper-route.js +18 -0
- package/dist/src/ops/registry.js +9 -14
- package/dist/src/plugins/standalone-packager.js +4 -3
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +430 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/cli-command.mjs +75 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/capability-matrix.js +8 -0
- package/dist/src/providers/capability-matrix.yaml +270 -0
- package/dist/src/providers/deployed-skill-roots.js +20 -0
- package/dist/src/providers/grok-build-acp.js +3 -2
- package/dist/src/providers/grok-build-headless.js +53 -23
- package/dist/src/providers/grok-build-inspect.js +1 -0
- package/dist/src/providers/omp-agent.d.mts +8 -0
- package/dist/src/providers/omp-diagnostics.d.mts +1 -0
- package/dist/src/providers/omp-paths.d.mts +13 -0
- package/dist/src/providers/provider-definitions.js +9 -1
- package/dist/src/providers/provider-definitions.mjs +9 -2
- package/dist/src/providers/skill-ownership.d.mts +9 -0
- package/dist/src/providers/skill-ownership.mjs +88 -0
- package/dist/src/rules/activation.d.mts +21 -0
- package/dist/src/rules/activation.mjs +62 -0
- package/dist/src/rules/inline-budget.d.mts +26 -0
- package/dist/src/rules/inline-budget.mjs +66 -0
- package/dist/src/rules/inventory.js +129 -0
- package/dist/src/rules/pin-state.js +73 -0
- package/dist/src/rules/pins.d.mts +14 -0
- package/dist/src/rules/pins.mjs +112 -0
- package/dist/src/rules/rule-frontmatter.schema.json +197 -0
- package/dist/src/serve/executor-registry.js +16 -24
- package/dist/src/serve/fleet-mission-conductor.js +9 -0
- package/dist/src/serve/mission-conductor.js +47 -12
- package/dist/src/serve/shared-host-scheduler.js +2 -0
- package/dist/src/serve/worker-invocation.js +558 -0
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/serve/worker-stdin-frame.mjs +107 -0
- package/dist/src/sessions/adapter-registry.js +49 -0
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +82 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +43 -9
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
- package/dist/src/sessions/adapters/grokbot.js +434 -24
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +128 -0
- package/dist/src/sessions/adapters/muse.js +200 -104
- package/dist/src/sessions/adapters/omp.js +28 -14
- package/dist/src/sessions/adapters/openclaw.js +89 -79
- package/dist/src/sessions/adapters/opencode.js +27 -24
- package/dist/src/sessions/adapters/openhuman.js +18 -60
- package/dist/src/sessions/adapters/pi.js +30 -14
- package/dist/src/sessions/adapters/warp.js +3 -3
- package/dist/src/sessions/adapters/windsurf.js +19 -48
- package/dist/src/sessions/batch-contracts.js +25 -5
- package/dist/src/sessions/batch-import.js +72 -51
- package/dist/src/sessions/candidates.js +47 -11
- package/dist/src/sessions/contracts.js +75 -2
- package/dist/src/sessions/coverage-preflight.js +134 -0
- package/dist/src/sessions/discovery.js +3 -0
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/grok-build-cli.js +67 -5
- package/dist/src/sessions/import-exclusions.js +72 -0
- package/dist/src/sessions/import-lease.js +34 -18
- package/dist/src/sessions/import-limits.js +37 -0
- package/dist/src/sessions/importer.js +84 -46
- package/dist/src/sessions/index.js +2 -0
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +133 -25
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +106 -0
- package/dist/src/sessions/repository.js +199 -63
- package/dist/src/sessions/workspace-discovery.js +315 -88
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/adapters/clawhub.js +10 -8
- package/dist/src/skills/adapters/openclaw.js +10 -7
- package/dist/src/skills/importer.js +1 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
- package/dist/src/smiths/context-pipeline/finalization.js +6 -5
- package/dist/src/smiths/context-pipeline/generator.js +21 -4
- package/dist/src/smiths/context-pipeline/parallelism-section.js +195 -14
- package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
- package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
- package/dist/src/smiths/mcpsmith/example.js +1 -1
- package/dist/src/smiths/mcpsmith/generator.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +85 -31
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/logseq.js +68 -7
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
- package/dist/src/testing/generators/test-code-generator.js +57 -30
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/uhp/client.js +11 -1
- package/dist/src/uhp/mission.js +5 -0
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/checker.mjs +5 -5
- package/dist/src/update/notifier.mjs +54 -2
- package/dist/src/update/service.d.mts +22 -0
- package/dist/src/utils/contained-filesystem.d.mts +37 -0
- package/dist/src/utils/contained-filesystem.mjs +683 -0
- package/dist/src/utils/temporary-directory.js +36 -0
- package/dist/src/writing/example-generator.js +1 -1
- package/dist/src/writing/fidelity.js +18 -1
- package/package.json +32 -2
- package/schemas/mission-v1.schema.json +2429 -0
- package/schemas/models/model-catalog.v1.schema.json +301 -0
- package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
- package/schemas/models/model-policy.v1.schema.json +14 -0
- package/schemas/models/provider-model-capabilities.v1.schema.json +1072 -0
- package/tools/_resolve-impl.mjs +39 -2
- package/tools/agents/deploy-agents.mjs +64 -23
- package/tools/agents/providers/base.mjs +153 -166
- package/tools/agents/providers/claude.mjs +8 -24
- package/tools/agents/providers/codex.mjs +18 -11
- package/tools/agents/providers/cursor.mjs +19 -18
- package/tools/agents/providers/grok-build.mjs +3 -1
- package/tools/agents/providers/grokbot.mjs +11 -12
- package/tools/agents/providers/hermes.mjs +62 -49
- package/tools/agents/providers/omp.mjs +5 -2
- package/tools/agents/providers/openclaw-translator.mjs +19 -5
- package/tools/agents/providers/openclaw.mjs +9 -4
- package/tools/agents/providers/pi.mjs +5 -0
- package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
- package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
- package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
- package/tools/agents/providers/rule-delivery.mjs +1270 -0
- package/tools/agents/providers/warp.mjs +20 -26
- package/tools/cli/decision-offload-report.mjs +40 -0
- package/tools/cli/workspace-status.mjs +862 -0
- package/tools/plugin/package-plugins.mjs +186 -8
- package/tools/plugin/plugin-links.mjs +114 -0
- package/tools/providers/antigravity-transport.mjs +3 -1
- package/tools/providers/deepseek-harness-transport.mjs +360 -0
- package/tools/providers/omp-transport.mjs +194 -0
- package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
- package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
- package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
- package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
- package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
- package/tools/ralph-external/lib/pi-adapter.mjs +329 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +748 -0
- package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
- package/tools/ralph-external/lib/session-environment.mjs +28 -0
- package/tools/ralph-external/lib/systemd-client.mjs +49 -0
- package/tools/ralph-external/lib/worker-health.mjs +439 -0
- package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
- package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
- package/tools/ralph-external/process-monitor.mjs +1017 -0
- package/tools/ralph-external/session-launcher.mjs +5057 -0
- package/tools/scripts/resource-preflight.mjs +541 -0
- package/tools/skills/deploy-skills-codex.mjs +2 -1
- package/vscode-extension/schemas/aiwg.config.v1.json +2916 -0
|
@@ -4,8 +4,13 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { parseDocument, stringify } from 'yaml';
|
|
6
6
|
import { findPackageRoot } from '../find-package-root.js';
|
|
7
|
+
import { isInteractive } from '../env.js';
|
|
8
|
+
import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
|
|
7
9
|
import { sha256 } from '../../security/artifact-trust.js';
|
|
8
10
|
import { artifactVerifyHandler } from './artifact-verify.js';
|
|
11
|
+
import { requireFlagValue, MissingFlagValueError, collectFlagValues, hasPositionalOrSwitch, argsTerminatorIndex, valueTokenIndexes } from '../flag-parsing.js';
|
|
12
|
+
import { exitCodeForSpawnResult } from '../child-process-result.js';
|
|
13
|
+
import { askString, createPromptInterface } from '../prompt-utils.js';
|
|
9
14
|
const SETUP_SCHEMA_REL = 'agentic/code/addons/agentic-installer/schemas/v1/setup-manifest.schema.json';
|
|
10
15
|
const GENERATE_HELP = `aiwg setup-generate - generate a starter setup.aiwg.io/v1 SetupManifest
|
|
11
16
|
|
|
@@ -67,50 +72,40 @@ Safety:
|
|
|
67
72
|
Mutating execution refuses to run without explicit confirmation.
|
|
68
73
|
Provider-orchestrated manifests are never handed to an agent unless their
|
|
69
74
|
exact bytes have status verified under the explicit trust policy.
|
|
75
|
+
|
|
76
|
+
Headless input steps (#3062):
|
|
77
|
+
A 'type: ask' step resolves its declared 'params' the same way a 'script'
|
|
78
|
+
step's env vars do (--param/--params-file/env/default); if every declared
|
|
79
|
+
param is already resolved it runs headless; otherwise a terminal prompts
|
|
80
|
+
for missing values. Without a terminal it refuses and names the missing params. A step marked
|
|
81
|
+
'interactive: true' runs an arbitrary script whose questions cannot be
|
|
82
|
+
enumerated; without a terminal it is refused outright. Every selected
|
|
83
|
+
step (including nested 'platform-route' steps) is preflighted for both
|
|
84
|
+
conditions before any step executes — never partway through.
|
|
70
85
|
`;
|
|
86
|
+
/**
|
|
87
|
+
* A value-taking flag present without a usable value is a usage error, not
|
|
88
|
+
* "not given" (#3062 review item 11) — delegates to the shared strict
|
|
89
|
+
* parser. Callers (the three handlers below) catch `MissingFlagValueError`
|
|
90
|
+
* and report it before any preflight check, spawn, or write.
|
|
91
|
+
* `precedence: 'first'` matches this command's own pre-#3062 behavior (a
|
|
92
|
+
* hand-rolled loop that returned on the first match), unchanged by the
|
|
93
|
+
* shared parser's second-round fix (#3062 second review, finding 4).
|
|
94
|
+
*/
|
|
71
95
|
function flagValue(args, name) {
|
|
72
|
-
|
|
73
|
-
if (args[i] === name) {
|
|
74
|
-
const value = args[i + 1];
|
|
75
|
-
return value && !value.startsWith('--') ? value : undefined;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return undefined;
|
|
96
|
+
return requireFlagValue(args, {}, name);
|
|
79
97
|
}
|
|
98
|
+
const VALUE_FLAGS = [
|
|
99
|
+
'--manifest', '--schema', '--platform', '--distro', '--params-file', '--param', '--set',
|
|
100
|
+
'--step', '--skip', '--type', '--output', '--name', '--attestation', '--policy', '--state',
|
|
101
|
+
'--asset-type', '--namespace', '--channel',
|
|
102
|
+
];
|
|
80
103
|
function hasFlag(args, ...names) {
|
|
81
|
-
return
|
|
104
|
+
return names.some(name => hasPositionalOrSwitch(args, name, VALUE_FLAGS));
|
|
82
105
|
}
|
|
83
106
|
function positionalManifest(args) {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
'--schema',
|
|
87
|
-
'--platform',
|
|
88
|
-
'--distro',
|
|
89
|
-
'--params-file',
|
|
90
|
-
'--param',
|
|
91
|
-
'--set',
|
|
92
|
-
'--step',
|
|
93
|
-
'--skip',
|
|
94
|
-
'--type',
|
|
95
|
-
'--output',
|
|
96
|
-
'--name',
|
|
97
|
-
'--attestation',
|
|
98
|
-
'--policy',
|
|
99
|
-
'--state',
|
|
100
|
-
'--asset-type',
|
|
101
|
-
'--namespace',
|
|
102
|
-
'--channel',
|
|
103
|
-
]);
|
|
104
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
105
|
-
const arg = args[i];
|
|
106
|
-
if (valueFlags.has(arg)) {
|
|
107
|
-
i += 1;
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
if (!arg.startsWith('-'))
|
|
111
|
-
return arg;
|
|
112
|
-
}
|
|
113
|
-
return undefined;
|
|
107
|
+
const consumed = valueTokenIndexes(args, VALUE_FLAGS);
|
|
108
|
+
return args.slice(0, argsTerminatorIndex(args)).find((arg, index) => !consumed.has(index) && !arg.startsWith('-'));
|
|
114
109
|
}
|
|
115
110
|
function resolveSchemaPath(frameworkRoot, cwd, override) {
|
|
116
111
|
if (override)
|
|
@@ -238,6 +233,83 @@ function allSteps(manifest) {
|
|
|
238
233
|
function existingRelative(manifestDir, rel) {
|
|
239
234
|
return !!rel && existsSync(path.resolve(manifestDir, rel));
|
|
240
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Detect `on_fail` reference cycles among recovery procedures themselves —
|
|
238
|
+
* go/no-go review blocker 3. A step's `on_fail` can name any recovery
|
|
239
|
+
* procedure, including (structurally, per the shared `SetupStep` type) a
|
|
240
|
+
* step *inside* a recovery procedure's own `steps`. A recovery procedure
|
|
241
|
+
* that (directly, or through another recovery procedure) names itself used
|
|
242
|
+
* to make the old, eager preflight recurse into it forever —
|
|
243
|
+
* `RangeError: Maximum call stack size exceeded`, reproduced in memory by
|
|
244
|
+
* the review.
|
|
245
|
+
*
|
|
246
|
+
* This is a one-time, bounded graph walk over `manifest.spec.recovery[]`
|
|
247
|
+
* only (at most `recovery.length` nodes) — a visited/"on the current path"
|
|
248
|
+
* marker per node means each node is fully explored exactly once, so this
|
|
249
|
+
* terminates and never re-enters a node already being explored; it reports
|
|
250
|
+
* one finding per distinct cycle instead of throwing. An `on_fail` that
|
|
251
|
+
* does not resolve to a known recovery id is reported separately by the
|
|
252
|
+
* existing unresolved-recovery check (`installerConsistencyChecks` above)
|
|
253
|
+
* and is not itself a cycle.
|
|
254
|
+
*
|
|
255
|
+
* This graph check is independent of, and does not change, how `on_fail`
|
|
256
|
+
* references are actually used (`collectPreflightBlockers()`/`runStep()`
|
|
257
|
+
* below): execution never follows an `on_fail` reference more than once
|
|
258
|
+
* (a failing recovery step reports failure directly, it does not itself
|
|
259
|
+
* trigger further recovery), so neither the main-sequence preflight nor a
|
|
260
|
+
* selected recovery's own preflight recurses through `on_fail` at all —
|
|
261
|
+
* see `collectPreflightBlockers()`'s own doc comment. This cycle check
|
|
262
|
+
* exists for the case the type system permits but execution never
|
|
263
|
+
* exercises: a recovery procedure's own steps declaring `on_fail`.
|
|
264
|
+
*/
|
|
265
|
+
function detectRecoveryReferenceCycles(manifest) {
|
|
266
|
+
const recoveries = manifest.spec.recovery ?? [];
|
|
267
|
+
if (recoveries.length === 0)
|
|
268
|
+
return [];
|
|
269
|
+
const onFailTargets = new Map();
|
|
270
|
+
for (const recovery of recoveries) {
|
|
271
|
+
const targets = [];
|
|
272
|
+
const collect = (steps) => {
|
|
273
|
+
steps?.forEach((step) => {
|
|
274
|
+
if (step.on_fail && step.on_fail !== 'recover')
|
|
275
|
+
targets.push(step.on_fail);
|
|
276
|
+
step.routes?.forEach((route) => collect(route.steps));
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
collect(recovery.steps);
|
|
280
|
+
onFailTargets.set(recovery.id, targets);
|
|
281
|
+
}
|
|
282
|
+
const findings = [];
|
|
283
|
+
const reportedCycles = new Set();
|
|
284
|
+
const state = new Map();
|
|
285
|
+
const visit = (id, path) => {
|
|
286
|
+
if (state.get(id) === 'done')
|
|
287
|
+
return;
|
|
288
|
+
if (state.get(id) === 'on-path') {
|
|
289
|
+
const cycle = [...path.slice(path.indexOf(id)), id];
|
|
290
|
+
const key = [...new Set(cycle)].sort().join('\u0000');
|
|
291
|
+
if (!reportedCycles.has(key)) {
|
|
292
|
+
reportedCycles.add(key);
|
|
293
|
+
findings.push({
|
|
294
|
+
severity: 'error',
|
|
295
|
+
path: '/spec/recovery',
|
|
296
|
+
rule: 'recoveryCycle',
|
|
297
|
+
message: `recovery reference cycle: ${cycle.join(' -> ')}`,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
state.set(id, 'on-path');
|
|
303
|
+
for (const next of onFailTargets.get(id) ?? []) {
|
|
304
|
+
if (onFailTargets.has(next))
|
|
305
|
+
visit(next, [...path, id]);
|
|
306
|
+
}
|
|
307
|
+
state.set(id, 'done');
|
|
308
|
+
};
|
|
309
|
+
for (const recovery of recoveries)
|
|
310
|
+
visit(recovery.id, []);
|
|
311
|
+
return findings;
|
|
312
|
+
}
|
|
241
313
|
function installerConsistencyChecks(manifest, manifestDir) {
|
|
242
314
|
if (!manifest)
|
|
243
315
|
return [];
|
|
@@ -262,6 +334,8 @@ function installerConsistencyChecks(manifest, manifestDir) {
|
|
|
262
334
|
findings.push({ severity: 'error', path: `/spec/steps/${index}/on_fail`, rule: 'on_fail', message: `unresolved recovery '${step.on_fail}'` });
|
|
263
335
|
}
|
|
264
336
|
}
|
|
337
|
+
// Go/no-go review blocker 3.
|
|
338
|
+
findings.push(...detectRecoveryReferenceCycles(manifest));
|
|
265
339
|
for (const { step, pointer } of allSteps(manifest)) {
|
|
266
340
|
if (step.type === 'script' && !existingRelative(manifestDir, step.script)) {
|
|
267
341
|
findings.push({ severity: 'error', path: `${pointer}/script`, rule: 'scriptExists', message: `script not found: ${step.script ?? '(missing)'}` });
|
|
@@ -374,15 +448,12 @@ function renderValidationText(result) {
|
|
|
374
448
|
}
|
|
375
449
|
function parseParamValues(args) {
|
|
376
450
|
const values = {};
|
|
377
|
-
for (
|
|
378
|
-
if (args[i] !== '--param' && args[i] !== '--set')
|
|
379
|
-
continue;
|
|
380
|
-
const raw = args[i + 1] ?? '';
|
|
451
|
+
for (const raw of collectFlagValues(args, {}, '--param', '--set')) {
|
|
381
452
|
const eq = raw.indexOf('=');
|
|
382
|
-
if (eq <= 0)
|
|
383
|
-
throw new
|
|
453
|
+
if (eq <= 0 || !raw.slice(0, eq).trim())
|
|
454
|
+
throw new MissingFlagValueError('--param KEY=VALUE');
|
|
455
|
+
// An empty parameter value is supported (e.g. explicitly clearing a default).
|
|
384
456
|
values[raw.slice(0, eq)] = raw.slice(eq + 1);
|
|
385
|
-
i += 1;
|
|
386
457
|
}
|
|
387
458
|
return values;
|
|
388
459
|
}
|
|
@@ -602,13 +673,41 @@ function planLines(manifest, manifestPath, target, params, steps) {
|
|
|
602
673
|
});
|
|
603
674
|
return lines.join('\n') + '\n';
|
|
604
675
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
676
|
+
/**
|
|
677
|
+
* #3062 review item 7: a `null` status already falls through to `code = 1`
|
|
678
|
+
* (not success); this also surfaces the bounded helper's `error`/`signal`
|
|
679
|
+
* diagnostic (e.g. the enriched timeout message naming the bound and
|
|
680
|
+
* override variable) instead of silently dropping it.
|
|
681
|
+
*/
|
|
682
|
+
function commandResult(result) {
|
|
683
|
+
const outcome = exitCodeForSpawnResult(result);
|
|
684
|
+
if (outcome.message)
|
|
685
|
+
console.error(`[setup] ${outcome.message}`);
|
|
686
|
+
const code = result.status === null ? 1 : result.status;
|
|
687
|
+
return { ok: code === 0 && !result.error && !result.signal, status: code };
|
|
608
688
|
}
|
|
689
|
+
/**
|
|
690
|
+
* Run one manifest-author-defined shell command (#3062).
|
|
691
|
+
*
|
|
692
|
+
* Without a terminal, a command that tries to prompt must fail fast instead
|
|
693
|
+
* of hanging forever: bounded (long tier, stdin ignored). With a terminal,
|
|
694
|
+
* unchanged — inherited stdio, unbounded, so a human can actually answer a
|
|
695
|
+
* prompt the command issues.
|
|
696
|
+
*/
|
|
609
697
|
function runShell(command, cwd, env) {
|
|
698
|
+
if (!isInteractive()) {
|
|
699
|
+
const result = boundedSpawnSync(command, [], 'long', {
|
|
700
|
+
cwd, env, shell: true, stdio: ['ignore', 'inherit', 'inherit'],
|
|
701
|
+
});
|
|
702
|
+
return commandResult(result);
|
|
703
|
+
}
|
|
704
|
+
// sync-spawn: interactive manifest-shell-command — runs an arbitrary
|
|
705
|
+
// manifest-author-defined shell command with inherited stdio; it may
|
|
706
|
+
// itself prompt (see tools/testing/sync-spawn-allowlist.json). Reached
|
|
707
|
+
// only when isInteractive() is true (#3062) — the no-terminal path above
|
|
708
|
+
// uses the bounded helper instead. Not a candidate for a bounded timeout.
|
|
610
709
|
const result = spawnSync(command, { cwd, env, shell: true, stdio: 'inherit' });
|
|
611
|
-
return commandResult(result
|
|
710
|
+
return commandResult(result);
|
|
612
711
|
}
|
|
613
712
|
function isDestructiveScript(filePath) {
|
|
614
713
|
const name = path.basename(filePath);
|
|
@@ -619,9 +718,180 @@ function isDestructiveScript(filePath) {
|
|
|
619
718
|
const text = readFileSync(filePath, 'utf8');
|
|
620
719
|
return /\brm\s+-rf\b|\b(cp|mv)\s+-f\b|>\s*\S+/.test(text);
|
|
621
720
|
}
|
|
622
|
-
|
|
721
|
+
/**
|
|
722
|
+
* Param names a `type: ask` step resolves (#3062 review item 10). Reuses
|
|
723
|
+
* the same `params` field `type: script` steps already use to pass env
|
|
724
|
+
* vars — there is no separate "ask step input" field in the schema, and
|
|
725
|
+
* this is the same mechanism, just read by the `ask` branch below instead
|
|
726
|
+
* of passed to a script.
|
|
727
|
+
*/
|
|
728
|
+
function askStepParams(step) {
|
|
729
|
+
return step.params ?? [];
|
|
730
|
+
}
|
|
731
|
+
/** Which of an `ask` step's declared params are not yet resolved (missing from `--param`/params-file/env/default). */
|
|
732
|
+
function missingAskParams(step, params) {
|
|
733
|
+
return askStepParams(step).filter((name) => params[name] === undefined);
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Preflight every step in `steps` (recursively into `platform-route` nested
|
|
737
|
+
* steps only) for conditions that would block it from running at all, and
|
|
738
|
+
* return one message per blocker — never a partial list halted at the
|
|
739
|
+
* first step (#3062 review item 13, and the second review's finding 7:
|
|
740
|
+
* refusal must happen before ANY step executes).
|
|
741
|
+
*
|
|
742
|
+
* Used for two distinct call sites, both passing only the steps that are
|
|
743
|
+
* actually about to run:
|
|
744
|
+
*
|
|
745
|
+
* 1. The main selected sequence, up front, before any of it runs
|
|
746
|
+
* (`runSetupManifest()`'s own preflight call, below).
|
|
747
|
+
* 2. A *specific* recovery procedure's own steps, called again at the
|
|
748
|
+
* moment a failed step selects it — completely, before that
|
|
749
|
+
* recovery's first step runs (go/no-go review blocker 3).
|
|
750
|
+
*
|
|
751
|
+
* This function never itself follows a step's `on_fail` into another
|
|
752
|
+
* recovery procedure, in either case — go/no-go review blocker 3's
|
|
753
|
+
* "preflight is too eager" finding: the previous version always recursed
|
|
754
|
+
* into every selected step's `on_fail` recovery during the main, up-front
|
|
755
|
+
* preflight, which (a) refused a manifest whose main sequence would
|
|
756
|
+
* succeed outright, solely because its *unused* recovery contained an
|
|
757
|
+
* unsupported step, and (b) had no cycle protection, so a recovery whose
|
|
758
|
+
* own steps' `on_fail` pointed back into a recovery already being
|
|
759
|
+
* preflighted recursed without bound (`RangeError: Maximum call stack
|
|
760
|
+
* size exceeded`, reproduced by the review). Not recursing into `on_fail`
|
|
761
|
+
* here also matches what execution itself does: a failing step triggers
|
|
762
|
+
* its *one* named recovery exactly once; a failing step *within* that
|
|
763
|
+
* recovery reports failure directly rather than following a further
|
|
764
|
+
* `on_fail` — so preflight never needs to follow one either. (A
|
|
765
|
+
* recovery-to-recovery `on_fail` reference cycle in the manifest itself is
|
|
766
|
+
* still caught — as a graph check over `manifest.spec.recovery[]`, not a
|
|
767
|
+
* recursive preflight — by `detectRecoveryReferenceCycles()` during
|
|
768
|
+
* manifest validation.)
|
|
769
|
+
*
|
|
770
|
+
* - `interactive: true` without a terminal AND without every declared
|
|
771
|
+
* `params` entry resolved: with every declared param supplied, the
|
|
772
|
+
* step runs headless instead (review item 10; second review finding
|
|
773
|
+
* 5b — the step itself is trusted not to need to prompt once its
|
|
774
|
+
* declared inputs are present). With no declared params at all
|
|
775
|
+
* ("undeclared interaction" — the step's questions cannot be
|
|
776
|
+
* enumerated or supplied via `--param`) this always requires a
|
|
777
|
+
* terminal.
|
|
778
|
+
* - `type: ask` with no declared `params`: always refused — unlike
|
|
779
|
+
* `interactive: true`, this CLI itself owns the prompting for `ask`
|
|
780
|
+
* steps (there is no separate script to hand a terminal to), and
|
|
781
|
+
* with nothing declared there is no destination to store an answer
|
|
782
|
+
* in. This is one of the two agreed, permanent exceptions (the other
|
|
783
|
+
* is the contributor sync-upstream strategy choice).
|
|
784
|
+
* - `type: ask` with declared params not all resolved, and no
|
|
785
|
+
* terminal: refused, naming the missing names so `--param` can
|
|
786
|
+
* supply them. With a terminal, this is *not* a blocker — the
|
|
787
|
+
* missing ones are prompted for when the step actually runs (second
|
|
788
|
+
* review finding 5b).
|
|
789
|
+
* - `type: agentic`/`type: chain`: `runStep()` refuses both
|
|
790
|
+
* unconditionally; preflighted the same way so an earlier, unrelated
|
|
791
|
+
* step is never executed ahead of this unconditional refusal (second
|
|
792
|
+
* review finding 7).
|
|
793
|
+
*/
|
|
794
|
+
function collectPreflightBlockers(steps, manifest, targetOs, params, terminalPresent) {
|
|
795
|
+
const blockers = [];
|
|
796
|
+
for (const step of steps) {
|
|
797
|
+
if (!stepPlatformMatches(targetOs, step))
|
|
798
|
+
continue;
|
|
799
|
+
if (step.interactive) {
|
|
800
|
+
const names = askStepParams(step);
|
|
801
|
+
const missing = missingAskParams(step, params);
|
|
802
|
+
const fullySupplied = names.length > 0 && missing.length === 0;
|
|
803
|
+
if (!fullySupplied && !terminalPresent) {
|
|
804
|
+
blockers.push(names.length === 0
|
|
805
|
+
? `step '${step.id}' is marked interactive (its prompts cannot be enumerated or supplied via --param) and requires a terminal`
|
|
806
|
+
: `step '${step.id}' is marked interactive and is missing required param(s): ${missing.join(', ')} (supply with --param <name>=<value>, or run with a terminal attached)`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (step.type === 'ask') {
|
|
810
|
+
const names = askStepParams(step);
|
|
811
|
+
if (names.length === 0) {
|
|
812
|
+
blockers.push(`ask step '${step.id}' declares no params to resolve via --param; interactive capture for ask ` +
|
|
813
|
+
"steps is not implemented — declare 'params' on the step and supply them with --param <name>=<value>");
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
const missing = missingAskParams(step, params);
|
|
817
|
+
if (missing.length > 0 && !terminalPresent) {
|
|
818
|
+
blockers.push(`ask step '${step.id}' is missing required param(s): ${missing.join(', ')} (supply with --param ` +
|
|
819
|
+
'<name>=<value>, or run with a terminal attached to be prompted for them)');
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
if (step.type === 'agentic' || step.type === 'chain') {
|
|
824
|
+
blockers.push(`${step.type} step '${step.id}' is not supported by deterministic CLI execution and is always refused`);
|
|
825
|
+
}
|
|
826
|
+
if (step.type === 'platform-route') {
|
|
827
|
+
const route = step.routes?.find((item) => item.platform === targetOs);
|
|
828
|
+
if (route)
|
|
829
|
+
blockers.push(...collectPreflightBlockers(route.steps, manifest, targetOs, params, terminalPresent));
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return blockers;
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Preflight a *specific* recovery procedure's own steps — go/no-go review
|
|
836
|
+
* blocker 3. Called at the moment a failed step selects it, completely,
|
|
837
|
+
* before any of its steps run. Returns `undefined` when the recovery id
|
|
838
|
+
* does not resolve to a known procedure (the schema/consistency check
|
|
839
|
+
* already reports that as a manifest error; this function's caller treats
|
|
840
|
+
* an unresolved id as "no recovery available", same as before).
|
|
841
|
+
*/
|
|
842
|
+
function findRecoveryProcedure(manifest, recoveryId) {
|
|
843
|
+
return manifest.spec.recovery?.find((item) => item.id === recoveryId);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Prompt interactively for each of `missingNames`, one per declared `ask`-
|
|
847
|
+
* step param still unresolved (#3062 second review, finding 5b: declared
|
|
848
|
+
* ask inputs missing AT a terminal are prompted for, not refused — this
|
|
849
|
+
* is reachable only when `isInteractive()` is true, so it is always safe
|
|
850
|
+
* to open a prompt interface here).
|
|
851
|
+
*/
|
|
852
|
+
async function promptForAskStep(step, missingNames) {
|
|
853
|
+
const rl = createPromptInterface();
|
|
854
|
+
const answers = {};
|
|
855
|
+
try {
|
|
856
|
+
if (step.message)
|
|
857
|
+
console.log(`[setup] ${step.message}`);
|
|
858
|
+
for (const name of missingNames) {
|
|
859
|
+
const answer = await askString(rl, ` ${name}: `, '', undefined);
|
|
860
|
+
if (answer)
|
|
861
|
+
answers[name] = answer;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
finally {
|
|
865
|
+
rl.close();
|
|
866
|
+
}
|
|
867
|
+
return answers;
|
|
868
|
+
}
|
|
869
|
+
async function runStep(step, manifest, manifestDir, params, targetOs, confirmed, recovery = false) {
|
|
623
870
|
if (!stepPlatformMatches(targetOs, step))
|
|
624
871
|
return { ok: true, status: 0 };
|
|
872
|
+
// A step marked `interactive: true` (#3062) declares that it prompts
|
|
873
|
+
// directly. For a top-level selected step this condition is already
|
|
874
|
+
// preflighted before any step runs (review item 13; second review
|
|
875
|
+
// finding 7); this remains the only gate for a recovery step, which
|
|
876
|
+
// isn't known until a step actually fails. With every declared `params`
|
|
877
|
+
// entry resolved, the step is trusted not to need to prompt and runs
|
|
878
|
+
// headless below like any other step (second review finding 5b — the
|
|
879
|
+
// bug this fixes: it used to be refused here even when fully supplied).
|
|
880
|
+
// With no declared params at all ("undeclared interaction") or some
|
|
881
|
+
// still missing, a terminal is required.
|
|
882
|
+
if (step.interactive) {
|
|
883
|
+
const names = askStepParams(step);
|
|
884
|
+
const missing = missingAskParams(step, params);
|
|
885
|
+
const fullySupplied = names.length > 0 && missing.length === 0;
|
|
886
|
+
if (!fullySupplied && !isInteractive()) {
|
|
887
|
+
console.error(names.length === 0
|
|
888
|
+
? `setup-run: step '${step.id}' is marked interactive (its prompts cannot be enumerated or supplied ` +
|
|
889
|
+
'via --param) and requires a terminal; re-run from an interactive shell.'
|
|
890
|
+
: `setup-run: step '${step.id}' is marked interactive and is missing required param(s): ` +
|
|
891
|
+
`${missing.join(', ')}; supply with --param <name>=<value>, or re-run from an interactive shell.`);
|
|
892
|
+
return { ok: false, status: 2, failedStep: step.id };
|
|
893
|
+
}
|
|
894
|
+
}
|
|
625
895
|
const env = { ...process.env, ...params };
|
|
626
896
|
console.log(`[setup] Step ${step.id} (${step.type})`);
|
|
627
897
|
if (step.type === 'script') {
|
|
@@ -634,8 +904,19 @@ function runStep(step, manifest, manifestDir, params, targetOs, confirmed, recov
|
|
|
634
904
|
console.error(`setup-run: refusing destructive step '${step.id}' without --yes/--confirm`);
|
|
635
905
|
return { ok: false, status: 2, failedStep: step.id };
|
|
636
906
|
}
|
|
637
|
-
|
|
638
|
-
|
|
907
|
+
// Without a terminal, a script that tries to prompt must fail fast
|
|
908
|
+
// instead of hanging forever: bounded (long tier, stdin ignored). With
|
|
909
|
+
// a terminal, unchanged (#3062).
|
|
910
|
+
const result = isInteractive()
|
|
911
|
+
// sync-spawn: interactive manifest-script-step — runs an arbitrary
|
|
912
|
+
// manifest-author-defined script file with inherited stdio; it may
|
|
913
|
+
// itself prompt (see tools/testing/sync-spawn-allowlist.json). Reached
|
|
914
|
+
// only when isInteractive() is true (#3062) — the no-terminal path
|
|
915
|
+
// uses the bounded helper instead. Not a candidate for a bounded
|
|
916
|
+
// timeout.
|
|
917
|
+
? spawnSync(scriptPath, [], { cwd: manifestDir, env, stdio: 'inherit' })
|
|
918
|
+
: boundedSpawnSync(scriptPath, [], 'long', { cwd: manifestDir, env, stdio: ['ignore', 'inherit', 'inherit'] });
|
|
919
|
+
const ran = commandResult(result);
|
|
639
920
|
if (!ran.ok)
|
|
640
921
|
return { ...ran, failedStep: step.id };
|
|
641
922
|
const verifies = Array.isArray(step.verify) ? step.verify : step.verify ? [step.verify] : [];
|
|
@@ -677,13 +958,51 @@ function runStep(step, manifest, manifestDir, params, targetOs, confirmed, recov
|
|
|
677
958
|
return { ok: false, status: 2, failedStep: step.id };
|
|
678
959
|
}
|
|
679
960
|
if (step.type === 'ask') {
|
|
680
|
-
|
|
681
|
-
|
|
961
|
+
// #3062 review item 10: a top-level selected step with all its declared
|
|
962
|
+
// params already resolved is already caught as *not* blocking by
|
|
963
|
+
// `collectPreflightBlockers` and runs headless here; this remains the
|
|
964
|
+
// gate for a recovery-path `ask` step, which isn't preflighted.
|
|
965
|
+
const names = askStepParams(step);
|
|
966
|
+
if (names.length === 0) {
|
|
967
|
+
// Agreed, permanent exception (second review finding 5b): this CLI
|
|
968
|
+
// owns the prompting for `ask` steps directly (there is no separate
|
|
969
|
+
// script to hand a terminal to), and with nothing declared there is
|
|
970
|
+
// no destination to store an answer in — always refused, with or
|
|
971
|
+
// without a terminal.
|
|
972
|
+
console.error(`setup-run: ask step '${step.id}' declares no params to resolve via --param; interactive capture ` +
|
|
973
|
+
"for ask steps is not implemented — declare 'params' on the step and supply them with --param " +
|
|
974
|
+
'<name>=<value>.');
|
|
975
|
+
return { ok: false, status: 2, failedStep: step.id };
|
|
976
|
+
}
|
|
977
|
+
const missing = missingAskParams(step, params);
|
|
978
|
+
if (missing.length === 0) {
|
|
979
|
+
console.log(` ask: ${step.message ?? step.id} -> answered via --param (${names.join(', ')})`);
|
|
980
|
+
return { ok: true, status: 0 };
|
|
981
|
+
}
|
|
982
|
+
if (!isInteractive()) {
|
|
983
|
+
console.error(`setup-run: ask step '${step.id}' is missing required param(s): ${missing.join(', ')}; supply with ` +
|
|
984
|
+
'--param <name>=<value>, or re-run from an interactive shell to be prompted for them.');
|
|
985
|
+
return { ok: false, status: 2, failedStep: step.id };
|
|
986
|
+
}
|
|
987
|
+
// #3062 second review, finding 5b: declared ask inputs missing AT a
|
|
988
|
+
// terminal are prompted for, not refused.
|
|
989
|
+
const answers = await promptForAskStep(step, missing);
|
|
990
|
+
const stillMissing = missing.filter((name) => !answers[name]);
|
|
991
|
+
if (stillMissing.length > 0) {
|
|
992
|
+
console.error(`setup-run: ask step '${step.id}' did not receive a value for: ${stillMissing.join(', ')}.`);
|
|
993
|
+
return { ok: false, status: 2, failedStep: step.id };
|
|
994
|
+
}
|
|
995
|
+
// Merged into the same object reference the caller's loop (and every
|
|
996
|
+
// later step) reads from, so a later step sees the interactively-
|
|
997
|
+
// captured answer exactly like a `--param`-supplied one.
|
|
998
|
+
Object.assign(params, answers);
|
|
999
|
+
console.log(` ask: ${step.message ?? step.id} -> answered interactively (${missing.join(', ')})`);
|
|
1000
|
+
return { ok: true, status: 0 };
|
|
682
1001
|
}
|
|
683
1002
|
if (step.type === 'platform-route') {
|
|
684
1003
|
const route = step.routes?.find((item) => item.platform === targetOs);
|
|
685
1004
|
for (const nested of route?.steps ?? []) {
|
|
686
|
-
const result = runStep(nested, manifest, manifestDir, params, targetOs, confirmed);
|
|
1005
|
+
const result = await runStep(nested, manifest, manifestDir, params, targetOs, confirmed);
|
|
687
1006
|
if (!result.ok)
|
|
688
1007
|
return result;
|
|
689
1008
|
}
|
|
@@ -691,7 +1010,7 @@ function runStep(step, manifest, manifestDir, params, targetOs, confirmed, recov
|
|
|
691
1010
|
}
|
|
692
1011
|
return { ok: true, status: 0 };
|
|
693
1012
|
}
|
|
694
|
-
export function runSetupManifest(options) {
|
|
1013
|
+
export async function runSetupManifest(options) {
|
|
695
1014
|
const validation = validateSetupManifest({
|
|
696
1015
|
cwd: options.cwd,
|
|
697
1016
|
frameworkRoot: options.frameworkRoot,
|
|
@@ -722,10 +1041,21 @@ export function runSetupManifest(options) {
|
|
|
722
1041
|
return { exitCode: 1, message: `setup-run: platform ${target.os}${target.distro ? `/${target.distro}` : ''}/${target.arch}/${target.shell} is not declared in the manifest` };
|
|
723
1042
|
}
|
|
724
1043
|
const params = resolveParams(manifest, options);
|
|
725
|
-
|
|
1044
|
+
const steps = selectedSteps(manifest, options);
|
|
1045
|
+
const askSteps = [];
|
|
1046
|
+
const collectAsks = (selected) => {
|
|
1047
|
+
for (const step of selected) {
|
|
1048
|
+
if (step.type === 'ask')
|
|
1049
|
+
askSteps.push(step);
|
|
1050
|
+
if (step.type === 'platform-route')
|
|
1051
|
+
collectAsks(step.routes?.find(route => route.platform === target.os)?.steps ?? []);
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
collectAsks(steps);
|
|
1055
|
+
const promptable = new Set(isInteractive() ? askSteps.flatMap(askStepParams) : []);
|
|
1056
|
+
if (params.missing.some(name => !promptable.has(name))) {
|
|
726
1057
|
return { exitCode: 1, message: `setup-run: required params missing before execution: ${params.missing.join(', ')}` };
|
|
727
1058
|
}
|
|
728
|
-
const steps = selectedSteps(manifest, options);
|
|
729
1059
|
if (options.step && steps.length === 0)
|
|
730
1060
|
return { exitCode: 1, message: `setup-run: step '${options.step}' not found` };
|
|
731
1061
|
if (options.dryRun) {
|
|
@@ -735,15 +1065,55 @@ export function runSetupManifest(options) {
|
|
|
735
1065
|
if (!options.yes) {
|
|
736
1066
|
return { exitCode: 2, message: 'setup-run: mutating execution requires --yes or --confirm after reviewing the plan with --dry-run' };
|
|
737
1067
|
}
|
|
1068
|
+
// #3062 review item 13: preflight every selected step (recursively into
|
|
1069
|
+
// `platform-route` nesting) before executing any of them — the previous
|
|
1070
|
+
// behavior checked `interactive`/`ask` conditions inside the per-step
|
|
1071
|
+
// loop below, so an earlier normal step could already perform work
|
|
1072
|
+
// before a later step's refusal fired.
|
|
1073
|
+
const blockers = collectPreflightBlockers(steps, manifest, target.os, params.values, isInteractive());
|
|
1074
|
+
if (blockers.length > 0) {
|
|
1075
|
+
for (const blocker of blockers)
|
|
1076
|
+
console.error(`setup-run: ${blocker}.`);
|
|
1077
|
+
return {
|
|
1078
|
+
exitCode: 2,
|
|
1079
|
+
message: `setup-run: refusing before executing any step:\n${blockers.map((b) => ` - ${b}`).join('\n')}`,
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
// Resolve required ask inputs before any scripts can consume them.
|
|
1083
|
+
for (const step of askSteps) {
|
|
1084
|
+
const missing = missingAskParams(step, params.values).filter(name => params.missing.includes(name));
|
|
1085
|
+
if (!missing.length)
|
|
1086
|
+
continue;
|
|
1087
|
+
Object.assign(params.values, await promptForAskStep(step, missing));
|
|
1088
|
+
}
|
|
1089
|
+
const checked = resolveParams(manifest, { ...options, paramValues: { ...options.paramValues, ...params.values } });
|
|
1090
|
+
if (checked.missing.length)
|
|
1091
|
+
return { exitCode: 1, message: `setup-run: required params missing before execution: ${checked.missing.join(', ')}` };
|
|
1092
|
+
Object.assign(params.values, checked.values);
|
|
738
1093
|
for (const step of steps) {
|
|
739
|
-
const result = runStep(step, manifest, validation.manifestDir, params.values, target.os, options.yes);
|
|
1094
|
+
const result = await runStep(step, manifest, validation.manifestDir, params.values, target.os, options.yes);
|
|
740
1095
|
if (!result.ok) {
|
|
741
1096
|
if (result.failedStep && step.on_fail) {
|
|
742
|
-
const recovery = manifest
|
|
1097
|
+
const recovery = findRecoveryProcedure(manifest, step.on_fail);
|
|
743
1098
|
if (recovery) {
|
|
1099
|
+
// Go/no-go review blocker 3: preflight the *selected* recovery's
|
|
1100
|
+
// own steps completely, now that a failure has actually chosen
|
|
1101
|
+
// it — not when the manifest was first loaded (that preflighted
|
|
1102
|
+
// the main sequence only, above) — and before any of its steps
|
|
1103
|
+
// run. Refuses with zero recovery steps executed, same
|
|
1104
|
+
// all-blockers-at-once shape as the main-sequence preflight.
|
|
1105
|
+
const recoveryBlockers = collectPreflightBlockers(recovery.steps, manifest, target.os, params.values, isInteractive());
|
|
1106
|
+
if (recoveryBlockers.length > 0) {
|
|
1107
|
+
for (const blocker of recoveryBlockers)
|
|
1108
|
+
console.error(`setup-run: [recovery '${recovery.id}'] ${blocker}.`);
|
|
1109
|
+
return {
|
|
1110
|
+
exitCode: 2,
|
|
1111
|
+
message: `setup-run: recovery '${recovery.id}' (for failed step '${result.failedStep}') refusing before executing any recovery step:\n${recoveryBlockers.map((b) => ` - ${b}`).join('\n')}`,
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
744
1114
|
console.error(`[setup] recovery '${recovery.id}' available for failed step '${result.failedStep}'`);
|
|
745
1115
|
for (const recoveryStep of recovery.steps) {
|
|
746
|
-
const recoveryResult = runStep(recoveryStep, manifest, validation.manifestDir, params.values, target.os, options.yes, true);
|
|
1116
|
+
const recoveryResult = await runStep(recoveryStep, manifest, validation.manifestDir, params.values, target.os, options.yes, true);
|
|
747
1117
|
if (!recoveryResult.ok)
|
|
748
1118
|
return { exitCode: recoveryResult.status || 1, message: `setup-run: recovery '${recovery.id}' failed` };
|
|
749
1119
|
}
|
|
@@ -771,11 +1141,22 @@ export const setupValidateHandler = {
|
|
|
771
1141
|
return { exitCode: 0 };
|
|
772
1142
|
}
|
|
773
1143
|
const strict = hasFlag(ctx.args, '--strict');
|
|
1144
|
+
let manifestPath;
|
|
1145
|
+
let schemaPath;
|
|
1146
|
+
try {
|
|
1147
|
+
manifestPath = flagValue(ctx.args, '--manifest') ?? positionalManifest(ctx.args);
|
|
1148
|
+
schemaPath = flagValue(ctx.args, '--schema');
|
|
1149
|
+
}
|
|
1150
|
+
catch (err) {
|
|
1151
|
+
if (err instanceof MissingFlagValueError)
|
|
1152
|
+
return { exitCode: 2, message: `${err.message}.` };
|
|
1153
|
+
throw err;
|
|
1154
|
+
}
|
|
774
1155
|
const result = validateSetupManifest({
|
|
775
1156
|
cwd: ctx.cwd,
|
|
776
1157
|
frameworkRoot: ctx.frameworkRoot,
|
|
777
|
-
manifestPath
|
|
778
|
-
schemaPath
|
|
1158
|
+
manifestPath,
|
|
1159
|
+
schemaPath,
|
|
779
1160
|
strict,
|
|
780
1161
|
});
|
|
781
1162
|
const payload = {
|
|
@@ -807,8 +1188,9 @@ export const setupGenerateHandler = {
|
|
|
807
1188
|
}
|
|
808
1189
|
catch (error) {
|
|
809
1190
|
return {
|
|
810
|
-
|
|
811
|
-
|
|
1191
|
+
// #3062 review item 11: a missing flag value is a usage error.
|
|
1192
|
+
exitCode: error instanceof MissingFlagValueError ? 2 : 1,
|
|
1193
|
+
message: error instanceof Error ? `${error.message}.` : String(error),
|
|
812
1194
|
};
|
|
813
1195
|
}
|
|
814
1196
|
},
|
|
@@ -824,21 +1206,34 @@ export const setupRunHandler = {
|
|
|
824
1206
|
process.stdout.write(RUN_HELP);
|
|
825
1207
|
return { exitCode: 0 };
|
|
826
1208
|
}
|
|
827
|
-
|
|
828
|
-
|
|
1209
|
+
let options;
|
|
1210
|
+
let policyFlag;
|
|
1211
|
+
let verificationArgs = [];
|
|
1212
|
+
try {
|
|
1213
|
+
options = parseRunOptions(ctx);
|
|
1214
|
+
policyFlag = flagValue(ctx.args, '--policy');
|
|
1215
|
+
if (policyFlag) {
|
|
1216
|
+
const manifestPathForVerification = options.manifestPath ?? 'setup.manifest.yaml';
|
|
1217
|
+
verificationArgs = [
|
|
1218
|
+
manifestPathForVerification,
|
|
1219
|
+
'--attestation', flagValue(ctx.args, '--attestation') ?? `${manifestPathForVerification}.aiwg-attestation.json`,
|
|
1220
|
+
'--policy', policyFlag,
|
|
1221
|
+
'--asset-type', flagValue(ctx.args, '--asset-type') ?? 'setup-manifest',
|
|
1222
|
+
'--namespace', flagValue(ctx.args, '--namespace') ?? 'aiwg',
|
|
1223
|
+
'--channel', flagValue(ctx.args, '--channel') ?? 'stable',
|
|
1224
|
+
'--json',
|
|
1225
|
+
...(flagValue(ctx.args, '--state') ? ['--state', flagValue(ctx.args, '--state')] : []),
|
|
1226
|
+
...(hasFlag(ctx.args, '--offline') ? ['--offline'] : []),
|
|
1227
|
+
];
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
catch (err) {
|
|
1231
|
+
if (err instanceof MissingFlagValueError)
|
|
1232
|
+
return { exitCode: 2, message: `setup-run: ${err.message}.` };
|
|
1233
|
+
throw err;
|
|
1234
|
+
}
|
|
829
1235
|
let artifactVerification;
|
|
830
|
-
if (
|
|
831
|
-
const verificationArgs = [
|
|
832
|
-
manifestPath,
|
|
833
|
-
'--attestation', flagValue(ctx.args, '--attestation') ?? `${manifestPath}.aiwg-attestation.json`,
|
|
834
|
-
'--policy', flagValue(ctx.args, '--policy'),
|
|
835
|
-
'--asset-type', flagValue(ctx.args, '--asset-type') ?? 'setup-manifest',
|
|
836
|
-
'--namespace', flagValue(ctx.args, '--namespace') ?? 'aiwg',
|
|
837
|
-
'--channel', flagValue(ctx.args, '--channel') ?? 'stable',
|
|
838
|
-
'--json',
|
|
839
|
-
...(flagValue(ctx.args, '--state') ? ['--state', flagValue(ctx.args, '--state')] : []),
|
|
840
|
-
...(hasFlag(ctx.args, '--offline') ? ['--offline'] : []),
|
|
841
|
-
];
|
|
1236
|
+
if (policyFlag) {
|
|
842
1237
|
const verification = await artifactVerifyHandler.execute({ ...ctx, args: verificationArgs, rawArgs: verificationArgs });
|
|
843
1238
|
try {
|
|
844
1239
|
artifactVerification = JSON.parse(verification.message ?? '');
|