@hunter-harness/workflow-harness 0.2.43 → 0.2.45
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/harness/bundles/general/claude-code/.harness-build.json +1 -1
- package/harness/bundles/general/claude-code/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/general/claude-code/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/general/claude-code/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/general/claude-code/contracts/run-session.schema.json +142 -0
- package/harness/bundles/general/claude-code/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/general/claude-code/contracts/service-session.schema.json +147 -0
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/checklist.md +12 -0
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +324 -28
- package/harness/bundles/general/claude-code/scripts/harness_change.py +137 -2
- package/harness/bundles/general/claude-code/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/general/claude-code/scripts/harness_environment.py +55 -25
- package/harness/bundles/general/claude-code/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/general/claude-code/scripts/harness_integration.py +289 -52
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +5 -0
- package/harness/bundles/general/claude-code/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/general/claude-code/scripts/harness_process.py +1065 -0
- package/harness/bundles/general/claude-code/scripts/harness_profile.py +244 -4
- package/harness/bundles/general/claude-code/scripts/harness_runtime.py +646 -132
- package/harness/bundles/general/claude-code/scripts/harness_service.py +1469 -194
- package/harness/bundles/general/claude-code/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/general/claude-code/scripts/harness_verification.py +79 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/general/codebuddy/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/general/codebuddy/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/general/codebuddy/contracts/run-session.schema.json +142 -0
- package/harness/bundles/general/codebuddy/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/general/codebuddy/contracts/service-session.schema.json +147 -0
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/checklist.md +12 -0
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +324 -28
- package/harness/bundles/general/codebuddy/scripts/harness_change.py +137 -2
- package/harness/bundles/general/codebuddy/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/general/codebuddy/scripts/harness_environment.py +55 -25
- package/harness/bundles/general/codebuddy/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/general/codebuddy/scripts/harness_integration.py +289 -52
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +5 -0
- package/harness/bundles/general/codebuddy/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/general/codebuddy/scripts/harness_process.py +1065 -0
- package/harness/bundles/general/codebuddy/scripts/harness_profile.py +244 -4
- package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +646 -132
- package/harness/bundles/general/codebuddy/scripts/harness_service.py +1469 -194
- package/harness/bundles/general/codebuddy/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/general/codebuddy/scripts/harness_verification.py +79 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/general/codex/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/general/codex/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/general/codex/contracts/run-session.schema.json +142 -0
- package/harness/bundles/general/codex/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/general/codex/contracts/service-session.schema.json +147 -0
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-submit/checklist.md +12 -0
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codex/scripts/harness_archive.py +324 -28
- package/harness/bundles/general/codex/scripts/harness_change.py +137 -2
- package/harness/bundles/general/codex/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/general/codex/scripts/harness_environment.py +55 -25
- package/harness/bundles/general/codex/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/general/codex/scripts/harness_integration.py +289 -52
- package/harness/bundles/general/codex/scripts/harness_ledger.py +5 -0
- package/harness/bundles/general/codex/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/general/codex/scripts/harness_process.py +1065 -0
- package/harness/bundles/general/codex/scripts/harness_profile.py +244 -4
- package/harness/bundles/general/codex/scripts/harness_runtime.py +646 -132
- package/harness/bundles/general/codex/scripts/harness_service.py +1469 -194
- package/harness/bundles/general/codex/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/general/codex/scripts/harness_verification.py +79 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/general/cursor/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/general/cursor/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/general/cursor/contracts/run-session.schema.json +142 -0
- package/harness/bundles/general/cursor/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/general/cursor/contracts/service-session.schema.json +147 -0
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/checklist.md +12 -0
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/cursor/scripts/harness_archive.py +324 -28
- package/harness/bundles/general/cursor/scripts/harness_change.py +137 -2
- package/harness/bundles/general/cursor/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/general/cursor/scripts/harness_environment.py +55 -25
- package/harness/bundles/general/cursor/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/general/cursor/scripts/harness_integration.py +289 -52
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +5 -0
- package/harness/bundles/general/cursor/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/general/cursor/scripts/harness_process.py +1065 -0
- package/harness/bundles/general/cursor/scripts/harness_profile.py +244 -4
- package/harness/bundles/general/cursor/scripts/harness_runtime.py +646 -132
- package/harness/bundles/general/cursor/scripts/harness_service.py +1469 -194
- package/harness/bundles/general/cursor/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/general/cursor/scripts/harness_verification.py +79 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/java/claude-code/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/java/claude-code/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/java/claude-code/contracts/run-session.schema.json +142 -0
- package/harness/bundles/java/claude-code/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/java/claude-code/contracts/service-session.schema.json +147 -0
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/checklist.md +12 -0
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +324 -28
- package/harness/bundles/java/claude-code/scripts/harness_change.py +137 -2
- package/harness/bundles/java/claude-code/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/java/claude-code/scripts/harness_environment.py +55 -25
- package/harness/bundles/java/claude-code/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/java/claude-code/scripts/harness_integration.py +289 -52
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +5 -0
- package/harness/bundles/java/claude-code/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/java/claude-code/scripts/harness_process.py +1065 -0
- package/harness/bundles/java/claude-code/scripts/harness_profile.py +244 -4
- package/harness/bundles/java/claude-code/scripts/harness_runtime.py +646 -132
- package/harness/bundles/java/claude-code/scripts/harness_service.py +1469 -194
- package/harness/bundles/java/claude-code/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/java/claude-code/scripts/harness_verification.py +79 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/java/codebuddy/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/java/codebuddy/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/java/codebuddy/contracts/run-session.schema.json +142 -0
- package/harness/bundles/java/codebuddy/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/java/codebuddy/contracts/service-session.schema.json +147 -0
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/checklist.md +12 -0
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +324 -28
- package/harness/bundles/java/codebuddy/scripts/harness_change.py +137 -2
- package/harness/bundles/java/codebuddy/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/java/codebuddy/scripts/harness_environment.py +55 -25
- package/harness/bundles/java/codebuddy/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/java/codebuddy/scripts/harness_integration.py +289 -52
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +5 -0
- package/harness/bundles/java/codebuddy/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/java/codebuddy/scripts/harness_process.py +1065 -0
- package/harness/bundles/java/codebuddy/scripts/harness_profile.py +244 -4
- package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +646 -132
- package/harness/bundles/java/codebuddy/scripts/harness_service.py +1469 -194
- package/harness/bundles/java/codebuddy/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/java/codebuddy/scripts/harness_verification.py +79 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/java/codex/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/java/codex/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/java/codex/contracts/run-session.schema.json +142 -0
- package/harness/bundles/java/codex/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/java/codex/contracts/service-session.schema.json +147 -0
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-submit/checklist.md +12 -0
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codex/scripts/harness_archive.py +324 -28
- package/harness/bundles/java/codex/scripts/harness_change.py +137 -2
- package/harness/bundles/java/codex/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/java/codex/scripts/harness_environment.py +55 -25
- package/harness/bundles/java/codex/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/java/codex/scripts/harness_integration.py +289 -52
- package/harness/bundles/java/codex/scripts/harness_ledger.py +5 -0
- package/harness/bundles/java/codex/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/java/codex/scripts/harness_process.py +1065 -0
- package/harness/bundles/java/codex/scripts/harness_profile.py +244 -4
- package/harness/bundles/java/codex/scripts/harness_runtime.py +646 -132
- package/harness/bundles/java/codex/scripts/harness_service.py +1469 -194
- package/harness/bundles/java/codex/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/java/codex/scripts/harness_verification.py +79 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/java/cursor/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/java/cursor/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/java/cursor/contracts/run-session.schema.json +142 -0
- package/harness/bundles/java/cursor/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/java/cursor/contracts/service-session.schema.json +147 -0
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/checklist.md +12 -0
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/cursor/scripts/harness_archive.py +324 -28
- package/harness/bundles/java/cursor/scripts/harness_change.py +137 -2
- package/harness/bundles/java/cursor/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/java/cursor/scripts/harness_environment.py +55 -25
- package/harness/bundles/java/cursor/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/java/cursor/scripts/harness_integration.py +289 -52
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +5 -0
- package/harness/bundles/java/cursor/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/java/cursor/scripts/harness_process.py +1065 -0
- package/harness/bundles/java/cursor/scripts/harness_profile.py +244 -4
- package/harness/bundles/java/cursor/scripts/harness_runtime.py +646 -132
- package/harness/bundles/java/cursor/scripts/harness_service.py +1469 -194
- package/harness/bundles/java/cursor/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/java/cursor/scripts/harness_verification.py +79 -0
- package/harness/manifests/general/claude-code.json +58 -26
- package/harness/manifests/general/codebuddy.json +58 -26
- package/harness/manifests/general/codex.json +58 -26
- package/harness/manifests/general/cursor.json +58 -26
- package/harness/manifests/java/claude-code.json +60 -28
- package/harness/manifests/java/codebuddy.json +60 -28
- package/harness/manifests/java/codex.json +60 -28
- package/harness/manifests/java/cursor.json +60 -28
- package/hunter-workflow-family.json +4 -4
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ import hashlib
|
|
|
21
21
|
import json
|
|
22
22
|
import os
|
|
23
23
|
import re
|
|
24
|
+
import shlex
|
|
24
25
|
import shutil
|
|
25
26
|
import subprocess
|
|
26
27
|
import sys
|
|
@@ -44,6 +45,212 @@ SCHEMA_VERSION = 3
|
|
|
44
45
|
PROFILE_REL = Path(".harness") / "config" / "build-profile.json"
|
|
45
46
|
|
|
46
47
|
|
|
48
|
+
def _legacy_command_has_shell_operator(command: str, *, platform: str) -> bool:
|
|
49
|
+
"""Return whether direct argv migration would change shell semantics."""
|
|
50
|
+
|
|
51
|
+
quote: str | None = None
|
|
52
|
+
escaped = False
|
|
53
|
+
windows = platform == "windows"
|
|
54
|
+
for character in command:
|
|
55
|
+
if escaped:
|
|
56
|
+
escaped = False
|
|
57
|
+
continue
|
|
58
|
+
if not windows and character == "\\" and quote != "'":
|
|
59
|
+
escaped = True
|
|
60
|
+
continue
|
|
61
|
+
if character == '"' or (not windows and character == "'"):
|
|
62
|
+
quote = None if quote == character else (character if quote is None else quote)
|
|
63
|
+
continue
|
|
64
|
+
if quote is None and character in {"&", "|", "<", ">", ";"}:
|
|
65
|
+
return True
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _parse_windows_command_line(command: str) -> list[str]:
|
|
70
|
+
"""Parse CreateProcess-style argv without POSIX ``shlex`` semantics."""
|
|
71
|
+
|
|
72
|
+
argv: list[str] = []
|
|
73
|
+
length = len(command)
|
|
74
|
+
index = 0
|
|
75
|
+
while index < length:
|
|
76
|
+
while index < length and command[index] in {" ", "\t"}:
|
|
77
|
+
index += 1
|
|
78
|
+
if index >= length:
|
|
79
|
+
break
|
|
80
|
+
token: list[str] = []
|
|
81
|
+
token_started = False
|
|
82
|
+
quoted = False
|
|
83
|
+
while index < length:
|
|
84
|
+
character = command[index]
|
|
85
|
+
if character in {" ", "\t"} and not quoted:
|
|
86
|
+
break
|
|
87
|
+
if character == "\\":
|
|
88
|
+
slash_start = index
|
|
89
|
+
while index < length and command[index] == "\\":
|
|
90
|
+
index += 1
|
|
91
|
+
slash_count = index - slash_start
|
|
92
|
+
if index < length and command[index] == '"':
|
|
93
|
+
token.extend("\\" for _ in range(slash_count // 2))
|
|
94
|
+
if slash_count % 2:
|
|
95
|
+
token.append('"')
|
|
96
|
+
index += 1
|
|
97
|
+
else:
|
|
98
|
+
quoted = not quoted
|
|
99
|
+
token_started = True
|
|
100
|
+
index += 1
|
|
101
|
+
else:
|
|
102
|
+
token.extend("\\" for _ in range(slash_count))
|
|
103
|
+
token_started = True
|
|
104
|
+
continue
|
|
105
|
+
if character == '"':
|
|
106
|
+
quoted = not quoted
|
|
107
|
+
token_started = True
|
|
108
|
+
index += 1
|
|
109
|
+
continue
|
|
110
|
+
token.append(character)
|
|
111
|
+
token_started = True
|
|
112
|
+
index += 1
|
|
113
|
+
if quoted:
|
|
114
|
+
raise ValueError("PROFILE_MIGRATION_UNSAFE: unclosed Windows quote")
|
|
115
|
+
if token_started:
|
|
116
|
+
argv.append("".join(token))
|
|
117
|
+
while index < length and command[index] in {" ", "\t"}:
|
|
118
|
+
index += 1
|
|
119
|
+
return argv
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def parse_legacy_service_command(
|
|
123
|
+
command: str,
|
|
124
|
+
*,
|
|
125
|
+
platform: str | None = None,
|
|
126
|
+
) -> dict[str, Any]:
|
|
127
|
+
"""Classify and parse a legacy service command without invoking a shell."""
|
|
128
|
+
|
|
129
|
+
normalized_platform = (
|
|
130
|
+
platform or ("windows" if os.name == "nt" else "posix")
|
|
131
|
+
).strip().lower()
|
|
132
|
+
if normalized_platform not in {"windows", "posix"}:
|
|
133
|
+
return {
|
|
134
|
+
"ok": False,
|
|
135
|
+
"reasonCode": "PROFILE_INVALID",
|
|
136
|
+
"message": f"unsupported command platform: {normalized_platform}",
|
|
137
|
+
"platform": normalized_platform,
|
|
138
|
+
}
|
|
139
|
+
if not isinstance(command, str) or not command.strip():
|
|
140
|
+
return {
|
|
141
|
+
"ok": False,
|
|
142
|
+
"reasonCode": "PROFILE_INVALID",
|
|
143
|
+
"message": "legacy service command must be a non-empty string",
|
|
144
|
+
"platform": normalized_platform,
|
|
145
|
+
}
|
|
146
|
+
if _legacy_command_has_shell_operator(
|
|
147
|
+
command, platform=normalized_platform
|
|
148
|
+
):
|
|
149
|
+
return {
|
|
150
|
+
"ok": False,
|
|
151
|
+
"reasonCode": "PROFILE_MIGRATION_UNSAFE",
|
|
152
|
+
"message": "legacy command contains shell operators",
|
|
153
|
+
"platform": normalized_platform,
|
|
154
|
+
}
|
|
155
|
+
try:
|
|
156
|
+
argv = (
|
|
157
|
+
_parse_windows_command_line(command)
|
|
158
|
+
if normalized_platform == "windows"
|
|
159
|
+
else shlex.split(command, posix=True)
|
|
160
|
+
)
|
|
161
|
+
except ValueError as exc:
|
|
162
|
+
return {
|
|
163
|
+
"ok": False,
|
|
164
|
+
"reasonCode": "PROFILE_MIGRATION_UNSAFE",
|
|
165
|
+
"message": str(exc),
|
|
166
|
+
"platform": normalized_platform,
|
|
167
|
+
}
|
|
168
|
+
if not argv or any("\0" in item for item in argv):
|
|
169
|
+
return {
|
|
170
|
+
"ok": False,
|
|
171
|
+
"reasonCode": "PROFILE_MIGRATION_UNSAFE",
|
|
172
|
+
"message": "legacy command cannot be represented as a safe argv array",
|
|
173
|
+
"platform": normalized_platform,
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
"ok": True,
|
|
177
|
+
"reasonCode": None,
|
|
178
|
+
"message": "",
|
|
179
|
+
"platform": normalized_platform,
|
|
180
|
+
"argv": argv,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def normalize_service_start(
|
|
185
|
+
service_start: dict[str, Any] | None,
|
|
186
|
+
*,
|
|
187
|
+
platform: str | None = None,
|
|
188
|
+
) -> dict[str, Any]:
|
|
189
|
+
"""Normalize serviceStart while preserving unsafe legacy strings verbatim."""
|
|
190
|
+
|
|
191
|
+
source = json.loads(json.dumps(service_start or {}))
|
|
192
|
+
has_command = isinstance(source.get("command"), str) and bool(
|
|
193
|
+
source["command"].strip()
|
|
194
|
+
)
|
|
195
|
+
has_argv = isinstance(source.get("argvTemplate"), list)
|
|
196
|
+
if has_command and has_argv:
|
|
197
|
+
raise ValueError(
|
|
198
|
+
"PROFILE_MIGRATION_UNSAFE: serviceStart.command and "
|
|
199
|
+
"serviceStart.argvTemplate are mutually exclusive"
|
|
200
|
+
)
|
|
201
|
+
if has_argv:
|
|
202
|
+
argv = source["argvTemplate"]
|
|
203
|
+
if any(not isinstance(item, str) or "\0" in item for item in argv):
|
|
204
|
+
raise ValueError(
|
|
205
|
+
"PROFILE_INVALID: serviceStart.argvTemplate must contain strings"
|
|
206
|
+
)
|
|
207
|
+
source.pop("command", None)
|
|
208
|
+
source.pop("legacyWarning", None)
|
|
209
|
+
return source
|
|
210
|
+
if has_command:
|
|
211
|
+
parsed = parse_legacy_service_command(
|
|
212
|
+
source["command"], platform=platform
|
|
213
|
+
)
|
|
214
|
+
if parsed["ok"]:
|
|
215
|
+
source["argvTemplate"] = parsed["argv"]
|
|
216
|
+
source.pop("command", None)
|
|
217
|
+
source.pop("legacyWarning", None)
|
|
218
|
+
else:
|
|
219
|
+
source["legacyWarning"] = {
|
|
220
|
+
"reasonCode": parsed["reasonCode"],
|
|
221
|
+
"platform": parsed["platform"],
|
|
222
|
+
"message": parsed["message"],
|
|
223
|
+
}
|
|
224
|
+
return source
|
|
225
|
+
source.pop("command", None)
|
|
226
|
+
source["argvTemplate"] = []
|
|
227
|
+
source.pop("legacyWarning", None)
|
|
228
|
+
return source
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def resolve_service_argv(
|
|
232
|
+
service_start: dict[str, Any],
|
|
233
|
+
replacements: dict[str, str] | None = None,
|
|
234
|
+
*,
|
|
235
|
+
platform: str | None = None,
|
|
236
|
+
) -> list[str]:
|
|
237
|
+
"""Resolve placeholders per argv element; never reparse a joined string."""
|
|
238
|
+
|
|
239
|
+
normalized = normalize_service_start(service_start, platform=platform)
|
|
240
|
+
argv = normalized.get("argvTemplate")
|
|
241
|
+
if not isinstance(argv, list):
|
|
242
|
+
warning = normalized.get("legacyWarning") or {}
|
|
243
|
+
reason = warning.get("reasonCode") or "PROFILE_MIGRATION_UNSAFE"
|
|
244
|
+
raise ValueError(f"{reason}: legacy service command requires review")
|
|
245
|
+
resolved: list[str] = []
|
|
246
|
+
for element in argv:
|
|
247
|
+
value = element
|
|
248
|
+
for placeholder, replacement in (replacements or {}).items():
|
|
249
|
+
value = value.replace(placeholder, replacement)
|
|
250
|
+
resolved.append(value)
|
|
251
|
+
return resolved
|
|
252
|
+
|
|
253
|
+
|
|
47
254
|
def recommend(project: Path) -> dict[str, Any]:
|
|
48
255
|
"""Return a reviewable profile recommendation without writing it."""
|
|
49
256
|
project = Path(project).resolve()
|
|
@@ -147,7 +354,11 @@ def recommend(project: Path) -> dict[str, Any]:
|
|
|
147
354
|
}
|
|
148
355
|
|
|
149
356
|
|
|
150
|
-
def project_profile_v3(
|
|
357
|
+
def project_profile_v3(
|
|
358
|
+
profile: dict[str, Any],
|
|
359
|
+
*,
|
|
360
|
+
platform: str | None = None,
|
|
361
|
+
) -> dict[str, Any]:
|
|
151
362
|
"""Compatibility projection for review before a protected v3 update."""
|
|
152
363
|
projected = json.loads(json.dumps(profile))
|
|
153
364
|
source_version = projected.get("schemaVersion")
|
|
@@ -195,6 +406,16 @@ def project_profile_v3(profile: dict[str, Any]) -> dict[str, Any]:
|
|
|
195
406
|
if isinstance(command, dict) and command.get("source") == "user"
|
|
196
407
|
),
|
|
197
408
|
}
|
|
409
|
+
projected["serviceStart"] = normalize_service_start(
|
|
410
|
+
projected.get("serviceStart")
|
|
411
|
+
if isinstance(projected.get("serviceStart"), dict)
|
|
412
|
+
else None,
|
|
413
|
+
platform=platform,
|
|
414
|
+
)
|
|
415
|
+
legacy_warning = projected["serviceStart"].get("legacyWarning")
|
|
416
|
+
if isinstance(legacy_warning, dict):
|
|
417
|
+
projected["migration"]["needsReview"] = True
|
|
418
|
+
projected["migration"]["serviceStartWarning"] = legacy_warning
|
|
198
419
|
projected["defaultsFingerprint"] = profile_defaults_fingerprint()
|
|
199
420
|
_derive_verification_inputs(projected)
|
|
200
421
|
projected["verificationInputs"] = {
|
|
@@ -673,7 +894,7 @@ def empty_profile_skeleton(excluded: tuple[str, ...] | list[str]) -> dict[str, A
|
|
|
673
894
|
"targets": {},
|
|
674
895
|
},
|
|
675
896
|
"serviceStart": {
|
|
676
|
-
"
|
|
897
|
+
"argvTemplate": [],
|
|
677
898
|
"healthUrl": "",
|
|
678
899
|
"startTimeoutSec": 120,
|
|
679
900
|
"inputFiles": [],
|
|
@@ -1352,8 +1573,27 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
|
1352
1573
|
if isinstance(val, str) and val.strip():
|
|
1353
1574
|
dropped.append(f"serviceStart.{field}(无 provenance,已备份;请用 record-quirk 重配)")
|
|
1354
1575
|
|
|
1576
|
+
projected: dict[str, Any] | None = None
|
|
1577
|
+
if sv == 2:
|
|
1578
|
+
try:
|
|
1579
|
+
projected = project_profile_v3(existing)
|
|
1580
|
+
except ValueError as exc:
|
|
1581
|
+
code = str(exc).split(":", 1)[0]
|
|
1582
|
+
return {
|
|
1583
|
+
"ok": False,
|
|
1584
|
+
"code": (
|
|
1585
|
+
code
|
|
1586
|
+
if code in {"PROFILE_INVALID", "PROFILE_MIGRATION_UNSAFE"}
|
|
1587
|
+
else "PROFILE_MIGRATION_UNSAFE"
|
|
1588
|
+
),
|
|
1589
|
+
"action": "migrate",
|
|
1590
|
+
"dry_run": dry_run,
|
|
1591
|
+
"needsMigration": True,
|
|
1592
|
+
"error": str(exc),
|
|
1593
|
+
"profilePath": str(profile_path),
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1355
1596
|
if dry_run:
|
|
1356
|
-
projected = project_profile_v3(existing) if sv == 2 else None
|
|
1357
1597
|
return {
|
|
1358
1598
|
"ok": True,
|
|
1359
1599
|
"code": "PROFILE_MIGRATION_PLANNED",
|
|
@@ -1376,7 +1616,7 @@ def migrate(project: Path, *, dry_run: bool = True) -> dict[str, Any]:
|
|
|
1376
1616
|
if not backup.is_file():
|
|
1377
1617
|
backup.write_text(profile_path.read_text(encoding="utf-8-sig"), encoding="utf-8", newline="\n")
|
|
1378
1618
|
if sv == 2:
|
|
1379
|
-
projected
|
|
1619
|
+
assert projected is not None
|
|
1380
1620
|
projected.setdefault("migration", {})["appliedAt"] = now_iso()
|
|
1381
1621
|
write_json(profile_path, projected)
|
|
1382
1622
|
else:
|