opkg 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -186
- package/assets/openpackage_ascii_dark.png +0 -0
- package/assets/openpackage_ascii_light.png +0 -0
- package/dist/commands/add.js +34 -10
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/apply.js +16 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/delete.js +1 -1
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/install.js +177 -8
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/list.js +2 -2
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/login.js +1 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.js +1 -1
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/new.js +125 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/pack.js +7 -13
- package/dist/commands/pack.js.map +1 -1
- package/dist/commands/pull.js +1 -1
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.js +1 -1
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/remove.js +63 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/save.js +11 -17
- package/dist/commands/save.js.map +1 -1
- package/dist/commands/set.js +33 -0
- package/dist/commands/set.js.map +1 -0
- package/dist/commands/show.js +16 -94
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/status.js +26 -701
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/uninstall.js +14 -427
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/constants/index.js +72 -16
- package/dist/constants/index.js.map +1 -1
- package/dist/core/add/add-conflict-handler.js +1 -8
- package/dist/core/add/add-conflict-handler.js.map +1 -1
- package/dist/core/add/add-pipeline.js +12 -10
- package/dist/core/add/add-pipeline.js.map +1 -1
- package/dist/core/add/add-to-source-pipeline.js +123 -0
- package/dist/core/add/add-to-source-pipeline.js.map +1 -0
- package/dist/core/add/package-index-updater.js +77 -78
- package/dist/core/add/package-index-updater.js.map +1 -1
- package/dist/core/add/platform-path-transformer.js +6 -4
- package/dist/core/add/platform-path-transformer.js.map +1 -1
- package/dist/core/add/source-collector.js +2 -3
- package/dist/core/add/source-collector.js.map +1 -1
- package/dist/core/apply/apply-pipeline.js +110 -0
- package/dist/core/apply/apply-pipeline.js.map +1 -0
- package/dist/core/dependency-resolver.js +263 -21
- package/dist/core/dependency-resolver.js.map +1 -1
- package/dist/core/discovery/file-discovery.js +1 -2
- package/dist/core/discovery/file-discovery.js.map +1 -1
- package/dist/core/discovery/platform-files-discovery.js +33 -18
- package/dist/core/discovery/platform-files-discovery.js.map +1 -1
- package/dist/core/flows/flow-executor.js +974 -0
- package/dist/core/flows/flow-executor.js.map +1 -0
- package/dist/core/flows/flow-inverter.js +442 -0
- package/dist/core/flows/flow-inverter.js.map +1 -0
- package/dist/core/flows/flow-key-extractor.js +101 -0
- package/dist/core/flows/flow-key-extractor.js.map +1 -0
- package/dist/core/flows/flow-key-mapper.js +382 -0
- package/dist/core/flows/flow-key-mapper.js.map +1 -0
- package/dist/core/flows/flow-transforms.js +632 -0
- package/dist/core/flows/flow-transforms.js.map +1 -0
- package/dist/core/flows/map-pipeline/context.js +73 -0
- package/dist/core/flows/map-pipeline/context.js.map +1 -0
- package/dist/core/flows/map-pipeline/index.js +156 -0
- package/dist/core/flows/map-pipeline/index.js.map +1 -0
- package/dist/core/flows/map-pipeline/operations/copy.js +104 -0
- package/dist/core/flows/map-pipeline/operations/copy.js.map +1 -0
- package/dist/core/flows/map-pipeline/operations/pipe.js +70 -0
- package/dist/core/flows/map-pipeline/operations/pipe.js.map +1 -0
- package/dist/core/flows/map-pipeline/operations/rename.js +102 -0
- package/dist/core/flows/map-pipeline/operations/rename.js.map +1 -0
- package/dist/core/flows/map-pipeline/operations/set.js +50 -0
- package/dist/core/flows/map-pipeline/operations/set.js.map +1 -0
- package/dist/core/flows/map-pipeline/operations/switch.js +79 -0
- package/dist/core/flows/map-pipeline/operations/switch.js.map +1 -0
- package/dist/core/flows/map-pipeline/operations/transform.js +543 -0
- package/dist/core/flows/map-pipeline/operations/transform.js.map +1 -0
- package/dist/core/flows/map-pipeline/operations/unset.js +65 -0
- package/dist/core/flows/map-pipeline/operations/unset.js.map +1 -0
- package/dist/core/flows/map-pipeline/types.js +8 -0
- package/dist/core/flows/map-pipeline/types.js.map +1 -0
- package/dist/core/flows/map-pipeline/utils.js +278 -0
- package/dist/core/flows/map-pipeline/utils.js.map +1 -0
- package/dist/core/flows/platform-converter.js +328 -0
- package/dist/core/flows/platform-converter.js.map +1 -0
- package/dist/core/flows/source-resolver.js +192 -0
- package/dist/core/flows/source-resolver.js.map +1 -0
- package/dist/core/flows/toml-domain-transforms.js +23 -0
- package/dist/core/flows/toml-domain-transforms.js.map +1 -0
- package/dist/core/install/bulk-install-pipeline.js +68 -7
- package/dist/core/install/bulk-install-pipeline.js.map +1 -1
- package/dist/core/install/canonical-plan.js +3 -3
- package/dist/core/install/canonical-plan.js.map +1 -1
- package/dist/core/install/dry-run.js +3 -3
- package/dist/core/install/dry-run.js.map +1 -1
- package/dist/core/install/flow-based-installer.js +1158 -0
- package/dist/core/install/flow-based-installer.js.map +1 -0
- package/dist/core/install/flow-workspace-tracker.js +111 -0
- package/dist/core/install/flow-workspace-tracker.js.map +1 -0
- package/dist/core/install/format-detector.js +228 -0
- package/dist/core/install/format-detector.js.map +1 -0
- package/dist/core/install/git-package-loader.js +20 -0
- package/dist/core/install/git-package-loader.js.map +1 -0
- package/dist/core/install/install-errors.js +1 -1
- package/dist/core/install/install-errors.js.map +1 -1
- package/dist/core/install/install-flow.js +34 -14
- package/dist/core/install/install-flow.js.map +1 -1
- package/dist/core/install/install-pipeline.js +52 -17
- package/dist/core/install/install-pipeline.js.map +1 -1
- package/dist/core/install/install-reporting.js +26 -8
- package/dist/core/install/install-reporting.js.map +1 -1
- package/dist/core/install/local-source-resolution.js +103 -0
- package/dist/core/install/local-source-resolution.js.map +1 -0
- package/dist/core/install/marketplace-handler.js +221 -0
- package/dist/core/install/marketplace-handler.js.map +1 -0
- package/dist/core/install/path-install-pipeline.js +241 -0
- package/dist/core/install/path-install-pipeline.js.map +1 -0
- package/dist/core/install/path-package-loader.js +116 -0
- package/dist/core/install/path-package-loader.js.map +1 -0
- package/dist/core/install/plugin-detector.js +72 -0
- package/dist/core/install/plugin-detector.js.map +1 -0
- package/dist/core/install/plugin-to-universal-converter.js +218 -0
- package/dist/core/install/plugin-to-universal-converter.js.map +1 -0
- package/dist/core/install/plugin-transformer.js +191 -0
- package/dist/core/install/plugin-transformer.js.map +1 -0
- package/dist/core/install/version-selection.js +1 -1
- package/dist/core/install/version-selection.js.map +1 -1
- package/dist/core/openpackage.js +40 -22
- package/dist/core/openpackage.js.map +1 -1
- package/dist/core/pack/pack-output.js +62 -0
- package/dist/core/pack/pack-output.js.map +1 -0
- package/dist/core/pack/pack-pipeline.js +186 -0
- package/dist/core/pack/pack-pipeline.js.map +1 -0
- package/dist/core/package-context.js +45 -70
- package/dist/core/package-context.js.map +1 -1
- package/dist/core/package-creation.js +203 -0
- package/dist/core/package-creation.js.map +1 -0
- package/dist/core/package.js +20 -6
- package/dist/core/package.js.map +1 -1
- package/dist/core/platforms.js +665 -209
- package/dist/core/platforms.js.map +1 -1
- package/dist/core/push/push-context.js +1 -1
- package/dist/core/push/push-context.js.map +1 -1
- package/dist/core/push/push-upload.js +2 -2
- package/dist/core/push/push-upload.js.map +1 -1
- package/dist/core/registry.js +6 -6
- package/dist/core/registry.js.map +1 -1
- package/dist/core/remote-pull.js +2 -2
- package/dist/core/remote-pull.js.map +1 -1
- package/dist/core/remove/removal-collector.js +52 -0
- package/dist/core/remove/removal-collector.js.map +1 -0
- package/dist/core/remove/removal-confirmation.js +39 -0
- package/dist/core/remove/removal-confirmation.js.map +1 -0
- package/dist/core/remove/remove-from-source-pipeline.js +173 -0
- package/dist/core/remove/remove-from-source-pipeline.js.map +1 -0
- package/dist/core/save/constants.js +3 -3
- package/dist/core/save/constants.js.map +1 -1
- package/dist/core/save/flow-based-saver.js +270 -0
- package/dist/core/save/flow-based-saver.js.map +1 -0
- package/dist/core/save/name-resolution.js +1 -1
- package/dist/core/save/name-resolution.js.map +1 -1
- package/dist/core/save/package-yml-generator.js +4 -5
- package/dist/core/save/package-yml-generator.js.map +1 -1
- package/dist/core/save/save-candidate-builder.js +215 -0
- package/dist/core/save/save-candidate-builder.js.map +1 -0
- package/dist/core/save/save-candidate-loader.js +12 -11
- package/dist/core/save/save-candidate-loader.js.map +1 -1
- package/dist/core/save/save-conflict-analyzer.js +150 -0
- package/dist/core/save/save-conflict-analyzer.js.map +1 -0
- package/dist/core/save/save-conflict-resolution.js +28 -14
- package/dist/core/save/save-conflict-resolution.js.map +1 -1
- package/dist/core/save/save-conflict-resolver.js +31 -275
- package/dist/core/save/save-conflict-resolver.js.map +1 -1
- package/dist/core/save/save-group-builder.js +52 -0
- package/dist/core/save/save-group-builder.js.map +1 -0
- package/dist/core/save/save-interactive-resolver.js +190 -0
- package/dist/core/save/save-interactive-resolver.js.map +1 -0
- package/dist/core/save/save-pipeline.js +58 -34
- package/dist/core/save/save-pipeline.js.map +1 -1
- package/dist/core/save/save-platform-handler.js +53 -0
- package/dist/core/save/save-platform-handler.js.map +1 -0
- package/dist/core/save/save-resolution-executor.js +145 -0
- package/dist/core/save/save-resolution-executor.js.map +1 -0
- package/dist/core/save/save-result-reporter.js +167 -0
- package/dist/core/save/save-result-reporter.js.map +1 -0
- package/dist/core/save/save-to-source-pipeline.js +154 -0
- package/dist/core/save/save-to-source-pipeline.js.map +1 -0
- package/dist/core/save/save-versioning.js +4 -4
- package/dist/core/save/save-versioning.js.map +1 -1
- package/dist/core/save/save-write-coordinator.js +204 -0
- package/dist/core/save/save-write-coordinator.js.map +1 -0
- package/dist/core/save/save-yml-resolution.js +28 -216
- package/dist/core/save/save-yml-resolution.js.map +1 -1
- package/dist/core/save/workspace-rename.js +7 -8
- package/dist/core/save/workspace-rename.js.map +1 -1
- package/dist/core/set/set-output.js +72 -0
- package/dist/core/set/set-output.js.map +1 -0
- package/dist/core/set/set-pipeline.js +361 -0
- package/dist/core/set/set-pipeline.js.map +1 -0
- package/dist/core/set/set-types.js +5 -0
- package/dist/core/set/set-types.js.map +1 -0
- package/dist/core/show/package-resolver.js +257 -0
- package/dist/core/show/package-resolver.js.map +1 -0
- package/dist/core/show/scope-discovery.js +165 -0
- package/dist/core/show/scope-discovery.js.map +1 -0
- package/dist/core/show/show-output.js +168 -0
- package/dist/core/show/show-output.js.map +1 -0
- package/dist/core/show/show-pipeline.js +113 -0
- package/dist/core/show/show-pipeline.js.map +1 -0
- package/dist/core/show/show-types.js +5 -0
- package/dist/core/show/show-types.js.map +1 -0
- package/dist/core/source-resolution/dependency-graph.js +104 -0
- package/dist/core/source-resolution/dependency-graph.js.map +1 -0
- package/dist/core/source-resolution/resolve-mutable-source.js +109 -0
- package/dist/core/source-resolution/resolve-mutable-source.js.map +1 -0
- package/dist/core/source-resolution/resolve-package-source.js +29 -0
- package/dist/core/source-resolution/resolve-package-source.js.map +1 -0
- package/dist/core/source-resolution/resolve-registry-version.js +35 -0
- package/dist/core/source-resolution/resolve-registry-version.js.map +1 -0
- package/dist/core/source-resolution/types.js.map +1 -0
- package/dist/core/status/status-file-discovery.js +23 -12
- package/dist/core/status/status-file-discovery.js.map +1 -1
- package/dist/core/status/status-pipeline.js +134 -0
- package/dist/core/status/status-pipeline.js.map +1 -0
- package/dist/core/sync/platform-sync-summary.js +27 -0
- package/dist/core/sync/platform-sync-summary.js.map +1 -0
- package/dist/core/uninstall/flow-aware-uninstaller.js +189 -0
- package/dist/core/uninstall/flow-aware-uninstaller.js.map +1 -0
- package/dist/core/uninstall/uninstall-file-discovery.js +11 -6
- package/dist/core/uninstall/uninstall-file-discovery.js.map +1 -1
- package/dist/core/uninstall/uninstall-pipeline.js +141 -0
- package/dist/core/uninstall/uninstall-pipeline.js.map +1 -0
- package/dist/core/universal-patterns.js +64 -0
- package/dist/core/universal-patterns.js.map +1 -0
- package/dist/index.js +99 -6
- package/dist/index.js.map +1 -1
- package/dist/types/flows.js +8 -0
- package/dist/types/flows.js.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/platform-flows.js +8 -0
- package/dist/types/platform-flows.js.map +1 -0
- package/dist/types/workspace-index.js +6 -0
- package/dist/types/workspace-index.js.map +1 -0
- package/dist/utils/custom-path-resolution.js +160 -0
- package/dist/utils/custom-path-resolution.js.map +1 -0
- package/dist/utils/dependency-coverage.js +1 -1
- package/dist/utils/dependency-coverage.js.map +1 -1
- package/dist/utils/file-processing.js +1 -1
- package/dist/utils/flow-index-installer.js +209 -0
- package/dist/utils/flow-index-installer.js.map +1 -0
- package/dist/utils/formatters.js +47 -1
- package/dist/utils/formatters.js.map +1 -1
- package/dist/utils/fs.js +17 -0
- package/dist/utils/fs.js.map +1 -1
- package/dist/utils/git-clone-registry.js +88 -0
- package/dist/utils/git-clone-registry.js.map +1 -0
- package/dist/utils/git-clone.js +69 -0
- package/dist/utils/git-clone.js.map +1 -0
- package/dist/utils/git-spec.js +96 -0
- package/dist/utils/git-spec.js.map +1 -0
- package/dist/utils/http-client.js +7 -0
- package/dist/utils/http-client.js.map +1 -1
- package/dist/utils/index-based-installer.js +356 -163
- package/dist/utils/index-based-installer.js.map +1 -1
- package/dist/utils/install-conflict-handler.js +2 -2
- package/dist/utils/install-conflict-handler.js.map +1 -1
- package/dist/utils/install-file-discovery.js +18 -13
- package/dist/utils/install-file-discovery.js.map +1 -1
- package/dist/utils/install-helpers.js +43 -20
- package/dist/utils/install-helpers.js.map +1 -1
- package/dist/utils/jsonc.js +23 -1
- package/dist/utils/jsonc.js.map +1 -1
- package/dist/utils/manifest-paths.js +1 -1
- package/dist/utils/manifest-paths.js.map +1 -1
- package/dist/utils/markdown-frontmatter.js +46 -0
- package/dist/utils/markdown-frontmatter.js.map +1 -1
- package/dist/utils/package-copy.js +5 -103
- package/dist/utils/package-copy.js.map +1 -1
- package/dist/utils/package-filters.js +9 -105
- package/dist/utils/package-filters.js.map +1 -1
- package/dist/utils/package-index-yml.js +27 -6
- package/dist/utils/package-index-yml.js.map +1 -1
- package/dist/utils/package-input.js +98 -0
- package/dist/utils/package-input.js.map +1 -0
- package/dist/utils/package-management.js +80 -28
- package/dist/utils/package-management.js.map +1 -1
- package/dist/utils/package-name-resolution.js +327 -0
- package/dist/utils/package-name-resolution.js.map +1 -0
- package/dist/utils/package-name.js +18 -16
- package/dist/utils/package-name.js.map +1 -1
- package/dist/utils/package-versioning.js +2 -33
- package/dist/utils/package-versioning.js.map +1 -1
- package/dist/utils/package-yml.js +19 -28
- package/dist/utils/package-yml.js.map +1 -1
- package/dist/utils/path-resolution.js +102 -0
- package/dist/utils/path-resolution.js.map +1 -0
- package/dist/utils/paths.js +6 -6
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/platform-file.js +36 -24
- package/dist/utils/platform-file.js.map +1 -1
- package/dist/utils/platform-mapper.js +222 -68
- package/dist/utils/platform-mapper.js.map +1 -1
- package/dist/utils/platform-root-files.js +44 -0
- package/dist/utils/platform-root-files.js.map +1 -0
- package/dist/utils/platform-utils.js +35 -54
- package/dist/utils/platform-utils.js.map +1 -1
- package/dist/utils/platform-yaml-merge.js +20 -140
- package/dist/utils/platform-yaml-merge.js.map +1 -1
- package/dist/utils/prompts.js +92 -7
- package/dist/utils/prompts.js.map +1 -1
- package/dist/utils/registry-entry-filter.js +50 -27
- package/dist/utils/registry-entry-filter.js.map +1 -1
- package/dist/utils/registry-paths.js +5 -4
- package/dist/utils/registry-paths.js.map +1 -1
- package/dist/utils/scope-resolution.js +156 -0
- package/dist/utils/scope-resolution.js.map +1 -0
- package/dist/utils/source-mutability.js +15 -0
- package/dist/utils/source-mutability.js.map +1 -0
- package/dist/utils/tarball.js +29 -4
- package/dist/utils/tarball.js.map +1 -1
- package/dist/utils/version-ranges.js +1 -32
- package/dist/utils/version-ranges.js.map +1 -1
- package/dist/utils/workspace-index-helpers.js +28 -0
- package/dist/utils/workspace-index-helpers.js.map +1 -0
- package/dist/utils/workspace-index-ownership.js +100 -0
- package/dist/utils/workspace-index-ownership.js.map +1 -0
- package/dist/utils/workspace-index-yml.js +173 -0
- package/dist/utils/workspace-index-yml.js.map +1 -0
- package/examples/custom-subdirs-platform.jsonc +157 -0
- package/package.json +7 -2
- package/platforms.jsonc +531 -84
- package/schemas/map-pipeline-v1.json +256 -0
- package/schemas/platforms-v1.json +400 -0
- package/specs/README.md +88 -0
- package/specs/add/README.md +166 -0
- package/specs/agents-claude.md +570 -0
- package/specs/agents-opencode.md +622 -0
- package/specs/apply/README.md +21 -0
- package/specs/apply/apply-behavior.md +58 -0
- package/specs/apply/apply-command.md +51 -0
- package/specs/apply/conflicts.md +41 -0
- package/specs/apply/index-effects.md +81 -0
- package/specs/architecture.md +107 -0
- package/specs/auth/README.md +17 -0
- package/specs/auth/auth-http-contract.md +25 -0
- package/specs/auth/cli/credentials.md +39 -0
- package/specs/auth/cli/login.md +32 -0
- package/specs/auth/cli/logout.md +16 -0
- package/specs/claude-mcp.md +1065 -0
- package/specs/claude-plugins-marketplace.md +363 -0
- package/specs/claude-plugins.md +413 -0
- package/specs/cli-options.md +52 -0
- package/specs/codex-mcp.md +114 -0
- package/specs/commands-overview.md +175 -0
- package/specs/directory-layout.md +95 -0
- package/specs/install/README.md +12 -4
- package/specs/install/git-sources.md +230 -0
- package/specs/install/install-behavior.md +483 -73
- package/specs/install/package-yml-canonical.md +67 -35
- package/specs/install/version-resolution.md +69 -115
- package/specs/new/README.md +769 -0
- package/specs/new/SUMMARY.md +310 -0
- package/specs/new/scope-behavior.md +793 -0
- package/specs/pack/README.md +77 -0
- package/specs/pack/package-name-resolution.md +330 -0
- package/specs/package/README.md +18 -17
- package/specs/package/nested-packages-and-parent-packages.md +32 -31
- package/specs/package/package-index-yml.md +95 -101
- package/specs/package/package-root-layout.md +64 -46
- package/specs/package/registry-payload-and-copy.md +50 -44
- package/specs/package/universal-content.md +33 -56
- package/specs/package-sources.md +248 -0
- package/specs/platforms/README.md +52 -0
- package/specs/platforms/configuration.md +571 -0
- package/specs/platforms/detection.md +552 -0
- package/specs/platforms/directory-layout.md +599 -0
- package/specs/platforms/examples.md +1146 -0
- package/specs/platforms/flow-reference.md +1240 -0
- package/specs/platforms/flows.md +1488 -0
- package/specs/platforms/map-pipeline.md +801 -0
- package/specs/platforms/overview.md +349 -0
- package/specs/platforms/specification.md +700 -0
- package/specs/platforms/troubleshooting.md +697 -0
- package/specs/platforms/universal-converter.md +520 -0
- package/specs/push/README.md +1 -0
- package/specs/push/push-behavior.md +11 -3
- package/specs/push/push-remote-upload.md +1 -1
- package/specs/push/push-scoping.md +1 -1
- package/specs/push/push-version-selection.md +1 -1
- package/specs/registry.md +111 -0
- package/specs/remove/README.md +257 -0
- package/specs/save/README.md +21 -17
- package/specs/save/save-conflict-resolution.md +205 -83
- package/specs/save/save-file-discovery.md +6 -4
- package/specs/save/save-frontmatter-overrides.md +11 -15
- package/specs/save/save-modes-inputs.md +9 -39
- package/specs/save/save-naming-scoping.md +4 -4
- package/specs/save/save-package-detection.md +13 -13
- package/specs/save/save-registry-sync.md +16 -106
- package/specs/save/save-versioning.md +80 -0
- package/specs/scope-management.md +92 -0
- package/specs/set/README.md +520 -0
- package/specs/set/set-behavior.md +563 -0
- package/specs/show/README.md +483 -0
- package/specs/show/show-remote.md +494 -0
- package/specs/status/README.md +38 -0
- package/specs/uninstall/README.md +231 -0
- package/dist/commands/duplicate.js +0 -69
- package/dist/commands/duplicate.js.map +0 -1
- package/dist/commands/init.js +0 -117
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/prune.js +0 -357
- package/dist/commands/prune.js.map +0 -1
- package/dist/commands/tui.js +0 -61
- package/dist/commands/tui.js.map +0 -1
- package/dist/core/install/index.js +0 -3
- package/dist/core/install/index.js.map +0 -1
- package/dist/core/push/push-single-file.js +0 -56
- package/dist/core/push/push-single-file.js.map +0 -1
- package/dist/core/save/package-detection.js +0 -147
- package/dist/core/save/package-detection.js.map +0 -1
- package/dist/core/save/save-single-file.js +0 -124
- package/dist/core/save/save-single-file.js.map +0 -1
- package/dist/core/token-store.js +0 -73
- package/dist/core/token-store.js.map +0 -1
- package/dist/tui/app.js +0 -95
- package/dist/tui/app.js.map +0 -1
- package/dist/tui/components/package-list.js +0 -73
- package/dist/tui/components/package-list.js.map +0 -1
- package/dist/tui/controller.js +0 -365
- package/dist/tui/controller.js.map +0 -1
- package/dist/tui/index.js +0 -12
- package/dist/tui/index.js.map +0 -1
- package/dist/tui/services/file-index.js +0 -64
- package/dist/tui/services/file-index.js.map +0 -1
- package/dist/tui/services/packages.js +0 -18
- package/dist/tui/services/packages.js.map +0 -1
- package/dist/tui/services/save.js +0 -21
- package/dist/tui/services/save.js.map +0 -1
- package/dist/tui/state/app-state.js +0 -15
- package/dist/tui/state/app-state.js.map +0 -1
- package/dist/tui/state.js +0 -17
- package/dist/tui/state.js.map +0 -1
- package/dist/tui/types.js.map +0 -1
- package/dist/tui/views/add-file-modal.js +0 -129
- package/dist/tui/views/add-file-modal.js.map +0 -1
- package/dist/tui/views/file-preview.js +0 -44
- package/dist/tui/views/file-preview.js.map +0 -1
- package/dist/tui/views/list-packages.js +0 -73
- package/dist/tui/views/list-packages.js.map +0 -1
- package/dist/tui/views/main-menu.js +0 -29
- package/dist/tui/views/main-menu.js.map +0 -1
- package/dist/tui/views/manage-view.js +0 -81
- package/dist/tui/views/manage-view.js.map +0 -1
- package/dist/tui/views/package-hub.js +0 -120
- package/dist/tui/views/package-hub.js.map +0 -1
- package/dist/tui/views/placeholder.js +0 -24
- package/dist/tui/views/placeholder.js.map +0 -1
- package/dist/utils/bun-bootstrap.js +0 -72
- package/dist/utils/bun-bootstrap.js.map +0 -1
- package/dist/utils/entity-id.js +0 -19
- package/dist/utils/entity-id.js.map +0 -1
- package/dist/utils/package-local-files.js +0 -5
- package/dist/utils/package-local-files.js.map +0 -1
- package/dist/utils/path-matching.js +0 -74
- package/dist/utils/path-matching.js.map +0 -1
- package/dist/utils/root-file-operations.js +0 -39
- package/dist/utils/root-file-operations.js.map +0 -1
- package/dist/utils/root-file-transformer.js +0 -27
- package/dist/utils/root-file-transformer.js.map +0 -1
- package/dist/utils/yaml-frontmatter.js +0 -25
- package/dist/utils/yaml-frontmatter.js.map +0 -1
- package/specs/auth/auth-device-flow.md +0 -70
- package/specs/login/login-device-flow.md +0 -70
- package/specs/platforms.md +0 -193
- package/specs/save-pack-versioning.md +0 -224
- package/specs/save-pack.md +0 -68
- /package/dist/{tui → core/source-resolution}/types.js +0 -0
|
@@ -0,0 +1,974 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow Executor
|
|
3
|
+
*
|
|
4
|
+
* Executes flows through a multi-stage pipeline:
|
|
5
|
+
* 1. Load source file and parse format
|
|
6
|
+
* 2. Extract JSONPath (if specified)
|
|
7
|
+
* 3. Pick/omit keys
|
|
8
|
+
* 4. Map keys (with transforms)
|
|
9
|
+
* 5. Apply pipe transforms
|
|
10
|
+
* 6. Embed in target structure
|
|
11
|
+
* 7. Merge with existing target (priority-based)
|
|
12
|
+
* 8. Write to target file
|
|
13
|
+
*/
|
|
14
|
+
import fsSync from 'fs';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import yaml from 'js-yaml';
|
|
17
|
+
import * as TOML from 'smol-toml';
|
|
18
|
+
import { parse as parseJsonc } from 'jsonc-parser';
|
|
19
|
+
import { JSONPath } from 'jsonpath-plus';
|
|
20
|
+
import * as fsUtils from '../../utils/fs.js';
|
|
21
|
+
import { mergePackageContentIntoRootFile } from '../../utils/root-file-merger.js';
|
|
22
|
+
import { logger } from '../../utils/logger.js';
|
|
23
|
+
import { defaultTransformRegistry, } from './flow-transforms.js';
|
|
24
|
+
import { mergeInlinePlatformOverride } from '../../utils/platform-yaml-merge.js';
|
|
25
|
+
import { getNestedValue, setNestedValue, deleteNestedValue } from './flow-key-mapper.js';
|
|
26
|
+
import { extractAllKeys } from './flow-key-extractor.js';
|
|
27
|
+
import { applyMapPipeline, createMapContext, validateMapPipeline } from './map-pipeline/index.js';
|
|
28
|
+
import { SourcePatternResolver } from './source-resolver.js';
|
|
29
|
+
/**
|
|
30
|
+
* Default flow executor implementation
|
|
31
|
+
*/
|
|
32
|
+
export class DefaultFlowExecutor {
|
|
33
|
+
constructor(transformRegistry) {
|
|
34
|
+
this.transformRegistry = transformRegistry || defaultTransformRegistry;
|
|
35
|
+
this.sourceResolver = new SourcePatternResolver();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Execute a single flow (now supports glob patterns)
|
|
39
|
+
*/
|
|
40
|
+
async executeFlow(flow, context) {
|
|
41
|
+
const startTime = Date.now();
|
|
42
|
+
try {
|
|
43
|
+
// Check if this is a multi-target flow
|
|
44
|
+
if (typeof flow.to !== 'string') {
|
|
45
|
+
const results = await this.executeMultiTarget(flow, context);
|
|
46
|
+
// Aggregate results
|
|
47
|
+
return this.aggregateResults(results, startTime);
|
|
48
|
+
}
|
|
49
|
+
// Validate flow
|
|
50
|
+
const validation = this.validateFlow(flow);
|
|
51
|
+
if (!validation.valid) {
|
|
52
|
+
return {
|
|
53
|
+
source: this.normalizeFromPattern(flow.from),
|
|
54
|
+
target: flow.to,
|
|
55
|
+
success: false,
|
|
56
|
+
transformed: false,
|
|
57
|
+
error: new Error(`Invalid flow: ${validation.errors.map(e => e.message).join(', ')}`),
|
|
58
|
+
executionTime: Date.now() - startTime,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Evaluate conditions
|
|
62
|
+
if (flow.when && !this.evaluateCondition(flow.when, context)) {
|
|
63
|
+
const normalized = this.normalizeFromPattern(flow.from);
|
|
64
|
+
logger.debug(`Flow skipped due to condition: ${normalized} -> ${flow.to}`);
|
|
65
|
+
return {
|
|
66
|
+
source: normalized,
|
|
67
|
+
target: flow.to,
|
|
68
|
+
success: true,
|
|
69
|
+
transformed: false,
|
|
70
|
+
warnings: ['Flow skipped due to condition'],
|
|
71
|
+
executionTime: Date.now() - startTime,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Resolve source paths (may return multiple files for glob patterns)
|
|
75
|
+
const resolution = await this.resolveSourcePattern(flow.from, context);
|
|
76
|
+
const sourcePaths = resolution.paths;
|
|
77
|
+
const resolutionWarnings = resolution.warnings;
|
|
78
|
+
// If no files matched, return success with no files processed
|
|
79
|
+
if (sourcePaths.length === 0) {
|
|
80
|
+
return {
|
|
81
|
+
source: this.normalizeFromPattern(flow.from),
|
|
82
|
+
target: flow.to,
|
|
83
|
+
success: true,
|
|
84
|
+
transformed: false,
|
|
85
|
+
warnings: resolutionWarnings.length > 0 ? resolutionWarnings : ['No files matched pattern'],
|
|
86
|
+
executionTime: Date.now() - startTime,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// Execute pipeline for each matched file
|
|
90
|
+
const results = [];
|
|
91
|
+
const firstFromPattern = this.getFirstPattern(flow.from);
|
|
92
|
+
for (const sourcePath of sourcePaths) {
|
|
93
|
+
const targetPath = this.resolveTargetFromGlob(sourcePath, firstFromPattern, flow.to, context);
|
|
94
|
+
const result = await this.executePipeline(flow, sourcePath, targetPath, context);
|
|
95
|
+
results.push({
|
|
96
|
+
...result,
|
|
97
|
+
executionTime: Date.now() - startTime,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// Add resolution warnings to first result if any
|
|
101
|
+
if (resolutionWarnings.length > 0 && results.length > 0) {
|
|
102
|
+
results[0].warnings = [
|
|
103
|
+
...(results[0].warnings || []),
|
|
104
|
+
...resolutionWarnings,
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
// If single file, return single result
|
|
108
|
+
if (results.length === 1) {
|
|
109
|
+
return results[0];
|
|
110
|
+
}
|
|
111
|
+
// Aggregate multiple results
|
|
112
|
+
return this.aggregateResults(results, startTime);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return {
|
|
116
|
+
source: this.normalizeFromPattern(flow.from),
|
|
117
|
+
target: typeof flow.to === 'string' ? flow.to : Object.keys(flow.to).join(', '),
|
|
118
|
+
success: false,
|
|
119
|
+
transformed: false,
|
|
120
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
121
|
+
executionTime: Date.now() - startTime,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Execute multiple flows
|
|
127
|
+
*/
|
|
128
|
+
async executeFlows(flows, context) {
|
|
129
|
+
const results = [];
|
|
130
|
+
for (const flow of flows) {
|
|
131
|
+
const result = await this.executeFlow(flow, context);
|
|
132
|
+
results.push(result);
|
|
133
|
+
}
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Execute a multi-target flow
|
|
138
|
+
*/
|
|
139
|
+
async executeMultiTarget(flow, context) {
|
|
140
|
+
if (typeof flow.to === 'string') {
|
|
141
|
+
throw new Error('Flow is not a multi-target flow');
|
|
142
|
+
}
|
|
143
|
+
const multiTarget = flow.to;
|
|
144
|
+
const normalizedFrom = this.normalizeFromPattern(flow.from);
|
|
145
|
+
// Resolve source paths (may be multiple files with glob)
|
|
146
|
+
const resolution = await this.resolveSourcePattern(flow.from, context);
|
|
147
|
+
const sourcePaths = resolution.paths;
|
|
148
|
+
// If no files matched
|
|
149
|
+
if (sourcePaths.length === 0) {
|
|
150
|
+
return Object.keys(multiTarget).map(target => ({
|
|
151
|
+
source: normalizedFrom,
|
|
152
|
+
target,
|
|
153
|
+
success: true,
|
|
154
|
+
transformed: false,
|
|
155
|
+
warnings: resolution.warnings.length > 0 ? resolution.warnings : ['No files matched pattern'],
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
// Execute each source file
|
|
159
|
+
const allResults = [];
|
|
160
|
+
const firstFromPattern = this.getFirstPattern(flow.from);
|
|
161
|
+
for (const sourcePath of sourcePaths) {
|
|
162
|
+
// Load and parse source once
|
|
163
|
+
const sourceContent = await this.loadSourceFile(sourcePath, context);
|
|
164
|
+
// Execute each target
|
|
165
|
+
for (const [targetPath, targetFlow] of Object.entries(multiTarget)) {
|
|
166
|
+
const startTime = Date.now();
|
|
167
|
+
try {
|
|
168
|
+
// Merge target flow with base flow
|
|
169
|
+
const mergedFlow = {
|
|
170
|
+
...flow,
|
|
171
|
+
...targetFlow,
|
|
172
|
+
from: flow.from,
|
|
173
|
+
to: targetPath,
|
|
174
|
+
};
|
|
175
|
+
// Evaluate conditions
|
|
176
|
+
if (mergedFlow.when && !this.evaluateCondition(mergedFlow.when, context)) {
|
|
177
|
+
logger.debug(`Multi-target flow skipped due to condition: ${normalizedFrom} -> ${targetPath}`);
|
|
178
|
+
allResults.push({
|
|
179
|
+
source: normalizedFrom,
|
|
180
|
+
target: targetPath,
|
|
181
|
+
success: true,
|
|
182
|
+
transformed: false,
|
|
183
|
+
warnings: ['Flow skipped due to condition'],
|
|
184
|
+
executionTime: Date.now() - startTime,
|
|
185
|
+
});
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const resolvedTargetPath = this.resolveTargetFromGlob(sourcePath, firstFromPattern, targetPath, context);
|
|
189
|
+
// Execute pipeline with pre-loaded content
|
|
190
|
+
const result = await this.executePipelineWithContent(mergedFlow, sourceContent, sourcePath, resolvedTargetPath, context);
|
|
191
|
+
allResults.push({
|
|
192
|
+
...result,
|
|
193
|
+
executionTime: Date.now() - startTime,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
allResults.push({
|
|
198
|
+
source: normalizedFrom,
|
|
199
|
+
target: targetPath,
|
|
200
|
+
success: false,
|
|
201
|
+
transformed: false,
|
|
202
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
203
|
+
executionTime: Date.now() - startTime,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return allResults;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Validate a flow configuration
|
|
212
|
+
*/
|
|
213
|
+
validateFlow(flow) {
|
|
214
|
+
const errors = [];
|
|
215
|
+
// Check required fields
|
|
216
|
+
if (!flow.from) {
|
|
217
|
+
errors.push({ message: 'Flow missing required field "from"', code: 'MISSING_FROM' });
|
|
218
|
+
}
|
|
219
|
+
if (!flow.to) {
|
|
220
|
+
errors.push({ message: 'Flow missing required field "to"', code: 'MISSING_TO' });
|
|
221
|
+
}
|
|
222
|
+
// Validate pick/omit
|
|
223
|
+
if (flow.pick && flow.omit) {
|
|
224
|
+
errors.push({ message: 'Flow cannot have both "pick" and "omit"', code: 'CONFLICTING_FILTERS' });
|
|
225
|
+
}
|
|
226
|
+
// Validate merge strategy
|
|
227
|
+
if (flow.merge && !['deep', 'shallow', 'replace', 'composite'].includes(flow.merge)) {
|
|
228
|
+
errors.push({
|
|
229
|
+
message: `Invalid merge strategy: ${flow.merge}. Must be one of: deep, shallow, replace, composite`,
|
|
230
|
+
code: 'INVALID_MERGE',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// Validate JSONPath expression
|
|
234
|
+
if (flow.path) {
|
|
235
|
+
try {
|
|
236
|
+
// Try to validate JSONPath syntax
|
|
237
|
+
JSONPath({ path: flow.path, json: {} });
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
errors.push({
|
|
241
|
+
message: `Invalid JSONPath expression: ${flow.path}`,
|
|
242
|
+
code: 'INVALID_JSONPATH',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Validate map pipeline
|
|
247
|
+
if (flow.map) {
|
|
248
|
+
const mapPipelineValidation = validateMapPipeline(flow.map);
|
|
249
|
+
if (!mapPipelineValidation.valid) {
|
|
250
|
+
for (const error of mapPipelineValidation.errors) {
|
|
251
|
+
errors.push({
|
|
252
|
+
message: error,
|
|
253
|
+
code: 'INVALID_MAP_PIPELINE',
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
valid: errors.length === 0,
|
|
260
|
+
errors,
|
|
261
|
+
warnings: [],
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Execute the transformation pipeline
|
|
266
|
+
*/
|
|
267
|
+
async executePipeline(flow, sourcePath, targetPath, context) {
|
|
268
|
+
// Step 1: Load source file
|
|
269
|
+
const sourceContent = await this.loadSourceFile(sourcePath, context);
|
|
270
|
+
return this.executePipelineWithContent(flow, sourceContent, sourcePath, targetPath, context);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Execute pipeline with pre-loaded content (for multi-target flows)
|
|
274
|
+
*/
|
|
275
|
+
async executePipelineWithContent(flow, sourceContent, sourcePath, targetPath, context) {
|
|
276
|
+
const warnings = [];
|
|
277
|
+
const conflicts = [];
|
|
278
|
+
let data = sourceContent.data;
|
|
279
|
+
let transformed = false;
|
|
280
|
+
try {
|
|
281
|
+
// Step 2: Extract JSONPath (if specified)
|
|
282
|
+
if (flow.path) {
|
|
283
|
+
data = this.extractJSONPath(data, flow.path);
|
|
284
|
+
transformed = true;
|
|
285
|
+
}
|
|
286
|
+
// Step 3: Pick/omit keys
|
|
287
|
+
if (flow.pick) {
|
|
288
|
+
data = this.pickKeys(data, flow.pick);
|
|
289
|
+
transformed = true;
|
|
290
|
+
}
|
|
291
|
+
else if (flow.omit) {
|
|
292
|
+
data = this.omitKeys(data, flow.omit);
|
|
293
|
+
transformed = true;
|
|
294
|
+
}
|
|
295
|
+
// Step 4: Apply map pipeline
|
|
296
|
+
// Split into schema operations and pipe operations
|
|
297
|
+
// Schema ops are applied BEFORE merge, pipe ops are applied AFTER merge
|
|
298
|
+
let contributedKeys;
|
|
299
|
+
let schemaOps = [];
|
|
300
|
+
let pipeOps = [];
|
|
301
|
+
if (flow.map) {
|
|
302
|
+
// Separate schema operations from pipe operations
|
|
303
|
+
for (const op of flow.map) {
|
|
304
|
+
if ('$pipe' in op) {
|
|
305
|
+
pipeOps.push(op);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
schemaOps.push(op);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Apply schema operations first (before merge)
|
|
312
|
+
if (schemaOps.length > 0) {
|
|
313
|
+
const mapContext = createMapContext({
|
|
314
|
+
filename: path.basename(sourcePath, path.extname(sourcePath)),
|
|
315
|
+
dirname: path.basename(path.dirname(sourcePath)),
|
|
316
|
+
path: path.relative(context.packageRoot, sourcePath),
|
|
317
|
+
ext: path.extname(sourcePath),
|
|
318
|
+
});
|
|
319
|
+
// For markdown files, apply to frontmatter
|
|
320
|
+
if (data && typeof data === 'object' && 'frontmatter' in data) {
|
|
321
|
+
data.frontmatter = applyMapPipeline(data.frontmatter, schemaOps, mapContext, this.transformRegistry);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
// Apply to entire document
|
|
325
|
+
data = applyMapPipeline(data, schemaOps, mapContext, this.transformRegistry);
|
|
326
|
+
}
|
|
327
|
+
transformed = true;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Track keys AFTER schema transforms but BEFORE merge and pipe transforms
|
|
331
|
+
// This represents the structured data this package contributes
|
|
332
|
+
const shouldTrackKeys = Boolean(flow.merge) &&
|
|
333
|
+
flow.merge !== 'replace' &&
|
|
334
|
+
flow.merge !== 'composite';
|
|
335
|
+
if (shouldTrackKeys && typeof data === 'object' && data !== null) {
|
|
336
|
+
// Extract from frontmatter if it's a markdown file
|
|
337
|
+
const dataToExtract = (data && 'frontmatter' in data) ? data.frontmatter : data;
|
|
338
|
+
if (typeof dataToExtract === 'object' && dataToExtract !== null) {
|
|
339
|
+
contributedKeys = extractAllKeys(dataToExtract);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const targetExists = await fsUtils.exists(targetPath);
|
|
343
|
+
// Step 6: Embed in target structure
|
|
344
|
+
if (flow.embed) {
|
|
345
|
+
data = this.embedContent(data, flow.embed);
|
|
346
|
+
transformed = true;
|
|
347
|
+
}
|
|
348
|
+
// Step 7: Merge with existing target (if needed)
|
|
349
|
+
if (targetExists) {
|
|
350
|
+
const targetContent = await this.loadSourceFile(targetPath, context);
|
|
351
|
+
// Special handling for composite merge - works with raw text
|
|
352
|
+
if (flow.merge === 'composite') {
|
|
353
|
+
// Use raw content for composite merge
|
|
354
|
+
const sourceRaw = sourceContent.raw;
|
|
355
|
+
const targetRaw = targetContent.raw;
|
|
356
|
+
data = mergePackageContentIntoRootFile(targetRaw, context.packageName, sourceRaw);
|
|
357
|
+
transformed = true;
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
// Normal merge for other strategies
|
|
361
|
+
const mergeResult = this.mergeContent(data, targetContent.data, flow.merge || 'replace', context);
|
|
362
|
+
data = mergeResult.data;
|
|
363
|
+
conflicts.push(...mergeResult.conflicts);
|
|
364
|
+
if (mergeResult.conflicts.length > 0) {
|
|
365
|
+
transformed = true;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// Step 7.5: Apply pipe operations AFTER merge (format conversions)
|
|
370
|
+
// These operations may convert the data to a string format (e.g., json-to-toml)
|
|
371
|
+
if (pipeOps.length > 0) {
|
|
372
|
+
const mapContext = createMapContext({
|
|
373
|
+
filename: path.basename(sourcePath, path.extname(sourcePath)),
|
|
374
|
+
dirname: path.basename(path.dirname(sourcePath)),
|
|
375
|
+
path: path.relative(context.packageRoot, sourcePath),
|
|
376
|
+
ext: path.extname(sourcePath),
|
|
377
|
+
});
|
|
378
|
+
// For markdown files, apply to frontmatter
|
|
379
|
+
if (data && typeof data === 'object' && 'frontmatter' in data) {
|
|
380
|
+
data.frontmatter = applyMapPipeline(data.frontmatter, pipeOps, mapContext, this.transformRegistry);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
// Apply to entire document
|
|
384
|
+
data = applyMapPipeline(data, pipeOps, mapContext, this.transformRegistry);
|
|
385
|
+
}
|
|
386
|
+
transformed = true;
|
|
387
|
+
}
|
|
388
|
+
// Step 8: Write to target file
|
|
389
|
+
if (!context.dryRun) {
|
|
390
|
+
await this.writeTargetFile(targetPath, data, sourceContent.format);
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
source: sourcePath,
|
|
394
|
+
target: targetPath,
|
|
395
|
+
success: true,
|
|
396
|
+
transformed,
|
|
397
|
+
keys: contributedKeys,
|
|
398
|
+
merge: flow.merge,
|
|
399
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
400
|
+
conflicts: conflicts.length > 0 ? conflicts : undefined,
|
|
401
|
+
pipeline: this.getPipeline(flow),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
return {
|
|
406
|
+
source: sourcePath,
|
|
407
|
+
target: targetPath,
|
|
408
|
+
success: false,
|
|
409
|
+
transformed,
|
|
410
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
411
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Load and parse source file
|
|
417
|
+
*/
|
|
418
|
+
async loadSourceFile(filePath, context) {
|
|
419
|
+
let raw = await fsUtils.readTextFile(filePath);
|
|
420
|
+
const format = this.detectFormat(filePath, raw);
|
|
421
|
+
// Apply platform-specific frontmatter overrides for markdown files during install
|
|
422
|
+
if ((format === 'markdown' || format === 'md') && context?.platform && context?.direction === 'install') {
|
|
423
|
+
raw = mergeInlinePlatformOverride(raw, context.platform, context.workspaceRoot);
|
|
424
|
+
}
|
|
425
|
+
const data = this.parseSourceContent(raw, format);
|
|
426
|
+
return { data, format, raw };
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Write transformed content to target file
|
|
430
|
+
*/
|
|
431
|
+
async writeTargetFile(filePath, content, sourceFormat) {
|
|
432
|
+
await fsUtils.ensureDir(path.dirname(filePath));
|
|
433
|
+
// Detect target format from file extension
|
|
434
|
+
const targetFormat = this.detectFormat(filePath, '');
|
|
435
|
+
const serialized = this.serializeTargetContent(content, targetFormat);
|
|
436
|
+
await fsUtils.writeTextFile(filePath, serialized);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Parse source content based on format
|
|
440
|
+
*/
|
|
441
|
+
parseSourceContent(content, format) {
|
|
442
|
+
try {
|
|
443
|
+
switch (format) {
|
|
444
|
+
case 'json':
|
|
445
|
+
case 'jsonc':
|
|
446
|
+
// Remove comments for JSONC
|
|
447
|
+
const cleaned = content;
|
|
448
|
+
if (format === 'jsonc') {
|
|
449
|
+
const parsed = parseJsonc(cleaned);
|
|
450
|
+
if (parsed === undefined) {
|
|
451
|
+
throw new Error('jsonc-parser returned undefined');
|
|
452
|
+
}
|
|
453
|
+
return parsed;
|
|
454
|
+
}
|
|
455
|
+
return JSON.parse(cleaned);
|
|
456
|
+
case 'yaml':
|
|
457
|
+
case 'yml':
|
|
458
|
+
return yaml.load(content);
|
|
459
|
+
case 'toml':
|
|
460
|
+
try {
|
|
461
|
+
return TOML.parse(content);
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
throw new Error(`TOML parse error: ${error instanceof Error ? error.message : String(error)}`);
|
|
465
|
+
}
|
|
466
|
+
case 'markdown':
|
|
467
|
+
case 'md':
|
|
468
|
+
return this.parseMarkdown(content);
|
|
469
|
+
case 'text':
|
|
470
|
+
case 'txt':
|
|
471
|
+
default:
|
|
472
|
+
return content;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
catch (error) {
|
|
476
|
+
throw new Error(`Failed to parse ${format} content: ${error instanceof Error ? error.message : String(error)}`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Serialize content to target format
|
|
481
|
+
*/
|
|
482
|
+
serializeTargetContent(content, format) {
|
|
483
|
+
try {
|
|
484
|
+
switch (format) {
|
|
485
|
+
case 'json':
|
|
486
|
+
case 'jsonc':
|
|
487
|
+
return JSON.stringify(content, null, 2);
|
|
488
|
+
case 'yaml':
|
|
489
|
+
case 'yml':
|
|
490
|
+
return yaml.dump(content, { indent: 2, lineWidth: -1 });
|
|
491
|
+
case 'toml':
|
|
492
|
+
// If a pipeline already produced TOML text (e.g. via domain transforms),
|
|
493
|
+
// don't stringify again.
|
|
494
|
+
if (typeof content === 'string') {
|
|
495
|
+
return content;
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
// Serialize to TOML
|
|
499
|
+
let toml = TOML.stringify(content);
|
|
500
|
+
// Apply inline table formatting for Codex MCP configs
|
|
501
|
+
if (content && typeof content === 'object' && content.mcp_servers) {
|
|
502
|
+
toml = this.applyCodexTomlFormatting(toml);
|
|
503
|
+
}
|
|
504
|
+
return toml;
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
throw new Error(`TOML stringify error: ${error instanceof Error ? error.message : String(error)}`);
|
|
508
|
+
}
|
|
509
|
+
case 'markdown':
|
|
510
|
+
case 'md':
|
|
511
|
+
return this.serializeMarkdown(content);
|
|
512
|
+
case 'text':
|
|
513
|
+
case 'txt':
|
|
514
|
+
default:
|
|
515
|
+
return typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
throw new Error(`Failed to serialize ${format} content: ${error instanceof Error ? error.message : String(error)}`);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Detect format from file extension or content
|
|
524
|
+
*/
|
|
525
|
+
detectFormat(filePath, content) {
|
|
526
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
527
|
+
const extMap = {
|
|
528
|
+
'.json': 'json',
|
|
529
|
+
'.jsonc': 'jsonc',
|
|
530
|
+
'.yaml': 'yaml',
|
|
531
|
+
'.yml': 'yml',
|
|
532
|
+
'.toml': 'toml',
|
|
533
|
+
'.md': 'markdown',
|
|
534
|
+
'.markdown': 'markdown',
|
|
535
|
+
'.txt': 'text',
|
|
536
|
+
};
|
|
537
|
+
if (extMap[ext]) {
|
|
538
|
+
return extMap[ext];
|
|
539
|
+
}
|
|
540
|
+
// Try to detect from content
|
|
541
|
+
if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
|
|
542
|
+
return content.includes('//') || content.includes('/*') ? 'jsonc' : 'json';
|
|
543
|
+
}
|
|
544
|
+
if (content.includes('---\n') || content.includes('\n---\n')) {
|
|
545
|
+
return 'markdown';
|
|
546
|
+
}
|
|
547
|
+
return 'text';
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Extract data using JSONPath
|
|
551
|
+
*/
|
|
552
|
+
extractJSONPath(data, jsonPath) {
|
|
553
|
+
try {
|
|
554
|
+
const result = JSONPath({ path: jsonPath, json: data });
|
|
555
|
+
return result.length === 1 ? result[0] : result;
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
throw new Error(`JSONPath extraction failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Pick specified keys
|
|
563
|
+
*/
|
|
564
|
+
pickKeys(data, keys) {
|
|
565
|
+
if (typeof data !== 'object' || data === null) {
|
|
566
|
+
return data;
|
|
567
|
+
}
|
|
568
|
+
const result = Array.isArray(data) ? [] : {};
|
|
569
|
+
for (const key of keys) {
|
|
570
|
+
if (key.includes('.')) {
|
|
571
|
+
// Handle nested keys
|
|
572
|
+
this.setNestedValue(result, key, this.getNestedValue(data, key));
|
|
573
|
+
}
|
|
574
|
+
else if (key in data) {
|
|
575
|
+
result[key] = data[key];
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return result;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Omit specified keys
|
|
582
|
+
*/
|
|
583
|
+
omitKeys(data, keys) {
|
|
584
|
+
if (typeof data !== 'object' || data === null) {
|
|
585
|
+
return data;
|
|
586
|
+
}
|
|
587
|
+
const result = Array.isArray(data) ? [...data] : { ...data };
|
|
588
|
+
for (const key of keys) {
|
|
589
|
+
if (key.includes('.')) {
|
|
590
|
+
// Handle nested keys
|
|
591
|
+
this.deleteNestedValue(result, key);
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
delete result[key];
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return result;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Apply pipe transforms
|
|
601
|
+
*/
|
|
602
|
+
async applyPipeTransforms(data, transforms, context) {
|
|
603
|
+
let result = data;
|
|
604
|
+
for (const transformSpec of transforms) {
|
|
605
|
+
try {
|
|
606
|
+
// Parse transform specification
|
|
607
|
+
// Format: "transform-name" or "transform-name(option1=value1,option2=value2)"
|
|
608
|
+
const { name, options } = this.parseTransformSpec(transformSpec);
|
|
609
|
+
logger.debug(`Applying transform: ${name}`, options);
|
|
610
|
+
// Execute transform
|
|
611
|
+
result = this.transformRegistry.execute(name, result, options);
|
|
612
|
+
}
|
|
613
|
+
catch (error) {
|
|
614
|
+
throw new Error(`Transform '${transformSpec}' failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return result;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Parse transform specification
|
|
621
|
+
* Examples: "trim", "number", "pick-keys(keys=[a,b,c])"
|
|
622
|
+
*/
|
|
623
|
+
parseTransformSpec(spec) {
|
|
624
|
+
const match = spec.match(/^([a-z-]+)(?:\((.+)\))?$/);
|
|
625
|
+
if (!match) {
|
|
626
|
+
throw new Error(`Invalid transform specification: ${spec}`);
|
|
627
|
+
}
|
|
628
|
+
const [, name, optionsStr] = match;
|
|
629
|
+
if (!optionsStr) {
|
|
630
|
+
return { name };
|
|
631
|
+
}
|
|
632
|
+
// Parse options (simple key=value format)
|
|
633
|
+
const options = {};
|
|
634
|
+
const pairs = optionsStr.split(',').map(s => s.trim());
|
|
635
|
+
for (const pair of pairs) {
|
|
636
|
+
const [key, value] = pair.split('=').map(s => s.trim());
|
|
637
|
+
// Parse value type
|
|
638
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
639
|
+
// Array
|
|
640
|
+
options[key] = value.slice(1, -1).split(',').map(s => s.trim());
|
|
641
|
+
}
|
|
642
|
+
else if (value === 'true' || value === 'false') {
|
|
643
|
+
// Boolean
|
|
644
|
+
options[key] = value === 'true';
|
|
645
|
+
}
|
|
646
|
+
else if (!isNaN(Number(value))) {
|
|
647
|
+
// Number
|
|
648
|
+
options[key] = Number(value);
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
// String
|
|
652
|
+
options[key] = value;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return { name, options };
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Embed content under a key
|
|
659
|
+
*/
|
|
660
|
+
embedContent(data, key) {
|
|
661
|
+
return { [key]: data };
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Merge content with priority-based conflict resolution
|
|
665
|
+
*/
|
|
666
|
+
mergeContent(source, target, strategy, context) {
|
|
667
|
+
const conflicts = [];
|
|
668
|
+
let merged;
|
|
669
|
+
switch (strategy) {
|
|
670
|
+
case 'replace':
|
|
671
|
+
merged = source;
|
|
672
|
+
break;
|
|
673
|
+
case 'shallow':
|
|
674
|
+
merged = { ...target, ...source };
|
|
675
|
+
break;
|
|
676
|
+
case 'deep':
|
|
677
|
+
merged = this.deepMerge(target, source, conflicts, context);
|
|
678
|
+
break;
|
|
679
|
+
case 'composite':
|
|
680
|
+
// Composite merge is handled earlier in the pipeline (Step 7)
|
|
681
|
+
// This case should not be reached
|
|
682
|
+
merged = source;
|
|
683
|
+
break;
|
|
684
|
+
default:
|
|
685
|
+
merged = source;
|
|
686
|
+
}
|
|
687
|
+
return { data: merged, conflicts };
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Deep merge two objects
|
|
691
|
+
*/
|
|
692
|
+
deepMerge(target, source, conflicts, context, keyPath = '') {
|
|
693
|
+
if (typeof source !== 'object' || source === null) {
|
|
694
|
+
return source;
|
|
695
|
+
}
|
|
696
|
+
if (typeof target !== 'object' || target === null) {
|
|
697
|
+
return source;
|
|
698
|
+
}
|
|
699
|
+
if (Array.isArray(source) && Array.isArray(target)) {
|
|
700
|
+
// Merge arrays
|
|
701
|
+
return [...target, ...source];
|
|
702
|
+
}
|
|
703
|
+
const result = { ...target };
|
|
704
|
+
for (const key of Object.keys(source)) {
|
|
705
|
+
const currentPath = keyPath ? `${keyPath}.${key}` : key;
|
|
706
|
+
if (!(key in target)) {
|
|
707
|
+
result[key] = source[key];
|
|
708
|
+
}
|
|
709
|
+
else if (typeof source[key] === 'object' && typeof target[key] === 'object') {
|
|
710
|
+
result[key] = this.deepMerge(target[key], source[key], conflicts, context, currentPath);
|
|
711
|
+
}
|
|
712
|
+
else if (source[key] !== target[key]) {
|
|
713
|
+
// Conflict detected
|
|
714
|
+
conflicts.push({
|
|
715
|
+
path: currentPath,
|
|
716
|
+
winner: context.packageName,
|
|
717
|
+
losers: ['existing'],
|
|
718
|
+
type: 'value',
|
|
719
|
+
resolution: 'last-writer-wins',
|
|
720
|
+
});
|
|
721
|
+
result[key] = source[key];
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return result;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Evaluate condition
|
|
728
|
+
*/
|
|
729
|
+
/**
|
|
730
|
+
* Composite merge using comment delimiters to preserve multiple package contributions
|
|
731
|
+
* Each package's content is wrapped in HTML comment markers with package name
|
|
732
|
+
*/
|
|
733
|
+
evaluateCondition(condition, context) {
|
|
734
|
+
if (condition.and) {
|
|
735
|
+
return condition.and.every((c) => this.evaluateCondition(c, context));
|
|
736
|
+
}
|
|
737
|
+
if (condition.or) {
|
|
738
|
+
return condition.or.some((c) => this.evaluateCondition(c, context));
|
|
739
|
+
}
|
|
740
|
+
if (condition.not) {
|
|
741
|
+
return !this.evaluateCondition(condition.not, context);
|
|
742
|
+
}
|
|
743
|
+
if (condition.exists) {
|
|
744
|
+
const testPath = path.join(context.workspaceRoot, condition.exists);
|
|
745
|
+
// Use existsSync for synchronous condition evaluation
|
|
746
|
+
return fsSync.existsSync(testPath);
|
|
747
|
+
}
|
|
748
|
+
if (condition.platform) {
|
|
749
|
+
return context.platform === condition.platform;
|
|
750
|
+
}
|
|
751
|
+
return true;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Resolve pattern with glob support and priority-based array matching
|
|
755
|
+
* Returns resolved file paths (glob patterns return multiple files)
|
|
756
|
+
*/
|
|
757
|
+
async resolveSourcePattern(pattern, context) {
|
|
758
|
+
const result = await this.sourceResolver.resolve(pattern, {
|
|
759
|
+
baseDir: context.packageRoot,
|
|
760
|
+
logWarnings: true,
|
|
761
|
+
});
|
|
762
|
+
return {
|
|
763
|
+
paths: result.paths,
|
|
764
|
+
warnings: result.warnings,
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Resolve target path from source path and patterns
|
|
769
|
+
* Handles single-level (*) and recursive (**) globs
|
|
770
|
+
*/
|
|
771
|
+
resolveTargetFromGlob(sourcePath, fromPattern, toPattern, context) {
|
|
772
|
+
// Get relative path from package root
|
|
773
|
+
const relativePath = path.relative(context.packageRoot, sourcePath);
|
|
774
|
+
// If 'to' pattern has glob, map the structure
|
|
775
|
+
if (toPattern.includes('*')) {
|
|
776
|
+
// Handle ** recursive patterns
|
|
777
|
+
if (fromPattern.includes('**') && toPattern.includes('**')) {
|
|
778
|
+
// Extract the base directories before **
|
|
779
|
+
const fromParts = fromPattern.split('**');
|
|
780
|
+
const toParts = toPattern.split('**');
|
|
781
|
+
const fromBase = fromParts[0].replace(/\/$/, '');
|
|
782
|
+
const toBase = toParts[0].replace(/\/$/, '');
|
|
783
|
+
// Get the file pattern after **
|
|
784
|
+
const fromSuffix = fromParts[1] || '';
|
|
785
|
+
const toSuffix = toParts[1] || '';
|
|
786
|
+
// Extract the relative path after the base directory
|
|
787
|
+
let relativeSubpath = relativePath;
|
|
788
|
+
if (fromBase) {
|
|
789
|
+
relativeSubpath = relativePath.startsWith(fromBase + '/')
|
|
790
|
+
? relativePath.slice(fromBase.length + 1)
|
|
791
|
+
: relativePath;
|
|
792
|
+
}
|
|
793
|
+
// Handle extension mapping if suffixes specify extensions
|
|
794
|
+
// e.g., /**/*.md -> /**/*.mdc
|
|
795
|
+
if (fromSuffix && toSuffix) {
|
|
796
|
+
const fromExt = fromSuffix.replace(/^\/?\*+/, '');
|
|
797
|
+
const toExt = toSuffix.replace(/^\/?\*+/, '');
|
|
798
|
+
if (fromExt && toExt && fromExt !== toExt) {
|
|
799
|
+
relativeSubpath = relativeSubpath.replace(new RegExp(fromExt.replace('.', '\\.') + '$'), toExt);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
// Build target path
|
|
803
|
+
const targetPath = toBase ? path.join(toBase, relativeSubpath) : relativeSubpath;
|
|
804
|
+
return path.join(context.workspaceRoot, targetPath);
|
|
805
|
+
}
|
|
806
|
+
// Handle single-level * patterns
|
|
807
|
+
const sourceFileName = path.basename(sourcePath);
|
|
808
|
+
const sourceExt = path.extname(sourcePath);
|
|
809
|
+
const sourceBase = path.basename(sourcePath, sourceExt);
|
|
810
|
+
const toParts = toPattern.split('*');
|
|
811
|
+
const toPrefix = toParts[0];
|
|
812
|
+
const toSuffix = toParts[1] || '';
|
|
813
|
+
const targetExt = toSuffix.startsWith('.') ? toSuffix : (sourceExt + toSuffix);
|
|
814
|
+
const targetFileName = sourceBase + targetExt;
|
|
815
|
+
const resolvedTo = toPrefix + targetFileName;
|
|
816
|
+
return path.join(context.workspaceRoot, resolvedTo);
|
|
817
|
+
}
|
|
818
|
+
// No glob in target - use as-is
|
|
819
|
+
return path.join(context.workspaceRoot, toPattern);
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Get pipeline description
|
|
823
|
+
*/
|
|
824
|
+
getPipeline(flow) {
|
|
825
|
+
const pipeline = ['load'];
|
|
826
|
+
if (flow.path)
|
|
827
|
+
pipeline.push('extract');
|
|
828
|
+
if (flow.pick)
|
|
829
|
+
pipeline.push('pick');
|
|
830
|
+
if (flow.omit)
|
|
831
|
+
pipeline.push('omit');
|
|
832
|
+
if (flow.map)
|
|
833
|
+
pipeline.push('map');
|
|
834
|
+
if (flow.embed)
|
|
835
|
+
pipeline.push('embed');
|
|
836
|
+
if (flow.merge)
|
|
837
|
+
pipeline.push(`merge:${flow.merge}`);
|
|
838
|
+
pipeline.push('write');
|
|
839
|
+
return pipeline;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Normalize from pattern for display in results
|
|
843
|
+
* Converts array to comma-separated string
|
|
844
|
+
*/
|
|
845
|
+
normalizeFromPattern(pattern) {
|
|
846
|
+
return Array.isArray(pattern) ? pattern.join(', ') : pattern;
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Get the first pattern from a pattern or array of patterns
|
|
850
|
+
* Used for path resolution when multiple sources are specified
|
|
851
|
+
*/
|
|
852
|
+
getFirstPattern(pattern) {
|
|
853
|
+
return Array.isArray(pattern) ? pattern[0] : pattern;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Aggregate multi-target results
|
|
857
|
+
*/
|
|
858
|
+
aggregateResults(results, startTime) {
|
|
859
|
+
const successful = results.filter(r => r.success);
|
|
860
|
+
const failed = results.filter(r => !r.success);
|
|
861
|
+
if (failed.length > 0) {
|
|
862
|
+
return {
|
|
863
|
+
source: results[0]?.source || '',
|
|
864
|
+
target: results.map(r => r.target).join(', '),
|
|
865
|
+
success: false,
|
|
866
|
+
transformed: results.some(r => r.transformed),
|
|
867
|
+
error: failed[0]?.error,
|
|
868
|
+
warnings: results.flatMap(r => r.warnings || []),
|
|
869
|
+
executionTime: Date.now() - startTime,
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
return {
|
|
873
|
+
source: results[0]?.source || '',
|
|
874
|
+
target: results.flatMap(r => typeof r.target === 'string' ? [r.target] : r.target),
|
|
875
|
+
success: true,
|
|
876
|
+
transformed: results.some(r => r.transformed),
|
|
877
|
+
warnings: results.flatMap(r => r.warnings || []),
|
|
878
|
+
conflicts: results.flatMap(r => r.conflicts || []),
|
|
879
|
+
executionTime: Date.now() - startTime,
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Parse markdown with frontmatter
|
|
884
|
+
*/
|
|
885
|
+
parseMarkdown(content) {
|
|
886
|
+
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
|
|
887
|
+
const match = content.match(frontmatterRegex);
|
|
888
|
+
if (match) {
|
|
889
|
+
const frontmatter = yaml.load(match[1]);
|
|
890
|
+
const body = match[2];
|
|
891
|
+
return { frontmatter, body };
|
|
892
|
+
}
|
|
893
|
+
return { body: content };
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Serialize markdown with frontmatter
|
|
897
|
+
*/
|
|
898
|
+
serializeMarkdown(content) {
|
|
899
|
+
if (typeof content === 'string') {
|
|
900
|
+
return content;
|
|
901
|
+
}
|
|
902
|
+
if (content.frontmatter) {
|
|
903
|
+
const frontmatterStr = yaml.dump(content.frontmatter, { indent: 2 });
|
|
904
|
+
return `---\n${frontmatterStr}---\n${content.body || ''}`;
|
|
905
|
+
}
|
|
906
|
+
return content.body || '';
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Strip JSON comments
|
|
910
|
+
*/
|
|
911
|
+
stripJSONComments(content) {
|
|
912
|
+
// Remove single-line comments
|
|
913
|
+
let result = content.replace(/\/\/.*$/gm, '');
|
|
914
|
+
// Remove multi-line comments
|
|
915
|
+
result = result.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
916
|
+
return result;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Get nested value using dot notation (delegates to key mapper)
|
|
920
|
+
*/
|
|
921
|
+
getNestedValue(obj, path) {
|
|
922
|
+
return getNestedValue(obj, path);
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Set nested value using dot notation (delegates to key mapper)
|
|
926
|
+
*/
|
|
927
|
+
setNestedValue(obj, path, value) {
|
|
928
|
+
setNestedValue(obj, path, value);
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Delete nested value using dot notation (delegates to key mapper)
|
|
932
|
+
*/
|
|
933
|
+
deleteNestedValue(obj, path) {
|
|
934
|
+
deleteNestedValue(obj, path);
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Apply Codex-specific TOML formatting
|
|
938
|
+
* Converts nested table sections to inline format for http_headers and env_http_headers
|
|
939
|
+
*/
|
|
940
|
+
applyCodexTomlFormatting(toml) {
|
|
941
|
+
const inlineKeys = ['http_headers', 'env_http_headers'];
|
|
942
|
+
let result = toml;
|
|
943
|
+
for (const key of inlineKeys) {
|
|
944
|
+
// Pattern to match nested table sections for the key
|
|
945
|
+
const pattern = new RegExp(`\\[([\\w-]+(?:\\.[\\w-]+|\\."[^"]+")*)?\\.${key}\\]\\s*\\n([\\s\\S]*?)(?=\\n\\[|\\n*$)`, 'g');
|
|
946
|
+
result = result.replace(pattern, (match, parentPath, content) => {
|
|
947
|
+
const pairs = [];
|
|
948
|
+
const lines = content.trim().split('\n');
|
|
949
|
+
for (const line of lines) {
|
|
950
|
+
const trimmed = line.trim();
|
|
951
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
952
|
+
continue;
|
|
953
|
+
const kvMatch = trimmed.match(/^([\w-]+)\s*=\s*(.+)$/);
|
|
954
|
+
if (kvMatch) {
|
|
955
|
+
const [, k, v] = kvMatch;
|
|
956
|
+
pairs.push(`"${k}" = ${v}`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
if (pairs.length === 0)
|
|
960
|
+
return match;
|
|
961
|
+
const inlineTable = `{ ${pairs.join(', ')} }`;
|
|
962
|
+
return `${key} = ${inlineTable}`;
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
return result;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Create a flow executor instance
|
|
970
|
+
*/
|
|
971
|
+
export function createFlowExecutor() {
|
|
972
|
+
return new DefaultFlowExecutor();
|
|
973
|
+
}
|
|
974
|
+
//# sourceMappingURL=flow-executor.js.map
|