@lesliechan721/aw 0.1.0-beta.5 → 0.1.0-beta.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring/assets/aw-config.mjs +100 -501
- package/dist/authoring/assets/aw-config.mjs.map +1 -1
- package/dist/authoring/assets/aw-runtime-config.d.mts +13 -0
- package/dist/authoring/assets/aw-runtime-config.mjs +199 -0
- package/dist/authoring/assets/aw-runtime-config.mjs.map +1 -0
- package/dist/authoring/scripts/add-skill.mjs +4 -4
- package/dist/authoring/scripts/add-skill.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +67 -97
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-root.mjs +4 -4
- package/dist/authoring/scripts/lib/plugin-root.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.d.mts +1 -0
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +229 -8
- package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
- package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -90
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -120
- package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
- package/dist/authoring/scripts/plugin-status.mjs +8 -11
- package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
- package/dist/authoring/scripts/remove-plugin.mjs +3 -2
- package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +346 -1131
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/aw-skill-eval-materializer.d.ts +26 -0
- package/dist/scripts/aw-skill-eval-materializer.js +306 -0
- package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
- package/dist/scripts/bundle-plugins.d.ts +1 -0
- package/dist/scripts/bundle-plugins.js +20 -5
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +7 -9
- package/dist/scripts/plugin-staging.js +18 -29
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.d.ts +2 -0
- package/dist/scripts/test-plugin-scripts.js +47 -76
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/scripts/test-workers.d.mts +24 -0
- package/dist/scripts/test-workers.mjs +59 -0
- package/dist/scripts/test-workers.mjs.map +1 -0
- package/dist/src/adapters/helpers.js +2 -2
- package/dist/src/adapters/helpers.js.map +1 -1
- package/dist/src/authoring/workspace-commands.d.ts +17 -1
- package/dist/src/authoring/workspace-commands.js +21 -4
- package/dist/src/authoring/workspace-commands.js.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +78 -0
- package/dist/src/commands/aw-capabilities.js +264 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -0
- package/dist/src/commands/catalog-dev-source.d.ts +154 -0
- package/dist/src/commands/catalog-dev-source.js +655 -0
- package/dist/src/commands/catalog-dev-source.js.map +1 -0
- package/dist/src/commands/catalog-generation.d.ts +21 -0
- package/dist/src/commands/catalog-generation.js +9 -0
- package/dist/src/commands/catalog-generation.js.map +1 -0
- package/dist/src/commands/catalog-publication-inspection.d.ts +71 -30
- package/dist/src/commands/catalog-publication-inspection.js +475 -63
- package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
- package/dist/src/commands/catalog-publication-journal.d.ts +68 -0
- package/dist/src/commands/catalog-publication-journal.js +292 -0
- package/dist/src/commands/catalog-publication-journal.js.map +1 -0
- package/dist/src/commands/catalog-publication-target.d.ts +18 -0
- package/dist/src/commands/catalog-publication-target.js +95 -0
- package/dist/src/commands/catalog-publication-target.js.map +1 -0
- package/dist/src/commands/catalog-publish.d.ts +73 -15
- package/dist/src/commands/catalog-publish.js +701 -212
- package/dist/src/commands/catalog-publish.js.map +1 -1
- package/dist/src/commands/catalog-source.d.ts +60 -22
- package/dist/src/commands/catalog-source.js +311 -91
- package/dist/src/commands/catalog-source.js.map +1 -1
- package/dist/src/commands/catalog-status.d.ts +18 -22
- package/dist/src/commands/catalog-status.js +81 -115
- package/dist/src/commands/catalog-status.js.map +1 -1
- package/dist/src/commands/catalog-store.d.ts +104 -0
- package/dist/src/commands/catalog-store.js +685 -0
- package/dist/src/commands/catalog-store.js.map +1 -0
- package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
- package/dist/src/commands/catalog-workspace-resolution.js +42 -0
- package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
- package/dist/src/commands/console.js +28 -17
- package/dist/src/commands/console.js.map +1 -1
- package/dist/src/commands/dependency-operation-plan.d.ts +52 -15
- package/dist/src/commands/dependency-operation-plan.js +289 -7
- package/dist/src/commands/dependency-operation-plan.js.map +1 -1
- package/dist/src/commands/init.d.ts +5 -11
- package/dist/src/commands/init.js +30 -51
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/offline-cleanup-evidence.d.ts +67 -0
- package/dist/src/commands/offline-cleanup-evidence.js +181 -0
- package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
- package/dist/src/commands/operation-plan.d.ts +146 -0
- package/dist/src/commands/operation-plan.js +71 -0
- package/dist/src/commands/operation-plan.js.map +1 -0
- package/dist/src/commands/operation-planner.d.ts +46 -0
- package/dist/src/commands/operation-planner.js +141 -0
- package/dist/src/commands/operation-planner.js.map +1 -0
- package/dist/src/commands/options.d.ts +9 -7
- package/dist/src/commands/options.js +83 -21
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +23 -3
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +77 -31
- package/dist/src/commands/rebuild-command.js +4862 -729
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +131 -21
- package/dist/src/commands/rebuild-integration.js +2866 -1339
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.d.ts +58 -30
- package/dist/src/commands/rebuild-operations.js +260 -319
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/reconcile-operation-plan.d.ts +28 -0
- package/dist/src/commands/reconcile-operation-plan.js +47 -0
- package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
- package/dist/src/commands/review-inspection.d.ts +228 -0
- package/dist/src/commands/review-inspection.js +1341 -0
- package/dist/src/commands/review-inspection.js.map +1 -0
- package/dist/src/commands/review-preparation.d.ts +49 -0
- package/dist/src/commands/review-preparation.js +427 -0
- package/dist/src/commands/review-preparation.js.map +1 -0
- package/dist/src/commands/runtime.d.ts +1 -5
- package/dist/src/commands/runtime.js +24 -50
- package/dist/src/commands/runtime.js.map +1 -1
- package/dist/src/commands/skill-eval.d.ts +39 -0
- package/dist/src/commands/skill-eval.js +400 -0
- package/dist/src/commands/skill-eval.js.map +1 -0
- package/dist/src/commands/status-v6.d.ts +279 -0
- package/dist/src/commands/status-v6.js +2787 -0
- package/dist/src/commands/status-v6.js.map +1 -0
- package/dist/src/commands/sync-command.d.ts +40 -0
- package/dist/src/commands/sync-command.js +250 -0
- package/dist/src/commands/sync-command.js.map +1 -0
- package/dist/src/commands/wizard.d.ts +8 -5
- package/dist/src/commands/wizard.js +6 -4
- package/dist/src/commands/wizard.js.map +1 -1
- package/dist/src/commands/write-commands.d.ts +9 -3
- package/dist/src/commands/write-commands.js +127 -31
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/constants.d.ts +7 -1
- package/dist/src/constants.js +7 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/eval/manifest-v1.d.ts +71 -0
- package/dist/src/eval/manifest-v1.js +370 -0
- package/dist/src/eval/manifest-v1.js.map +1 -0
- package/dist/src/generators/local-plugin.d.ts +5 -1
- package/dist/src/generators/local-plugin.js +236 -109
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.d.ts +10 -4
- package/dist/src/index.js +1793 -486
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/command-receipt.d.ts +43 -46
- package/dist/src/output/command-receipt.js +256 -80
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/output/model.d.ts +3 -44
- package/dist/src/output/model.js +1 -144
- package/dist/src/output/model.js.map +1 -1
- package/dist/src/output/rebuild-progress.js +1 -13
- package/dist/src/output/rebuild-progress.js.map +1 -1
- package/dist/src/portable/commands.d.ts +46 -7
- package/dist/src/portable/commands.js +349 -37
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/materializer.d.ts +17 -4
- package/dist/src/portable/materializer.js +179 -41
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/portable/skills-cli.d.ts +15 -7
- package/dist/src/portable/skills-cli.js +74 -20
- package/dist/src/portable/skills-cli.js.map +1 -1
- package/dist/src/review/eval-source-input.d.ts +43 -0
- package/dist/src/review/eval-source-input.js +207 -0
- package/dist/src/review/eval-source-input.js.map +1 -0
- package/dist/src/review/review-profile.d.ts +17 -0
- package/dist/src/review/review-profile.js +234 -0
- package/dist/src/review/review-profile.js.map +1 -0
- package/dist/src/review/review-selection.d.ts +30 -0
- package/dist/src/review/review-selection.js +527 -0
- package/dist/src/review/review-selection.js.map +1 -0
- package/dist/src/review/review-types.d.ts +68 -0
- package/dist/src/review/review-types.js +16 -0
- package/dist/src/review/review-types.js.map +1 -0
- package/dist/src/review/skill-materialization.d.ts +13 -0
- package/dist/src/review/skill-materialization.js +75 -0
- package/dist/src/review/skill-materialization.js.map +1 -0
- package/dist/src/review/source-binding.d.ts +183 -0
- package/dist/src/review/source-binding.js +708 -0
- package/dist/src/review/source-binding.js.map +1 -0
- package/dist/src/tui/catalog-freshness.d.ts +1 -0
- package/dist/src/tui/catalog-freshness.js +2 -0
- package/dist/src/tui/catalog-freshness.js.map +1 -0
- package/dist/src/tui/catalog-generation.d.ts +21 -0
- package/dist/src/tui/catalog-generation.js +156 -0
- package/dist/src/tui/catalog-generation.js.map +1 -0
- package/dist/src/tui/catalog-sources/projection.d.ts +4 -0
- package/dist/src/tui/catalog-sources/projection.js +17 -0
- package/dist/src/tui/catalog-sources/projection.js.map +1 -0
- package/dist/src/tui/catalog-sources/review.d.ts +3 -0
- package/dist/src/tui/catalog-sources/review.js +13 -0
- package/dist/src/tui/catalog-sources/review.js.map +1 -0
- package/dist/src/tui/catalog-sources/screens.d.ts +12 -0
- package/dist/src/tui/catalog-sources/screens.js +40 -0
- package/dist/src/tui/catalog-sources/screens.js.map +1 -0
- package/dist/src/tui/catalog-sources/session.d.ts +32 -0
- package/dist/src/tui/catalog-sources/session.js +300 -0
- package/dist/src/tui/catalog-sources/session.js.map +1 -0
- package/dist/src/tui/catalog-sources/types.d.ts +54 -0
- package/dist/src/tui/catalog-sources/types.js +2 -0
- package/dist/src/tui/catalog-sources/types.js.map +1 -0
- package/dist/src/tui/catalog-store-task.d.ts +16 -0
- package/dist/src/tui/catalog-store-task.js +19 -0
- package/dist/src/tui/catalog-store-task.js.map +1 -0
- package/dist/src/tui/clipboard.d.ts +13 -0
- package/dist/src/tui/clipboard.js +95 -0
- package/dist/src/tui/clipboard.js.map +1 -0
- package/dist/src/tui/components.d.ts +110 -3
- package/dist/src/tui/components.js +183 -36
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/config-array-draft.d.ts +29 -0
- package/dist/src/tui/config-array-draft.js +48 -0
- package/dist/src/tui/config-array-draft.js.map +1 -0
- package/dist/src/tui/config-leaf-editor.d.ts +16 -0
- package/dist/src/tui/config-leaf-editor.js +206 -0
- package/dist/src/tui/config-leaf-editor.js.map +1 -0
- package/dist/src/tui/config-value-presentation.d.ts +20 -0
- package/dist/src/tui/config-value-presentation.js +104 -0
- package/dist/src/tui/config-value-presentation.js.map +1 -0
- package/dist/src/tui/console-app-v4.d.ts +14 -4
- package/dist/src/tui/console-app-v4.js +8941 -1872
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/content-item-card.d.ts +13 -0
- package/dist/src/tui/content-item-card.js +51 -0
- package/dist/src/tui/content-item-card.js.map +1 -0
- package/dist/src/tui/controller-runtime.d.ts +10 -2
- package/dist/src/tui/controller-runtime.js +3211 -388
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +615 -45
- package/dist/src/tui/controller.js +3558 -768
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/dependency-graph-view.d.ts +39 -0
- package/dist/src/tui/dependency-graph-view.js +121 -0
- package/dist/src/tui/dependency-graph-view.js.map +1 -0
- package/dist/src/tui/dependency-graph.d.ts +81 -0
- package/dist/src/tui/dependency-graph.js +452 -0
- package/dist/src/tui/dependency-graph.js.map +1 -0
- package/dist/src/tui/diagnostic-projection.d.ts +23 -0
- package/dist/src/tui/diagnostic-projection.js +567 -0
- package/dist/src/tui/diagnostic-projection.js.map +1 -0
- package/dist/src/tui/diagnostics.d.ts +3 -3
- package/dist/src/tui/diagnostics.js +14 -2
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/entity-cards.d.ts +55 -0
- package/dist/src/tui/entity-cards.js +220 -0
- package/dist/src/tui/entity-cards.js.map +1 -0
- package/dist/src/tui/glossary.d.ts +23 -0
- package/dist/src/tui/glossary.js +42 -0
- package/dist/src/tui/glossary.js.map +1 -0
- package/dist/src/tui/index.d.ts +6 -1
- package/dist/src/tui/index.js +5 -1
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/input-display.d.ts +9 -0
- package/dist/src/tui/input-display.js +58 -0
- package/dist/src/tui/input-display.js.map +1 -0
- package/dist/src/tui/keys.d.ts +44 -0
- package/dist/src/tui/keys.js +43 -15
- package/dist/src/tui/keys.js.map +1 -1
- package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
- package/dist/src/tui/lifecycle-action-view.js +36 -0
- package/dist/src/tui/lifecycle-action-view.js.map +1 -0
- package/dist/src/tui/model.d.ts +223 -31
- package/dist/src/tui/model.js +1510 -397
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-diagnostic-screens.d.ts +8 -0
- package/dist/src/tui/operation-diagnostic-screens.js +71 -0
- package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
- package/dist/src/tui/operation-history.d.ts +81 -0
- package/dist/src/tui/operation-history.js +136 -0
- package/dist/src/tui/operation-history.js.map +1 -0
- package/dist/src/tui/operation-review-planner.d.ts +21 -0
- package/dist/src/tui/operation-review-planner.js +1295 -0
- package/dist/src/tui/operation-review-planner.js.map +1 -0
- package/dist/src/tui/plugin-content.d.ts +31 -0
- package/dist/src/tui/plugin-content.js +680 -0
- package/dist/src/tui/plugin-content.js.map +1 -0
- package/dist/src/tui/plugin-detail.d.ts +52 -0
- package/dist/src/tui/plugin-detail.js +165 -0
- package/dist/src/tui/plugin-detail.js.map +1 -0
- package/dist/src/tui/plugin-write-task.d.ts +69 -0
- package/dist/src/tui/plugin-write-task.js +286 -0
- package/dist/src/tui/plugin-write-task.js.map +1 -0
- package/dist/src/tui/project-inventory.d.ts +68 -0
- package/dist/src/tui/project-inventory.js +203 -0
- package/dist/src/tui/project-inventory.js.map +1 -0
- package/dist/src/tui/prompt.js +4 -15
- package/dist/src/tui/prompt.js.map +1 -1
- package/dist/src/tui/query-header.d.ts +30 -0
- package/dist/src/tui/query-header.js +40 -0
- package/dist/src/tui/query-header.js.map +1 -0
- package/dist/src/tui/reader.d.ts +37 -0
- package/dist/src/tui/reader.js +47 -0
- package/dist/src/tui/reader.js.map +1 -0
- package/dist/src/tui/router.d.ts +197 -10
- package/dist/src/tui/router.js +338 -50
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +701 -0
- package/dist/src/tui/screen-registry.js +230 -0
- package/dist/src/tui/screen-registry.js.map +1 -0
- package/dist/src/tui/session-console-app.d.ts +6 -0
- package/dist/src/tui/session-console-app.js +13 -0
- package/dist/src/tui/session-console-app.js.map +1 -0
- package/dist/src/tui/session-host.d.ts +45 -0
- package/dist/src/tui/session-host.js +209 -0
- package/dist/src/tui/session-host.js.map +1 -0
- package/dist/src/tui/source-detail-card.d.ts +29 -0
- package/dist/src/tui/source-detail-card.js +45 -0
- package/dist/src/tui/source-detail-card.js.map +1 -0
- package/dist/src/tui/source-ref-presets.d.ts +3 -0
- package/dist/src/tui/source-ref-presets.js +11 -0
- package/dist/src/tui/source-ref-presets.js.map +1 -0
- package/dist/src/tui/theme.d.ts +12 -0
- package/dist/src/tui/theme.js +12 -0
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/viewport.d.ts +1 -0
- package/dist/src/tui/viewport.js +23 -0
- package/dist/src/tui/viewport.js.map +1 -1
- package/dist/src/tui/views.js +31 -3
- package/dist/src/tui/views.js.map +1 -1
- package/dist/src/tui/wizard.js +37 -13
- package/dist/src/tui/wizard.js.map +1 -1
- package/dist/src/types.d.ts +244 -20
- package/dist/src/types.js +1 -0
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.d.ts +7 -1
- package/dist/src/utils/agent-version.js +41 -8
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/artifact-plan.d.ts +5 -4
- package/dist/src/utils/artifact-plan.js +18 -7
- package/dist/src/utils/artifact-plan.js.map +1 -1
- package/dist/src/utils/artifact-renderer.d.ts +17 -7
- package/dist/src/utils/artifact-renderer.js +214 -216
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/artifact-request.d.ts +21 -0
- package/dist/src/utils/artifact-request.js +78 -0
- package/dist/src/utils/artifact-request.js.map +1 -0
- package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
- package/dist/src/utils/aw-home-mutation-lock.js +32 -4
- package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
- package/dist/src/utils/bundle-integrity.d.ts +0 -1
- package/dist/src/utils/bundle-integrity.js +0 -25
- package/dist/src/utils/bundle-integrity.js.map +1 -1
- package/dist/src/utils/bundle-layout.js +22 -29
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
- package/dist/src/utils/catalog-capability-projection.js +14 -0
- package/dist/src/utils/catalog-capability-projection.js.map +1 -0
- package/dist/src/utils/catalog-digests.d.ts +10 -0
- package/dist/src/utils/catalog-digests.js +57 -20
- package/dist/src/utils/catalog-digests.js.map +1 -1
- package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
- package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
- package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
- package/dist/src/utils/catalog-generation-error.d.ts +16 -0
- package/dist/src/utils/catalog-generation-error.js +30 -0
- package/dist/src/utils/catalog-generation-error.js.map +1 -0
- package/dist/src/utils/catalog-generation-resolver.d.ts +44 -0
- package/dist/src/utils/catalog-generation-resolver.js +273 -0
- package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
- package/dist/src/utils/catalog-generation-service.d.ts +172 -0
- package/dist/src/utils/catalog-generation-service.js +1271 -0
- package/dist/src/utils/catalog-generation-service.js.map +1 -0
- package/dist/src/utils/catalog-generation-state.d.ts +22 -0
- package/dist/src/utils/catalog-generation-state.js +192 -0
- package/dist/src/utils/catalog-generation-state.js.map +1 -0
- package/dist/src/utils/catalog-generation-store.d.ts +58 -0
- package/dist/src/utils/catalog-generation-store.js +452 -0
- package/dist/src/utils/catalog-generation-store.js.map +1 -0
- package/dist/src/utils/catalog-manifest-v7.d.ts +5 -0
- package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v7.js} +35 -43
- package/dist/src/utils/catalog-manifest-v7.js.map +1 -0
- package/dist/src/utils/catalog-operation-context.d.ts +20 -0
- package/dist/src/utils/catalog-operation-context.js +142 -0
- package/dist/src/utils/catalog-operation-context.js.map +1 -0
- package/dist/src/utils/catalog-public-error.d.ts +7 -3
- package/dist/src/utils/catalog-public-error.js +28 -8
- package/dist/src/utils/catalog-public-error.js.map +1 -1
- package/dist/src/utils/catalog-runtime-projection.d.ts +3 -0
- package/dist/src/utils/catalog-runtime-projection.js +5 -40
- package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
- package/dist/src/utils/catalog-snapshot.d.ts +5 -3
- package/dist/src/utils/catalog-snapshot.js +11 -3
- package/dist/src/utils/catalog-snapshot.js.map +1 -1
- package/dist/src/utils/catalog-source-identity.d.ts +1 -0
- package/dist/src/utils/catalog-source-identity.js +21 -2
- package/dist/src/utils/catalog-source-identity.js.map +1 -1
- package/dist/src/utils/catalog-source-impact.d.ts +21 -0
- package/dist/src/utils/catalog-source-impact.js +344 -0
- package/dist/src/utils/catalog-source-impact.js.map +1 -0
- package/dist/src/utils/catalog-source-references.d.ts +3 -2
- package/dist/src/utils/catalog-source-references.js +195 -347
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/catalog-sources.d.ts +15 -1
- package/dist/src/utils/catalog-sources.js +206 -27
- package/dist/src/utils/catalog-sources.js.map +1 -1
- package/dist/src/utils/codex-config.d.ts +2 -0
- package/dist/src/utils/codex-config.js +26 -5
- package/dist/src/utils/codex-config.js.map +1 -1
- package/dist/src/utils/codex-manager.d.ts +79 -0
- package/dist/src/utils/codex-manager.js +747 -0
- package/dist/src/utils/codex-manager.js.map +1 -0
- package/dist/src/utils/codex-marketplace-path.d.ts +5 -0
- package/dist/src/utils/codex-marketplace-path.js +45 -0
- package/dist/src/utils/codex-marketplace-path.js.map +1 -0
- package/dist/src/utils/codex-native-observation.d.ts +49 -0
- package/dist/src/utils/codex-native-observation.js +154 -0
- package/dist/src/utils/codex-native-observation.js.map +1 -0
- package/dist/src/utils/codex-native-render-receipt.d.ts +40 -0
- package/dist/src/utils/codex-native-render-receipt.js +205 -0
- package/dist/src/utils/codex-native-render-receipt.js.map +1 -0
- package/dist/src/utils/codex-reconciler.d.ts +126 -0
- package/dist/src/utils/codex-reconciler.js +298 -0
- package/dist/src/utils/codex-reconciler.js.map +1 -0
- package/dist/src/utils/codex-shared-native.d.ts +43 -0
- package/dist/src/utils/codex-shared-native.js +81 -0
- package/dist/src/utils/codex-shared-native.js.map +1 -0
- package/dist/src/utils/codex-skill-staging.d.ts +2 -0
- package/dist/src/utils/codex-skill-staging.js.map +1 -1
- package/dist/src/utils/codex-surface-analysis.d.ts +46 -0
- package/dist/src/utils/codex-surface-analysis.js +247 -0
- package/dist/src/utils/codex-surface-analysis.js.map +1 -0
- package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
- package/dist/src/utils/dynamic-skill-tree.js +35 -5
- package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
- package/dist/src/utils/git-catalog-provider.d.ts +16 -0
- package/dist/src/utils/git-catalog-provider.js +177 -145
- package/dist/src/utils/git-catalog-provider.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +18 -1
- package/dist/src/utils/local-marketplace.js +354 -84
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/manifest.d.ts +1 -3
- package/dist/src/utils/manifest.js +1 -3
- package/dist/src/utils/manifest.js.map +1 -1
- package/dist/src/utils/multi-source-catalog.d.ts +3 -9
- package/dist/src/utils/multi-source-catalog.js +54 -110
- package/dist/src/utils/multi-source-catalog.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +55 -7
- package/dist/src/utils/native-cli.js +400 -58
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/orphan-marketplace-cleanup.js +0 -4
- package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
- package/dist/src/utils/ownership.d.ts +84 -20
- package/dist/src/utils/ownership.js +179 -222
- package/dist/src/utils/ownership.js.map +1 -1
- package/dist/src/utils/path-catalog-provider.d.ts +1 -1
- package/dist/src/utils/path-catalog-provider.js +54 -65
- package/dist/src/utils/path-catalog-provider.js.map +1 -1
- package/dist/src/utils/paths.d.ts +0 -1
- package/dist/src/utils/paths.js +0 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/plugin-config-impact.d.ts +33 -0
- package/dist/src/utils/plugin-config-impact.js +298 -0
- package/dist/src/utils/plugin-config-impact.js.map +1 -0
- package/dist/src/utils/plugin-config-write.d.ts +52 -27
- package/dist/src/utils/plugin-config-write.js +1000 -296
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +153 -93
- package/dist/src/utils/plugin-config.js +1116 -968
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-intent-state.d.ts +49 -0
- package/dist/src/utils/plugin-intent-state.js +320 -0
- package/dist/src/utils/plugin-intent-state.js.map +1 -0
- package/dist/src/utils/plugin-scripts.d.ts +65 -125
- package/dist/src/utils/plugin-scripts.js +484 -524
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/process-tree.d.ts +2 -2
- package/dist/src/utils/process-tree.js +1 -1
- package/dist/src/utils/process-tree.js.map +1 -1
- package/dist/src/utils/project-boundary.d.ts +11 -13
- package/dist/src/utils/project-boundary.js +28 -34
- package/dist/src/utils/project-boundary.js.map +1 -1
- package/dist/src/utils/runtime-config.d.ts +20 -0
- package/dist/src/utils/runtime-config.js +75 -0
- package/dist/src/utils/runtime-config.js.map +1 -0
- package/dist/src/utils/safe-fs.d.ts +21 -0
- package/dist/src/utils/safe-fs.js +111 -0
- package/dist/src/utils/safe-fs.js.map +1 -0
- package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
- package/dist/src/utils/safe-operation-diagnostic.js +33 -0
- package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
- package/dist/src/utils/sensitive-content.d.ts +4 -0
- package/dist/src/utils/sensitive-content.js +36 -0
- package/dist/src/utils/sensitive-content.js.map +1 -0
- package/dist/src/utils/state-paths.js +7 -2
- package/dist/src/utils/state-paths.js.map +1 -1
- package/dist/src/utils/target-eligibility.d.ts +8 -2
- package/dist/src/utils/target-eligibility.js +17 -8
- package/dist/src/utils/target-eligibility.js.map +1 -1
- package/dist/src/utils/windows-claude-shim.d.ts +7 -0
- package/dist/src/utils/windows-claude-shim.js +10 -0
- package/dist/src/utils/windows-claude-shim.js.map +1 -0
- package/dist/src/utils/windows-npm-shim.d.ts +14 -0
- package/dist/src/utils/windows-npm-shim.js +140 -0
- package/dist/src/utils/windows-npm-shim.js.map +1 -0
- package/dist/vitest.config.js +92 -10
- package/dist/vitest.config.js.map +1 -1
- package/package.json +27 -18
- package/dist/authoring/scripts/check-plugin-versions.d.mts +0 -2
- package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
- package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
- package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
- package/dist/scripts/aw-skill-eval-engine.js +0 -31
- package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
- package/dist/scripts/codex-eval-app-server.d.ts +0 -89
- package/dist/scripts/codex-eval-app-server.js +0 -1228
- package/dist/scripts/codex-eval-app-server.js.map +0 -1
- package/dist/scripts/codex-eval-isolation.d.ts +0 -61
- package/dist/scripts/codex-eval-isolation.js +0 -318
- package/dist/scripts/codex-eval-isolation.js.map +0 -1
- package/dist/scripts/codex-eval-process-management.d.ts +0 -8
- package/dist/scripts/codex-eval-process-management.js +0 -69
- package/dist/scripts/codex-eval-process-management.js.map +0 -1
- package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
- package/dist/scripts/codex-eval-process-tree.js +0 -803
- package/dist/scripts/codex-eval-process-tree.js.map +0 -1
- package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
- package/dist/scripts/codex-eval-runtime-config.js +0 -843
- package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
- package/dist/scripts/codex-trigger-eval.d.ts +0 -84
- package/dist/scripts/codex-trigger-eval.js +0 -968
- package/dist/scripts/codex-trigger-eval.js.map +0 -1
- package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
- package/dist/scripts/eval-plugin-skills-audited.js +0 -261
- package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
- package/dist/scripts/eval-plugin-skills.d.ts +0 -45
- package/dist/scripts/eval-plugin-skills.js +0 -1586
- package/dist/scripts/eval-plugin-skills.js.map +0 -1
- package/dist/scripts/eval-run-layout.d.ts +0 -17
- package/dist/scripts/eval-run-layout.js +0 -26
- package/dist/scripts/eval-run-layout.js.map +0 -1
- package/dist/scripts/plugin-eval-contract.d.ts +0 -185
- package/dist/scripts/plugin-eval-contract.js +0 -695
- package/dist/scripts/plugin-eval-contract.js.map +0 -1
- package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
- package/dist/scripts/plugin-eval-materializer.js +0 -421
- package/dist/scripts/plugin-eval-materializer.js.map +0 -1
- package/dist/scripts/skill-reporting.d.ts +0 -212
- package/dist/scripts/skill-reporting.js +0 -922
- package/dist/scripts/skill-reporting.js.map +0 -1
- package/dist/src/commands/status-v4.d.ts +0 -123
- package/dist/src/commands/status-v4.js +0 -905
- package/dist/src/commands/status-v4.js.map +0 -1
- package/dist/src/commands/status.d.ts +0 -93
- package/dist/src/commands/status.js +0 -442
- package/dist/src/commands/status.js.map +0 -1
- package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
- package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
- package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
- package/dist/src/utils/catalog-manifest-v3.js +0 -3
- package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
- package/dist/src/utils/catalog-manifest-v4.js.map +0 -1
- package/dist/src/utils/catalog-source-cache.d.ts +0 -62
- package/dist/src/utils/catalog-source-cache.js +0 -296
- package/dist/src/utils/catalog-source-cache.js.map +0 -1
- package/dist/src/utils/codex-project-surface.d.ts +0 -3
- package/dist/src/utils/codex-project-surface.js +0 -49
- package/dist/src/utils/codex-project-surface.js.map +0 -1
- package/dist/src/utils/lifecycle-active-store.d.ts +0 -175
- package/dist/src/utils/lifecycle-active-store.js +0 -1276
- package/dist/src/utils/lifecycle-active-store.js.map +0 -1
- package/dist/src/utils/plugin-release.d.ts +0 -2
- package/dist/src/utils/plugin-release.js +0 -2
- package/dist/src/utils/plugin-release.js.map +0 -1
|
@@ -6,7 +6,7 @@ import { createRequire } from 'node:module';
|
|
|
6
6
|
import * as os from 'node:os';
|
|
7
7
|
import * as path from 'node:path';
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
-
import { getPluginAuthoringAcceptedValues as getCorePluginAuthoringAcceptedValues, getSkillFrontmatterCapabilityRegistry, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
9
|
+
import { AW_RUNTIME_CONFIG_READER_PATH, createRuntimeConfigSnapshot, getPluginAuthoringAcceptedValues as getCorePluginAuthoringAcceptedValues, getSkillFrontmatterCapabilityRegistry, normalizePluginConfigDefinition, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
10
10
|
import { checkPluginVersions } from './lib/plugin-version-check.mjs';
|
|
11
11
|
import { scanJavaScriptImportSpecifiers } from './lib/javascript-import-scanner.mjs';
|
|
12
12
|
import { assertAuthoringPluginPathFence, assertCanonicalAuthoringPluginRoot, } from './lib/plugin-root.mjs';
|
|
@@ -18,7 +18,8 @@ const CLI_PACKAGE_ROOT = path.basename(authoringRuntimeRoot) === 'dist'
|
|
|
18
18
|
: authoringRuntimeRoot;
|
|
19
19
|
const errors = [];
|
|
20
20
|
const warnings = [];
|
|
21
|
-
const SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION =
|
|
21
|
+
const SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION = 16;
|
|
22
|
+
let capabilityDiagnostic = null;
|
|
22
23
|
const MAX_SKILL_NAME_LEN = 64;
|
|
23
24
|
const MAX_SKILL_DESCRIPTION_LEN = 1024;
|
|
24
25
|
const MAX_SKILL_COMPATIBILITY_LEN = 500;
|
|
@@ -124,13 +125,8 @@ const TEMPLATE_TEXT_FILE_EXTENSIONS = /\.(md|markdown|txt|json|yaml|yml|toml|rul
|
|
|
124
125
|
const SLASH_COMMAND_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
125
126
|
const DEFAULT_SLASH_COMMANDS_DIR = 'slash-commands';
|
|
126
127
|
const SCRIPT_NODE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs']);
|
|
127
|
-
const SCRIPT_AUTO_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.sh', '.ps1', '.cmd', '.bat']);
|
|
128
|
-
const SCRIPT_RUNNERS = new Set(['node', 'sh', 'bash', 'pwsh', 'powershell', 'cmd', 'auto']);
|
|
129
|
-
const SCRIPT_OS_SELECTORS = new Set(['default', 'unix', 'windows', 'linux', 'darwin', 'win32']);
|
|
130
|
-
const SCRIPT_LIFECYCLE_EVENTS = new Set(['install', 'upgrade', 'uninstall']);
|
|
131
128
|
const SCRIPT_TIMEOUT_MIN_MS = 1000;
|
|
132
|
-
const SCRIPT_TIMEOUT_MAX_MS =
|
|
133
|
-
const CONFIG_FIELD_TYPES = new Set(getPluginAuthoringAcceptedValues('config', 'config.schema.<key>.type'));
|
|
129
|
+
const SCRIPT_TIMEOUT_MAX_MS = 1800000;
|
|
134
130
|
const ARTIFACT_TARGETS = new Set(getPluginAuthoringAcceptedValues('artifacts', 'artifacts.targets[].target'));
|
|
135
131
|
const ARTIFACT_STATES = new Set(getPluginAuthoringAcceptedValues('artifacts', 'artifacts.targets[].states'));
|
|
136
132
|
const ARTIFACT_SCOPES = new Set(['user', 'project']);
|
|
@@ -149,22 +145,9 @@ const ARTIFACT_PROJECT_MANAGED_KIND_BY_TARGET = {
|
|
|
149
145
|
'codex.skill': 'skill',
|
|
150
146
|
};
|
|
151
147
|
const PLUGIN_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
152
|
-
const PLUGIN_EVAL_SCHEMA_VERSION = 2;
|
|
153
|
-
const PLUGIN_EVAL_CASE_SCHEMA_VERSION = 1;
|
|
154
|
-
const PLUGIN_EVAL_SCOPES = new Set(['user', 'project']);
|
|
155
|
-
const PLUGIN_EVAL_SPLITS = new Set(['train', 'validation', 'sanity', 'all']);
|
|
156
|
-
const PLUGIN_EVAL_TRIGGER_CASE_SPLITS = new Set(['train', 'validation', 'sanity']);
|
|
157
|
-
const PLUGIN_EVAL_COMMAND_SUPPORTS = new Set([
|
|
158
|
-
'split',
|
|
159
|
-
'runs',
|
|
160
|
-
'case',
|
|
161
|
-
'model',
|
|
162
|
-
'codexBin',
|
|
163
|
-
'timeout',
|
|
164
|
-
'allowEnvironmentSkips',
|
|
165
|
-
]);
|
|
166
148
|
let yamlParser = null;
|
|
167
149
|
let pluginVersionCheckResult = null;
|
|
150
|
+
let skillEvalManifestTools = null;
|
|
168
151
|
function error(msg) {
|
|
169
152
|
errors.push(msg);
|
|
170
153
|
}
|
|
@@ -331,12 +314,16 @@ function loadSkillFrontmatterCapabilityRegistry() {
|
|
|
331
314
|
async function tryLoadCliManifestTools() {
|
|
332
315
|
const manifestModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'manifest.js');
|
|
333
316
|
const runtimeProjectionModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'catalog-runtime-projection.js');
|
|
334
|
-
|
|
317
|
+
const capabilityModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'commands', 'aw-capabilities.js');
|
|
318
|
+
if (!fs.existsSync(manifestModulePath)
|
|
319
|
+
|| !fs.existsSync(runtimeProjectionModulePath)
|
|
320
|
+
|| !fs.existsSync(capabilityModulePath))
|
|
335
321
|
return null;
|
|
336
322
|
try {
|
|
337
|
-
const [manifestModule, runtimeProjectionModule] = await Promise.all([
|
|
323
|
+
const [manifestModule, runtimeProjectionModule, capabilityModule] = await Promise.all([
|
|
338
324
|
import(pathToFileURL(manifestModulePath).href),
|
|
339
325
|
import(pathToFileURL(runtimeProjectionModulePath).href),
|
|
326
|
+
import(pathToFileURL(capabilityModulePath).href),
|
|
340
327
|
]);
|
|
341
328
|
if (manifestModule.SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION
|
|
342
329
|
!== SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION) {
|
|
@@ -347,6 +334,8 @@ async function tryLoadCliManifestTools() {
|
|
|
347
334
|
validateSourcePluginManifest: manifestModule.validateSourcePluginManifest ?? null,
|
|
348
335
|
readPluginManifest: manifestModule.readPluginManifest ?? null,
|
|
349
336
|
discoverPlugins: manifestModule.discoverPlugins ?? null,
|
|
337
|
+
resolvePluginRequirements: manifestModule.resolvePluginRequirements ?? null,
|
|
338
|
+
implementedCapabilities: capabilityModule.IMPLEMENTED_AW_CAPABILITIES ?? null,
|
|
350
339
|
createRuntimeResolver: runtimeProjectionModule.createCatalogRuntimeImportResolver ?? null,
|
|
351
340
|
};
|
|
352
341
|
}
|
|
@@ -498,11 +487,12 @@ function validateManifestLightweight(manifest) {
|
|
|
498
487
|
error('top-level "commands" has been removed; use "slashCommands" instead');
|
|
499
488
|
}
|
|
500
489
|
validateAllowedKeys(input, 'plugin manifest', [
|
|
490
|
+
'schemaVersion',
|
|
501
491
|
'name',
|
|
502
492
|
'version',
|
|
503
493
|
'description',
|
|
504
494
|
'dependencies',
|
|
505
|
-
'
|
|
495
|
+
'requires',
|
|
506
496
|
'skills',
|
|
507
497
|
'slashCommands',
|
|
508
498
|
'mcpServers',
|
|
@@ -613,7 +603,7 @@ function validatePortableNativeArtifactCapabilityLightweight(manifest) {
|
|
|
613
603
|
&& Array.isArray(target.states)
|
|
614
604
|
&& target.states.includes('present'));
|
|
615
605
|
if (!hasOtherNativeDeclaration && !hasPresentNativeArtifact) {
|
|
616
|
-
const message = 'Invalid plugin manifest: portable-only plugins are not supported
|
|
606
|
+
const message = 'Invalid plugin manifest: portable-only plugins are not supported. Declare at least one non-empty native capability.';
|
|
617
607
|
if (!errors.includes(message))
|
|
618
608
|
error(message);
|
|
619
609
|
}
|
|
@@ -950,703 +940,19 @@ function validateNonEmptyString(value, fieldName, maxLength) {
|
|
|
950
940
|
}
|
|
951
941
|
return trimmed;
|
|
952
942
|
}
|
|
953
|
-
function
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
!path.isAbsolute(relative));
|
|
958
|
-
}
|
|
959
|
-
function validateRealPathBoundary(boundary, candidate, fieldName, boundaryName) {
|
|
960
|
-
const canonicalBoundary = fs.realpathSync(boundary);
|
|
961
|
-
const canonicalCandidate = fs.realpathSync(candidate);
|
|
962
|
-
if (!isPathInsideOrEqual(canonicalBoundary, canonicalCandidate)) {
|
|
963
|
-
error(`${fieldName} must stay inside the ${boundaryName}`);
|
|
964
|
-
return false;
|
|
965
|
-
}
|
|
966
|
-
return true;
|
|
967
|
-
}
|
|
968
|
-
function validateEvalRelativeFile(evalDir, value, fieldName) {
|
|
969
|
-
const relativePath = validateNonEmptyString(value, fieldName);
|
|
970
|
-
if (!relativePath)
|
|
971
|
-
return null;
|
|
972
|
-
const normalized = stripDotSlash(relativePath).split('\\').join('/');
|
|
973
|
-
if (path.isAbsolute(normalized) || normalized.split('/').includes('..')) {
|
|
974
|
-
error(`${fieldName} must stay inside the Skill eval directory`);
|
|
975
|
-
return null;
|
|
976
|
-
}
|
|
977
|
-
const absolutePath = path.resolve(evalDir, normalized);
|
|
978
|
-
const relativeToRoot = path.relative(evalDir, absolutePath);
|
|
979
|
-
if (relativeToRoot.startsWith('..') || path.isAbsolute(relativeToRoot)) {
|
|
980
|
-
error(`${fieldName} must stay inside the Skill eval directory`);
|
|
981
|
-
return null;
|
|
982
|
-
}
|
|
983
|
-
if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
|
|
984
|
-
error(`${fieldName} does not exist: ${normalized}`);
|
|
985
|
-
return null;
|
|
986
|
-
}
|
|
987
|
-
if (!validateRealPathBoundary(evalDir, absolutePath, fieldName, 'Skill eval directory')) {
|
|
988
|
-
return null;
|
|
989
|
-
}
|
|
990
|
-
return absolutePath;
|
|
991
|
-
}
|
|
992
|
-
function validateEvalWorkspacePath(value, fieldName, options = {}) {
|
|
993
|
-
const raw = validateNonEmptyString(value, fieldName);
|
|
994
|
-
if (!raw)
|
|
995
|
-
return null;
|
|
996
|
-
const treePattern = options.allowTreePattern === true && raw.endsWith('/**');
|
|
997
|
-
const candidate = treePattern ? raw.slice(0, -3) : raw;
|
|
998
|
-
if (candidate === '' ||
|
|
999
|
-
candidate.includes('\0') ||
|
|
1000
|
-
candidate.includes('\\') ||
|
|
1001
|
-
candidate.includes('*') ||
|
|
1002
|
-
candidate.includes('?') ||
|
|
1003
|
-
candidate.includes('[') ||
|
|
1004
|
-
candidate.includes(']') ||
|
|
1005
|
-
path.isAbsolute(candidate) ||
|
|
1006
|
-
path.posix.normalize(candidate) !== candidate ||
|
|
1007
|
-
candidate.split('/').includes('..')) {
|
|
1008
|
-
error(`${fieldName} must be a normalized POSIX path inside the eval workspace`);
|
|
1009
|
-
return null;
|
|
1010
|
-
}
|
|
1011
|
-
const firstSegment = candidate.split('/')[0];
|
|
1012
|
-
if (firstSegment === '.git' || firstSegment === '.agents' || firstSegment === '.codex') {
|
|
1013
|
-
error(`${fieldName} must not target an eval control directory`);
|
|
1014
|
-
return null;
|
|
1015
|
-
}
|
|
1016
|
-
return raw;
|
|
1017
|
-
}
|
|
1018
|
-
function evalWorkspacePathMatchesPattern(relativePath, pattern) {
|
|
1019
|
-
if (pattern.endsWith('/**')) {
|
|
1020
|
-
const prefix = pattern.slice(0, -3);
|
|
1021
|
-
return relativePath === prefix || relativePath.startsWith(`${prefix}/`);
|
|
1022
|
-
}
|
|
1023
|
-
return relativePath === pattern;
|
|
1024
|
-
}
|
|
1025
|
-
function validateConversationCaseCorpus(evalDir, suite, fieldName) {
|
|
1026
|
-
const casesPath = validateEvalRelativeFile(evalDir, suite.cases, `${fieldName}.cases`);
|
|
1027
|
-
if (!casesPath)
|
|
1028
|
-
return;
|
|
1029
|
-
let document;
|
|
1030
|
-
try {
|
|
1031
|
-
document = JSON.parse(fs.readFileSync(casesPath, 'utf8'));
|
|
1032
|
-
}
|
|
1033
|
-
catch (e) {
|
|
1034
|
-
error(`${fieldName}.cases is not valid JSON: ${e.message}`);
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
const corpus = expectRecord(document, `${fieldName}.cases document`);
|
|
1038
|
-
if (!corpus)
|
|
1039
|
-
return;
|
|
1040
|
-
const skillName = validateNonEmptyString(corpus.skill_name, `${fieldName}.cases.skill_name`);
|
|
1041
|
-
if (skillName && skillName !== suite.skill) {
|
|
1042
|
-
error(`${fieldName}.cases.skill_name must match ${JSON.stringify(suite.skill)}`);
|
|
1043
|
-
}
|
|
1044
|
-
if (!Array.isArray(corpus.evals) || corpus.evals.length === 0) {
|
|
1045
|
-
error(`${fieldName}.cases.evals must be a non-empty array`);
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
const ids = new Set();
|
|
1049
|
-
corpus.evals.forEach((value, caseIndex) => {
|
|
1050
|
-
const caseField = `${fieldName}.cases.evals[${caseIndex}]`;
|
|
1051
|
-
const entry = expectRecord(value, caseField);
|
|
1052
|
-
if (!entry)
|
|
1053
|
-
return;
|
|
1054
|
-
const id = validateNonEmptyString(entry.id, `${caseField}.id`);
|
|
1055
|
-
if (id && ids.has(id)) {
|
|
1056
|
-
error(`${fieldName}.cases has duplicate case id ${JSON.stringify(id)}`);
|
|
1057
|
-
}
|
|
1058
|
-
else if (id) {
|
|
1059
|
-
ids.add(id);
|
|
1060
|
-
}
|
|
1061
|
-
validateNonEmptyString(entry.prompt, `${caseField}.prompt`);
|
|
1062
|
-
const sandbox = entry.sandbox === undefined
|
|
1063
|
-
? 'read-only'
|
|
1064
|
-
: validateNonEmptyString(entry.sandbox, `${caseField}.sandbox`);
|
|
1065
|
-
if (sandbox && sandbox !== 'read-only' && sandbox !== 'workspace-write') {
|
|
1066
|
-
error(`${caseField}.sandbox must be "read-only" or "workspace-write"`);
|
|
1067
|
-
}
|
|
1068
|
-
if (!Array.isArray(entry.scripted_replies)) {
|
|
1069
|
-
error(`${caseField}.scripted_replies must be an array`);
|
|
1070
|
-
}
|
|
1071
|
-
else {
|
|
1072
|
-
entry.scripted_replies.forEach((reply, replyIndex) => {
|
|
1073
|
-
const replyField = `${caseField}.scripted_replies[${replyIndex}]`;
|
|
1074
|
-
if (typeof reply === 'string')
|
|
1075
|
-
return;
|
|
1076
|
-
const replyEntry = expectRecord(reply, replyField);
|
|
1077
|
-
if (!replyEntry)
|
|
1078
|
-
return;
|
|
1079
|
-
if (!Array.isArray(replyEntry.answers_by_index) || replyEntry.answers_by_index.some((answer) => typeof answer !== 'string')) {
|
|
1080
|
-
error(`${replyField}.answers_by_index must be an array of strings`);
|
|
1081
|
-
}
|
|
1082
|
-
if (replyEntry.expected_question_count !== undefined &&
|
|
1083
|
-
(!Number.isInteger(replyEntry.expected_question_count) ||
|
|
1084
|
-
replyEntry.expected_question_count < 0)) {
|
|
1085
|
-
error(`${replyField}.expected_question_count must be a non-negative integer`);
|
|
1086
|
-
}
|
|
1087
|
-
validateNonEmptyString(replyEntry.unexpected_question_answer, `${replyField}.unexpected_question_answer`);
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
validateNonEmptyString(entry.expected_output, `${caseField}.expected_output`);
|
|
1091
|
-
if (entry.workspace_files !== undefined) {
|
|
1092
|
-
const workspaceFiles = expectRecord(entry.workspace_files, `${caseField}.workspace_files`);
|
|
1093
|
-
if (workspaceFiles) {
|
|
1094
|
-
const workspaceFileEntries = Object.entries(workspaceFiles);
|
|
1095
|
-
if (workspaceFileEntries.length === 0) {
|
|
1096
|
-
error(`${caseField}.workspace_files must contain at least one file`);
|
|
1097
|
-
}
|
|
1098
|
-
workspaceFileEntries.forEach(([relativePath, content]) => {
|
|
1099
|
-
validateEvalWorkspacePath(relativePath, `${caseField}.workspace_files.${relativePath}`, { allowTreePattern: false });
|
|
1100
|
-
validateNonEmptyString(content, `${caseField}.workspace_files.${relativePath}`);
|
|
1101
|
-
});
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
const review = expectRecord(entry.review, `${caseField}.review`);
|
|
1105
|
-
if (review) {
|
|
1106
|
-
if (!Array.isArray(review.checks) || review.checks.length === 0 || review.checks.some((check) => typeof check !== 'string' || check.trim() === '')) {
|
|
1107
|
-
error(`${caseField}.review.checks must be a non-empty array of strings`);
|
|
1108
|
-
}
|
|
1109
|
-
if (review.workspace_unchanged !== undefined &&
|
|
1110
|
-
typeof review.workspace_unchanged !== 'boolean') {
|
|
1111
|
-
error(`${caseField}.review.workspace_unchanged must be boolean`);
|
|
1112
|
-
}
|
|
1113
|
-
const allowedWorkspaceChanges = [];
|
|
1114
|
-
if (review.allowed_workspace_changes !== undefined) {
|
|
1115
|
-
if (!Array.isArray(review.allowed_workspace_changes) ||
|
|
1116
|
-
review.allowed_workspace_changes.length === 0) {
|
|
1117
|
-
error(`${caseField}.review.allowed_workspace_changes must be a non-empty array`);
|
|
1118
|
-
}
|
|
1119
|
-
else {
|
|
1120
|
-
review.allowed_workspace_changes.forEach((pathValue, pathIndex) => {
|
|
1121
|
-
const normalized = validateEvalWorkspacePath(pathValue, `${caseField}.review.allowed_workspace_changes[${pathIndex}]`, { allowTreePattern: true });
|
|
1122
|
-
if (normalized)
|
|
1123
|
-
allowedWorkspaceChanges.push(normalized);
|
|
1124
|
-
});
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
const artifactPaths = [];
|
|
1128
|
-
if (review.artifacts !== undefined) {
|
|
1129
|
-
if (!Array.isArray(review.artifacts) || review.artifacts.length === 0) {
|
|
1130
|
-
error(`${caseField}.review.artifacts must be a non-empty array`);
|
|
1131
|
-
}
|
|
1132
|
-
else {
|
|
1133
|
-
review.artifacts.forEach((artifactValue, artifactIndex) => {
|
|
1134
|
-
const artifactField = `${caseField}.review.artifacts[${artifactIndex}]`;
|
|
1135
|
-
const artifact = expectRecord(artifactValue, artifactField);
|
|
1136
|
-
if (!artifact)
|
|
1137
|
-
return;
|
|
1138
|
-
validateAllowedKeys(artifact, artifactField, ['path', 'exists', 'contains', 'not_contains', 'json']);
|
|
1139
|
-
const artifactPath = validateEvalWorkspacePath(artifact.path, `${artifactField}.path`);
|
|
1140
|
-
if (artifactPath)
|
|
1141
|
-
artifactPaths.push(artifactPath);
|
|
1142
|
-
if (artifact.exists !== undefined && typeof artifact.exists !== 'boolean') {
|
|
1143
|
-
error(`${artifactField}.exists must be boolean`);
|
|
1144
|
-
}
|
|
1145
|
-
for (const key of ['contains', 'not_contains']) {
|
|
1146
|
-
if (artifact[key] !== undefined &&
|
|
1147
|
-
(!Array.isArray(artifact[key]) || artifact[key].length === 0 || artifact[key].some((fragment) => typeof fragment !== 'string' || fragment.length === 0))) {
|
|
1148
|
-
error(`${artifactField}.${key} must be a non-empty array of strings`);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
if (artifact.json !== undefined && !isRecord(artifact.json)) {
|
|
1152
|
-
error(`${artifactField}.json must be an object`);
|
|
1153
|
-
}
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
if (new Set(artifactPaths).size !== artifactPaths.length) {
|
|
1158
|
-
error(`${caseField}.review.artifacts contains duplicate paths`);
|
|
1159
|
-
}
|
|
1160
|
-
if (sandbox === 'workspace-write') {
|
|
1161
|
-
if (review.workspace_unchanged !== false) {
|
|
1162
|
-
error(`${caseField} workspace-write requires review.workspace_unchanged=false`);
|
|
1163
|
-
}
|
|
1164
|
-
if (allowedWorkspaceChanges.length === 0 || artifactPaths.length === 0) {
|
|
1165
|
-
error(`${caseField} workspace-write requires allowed changes and artifacts`);
|
|
1166
|
-
}
|
|
1167
|
-
const unmatchedArtifact = artifactPaths.find((artifactPath) => !allowedWorkspaceChanges.some((pattern) => evalWorkspacePathMatchesPattern(artifactPath, pattern)));
|
|
1168
|
-
if (unmatchedArtifact) {
|
|
1169
|
-
error(`${caseField} artifact ${JSON.stringify(unmatchedArtifact)} is outside allowed changes`);
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
|
-
function validateTriggerCaseCorpus(evalDir, suite, fieldName) {
|
|
1176
|
-
const casesPath = validateEvalRelativeFile(evalDir, suite.cases, `${fieldName}.cases`);
|
|
1177
|
-
if (!casesPath)
|
|
1178
|
-
return;
|
|
1179
|
-
let document;
|
|
943
|
+
async function loadSkillEvalManifestTools() {
|
|
944
|
+
if (skillEvalManifestTools)
|
|
945
|
+
return skillEvalManifestTools;
|
|
946
|
+
const modulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'eval', 'manifest-v1.js');
|
|
1180
947
|
try {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
catch (e) {
|
|
1184
|
-
error(`${fieldName}.cases is not valid JSON: ${e.message}`);
|
|
1185
|
-
return;
|
|
948
|
+
skillEvalManifestTools = await import(pathToFileURL(modulePath).href);
|
|
949
|
+
return skillEvalManifestTools;
|
|
1186
950
|
}
|
|
1187
|
-
|
|
1188
|
-
if (!corpus)
|
|
1189
|
-
return;
|
|
1190
|
-
if (corpus.schemaVersion !== PLUGIN_EVAL_CASE_SCHEMA_VERSION) {
|
|
1191
|
-
error(`${fieldName}.cases.schemaVersion must be ${PLUGIN_EVAL_CASE_SCHEMA_VERSION}`);
|
|
1192
|
-
}
|
|
1193
|
-
if (corpus.skill !== suite.skill) {
|
|
1194
|
-
error(`${fieldName}.cases.skill must match ${JSON.stringify(suite.skill)}`);
|
|
1195
|
-
}
|
|
1196
|
-
if (!Array.isArray(corpus.cases) || corpus.cases.length === 0) {
|
|
1197
|
-
error(`${fieldName}.cases.cases must be a non-empty array`);
|
|
1198
|
-
return;
|
|
1199
|
-
}
|
|
1200
|
-
const ids = new Set();
|
|
1201
|
-
const seenSplits = new Set();
|
|
1202
|
-
let hasWorkspaceFixture = false;
|
|
1203
|
-
corpus.cases.forEach((value, caseIndex) => {
|
|
1204
|
-
const caseField = `${fieldName}.cases.cases[${caseIndex}]`;
|
|
1205
|
-
const entry = expectRecord(value, caseField);
|
|
1206
|
-
if (!entry)
|
|
1207
|
-
return;
|
|
1208
|
-
const id = validateNonEmptyString(entry.id, `${caseField}.id`);
|
|
1209
|
-
if (id && !/^[a-z0-9][a-z0-9_]*$/.test(id)) {
|
|
1210
|
-
error(`${caseField}.id must use lowercase snake_case`);
|
|
1211
|
-
}
|
|
1212
|
-
else if (id && ids.has(id)) {
|
|
1213
|
-
error(`${fieldName}.cases has duplicate case id ${JSON.stringify(id)}`);
|
|
1214
|
-
}
|
|
1215
|
-
else if (id) {
|
|
1216
|
-
ids.add(id);
|
|
1217
|
-
}
|
|
1218
|
-
if (!PLUGIN_EVAL_TRIGGER_CASE_SPLITS.has(entry.split)) {
|
|
1219
|
-
error(`${caseField}.split must be train, validation, or sanity`);
|
|
1220
|
-
}
|
|
1221
|
-
else {
|
|
1222
|
-
seenSplits.add(entry.split);
|
|
1223
|
-
}
|
|
1224
|
-
validateNonEmptyString(entry.query, `${caseField}.query`);
|
|
1225
|
-
const usesLegacyExpectation = entry.should_load_skill !== undefined;
|
|
1226
|
-
const usesRouteExpectation = entry.expected_loaded_skills !== undefined;
|
|
1227
|
-
if (usesLegacyExpectation === usesRouteExpectation) {
|
|
1228
|
-
error(`${caseField} must declare exactly one of should_load_skill or expected_loaded_skills`);
|
|
1229
|
-
}
|
|
1230
|
-
else if (usesLegacyExpectation && typeof entry.should_load_skill !== 'boolean') {
|
|
1231
|
-
error(`${caseField}.should_load_skill must be boolean`);
|
|
1232
|
-
}
|
|
1233
|
-
else if (usesRouteExpectation) {
|
|
1234
|
-
if (!Array.isArray(entry.expected_loaded_skills)) {
|
|
1235
|
-
error(`${caseField}.expected_loaded_skills must be an array`);
|
|
1236
|
-
}
|
|
1237
|
-
else {
|
|
1238
|
-
const expectedSkills = entry.expected_loaded_skills;
|
|
1239
|
-
expectedSkills.forEach((skillName, skillIndex) => {
|
|
1240
|
-
if (typeof skillName !== 'string' || skillName.trim() === '') {
|
|
1241
|
-
error(`${caseField}.expected_loaded_skills[${skillIndex}] must be a non-empty string`);
|
|
1242
|
-
}
|
|
1243
|
-
else if (!suite.skills.includes(skillName)) {
|
|
1244
|
-
error(`${caseField}.expected_loaded_skills[${skillIndex}] must name a participant Skill`);
|
|
1245
|
-
}
|
|
1246
|
-
});
|
|
1247
|
-
if (new Set(expectedSkills).size !== expectedSkills.length) {
|
|
1248
|
-
error(`${caseField}.expected_loaded_skills must not contain duplicates`);
|
|
1249
|
-
}
|
|
1250
|
-
if (suite.forbidMultipleSkillLoads === true && expectedSkills.length > 1) {
|
|
1251
|
-
error(`${caseField} expects multiple Skills but the suite forbids multiple Skill loads`);
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
if (suite.observeSpawnAgent === true) {
|
|
1256
|
-
if (typeof entry.should_spawn_agent !== 'boolean') {
|
|
1257
|
-
error(`${caseField}.should_spawn_agent is required when observeSpawnAgent=true`);
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
else if (entry.should_spawn_agent !== undefined) {
|
|
1261
|
-
error(`${caseField}.should_spawn_agent requires observeSpawnAgent=true`);
|
|
1262
|
-
}
|
|
1263
|
-
if (entry.workspace_fixture !== undefined) {
|
|
1264
|
-
hasWorkspaceFixture = true;
|
|
1265
|
-
validateNonEmptyString(entry.workspace_fixture, `${caseField}.workspace_fixture`);
|
|
1266
|
-
}
|
|
1267
|
-
if (entry.use_project_rules !== undefined &&
|
|
1268
|
-
typeof entry.use_project_rules !== 'boolean') {
|
|
1269
|
-
error(`${caseField}.use_project_rules must be boolean`);
|
|
1270
|
-
}
|
|
1271
|
-
if (entry.tags !== undefined) {
|
|
1272
|
-
if (!Array.isArray(entry.tags) || entry.tags.length === 0) {
|
|
1273
|
-
error(`${caseField}.tags must be a non-empty array`);
|
|
1274
|
-
}
|
|
1275
|
-
else {
|
|
1276
|
-
entry.tags.forEach((tag, tagIndex) => {
|
|
1277
|
-
validateNonEmptyString(tag, `${caseField}.tags[${tagIndex}]`);
|
|
1278
|
-
});
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
});
|
|
1282
|
-
if (hasWorkspaceFixture && suite.fixtureModule === undefined) {
|
|
1283
|
-
error(`${fieldName}.fixtureModule is required when cases declare workspace_fixture`);
|
|
1284
|
-
}
|
|
1285
|
-
if (Array.isArray(suite.releaseSplits)) {
|
|
1286
|
-
suite.releaseSplits.forEach((split) => {
|
|
1287
|
-
if (split !== 'all' && !seenSplits.has(split)) {
|
|
1288
|
-
error(`${fieldName}.cases has no cases for release split ${JSON.stringify(split)}`);
|
|
1289
|
-
}
|
|
1290
|
-
});
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
function resolveEvalProfilePlugin(pluginDir, ownerManifest, pluginName, fieldName) {
|
|
1294
|
-
if (pluginName === ownerManifest.name) {
|
|
1295
|
-
return { pluginDir, manifest: ownerManifest };
|
|
1296
|
-
}
|
|
1297
|
-
const companionDir = path.join(path.dirname(pluginDir), pluginName);
|
|
1298
|
-
const manifestPath = path.join(companionDir, '.agents-plugin', 'plugin.json');
|
|
1299
|
-
if (!fs.existsSync(manifestPath)) {
|
|
1300
|
-
error(`${fieldName} must reference repo-local plugin "${pluginName}"`);
|
|
1301
|
-
return null;
|
|
1302
|
-
}
|
|
1303
|
-
let manifest;
|
|
1304
|
-
try {
|
|
1305
|
-
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
1306
|
-
}
|
|
1307
|
-
catch (e) {
|
|
1308
|
-
error(`${fieldName} companion manifest is not valid JSON: ${e.message}`);
|
|
1309
|
-
return null;
|
|
1310
|
-
}
|
|
1311
|
-
if (!isRecord(manifest) || manifest.name !== pluginName) {
|
|
1312
|
-
error(`${fieldName} companion source must declare plugin name "${pluginName}"`);
|
|
951
|
+
catch {
|
|
1313
952
|
return null;
|
|
1314
953
|
}
|
|
1315
|
-
return { pluginDir: companionDir, manifest };
|
|
1316
|
-
}
|
|
1317
|
-
function validateOnePluginEvalManifest(pluginDir, pluginManifest, evalDir, expectedSkill) {
|
|
1318
|
-
const evalManifestPath = path.join(evalDir, 'manifest.json');
|
|
1319
|
-
const manifestField = path.relative(pluginDir, evalManifestPath).split(path.sep).join('/');
|
|
1320
|
-
let document;
|
|
1321
|
-
try {
|
|
1322
|
-
document = JSON.parse(fs.readFileSync(evalManifestPath, 'utf8'));
|
|
1323
|
-
}
|
|
1324
|
-
catch (e) {
|
|
1325
|
-
error(`${manifestField} is not valid JSON: ${e.message}`);
|
|
1326
|
-
return;
|
|
1327
|
-
}
|
|
1328
|
-
const manifest = expectRecord(document, manifestField);
|
|
1329
|
-
if (!manifest)
|
|
1330
|
-
return;
|
|
1331
|
-
validateAllowedKeys(manifest, manifestField, ['schemaVersion', 'plugin', 'skill', 'profiles', 'suites']);
|
|
1332
|
-
if (manifest.schemaVersion !== PLUGIN_EVAL_SCHEMA_VERSION) {
|
|
1333
|
-
error(`${manifestField}.schemaVersion must be ${PLUGIN_EVAL_SCHEMA_VERSION}`);
|
|
1334
|
-
}
|
|
1335
|
-
if (manifest.plugin !== pluginManifest.name) {
|
|
1336
|
-
error(`${manifestField}.plugin must match plugin name ${JSON.stringify(pluginManifest.name)}`);
|
|
1337
|
-
}
|
|
1338
|
-
const skill = validateNonEmptyString(manifest.skill, `${manifestField}.skill`);
|
|
1339
|
-
if (skill && skill !== expectedSkill) {
|
|
1340
|
-
error(`${manifestField}.skill must match owning Skill ${JSON.stringify(expectedSkill)}`);
|
|
1341
|
-
}
|
|
1342
|
-
if (skill && !collectDeclaredSkillNames(pluginDir, pluginManifest).all.has(skill)) {
|
|
1343
|
-
error(`${manifestField}.skill must reference a declared static or dynamic skill`);
|
|
1344
|
-
}
|
|
1345
|
-
if (!Array.isArray(manifest.profiles) || manifest.profiles.length === 0) {
|
|
1346
|
-
error(`${manifestField}.profiles must be a non-empty array`);
|
|
1347
|
-
return;
|
|
1348
|
-
}
|
|
1349
|
-
const profilesById = new Map();
|
|
1350
|
-
manifest.profiles.forEach((value, index) => {
|
|
1351
|
-
const fieldName = `${manifestField}.profiles[${index}]`;
|
|
1352
|
-
const profile = expectRecord(value, fieldName);
|
|
1353
|
-
if (!profile)
|
|
1354
|
-
return;
|
|
1355
|
-
validateAllowedKeys(profile, fieldName, ['id', 'scope', 'plugins', 'runModel', 'requiredForRelease']);
|
|
1356
|
-
const id = validateNonEmptyString(profile.id, `${fieldName}.id`);
|
|
1357
|
-
if (id && !PLUGIN_NAME_PATTERN.test(id)) {
|
|
1358
|
-
error(`${fieldName}.id must use lowercase kebab-case`);
|
|
1359
|
-
}
|
|
1360
|
-
else if (id && profilesById.has(id)) {
|
|
1361
|
-
error(`${manifestField} has duplicate profile id ${JSON.stringify(id)}`);
|
|
1362
|
-
}
|
|
1363
|
-
if (!PLUGIN_EVAL_SCOPES.has(profile.scope)) {
|
|
1364
|
-
error(`${fieldName}.scope must be user or project`);
|
|
1365
|
-
}
|
|
1366
|
-
const skillOwners = new Map();
|
|
1367
|
-
const installedPlugins = new Set();
|
|
1368
|
-
if (!Array.isArray(profile.plugins) || profile.plugins.length === 0) {
|
|
1369
|
-
error(`${fieldName}.plugins must be a non-empty array`);
|
|
1370
|
-
}
|
|
1371
|
-
else {
|
|
1372
|
-
profile.plugins.forEach((value, pluginIndex) => {
|
|
1373
|
-
const pluginField = `${fieldName}.plugins[${pluginIndex}]`;
|
|
1374
|
-
const installation = expectRecord(value, pluginField);
|
|
1375
|
-
if (!installation)
|
|
1376
|
-
return;
|
|
1377
|
-
validateAllowedKeys(installation, pluginField, ['name', 'config']);
|
|
1378
|
-
const pluginName = validateNonEmptyString(installation.name, `${pluginField}.name`);
|
|
1379
|
-
if (!pluginName)
|
|
1380
|
-
return;
|
|
1381
|
-
if (!PLUGIN_NAME_PATTERN.test(pluginName)) {
|
|
1382
|
-
error(`${pluginField}.name must use lowercase kebab-case`);
|
|
1383
|
-
}
|
|
1384
|
-
if (installedPlugins.has(pluginName)) {
|
|
1385
|
-
error(`${fieldName}.plugins has duplicate plugin ${JSON.stringify(pluginName)}`);
|
|
1386
|
-
return;
|
|
1387
|
-
}
|
|
1388
|
-
installedPlugins.add(pluginName);
|
|
1389
|
-
if (installation.config !== undefined && !isRecord(installation.config)) {
|
|
1390
|
-
error(`${pluginField}.config must be an object`);
|
|
1391
|
-
}
|
|
1392
|
-
const source = resolveEvalProfilePlugin(pluginDir, pluginManifest, pluginName, `${pluginField}.name`);
|
|
1393
|
-
if (!source)
|
|
1394
|
-
return;
|
|
1395
|
-
for (const skillName of collectDeclaredSkillNames(source.pluginDir, source.manifest).all) {
|
|
1396
|
-
const existing = skillOwners.get(skillName);
|
|
1397
|
-
if (existing && existing !== pluginName) {
|
|
1398
|
-
error(`${fieldName} installs duplicate Skill key ${JSON.stringify(skillName)} ` +
|
|
1399
|
-
`from plugins ${JSON.stringify(existing)} and ${JSON.stringify(pluginName)}`);
|
|
1400
|
-
}
|
|
1401
|
-
else {
|
|
1402
|
-
skillOwners.set(skillName, pluginName);
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
|
-
if (!installedPlugins.has(pluginManifest.name)) {
|
|
1408
|
-
error(`${fieldName}.plugins must include owner plugin ${JSON.stringify(pluginManifest.name)}`);
|
|
1409
|
-
}
|
|
1410
|
-
if (typeof profile.runModel !== 'boolean') {
|
|
1411
|
-
error(`${fieldName}.runModel must be boolean`);
|
|
1412
|
-
}
|
|
1413
|
-
if (typeof profile.requiredForRelease !== 'boolean') {
|
|
1414
|
-
error(`${fieldName}.requiredForRelease must be boolean`);
|
|
1415
|
-
}
|
|
1416
|
-
if (profile.scope === 'user' && profile.runModel === true) {
|
|
1417
|
-
error(`${fieldName}: eval schema v2 does not allow real model execution for user scope`);
|
|
1418
|
-
}
|
|
1419
|
-
if (id && PLUGIN_NAME_PATTERN.test(id) && !profilesById.has(id)) {
|
|
1420
|
-
profilesById.set(id, { ...profile, skillOwners });
|
|
1421
|
-
}
|
|
1422
|
-
});
|
|
1423
|
-
if (!manifest.profiles.some((profile) => isRecord(profile) && profile.scope === 'project' && profile.runModel === true)) {
|
|
1424
|
-
error(`${manifestField} must define at least one project profile with runModel=true`);
|
|
1425
|
-
}
|
|
1426
|
-
if (!Array.isArray(manifest.suites) || manifest.suites.length === 0) {
|
|
1427
|
-
error(`${manifestField}.suites must be a non-empty array`);
|
|
1428
|
-
return;
|
|
1429
|
-
}
|
|
1430
|
-
const suiteIds = new Set();
|
|
1431
|
-
manifest.suites.forEach((value, index) => {
|
|
1432
|
-
const fieldName = `${manifestField}.suites[${index}]`;
|
|
1433
|
-
const suite = expectRecord(value, fieldName);
|
|
1434
|
-
if (!suite)
|
|
1435
|
-
return;
|
|
1436
|
-
const id = validateNonEmptyString(suite.id, `${fieldName}.id`);
|
|
1437
|
-
if (id && !PLUGIN_NAME_PATTERN.test(id)) {
|
|
1438
|
-
error(`${fieldName}.id must use lowercase kebab-case`);
|
|
1439
|
-
}
|
|
1440
|
-
else if (id && suiteIds.has(id)) {
|
|
1441
|
-
error(`${manifestField} has duplicate suite id ${JSON.stringify(id)}`);
|
|
1442
|
-
}
|
|
1443
|
-
else if (id) {
|
|
1444
|
-
suiteIds.add(id);
|
|
1445
|
-
}
|
|
1446
|
-
if (suite.kind !== 'command' && suite.kind !== 'conversation' && suite.kind !== 'trigger') {
|
|
1447
|
-
error(`${fieldName}.kind must be command, conversation, or trigger`);
|
|
1448
|
-
}
|
|
1449
|
-
const suiteSkills = new Set();
|
|
1450
|
-
if (!Array.isArray(suite.skills) || suite.skills.length === 0) {
|
|
1451
|
-
error(`${fieldName}.skills must be a non-empty array`);
|
|
1452
|
-
}
|
|
1453
|
-
else {
|
|
1454
|
-
suite.skills.forEach((skillName, skillIndex) => {
|
|
1455
|
-
const skillField = `${fieldName}.skills[${skillIndex}]`;
|
|
1456
|
-
const normalized = validateNonEmptyString(skillName, skillField);
|
|
1457
|
-
if (!normalized)
|
|
1458
|
-
return;
|
|
1459
|
-
if (!PLUGIN_NAME_PATTERN.test(normalized)) {
|
|
1460
|
-
error(`${skillField} must use lowercase kebab-case`);
|
|
1461
|
-
}
|
|
1462
|
-
else if (suiteSkills.has(normalized)) {
|
|
1463
|
-
error(`${fieldName}.skills has duplicate Skill ${JSON.stringify(normalized)}`);
|
|
1464
|
-
}
|
|
1465
|
-
else {
|
|
1466
|
-
suiteSkills.add(normalized);
|
|
1467
|
-
}
|
|
1468
|
-
});
|
|
1469
|
-
}
|
|
1470
|
-
if (skill && !suiteSkills.has(skill)) {
|
|
1471
|
-
error(`${fieldName}.skills must include owner Skill ${JSON.stringify(skill)}`);
|
|
1472
|
-
}
|
|
1473
|
-
if (!Array.isArray(suite.profiles) || suite.profiles.length === 0) {
|
|
1474
|
-
error(`${fieldName}.profiles must be a non-empty array`);
|
|
1475
|
-
}
|
|
1476
|
-
else {
|
|
1477
|
-
suite.profiles.forEach((profileId, profileIndex) => {
|
|
1478
|
-
const profileField = `${fieldName}.profiles[${profileIndex}]`;
|
|
1479
|
-
if (typeof profileId !== 'string' || profileId.trim() === '') {
|
|
1480
|
-
error(`${profileField} must be a non-empty string`);
|
|
1481
|
-
return;
|
|
1482
|
-
}
|
|
1483
|
-
const profile = profilesById.get(profileId);
|
|
1484
|
-
if (!profile) {
|
|
1485
|
-
error(`${fieldName} references unknown profile ${JSON.stringify(profileId)}`);
|
|
1486
|
-
}
|
|
1487
|
-
else if (profile.runModel !== true) {
|
|
1488
|
-
error(`${fieldName} cannot execute materialize-only profile ${JSON.stringify(profileId)}`);
|
|
1489
|
-
}
|
|
1490
|
-
if (profile) {
|
|
1491
|
-
for (const skillName of suiteSkills) {
|
|
1492
|
-
if (!profile.skillOwners.has(skillName)) {
|
|
1493
|
-
error(`${fieldName} requires Skill ${JSON.stringify(skillName)}, but profile ` +
|
|
1494
|
-
`${JSON.stringify(profileId)} does not install a plugin that declares it`);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
if (!Number.isInteger(suite.defaultRuns) || suite.defaultRuns <= 0) {
|
|
1501
|
-
error(`${fieldName}.defaultRuns must be a positive integer`);
|
|
1502
|
-
}
|
|
1503
|
-
if (!Number.isInteger(suite.perRunTimeoutMs) || suite.perRunTimeoutMs <= 0) {
|
|
1504
|
-
error(`${fieldName}.perRunTimeoutMs must be a positive integer`);
|
|
1505
|
-
}
|
|
1506
|
-
if (typeof suite.requiredForRelease !== 'boolean') {
|
|
1507
|
-
error(`${fieldName}.requiredForRelease must be boolean`);
|
|
1508
|
-
}
|
|
1509
|
-
if (!Array.isArray(suite.releaseSplits)) {
|
|
1510
|
-
error(`${fieldName}.releaseSplits must be an array`);
|
|
1511
|
-
}
|
|
1512
|
-
else {
|
|
1513
|
-
suite.releaseSplits.forEach((split, splitIndex) => {
|
|
1514
|
-
if (!PLUGIN_EVAL_SPLITS.has(split)) {
|
|
1515
|
-
error(`${fieldName}.releaseSplits[${splitIndex}] is unsupported`);
|
|
1516
|
-
}
|
|
1517
|
-
});
|
|
1518
|
-
}
|
|
1519
|
-
if (suite.kind === 'command') {
|
|
1520
|
-
validateAllowedKeys(suite, fieldName, [
|
|
1521
|
-
'id', 'kind', 'skills', 'entry', 'cases', 'profiles', 'defaultRuns', 'releaseSplits',
|
|
1522
|
-
'requiredForRelease', 'perRunTimeoutMs', 'suiteTimeoutMs', 'supports',
|
|
1523
|
-
]);
|
|
1524
|
-
validateEvalRelativeFile(evalDir, suite.entry, `${fieldName}.entry`);
|
|
1525
|
-
if (!Number.isInteger(suite.suiteTimeoutMs) || suite.suiteTimeoutMs <= 0) {
|
|
1526
|
-
error(`${fieldName}.suiteTimeoutMs must be a positive integer`);
|
|
1527
|
-
}
|
|
1528
|
-
else if (suite.suiteTimeoutMs < suite.perRunTimeoutMs) {
|
|
1529
|
-
error(`${fieldName}.suiteTimeoutMs must be greater than or equal to perRunTimeoutMs`);
|
|
1530
|
-
}
|
|
1531
|
-
if (!Array.isArray(suite.supports)) {
|
|
1532
|
-
error(`${fieldName}.supports must be an array`);
|
|
1533
|
-
}
|
|
1534
|
-
else {
|
|
1535
|
-
suite.supports.forEach((support, supportIndex) => {
|
|
1536
|
-
if (!PLUGIN_EVAL_COMMAND_SUPPORTS.has(support)) {
|
|
1537
|
-
error(`${fieldName}.supports[${supportIndex}] is unsupported`);
|
|
1538
|
-
}
|
|
1539
|
-
});
|
|
1540
|
-
if (Array.isArray(suite.releaseSplits) &&
|
|
1541
|
-
suite.releaseSplits.length > 0 &&
|
|
1542
|
-
!suite.supports.includes('split')) {
|
|
1543
|
-
error(`${fieldName} declares releaseSplits but does not support split`);
|
|
1544
|
-
}
|
|
1545
|
-
const supportsCase = suite.supports.includes('case');
|
|
1546
|
-
if (supportsCase && suite.cases === undefined) {
|
|
1547
|
-
error(`${fieldName}.cases is required when supports includes case`);
|
|
1548
|
-
}
|
|
1549
|
-
else if (!supportsCase && suite.cases !== undefined) {
|
|
1550
|
-
error(`${fieldName}.cases requires supports to include case`);
|
|
1551
|
-
}
|
|
1552
|
-
if (supportsCase && suite.cases !== undefined) {
|
|
1553
|
-
const casesPath = validateEvalRelativeFile(evalDir, suite.cases, `${fieldName}.cases`);
|
|
1554
|
-
if (casesPath) {
|
|
1555
|
-
try {
|
|
1556
|
-
const casesDocument = JSON.parse(fs.readFileSync(casesPath, 'utf8'));
|
|
1557
|
-
if (!isRecord(casesDocument) || casesDocument.skill_name !== skill) {
|
|
1558
|
-
error(`${fieldName}.cases.skill_name must match ${JSON.stringify(skill)}`);
|
|
1559
|
-
}
|
|
1560
|
-
else if (!Array.isArray(casesDocument.evals) || casesDocument.evals.length === 0) {
|
|
1561
|
-
error(`${fieldName}.cases.evals must be a non-empty array`);
|
|
1562
|
-
}
|
|
1563
|
-
else {
|
|
1564
|
-
const caseIds = new Set();
|
|
1565
|
-
casesDocument.evals.forEach((entry, caseIndex) => {
|
|
1566
|
-
const caseField = `${fieldName}.cases.evals[${caseIndex}]`;
|
|
1567
|
-
if (!isRecord(entry) || typeof entry.id !== 'string' || !/^[a-z0-9_]+$/.test(entry.id)) {
|
|
1568
|
-
error(`${caseField}.id must use lowercase snake_case`);
|
|
1569
|
-
}
|
|
1570
|
-
else if (caseIds.has(entry.id)) {
|
|
1571
|
-
error(`${fieldName}.cases has duplicate case id ${JSON.stringify(entry.id)}`);
|
|
1572
|
-
}
|
|
1573
|
-
else {
|
|
1574
|
-
caseIds.add(entry.id);
|
|
1575
|
-
}
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
catch (cause) {
|
|
1580
|
-
error(`${fieldName}.cases is not valid JSON: ${cause.message}`);
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
else if (suite.kind === 'conversation' || suite.kind === 'trigger') {
|
|
1587
|
-
const normalizedSuite = { ...suite, skill };
|
|
1588
|
-
const isolation = suite.isolation ?? 'standard';
|
|
1589
|
-
if (isolation !== 'standard' && isolation !== 'strict') {
|
|
1590
|
-
error(`${fieldName}.isolation must be standard or strict`);
|
|
1591
|
-
}
|
|
1592
|
-
if (suite.kind === 'conversation') {
|
|
1593
|
-
validateAllowedKeys(suite, fieldName, [
|
|
1594
|
-
'id', 'kind', 'skills', 'cases', 'profiles', 'defaultRuns', 'releaseSplits',
|
|
1595
|
-
'requiredForRelease', 'perRunTimeoutMs', 'isolation',
|
|
1596
|
-
]);
|
|
1597
|
-
if (Array.isArray(suite.releaseSplits) && suite.releaseSplits.length > 0) {
|
|
1598
|
-
error(`${fieldName} cannot declare releaseSplits because conversation cases do not support split filtering`);
|
|
1599
|
-
}
|
|
1600
|
-
validateConversationCaseCorpus(evalDir, normalizedSuite, fieldName);
|
|
1601
|
-
}
|
|
1602
|
-
else {
|
|
1603
|
-
validateAllowedKeys(suite, fieldName, [
|
|
1604
|
-
'id', 'kind', 'skills', 'cases', 'fixtureModule', 'observeSpawnAgent', 'isolation',
|
|
1605
|
-
'strictNegativeSplits', 'minimumPassRate', 'forbidMultipleSkillLoads',
|
|
1606
|
-
'profiles', 'defaultRuns', 'releaseSplits',
|
|
1607
|
-
'requiredForRelease', 'perRunTimeoutMs',
|
|
1608
|
-
]);
|
|
1609
|
-
if (typeof suite.observeSpawnAgent !== 'boolean') {
|
|
1610
|
-
error(`${fieldName}.observeSpawnAgent must be boolean`);
|
|
1611
|
-
}
|
|
1612
|
-
if (!Array.isArray(suite.strictNegativeSplits)) {
|
|
1613
|
-
error(`${fieldName}.strictNegativeSplits must be an array`);
|
|
1614
|
-
}
|
|
1615
|
-
else {
|
|
1616
|
-
suite.strictNegativeSplits.forEach((split, splitIndex) => {
|
|
1617
|
-
if (!PLUGIN_EVAL_TRIGGER_CASE_SPLITS.has(split)) {
|
|
1618
|
-
error(`${fieldName}.strictNegativeSplits[${splitIndex}] is unsupported`);
|
|
1619
|
-
}
|
|
1620
|
-
});
|
|
1621
|
-
}
|
|
1622
|
-
if (suite.minimumPassRate !== undefined &&
|
|
1623
|
-
(typeof suite.minimumPassRate !== 'number' ||
|
|
1624
|
-
!Number.isFinite(suite.minimumPassRate) ||
|
|
1625
|
-
suite.minimumPassRate <= 0 ||
|
|
1626
|
-
suite.minimumPassRate > 1)) {
|
|
1627
|
-
error(`${fieldName}.minimumPassRate must be greater than 0 and at most 1`);
|
|
1628
|
-
}
|
|
1629
|
-
if (suite.forbidMultipleSkillLoads !== undefined &&
|
|
1630
|
-
typeof suite.forbidMultipleSkillLoads !== 'boolean') {
|
|
1631
|
-
error(`${fieldName}.forbidMultipleSkillLoads must be boolean`);
|
|
1632
|
-
}
|
|
1633
|
-
if (suite.fixtureModule !== undefined) {
|
|
1634
|
-
const fixturePath = validateEvalRelativeFile(evalDir, suite.fixtureModule, `${fieldName}.fixtureModule`);
|
|
1635
|
-
if (fixturePath && path.extname(fixturePath) !== '.mjs') {
|
|
1636
|
-
error(`${fieldName}.fixtureModule must reference an .mjs file`);
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
validateTriggerCaseCorpus(evalDir, normalizedSuite, fieldName);
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
});
|
|
1643
|
-
if (skill
|
|
1644
|
-
&& collectDeclaredSkillNames(pluginDir, pluginManifest).dynamic.has(skill)
|
|
1645
|
-
&& !manifest.suites.some((suite) => isRecord(suite) && suite.requiredForRelease === true)) {
|
|
1646
|
-
error(`${manifestField}.suites must include at least one release-required suite for dynamic Skill ${JSON.stringify(skill)}`);
|
|
1647
|
-
}
|
|
1648
954
|
}
|
|
1649
|
-
function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
955
|
+
async function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
1650
956
|
const legacyRoot = path.join(pluginDir, 'evals');
|
|
1651
957
|
if (fs.existsSync(legacyRoot)) {
|
|
1652
958
|
error('Plugin-root evals/ is legacy; put evals/ inside its owning static or dynamic Skill directory.');
|
|
@@ -1655,17 +961,33 @@ function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
|
1655
961
|
.sort()
|
|
1656
962
|
.map((skill) => ({ skill, root: path.join(pluginDir, 'skills', skill) }));
|
|
1657
963
|
const seenSkills = new Set();
|
|
964
|
+
let manifestTools = null;
|
|
1658
965
|
for (const owner of skillRoots) {
|
|
1659
966
|
const evalDir = path.join(owner.root, 'evals');
|
|
1660
967
|
const manifestPath = path.join(evalDir, 'manifest.json');
|
|
1661
968
|
if (!fs.existsSync(manifestPath))
|
|
1662
969
|
continue;
|
|
970
|
+
const manifestField = path.relative(pluginDir, manifestPath).split(path.sep).join('/');
|
|
1663
971
|
if (seenSkills.has(owner.skill)) {
|
|
1664
972
|
error(`Skill ${JSON.stringify(owner.skill)} has more than one evals/manifest.json owner`);
|
|
1665
973
|
continue;
|
|
1666
974
|
}
|
|
1667
975
|
seenSkills.add(owner.skill);
|
|
1668
|
-
|
|
976
|
+
manifestTools ??= await loadSkillEvalManifestTools();
|
|
977
|
+
if (typeof manifestTools?.validateAwSkillEvalManifest !== 'function') {
|
|
978
|
+
error('Canonical AwSkillEvalManifest v1 validator is unavailable; run the current built aw CLI.');
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
const validation = await manifestTools.validateAwSkillEvalManifest({
|
|
982
|
+
plugin: {
|
|
983
|
+
rootDir: pluginDir,
|
|
984
|
+
manifestPath: path.join(pluginDir, '.agents-plugin', 'plugin.json'),
|
|
985
|
+
manifest: pluginManifest,
|
|
986
|
+
},
|
|
987
|
+
skill: owner.skill,
|
|
988
|
+
});
|
|
989
|
+
for (const message of validation.errors)
|
|
990
|
+
error(`${manifestField}: ${message}`);
|
|
1669
991
|
}
|
|
1670
992
|
}
|
|
1671
993
|
function countCharacters(value) {
|
|
@@ -2058,33 +1380,6 @@ function validateDeclaredNativeAssets(pluginDir, manifest) {
|
|
|
2058
1380
|
}
|
|
2059
1381
|
}
|
|
2060
1382
|
}
|
|
2061
|
-
function validatePluginVersionContracts(manifest, options) {
|
|
2062
|
-
if (manifest.compatibility === undefined) {
|
|
2063
|
-
error('Invalid compatibility.aw.minVersion: expected a strict SemVer 2.0.0 string. Received undefined.');
|
|
2064
|
-
return;
|
|
2065
|
-
}
|
|
2066
|
-
const compatibility = expectRecord(manifest.compatibility, 'compatibility');
|
|
2067
|
-
if (!compatibility) {
|
|
2068
|
-
return;
|
|
2069
|
-
}
|
|
2070
|
-
validateAllowedKeys(compatibility, 'compatibility', ['aw']);
|
|
2071
|
-
if (compatibility.aw === undefined) {
|
|
2072
|
-
error('Invalid compatibility.aw.minVersion: expected a strict SemVer 2.0.0 string. Received undefined.');
|
|
2073
|
-
return;
|
|
2074
|
-
}
|
|
2075
|
-
const aw = expectRecord(compatibility.aw, 'compatibility.aw');
|
|
2076
|
-
if (!aw) {
|
|
2077
|
-
return;
|
|
2078
|
-
}
|
|
2079
|
-
validateAllowedKeys(aw, 'compatibility.aw', ['minVersion']);
|
|
2080
|
-
const pluginMinVersion = validateStrictSemVerString(aw.minVersion, 'compatibility.aw.minVersion');
|
|
2081
|
-
if (!pluginMinVersion || !options.catalogMinVersion) {
|
|
2082
|
-
return;
|
|
2083
|
-
}
|
|
2084
|
-
if (compareStrictSemVer(pluginMinVersion, options.catalogMinVersion) > 0) {
|
|
2085
|
-
error(`Invalid compatibility.aw.minVersion for plugin "${pluginNameForError(manifest)}": received ${JSON.stringify(pluginMinVersion)}, which is higher than target catalog minimum ${JSON.stringify(options.catalogMinVersion)}.`);
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
1383
|
function collectDeclaredSkillNames(pluginDir, manifest) {
|
|
2089
1384
|
const skillsRoot = typeof manifest.skills === 'string'
|
|
2090
1385
|
? path.join(pluginDir, manifest.skills)
|
|
@@ -2129,9 +1424,6 @@ function collectDeclaredSkillNames(pluginDir, manifest) {
|
|
|
2129
1424
|
function isNodeRunnablePath(relativePath) {
|
|
2130
1425
|
return SCRIPT_NODE_EXTENSIONS.has(path.posix.extname(normalizeRelativePath(relativePath)));
|
|
2131
1426
|
}
|
|
2132
|
-
function isAutoRunnablePath(relativePath) {
|
|
2133
|
-
return SCRIPT_AUTO_EXTENSIONS.has(path.posix.extname(normalizeRelativePath(relativePath)).toLowerCase());
|
|
2134
|
-
}
|
|
2135
1427
|
function normalizePluginScriptPath(pluginDir, value, fieldName) {
|
|
2136
1428
|
if (typeof value !== 'string' || value.trim() === '') {
|
|
2137
1429
|
error(`${fieldName} must be a non-empty string`);
|
|
@@ -2180,17 +1472,11 @@ function validateScriptEntry(pluginDir, value, fieldName, options = {}) {
|
|
|
2180
1472
|
}
|
|
2181
1473
|
validateAllowedKeys(entry, fieldName, ['runner', 'path', 'args']);
|
|
2182
1474
|
const runner = validateManifestString(entry.runner, `${fieldName}.runner`);
|
|
2183
|
-
if (runner
|
|
2184
|
-
error(`${fieldName}.runner has unsupported runner "${runner}"`);
|
|
2185
|
-
}
|
|
2186
|
-
if (options.requireNodeRunner && runner !== 'node') {
|
|
1475
|
+
if (runner !== 'node') {
|
|
2187
1476
|
error(`${fieldName}.runner must be "node"`);
|
|
2188
1477
|
}
|
|
2189
1478
|
const relativePath = normalizePluginScriptPath(pluginDir, entry.path, `${fieldName}.path`);
|
|
2190
1479
|
validateDeclaredScriptFile(pluginDir, relativePath, `${fieldName}.path`);
|
|
2191
|
-
if (runner === 'auto' && relativePath && !isAutoRunnablePath(relativePath)) {
|
|
2192
|
-
error(`${fieldName}.path uses runner "auto" but has no supported extension (${[...SCRIPT_AUTO_EXTENSIONS].join(', ')})`);
|
|
2193
|
-
}
|
|
2194
1480
|
if (options.requireNodePath && relativePath && !isNodeRunnablePath(relativePath)) {
|
|
2195
1481
|
error(`${fieldName}.path must use a Node runnable extension (.js, .mjs, or .cjs)`);
|
|
2196
1482
|
}
|
|
@@ -2208,51 +1494,6 @@ function validateScriptEntry(pluginDir, value, fieldName, options = {}) {
|
|
|
2208
1494
|
}
|
|
2209
1495
|
return runner && relativePath ? { runner, path: relativePath } : null;
|
|
2210
1496
|
}
|
|
2211
|
-
function validateLifecycleDefinition(pluginDir, value, fieldName, runtimeEntries) {
|
|
2212
|
-
if (typeof value === 'string') {
|
|
2213
|
-
const relativePath = normalizePluginScriptPath(pluginDir, value, fieldName);
|
|
2214
|
-
validateDeclaredScriptFile(pluginDir, relativePath, fieldName);
|
|
2215
|
-
if (relativePath && !isAutoRunnablePath(relativePath)) {
|
|
2216
|
-
error(`${fieldName} uses runner "auto" but has no supported extension (${[...SCRIPT_AUTO_EXTENSIONS].join(', ')})`);
|
|
2217
|
-
}
|
|
2218
|
-
if (relativePath && isNodeRunnablePath(relativePath)) {
|
|
2219
|
-
runtimeEntries.push({ path: relativePath, fieldName });
|
|
2220
|
-
}
|
|
2221
|
-
return { default: relativePath ? { runner: 'auto', path: relativePath } : null };
|
|
2222
|
-
}
|
|
2223
|
-
const selector = expectRecord(value, fieldName);
|
|
2224
|
-
if (!selector)
|
|
2225
|
-
return {};
|
|
2226
|
-
if ('runner' in selector || 'path' in selector || 'command' in selector) {
|
|
2227
|
-
error(`${fieldName} must declare lifecycle scripts under OS selectors`);
|
|
2228
|
-
}
|
|
2229
|
-
for (const selectorName of Object.keys(selector)) {
|
|
2230
|
-
if (!SCRIPT_OS_SELECTORS.has(selectorName)) {
|
|
2231
|
-
error(`${fieldName} has unknown OS selector "${selectorName}"`);
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
|
-
const normalized = {};
|
|
2235
|
-
for (const [selectorName, entryValue] of Object.entries(selector)) {
|
|
2236
|
-
const entry = validateScriptEntry(pluginDir, entryValue, `${fieldName}.${selectorName}`);
|
|
2237
|
-
if (entry) {
|
|
2238
|
-
normalized[selectorName] = entry;
|
|
2239
|
-
if (entry.runner === 'node' || (entry.runner === 'auto' && isNodeRunnablePath(entry.path))) {
|
|
2240
|
-
runtimeEntries.push({ path: entry.path, fieldName: `${fieldName}.${selectorName}.path` });
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
|
-
}
|
|
2244
|
-
const orders = {
|
|
2245
|
-
linux: ['linux', 'unix', 'default'],
|
|
2246
|
-
darwin: ['darwin', 'unix', 'default'],
|
|
2247
|
-
win32: ['win32', 'windows', 'default'],
|
|
2248
|
-
};
|
|
2249
|
-
for (const [osName, order] of Object.entries(orders)) {
|
|
2250
|
-
if (!order.some((selectorName) => normalized[selectorName])) {
|
|
2251
|
-
error(`${fieldName} must include an entry that resolves on ${osName}`);
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
return normalized;
|
|
2255
|
-
}
|
|
2256
1497
|
function isNonRegistryDependencySpecifier(specifier) {
|
|
2257
1498
|
const trimmed = String(specifier).trim();
|
|
2258
1499
|
return (/^(?:workspace|file|link|portal):/i.test(trimmed) ||
|
|
@@ -2390,6 +1631,10 @@ function scanNodeImportContract(pluginDir, entryPaths, options) {
|
|
|
2390
1631
|
continue;
|
|
2391
1632
|
}
|
|
2392
1633
|
if (!resolved.path) {
|
|
1634
|
+
const requested = path.posix.normalize(path.posix.join(path.posix.dirname(relativePath), specifier));
|
|
1635
|
+
if ((options.runtimeConfigSkillRoots ?? []).some((root) => (relativePath.startsWith(`${root}/`)
|
|
1636
|
+
&& requested === path.posix.join(root, AW_RUNTIME_CONFIG_READER_PATH))))
|
|
1637
|
+
continue;
|
|
2393
1638
|
error(`${options.label}: unable to resolve relative import "${specifier}" from "${relativePath}"`);
|
|
2394
1639
|
continue;
|
|
2395
1640
|
}
|
|
@@ -2439,271 +1684,61 @@ function listFilesRecursive(rootDir) {
|
|
|
2439
1684
|
walk(rootDir);
|
|
2440
1685
|
return output;
|
|
2441
1686
|
}
|
|
2442
|
-
function validateConfigValueLightweight(field, value, fieldName) {
|
|
2443
|
-
if (!isRecord(field) || !CONFIG_FIELD_TYPES.has(field.type))
|
|
2444
|
-
return;
|
|
2445
|
-
if (field.type === 'string' || field.type === 'file' || field.type === 'directory') {
|
|
2446
|
-
validateManifestString(value, fieldName);
|
|
2447
|
-
return;
|
|
2448
|
-
}
|
|
2449
|
-
if (field.type === 'boolean') {
|
|
2450
|
-
if (typeof value !== 'boolean') {
|
|
2451
|
-
error(`${fieldName} must be a boolean`);
|
|
2452
|
-
}
|
|
2453
|
-
return;
|
|
2454
|
-
}
|
|
2455
|
-
if (field.type === 'number') {
|
|
2456
|
-
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
2457
|
-
error(`${fieldName} must be a finite number`);
|
|
2458
|
-
return;
|
|
2459
|
-
}
|
|
2460
|
-
if (typeof field.min === 'number' && value < field.min) {
|
|
2461
|
-
error(`${fieldName} must be greater than or equal to ${field.min}`);
|
|
2462
|
-
}
|
|
2463
|
-
if (typeof field.max === 'number' && value > field.max) {
|
|
2464
|
-
error(`${fieldName} must be less than or equal to ${field.max}`);
|
|
2465
|
-
}
|
|
2466
|
-
return;
|
|
2467
|
-
}
|
|
2468
|
-
if (field.type === 'enum') {
|
|
2469
|
-
if (typeof value !== 'string' || !Array.isArray(field.values) || !field.values.includes(value)) {
|
|
2470
|
-
error(`${fieldName} must be one of config schema values`);
|
|
2471
|
-
}
|
|
2472
|
-
}
|
|
2473
|
-
}
|
|
2474
|
-
function validateConfigPresentationLightweight(presentation, schema) {
|
|
2475
|
-
if (presentation === undefined)
|
|
2476
|
-
return;
|
|
2477
|
-
const root = expectRecord(presentation, 'config.presentation');
|
|
2478
|
-
if (!root)
|
|
2479
|
-
return;
|
|
2480
|
-
validateAllowedKeys(root, 'config.presentation', ['groups', 'fields', 'presets']);
|
|
2481
|
-
const schemaKeys = new Set(Object.keys(schema));
|
|
2482
|
-
if (root.groups !== undefined) {
|
|
2483
|
-
if (!Array.isArray(root.groups)) {
|
|
2484
|
-
error('config.presentation.groups must be an array');
|
|
2485
|
-
}
|
|
2486
|
-
else {
|
|
2487
|
-
const groupIds = new Set();
|
|
2488
|
-
const groupedFields = new Set();
|
|
2489
|
-
root.groups.forEach((rawGroup, index) => {
|
|
2490
|
-
const fieldName = `config.presentation.groups[${index}]`;
|
|
2491
|
-
const group = expectRecord(rawGroup, fieldName);
|
|
2492
|
-
if (!group)
|
|
2493
|
-
return;
|
|
2494
|
-
validateAllowedKeys(group, fieldName, ['id', 'title', 'description', 'fields', 'advanced']);
|
|
2495
|
-
const validId = validateManifestString(group.id, `${fieldName}.id`);
|
|
2496
|
-
const id = validId ? group.id : null;
|
|
2497
|
-
if (id && groupIds.has(id)) {
|
|
2498
|
-
error(`${fieldName}.id has duplicate group id "${id}"`);
|
|
2499
|
-
}
|
|
2500
|
-
else if (id) {
|
|
2501
|
-
groupIds.add(id);
|
|
2502
|
-
}
|
|
2503
|
-
validateManifestString(group.title, `${fieldName}.title`);
|
|
2504
|
-
if (group.description !== undefined) {
|
|
2505
|
-
validateManifestString(group.description, `${fieldName}.description`);
|
|
2506
|
-
}
|
|
2507
|
-
if (group.advanced !== undefined && typeof group.advanced !== 'boolean') {
|
|
2508
|
-
error(`${fieldName}.advanced must be a boolean`);
|
|
2509
|
-
}
|
|
2510
|
-
if (!Array.isArray(group.fields)) {
|
|
2511
|
-
error(`${fieldName}.fields must be an array`);
|
|
2512
|
-
return;
|
|
2513
|
-
}
|
|
2514
|
-
group.fields.forEach((key, fieldIndex) => {
|
|
2515
|
-
const keyFieldName = `${fieldName}.fields[${fieldIndex}]`;
|
|
2516
|
-
const validKey = validateManifestString(key, keyFieldName);
|
|
2517
|
-
if (!validKey)
|
|
2518
|
-
return;
|
|
2519
|
-
if (!schemaKeys.has(key)) {
|
|
2520
|
-
error(`${keyFieldName} references unknown config key "${key}"`);
|
|
2521
|
-
}
|
|
2522
|
-
if (groupedFields.has(key)) {
|
|
2523
|
-
error(`${keyFieldName} assigns config key "${key}" to more than one group`);
|
|
2524
|
-
}
|
|
2525
|
-
else {
|
|
2526
|
-
groupedFields.add(key);
|
|
2527
|
-
}
|
|
2528
|
-
});
|
|
2529
|
-
});
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
if (root.fields !== undefined) {
|
|
2533
|
-
const fields = expectRecord(root.fields, 'config.presentation.fields');
|
|
2534
|
-
if (fields) {
|
|
2535
|
-
for (const [key, rawField] of Object.entries(fields)) {
|
|
2536
|
-
const fieldName = `config.presentation.fields.${key}`;
|
|
2537
|
-
const schemaField = schema[key];
|
|
2538
|
-
if (!schemaKeys.has(key)) {
|
|
2539
|
-
error(`config.presentation.fields references unknown config key "${key}"`);
|
|
2540
|
-
}
|
|
2541
|
-
const field = expectRecord(rawField, fieldName);
|
|
2542
|
-
if (!field)
|
|
2543
|
-
continue;
|
|
2544
|
-
validateAllowedKeys(field, fieldName, [
|
|
2545
|
-
'valueLabels',
|
|
2546
|
-
'recommendedValue',
|
|
2547
|
-
'placeholder',
|
|
2548
|
-
'unit',
|
|
2549
|
-
'advanced',
|
|
2550
|
-
]);
|
|
2551
|
-
if (field.placeholder !== undefined) {
|
|
2552
|
-
validateManifestString(field.placeholder, `${fieldName}.placeholder`);
|
|
2553
|
-
}
|
|
2554
|
-
if (field.unit !== undefined) {
|
|
2555
|
-
validateManifestString(field.unit, `${fieldName}.unit`);
|
|
2556
|
-
}
|
|
2557
|
-
if (field.advanced !== undefined && typeof field.advanced !== 'boolean') {
|
|
2558
|
-
error(`${fieldName}.advanced must be a boolean`);
|
|
2559
|
-
}
|
|
2560
|
-
if (field.valueLabels !== undefined) {
|
|
2561
|
-
const labels = expectRecord(field.valueLabels, `${fieldName}.valueLabels`);
|
|
2562
|
-
if (isRecord(schemaField) && schemaField.type !== 'enum') {
|
|
2563
|
-
error(`${fieldName}.valueLabels is only supported for enum config`);
|
|
2564
|
-
}
|
|
2565
|
-
if (labels) {
|
|
2566
|
-
for (const [token, label] of Object.entries(labels)) {
|
|
2567
|
-
if (!isRecord(schemaField) || !Array.isArray(schemaField.values) || !schemaField.values.includes(token)) {
|
|
2568
|
-
error(`${fieldName}.valueLabels references unknown enum token "${token}"`);
|
|
2569
|
-
}
|
|
2570
|
-
validateManifestString(label, `${fieldName}.valueLabels.${token}`);
|
|
2571
|
-
}
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
if (field.recommendedValue !== undefined) {
|
|
2575
|
-
validateConfigValueLightweight(schemaField, field.recommendedValue, `${fieldName}.recommendedValue`);
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
if (root.presets !== undefined) {
|
|
2581
|
-
if (!Array.isArray(root.presets)) {
|
|
2582
|
-
error('config.presentation.presets must be an array');
|
|
2583
|
-
}
|
|
2584
|
-
else {
|
|
2585
|
-
const presetIds = new Set();
|
|
2586
|
-
root.presets.forEach((rawPreset, index) => {
|
|
2587
|
-
const fieldName = `config.presentation.presets[${index}]`;
|
|
2588
|
-
const preset = expectRecord(rawPreset, fieldName);
|
|
2589
|
-
if (!preset)
|
|
2590
|
-
return;
|
|
2591
|
-
validateAllowedKeys(preset, fieldName, ['id', 'title', 'description', 'values']);
|
|
2592
|
-
const validId = validateManifestString(preset.id, `${fieldName}.id`);
|
|
2593
|
-
const id = validId ? preset.id : null;
|
|
2594
|
-
if (id && presetIds.has(id)) {
|
|
2595
|
-
error(`${fieldName}.id has duplicate preset id "${id}"`);
|
|
2596
|
-
}
|
|
2597
|
-
else if (id) {
|
|
2598
|
-
presetIds.add(id);
|
|
2599
|
-
}
|
|
2600
|
-
validateManifestString(preset.title, `${fieldName}.title`);
|
|
2601
|
-
if (preset.description !== undefined) {
|
|
2602
|
-
validateManifestString(preset.description, `${fieldName}.description`);
|
|
2603
|
-
}
|
|
2604
|
-
const values = expectRecord(preset.values, `${fieldName}.values`);
|
|
2605
|
-
if (!values)
|
|
2606
|
-
return;
|
|
2607
|
-
for (const [key, value] of Object.entries(values)) {
|
|
2608
|
-
if (!schemaKeys.has(key)) {
|
|
2609
|
-
error(`${fieldName}.values references unknown config key "${key}"`);
|
|
2610
|
-
continue;
|
|
2611
|
-
}
|
|
2612
|
-
const schemaField = schema[key];
|
|
2613
|
-
if (isRecord(schemaField)
|
|
2614
|
-
&& (schemaField.sensitive === true
|
|
2615
|
-
|| schemaField.type === 'file'
|
|
2616
|
-
|| schemaField.type === 'directory')) {
|
|
2617
|
-
error(`${fieldName}.values.${key} cannot contain sensitive, file, or directory config values`);
|
|
2618
|
-
continue;
|
|
2619
|
-
}
|
|
2620
|
-
validateConfigValueLightweight(schemaField, value, `${fieldName}.values.${key}`);
|
|
2621
|
-
}
|
|
2622
|
-
});
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
}
|
|
2626
1687
|
function validateConfigSchemaLightweight(config) {
|
|
2627
1688
|
if (config === undefined)
|
|
2628
1689
|
return;
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
if (!schema)
|
|
2635
|
-
return;
|
|
2636
|
-
for (const [key, rawField] of Object.entries(schema)) {
|
|
2637
|
-
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(key)) {
|
|
2638
|
-
error(`config.schema has invalid key "${key}"`);
|
|
2639
|
-
}
|
|
2640
|
-
const fieldName = `config.schema.${key}`;
|
|
2641
|
-
const field = expectRecord(rawField, fieldName);
|
|
2642
|
-
if (!field)
|
|
2643
|
-
continue;
|
|
2644
|
-
validateAllowedKeys(field, fieldName, [
|
|
2645
|
-
'type',
|
|
2646
|
-
'title',
|
|
2647
|
-
'description',
|
|
2648
|
-
'default',
|
|
2649
|
-
'required',
|
|
2650
|
-
'sensitive',
|
|
2651
|
-
'values',
|
|
2652
|
-
'min',
|
|
2653
|
-
'max',
|
|
2654
|
-
]);
|
|
2655
|
-
if (typeof field.type !== 'string' || !CONFIG_FIELD_TYPES.has(field.type)) {
|
|
2656
|
-
error(`${fieldName}.type must be string, number, boolean, enum, file, or directory`);
|
|
2657
|
-
}
|
|
2658
|
-
if (field.required !== undefined && typeof field.required !== 'boolean') {
|
|
2659
|
-
error(`${fieldName}.required must be a boolean`);
|
|
2660
|
-
}
|
|
2661
|
-
if (field.sensitive !== undefined && typeof field.sensitive !== 'boolean') {
|
|
2662
|
-
error(`${fieldName}.sensitive must be a boolean`);
|
|
2663
|
-
}
|
|
2664
|
-
if (field.values !== undefined) {
|
|
2665
|
-
if (field.type !== 'enum' || !Array.isArray(field.values) || field.values.length === 0) {
|
|
2666
|
-
error(`${fieldName}.values is only supported for enum config and must be non-empty`);
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
else if (field.type === 'enum') {
|
|
2670
|
-
error(`${fieldName}.values is required for enum config`);
|
|
2671
|
-
}
|
|
2672
|
-
if (field.type !== 'number' && (field.min !== undefined || field.max !== undefined)) {
|
|
2673
|
-
error(`${fieldName}.min/max are only supported for number config`);
|
|
2674
|
-
}
|
|
2675
|
-
if (field.min !== undefined && typeof field.min !== 'number') {
|
|
2676
|
-
error(`${fieldName}.min must be a number`);
|
|
2677
|
-
}
|
|
2678
|
-
if (field.max !== undefined && typeof field.max !== 'number') {
|
|
2679
|
-
error(`${fieldName}.max must be a number`);
|
|
2680
|
-
}
|
|
2681
|
-
if (typeof field.min === 'number' && typeof field.max === 'number' && field.min > field.max) {
|
|
2682
|
-
error(`${fieldName}.min must be less than or equal to max`);
|
|
2683
|
-
}
|
|
1690
|
+
try {
|
|
1691
|
+
normalizePluginConfigDefinition(config);
|
|
1692
|
+
}
|
|
1693
|
+
catch (validationError) {
|
|
1694
|
+
error(validationError instanceof Error ? validationError.message : String(validationError));
|
|
2684
1695
|
}
|
|
2685
|
-
validateConfigPresentationLightweight(configRoot.presentation, schema);
|
|
2686
1696
|
}
|
|
2687
1697
|
function configSchemaKeys(manifest) {
|
|
2688
|
-
const schema = manifest.config?.
|
|
1698
|
+
const schema = manifest.config?.fields;
|
|
2689
1699
|
return isRecord(schema) ? new Set(Object.keys(schema)) : new Set();
|
|
2690
1700
|
}
|
|
2691
|
-
function configSchemaField(manifest, key) {
|
|
2692
|
-
const field = manifest.config?.schema?.[key];
|
|
2693
|
-
return isRecord(field) ? field : null;
|
|
2694
|
-
}
|
|
2695
1701
|
function defaultConfigValues(manifest) {
|
|
2696
|
-
const schema = manifest.config?.
|
|
1702
|
+
const schema = manifest.config?.fields;
|
|
2697
1703
|
const values = {};
|
|
2698
1704
|
if (!isRecord(schema))
|
|
2699
1705
|
return values;
|
|
2700
1706
|
for (const [key, field] of Object.entries(schema)) {
|
|
2701
|
-
if (isRecord(field) &&
|
|
1707
|
+
if (isRecord(field) && field.default !== undefined) {
|
|
2702
1708
|
values[key] = field.default;
|
|
2703
1709
|
}
|
|
2704
1710
|
}
|
|
2705
1711
|
return values;
|
|
2706
1712
|
}
|
|
1713
|
+
function configFieldContainsSensitiveValue(field) {
|
|
1714
|
+
if (!isRecord(field))
|
|
1715
|
+
return false;
|
|
1716
|
+
if (field.sensitive === true)
|
|
1717
|
+
return true;
|
|
1718
|
+
if (field.type !== 'object')
|
|
1719
|
+
return false;
|
|
1720
|
+
return ((isRecord(field.properties)
|
|
1721
|
+
&& Object.values(field.properties).some((child) => isRecord(child) && child.sensitive === true))
|
|
1722
|
+
|| (isRecord(field.additionalProperties)
|
|
1723
|
+
&& field.additionalProperties.sensitive === true));
|
|
1724
|
+
}
|
|
1725
|
+
function codexStaticNativeComponentKeys(manifest) {
|
|
1726
|
+
if (!isCodexTargeted(manifest))
|
|
1727
|
+
return [];
|
|
1728
|
+
const keys = [];
|
|
1729
|
+
if (manifest.skills)
|
|
1730
|
+
keys.push('skills');
|
|
1731
|
+
if (manifest.mcpServers)
|
|
1732
|
+
keys.push('mcpServers');
|
|
1733
|
+
if ((Array.isArray(manifest.extensions?.hooks)
|
|
1734
|
+
&& manifest.extensions.hooks.some((hook) => hook?.agentOverrides?.codex !== null))
|
|
1735
|
+
|| (Array.isArray(manifest.extensions?.nativeHooks?.codex)
|
|
1736
|
+
&& manifest.extensions.nativeHooks.codex.length > 0))
|
|
1737
|
+
keys.push('hooks');
|
|
1738
|
+
if (manifest.apps)
|
|
1739
|
+
keys.push('apps');
|
|
1740
|
+
return keys;
|
|
1741
|
+
}
|
|
2707
1742
|
function validateArtifactId(value, fieldName) {
|
|
2708
1743
|
const id = validateManifestString(value, fieldName);
|
|
2709
1744
|
if (!id)
|
|
@@ -2764,7 +1799,7 @@ function validateStringArray(value, fieldName, options = {}) {
|
|
|
2764
1799
|
}
|
|
2765
1800
|
return output;
|
|
2766
1801
|
}
|
|
2767
|
-
function validateArtifactTargetEntry(entry, index, manifest,
|
|
1802
|
+
function validateArtifactTargetEntry(entry, index, manifest, pluginDir) {
|
|
2768
1803
|
const fieldName = `artifacts.targets[${index}]`;
|
|
2769
1804
|
const target = expectRecord(entry, fieldName);
|
|
2770
1805
|
if (!target)
|
|
@@ -2779,11 +1814,13 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2779
1814
|
'projectManagedKind',
|
|
2780
1815
|
'states',
|
|
2781
1816
|
'refreshKeys',
|
|
1817
|
+
'delivery',
|
|
2782
1818
|
'requiresCodexProjectConfig',
|
|
2783
1819
|
'roleKey',
|
|
2784
1820
|
'skillKey',
|
|
2785
1821
|
'sourceDirs',
|
|
2786
1822
|
'runtimeImports',
|
|
1823
|
+
'runtimeConfig',
|
|
2787
1824
|
]);
|
|
2788
1825
|
const id = validateArtifactId(target.id, `${fieldName}.id`);
|
|
2789
1826
|
const artifactTarget = validateManifestString(target.target, `${fieldName}.target`);
|
|
@@ -2791,6 +1828,30 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2791
1828
|
error(`${fieldName}.target: unsupported artifact target "${artifactTarget}"`);
|
|
2792
1829
|
}
|
|
2793
1830
|
const portable = artifactTarget === 'agent-skills.skill';
|
|
1831
|
+
let delivery;
|
|
1832
|
+
if (artifactTarget === 'codex.skill') {
|
|
1833
|
+
delivery = expectRecord(target.delivery, `${fieldName}.delivery`);
|
|
1834
|
+
if (delivery) {
|
|
1835
|
+
if (delivery.mode === 'aw-extension') {
|
|
1836
|
+
validateAllowedKeys(delivery, `${fieldName}.delivery`, ['mode']);
|
|
1837
|
+
}
|
|
1838
|
+
else if (delivery.mode === 'codex-native') {
|
|
1839
|
+
validateAllowedKeys(delivery, `${fieldName}.delivery`, ['mode', 'identity', 'renderProfile']);
|
|
1840
|
+
if (delivery.identity !== 'shared') {
|
|
1841
|
+
error(`${fieldName}.delivery.identity: expected "shared"`);
|
|
1842
|
+
}
|
|
1843
|
+
if (delivery.renderProfile !== 'user') {
|
|
1844
|
+
error(`${fieldName}.delivery.renderProfile: expected "user"`);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
else {
|
|
1848
|
+
error(`${fieldName}.delivery.mode: expected "codex-native" or "aw-extension"`);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
else if (target.delivery !== undefined) {
|
|
1853
|
+
error(`${fieldName}.delivery: delivery is only supported for codex.skill targets`);
|
|
1854
|
+
}
|
|
2794
1855
|
const platform = portable || target.platform === undefined
|
|
2795
1856
|
? undefined
|
|
2796
1857
|
: validateManifestString(target.platform, `${fieldName}.platform`);
|
|
@@ -2814,13 +1875,6 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2814
1875
|
error(`${fieldName}.distribution: platform targets do not support distribution`);
|
|
2815
1876
|
}
|
|
2816
1877
|
}
|
|
2817
|
-
const key = id && artifactTarget ? `${id}:${artifactTarget}` : null;
|
|
2818
|
-
if (key) {
|
|
2819
|
-
if (seen.has(key)) {
|
|
2820
|
-
error(`${fieldName}: duplicate artifact id + target "${key}"`);
|
|
2821
|
-
}
|
|
2822
|
-
seen.add(key);
|
|
2823
|
-
}
|
|
2824
1878
|
const expectedKind = ARTIFACT_KIND_BY_TARGET[artifactTarget];
|
|
2825
1879
|
const kind = target.kind === undefined
|
|
2826
1880
|
? expectedKind
|
|
@@ -2863,10 +1917,15 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2863
1917
|
error(`${fieldName}.refreshKeys[${refreshIndex}]: unknown config key "${refreshKey}"`);
|
|
2864
1918
|
continue;
|
|
2865
1919
|
}
|
|
2866
|
-
const
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
1920
|
+
const sensitiveRefreshKey = configFieldContainsSensitiveValue(manifest.config?.fields?.[refreshKey]);
|
|
1921
|
+
const skillTarget = artifactTarget === 'codex.skill'
|
|
1922
|
+
|| artifactTarget === 'claude.skill'
|
|
1923
|
+
|| artifactTarget === 'agent-skills.skill';
|
|
1924
|
+
if (skillTarget && sensitiveRefreshKey) {
|
|
1925
|
+
const native = delivery?.mode === 'codex-native';
|
|
1926
|
+
error(`${native ? 'codexNativeRenderSensitiveRefreshKey' : 'skillRenderSensitiveRefreshKey'}: ` +
|
|
1927
|
+
`${fieldName}.refreshKeys[${refreshIndex}]: ` +
|
|
1928
|
+
`${native ? 'codex-native renderers' : 'Skill renderers'} cannot read sensitive config field "${refreshKey}"`);
|
|
2870
1929
|
}
|
|
2871
1930
|
}
|
|
2872
1931
|
if (target.requiresCodexProjectConfig !== undefined &&
|
|
@@ -2955,9 +2014,11 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2955
2014
|
projectManagedKind,
|
|
2956
2015
|
states,
|
|
2957
2016
|
refreshKeys,
|
|
2017
|
+
delivery,
|
|
2958
2018
|
roleKey,
|
|
2959
2019
|
skillKey,
|
|
2960
2020
|
sourceDirs,
|
|
2021
|
+
runtimeConfig: target.runtimeConfig,
|
|
2961
2022
|
runtimeImports: Array.isArray(target.runtimeImports) ? target.runtimeImports : [],
|
|
2962
2023
|
};
|
|
2963
2024
|
}
|
|
@@ -2974,10 +2035,69 @@ function validateArtifactsConfigLightweight(pluginDir, manifest) {
|
|
|
2974
2035
|
error('artifacts.targets must be an array');
|
|
2975
2036
|
return [];
|
|
2976
2037
|
}
|
|
2977
|
-
const seen = new Set();
|
|
2978
2038
|
const declarations = artifacts.targets
|
|
2979
|
-
.map((entry, index) => validateArtifactTargetEntry(entry, index, manifest,
|
|
2039
|
+
.map((entry, index) => validateArtifactTargetEntry(entry, index, manifest, pluginDir))
|
|
2980
2040
|
.filter(Boolean);
|
|
2041
|
+
const idsBySurfaceScope = new Map();
|
|
2042
|
+
const nativeIds = new Map();
|
|
2043
|
+
const nativeSkillKeys = new Map();
|
|
2044
|
+
for (const [declarationIndex, declaration] of declarations.entries()) {
|
|
2045
|
+
for (const scope of declaration.scopes) {
|
|
2046
|
+
const surface = declaration.platform ?? declaration.distribution;
|
|
2047
|
+
const key = `${surface}:${scope}:${declaration.id}`;
|
|
2048
|
+
const existing = idsBySurfaceScope.get(key);
|
|
2049
|
+
if (existing !== undefined) {
|
|
2050
|
+
error(`artifacts.targets has duplicate artifact id "${declaration.id}" for surface/scope "${surface}:${scope}" in targets[${existing}] and targets[${declarationIndex}]`);
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
idsBySurfaceScope.set(key, declarationIndex);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
if (declaration.delivery?.mode === 'codex-native') {
|
|
2057
|
+
const existingId = nativeIds.get(declaration.id);
|
|
2058
|
+
if (existingId !== undefined) {
|
|
2059
|
+
error(`artifacts.targets has duplicate codex-native artifact id "${declaration.id}" in the shared projection at targets[${existingId}] and targets[${declarationIndex}]`);
|
|
2060
|
+
}
|
|
2061
|
+
else {
|
|
2062
|
+
nativeIds.set(declaration.id, declarationIndex);
|
|
2063
|
+
}
|
|
2064
|
+
const existingSkillKey = nativeSkillKeys.get(declaration.skillKey);
|
|
2065
|
+
if (existingSkillKey !== undefined) {
|
|
2066
|
+
error(`artifacts.targets has duplicate codex-native skillKey "${declaration.skillKey}" in the shared projection at targets[${existingSkillKey}] and targets[${declarationIndex}]`);
|
|
2067
|
+
}
|
|
2068
|
+
else {
|
|
2069
|
+
nativeSkillKeys.set(declaration.skillKey, declarationIndex);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
const nativeComponents = codexStaticNativeComponentKeys(manifest).map((key) => ({
|
|
2074
|
+
key,
|
|
2075
|
+
scopes: ['project', 'user'],
|
|
2076
|
+
}));
|
|
2077
|
+
declarations.forEach((declaration, index) => {
|
|
2078
|
+
if (declaration.delivery?.mode === 'codex-native') {
|
|
2079
|
+
nativeComponents.push({ key: `artifacts.targets[${index}]`, scopes: declaration.scopes });
|
|
2080
|
+
}
|
|
2081
|
+
});
|
|
2082
|
+
const expectedNativeComponent = nativeComponents[0];
|
|
2083
|
+
if (expectedNativeComponent) {
|
|
2084
|
+
const canonicalScopes = (component) => [...component.scopes].sort().join(',');
|
|
2085
|
+
const expectedScopes = canonicalScopes(expectedNativeComponent);
|
|
2086
|
+
const mismatch = nativeComponents.find((component) => canonicalScopes(component) !== expectedScopes);
|
|
2087
|
+
if (mismatch) {
|
|
2088
|
+
error(`codexSharedNativeScopeMismatch: Codex native component scope sets must match exactly; ` +
|
|
2089
|
+
`${expectedNativeComponent.key} has {${expectedScopes}} but ${mismatch.key} has ` +
|
|
2090
|
+
`{${canonicalScopes(mismatch)}}`);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
const staticSkillNames = new Set(collectDeclaredSkillNames(pluginDir, manifest).static);
|
|
2094
|
+
for (const [index, declaration] of declarations.entries()) {
|
|
2095
|
+
if (declaration.target === 'codex.skill'
|
|
2096
|
+
&& declaration.delivery?.mode === 'codex-native'
|
|
2097
|
+
&& staticSkillNames.has(declaration.skillKey)) {
|
|
2098
|
+
error(`artifacts.targets[${index}].skillKey: native dynamic Skill "${declaration.skillKey}" conflicts with a static Skill of the same name`);
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2981
2101
|
const owners = new Map();
|
|
2982
2102
|
for (const declaration of declarations) {
|
|
2983
2103
|
if ((declaration.target !== 'codex.skill' && declaration.target !== 'claude.skill' && declaration.target !== 'agent-skills.skill') ||
|
|
@@ -3244,20 +2364,11 @@ function validateRenderedSkillTree(options) {
|
|
|
3244
2364
|
if (!readme || readme.toString('utf8').trim() === '') {
|
|
3245
2365
|
error(`${fieldName} final file tree must contain a non-empty README.md`);
|
|
3246
2366
|
}
|
|
3247
|
-
const sensitiveValues = Object.entries(manifest.config?.schema ?? {})
|
|
3248
|
-
.filter(([, field]) => isRecord(field) && field.sensitive === true)
|
|
3249
|
-
.map(([key, field]) => Object.prototype.hasOwnProperty.call(field, 'default')
|
|
3250
|
-
? String(field.default)
|
|
3251
|
-
: String(defaultConfigValues(manifest)[key] ?? ''))
|
|
3252
|
-
.filter(Boolean);
|
|
3253
2367
|
for (const [relativePath, content] of files) {
|
|
3254
2368
|
const text = content.toString('utf8');
|
|
3255
2369
|
if (/\{\{[^{}]+\}\}/u.test(text)) {
|
|
3256
2370
|
error(`${fieldName} final file tree contains an unresolved template in "${relativePath}"`);
|
|
3257
2371
|
}
|
|
3258
|
-
if (sensitiveValues.some((value) => text.includes(value))) {
|
|
3259
|
-
error(`${fieldName} final file tree exposes sensitive config in "${relativePath}"`);
|
|
3260
|
-
}
|
|
3261
2372
|
}
|
|
3262
2373
|
if (declaration.target === 'codex.skill') {
|
|
3263
2374
|
const openAiYaml = files.get('agents/openai.yaml');
|
|
@@ -3453,26 +2564,83 @@ function validateRenderedArtifactPlan(plan, declarations, scopeLabel, options) {
|
|
|
3453
2564
|
}
|
|
3454
2565
|
}
|
|
3455
2566
|
}
|
|
2567
|
+
function resolveArtifactRendererFixtureRequests(declarations, manifest) {
|
|
2568
|
+
const awOwned = new Map();
|
|
2569
|
+
const codexNative = new Map();
|
|
2570
|
+
for (const declaration of declarations) {
|
|
2571
|
+
if (declaration.delivery?.mode === 'codex-native') {
|
|
2572
|
+
const scope = declaration.scopes.includes('user') ? 'user' : 'project';
|
|
2573
|
+
const request = codexNative.get(scope) ?? {
|
|
2574
|
+
kind: 'codex-native',
|
|
2575
|
+
surface: 'codex',
|
|
2576
|
+
scope,
|
|
2577
|
+
configScope: 'user',
|
|
2578
|
+
declarations: [],
|
|
2579
|
+
};
|
|
2580
|
+
request.declarations.push(declaration);
|
|
2581
|
+
codexNative.set(scope, request);
|
|
2582
|
+
continue;
|
|
2583
|
+
}
|
|
2584
|
+
const surface = declaration.platform ?? declaration.distribution;
|
|
2585
|
+
if (!surface)
|
|
2586
|
+
continue;
|
|
2587
|
+
for (const scope of declaration.scopes) {
|
|
2588
|
+
const key = `${surface}:${scope}`;
|
|
2589
|
+
const request = awOwned.get(key) ?? {
|
|
2590
|
+
kind: 'aw-owned',
|
|
2591
|
+
surface,
|
|
2592
|
+
scope,
|
|
2593
|
+
configScope: scope,
|
|
2594
|
+
declarations: [],
|
|
2595
|
+
};
|
|
2596
|
+
request.declarations.push(declaration);
|
|
2597
|
+
awOwned.set(key, request);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
const partitionedAwOwned = [...awOwned.values()].flatMap((request) => {
|
|
2601
|
+
const hasSkill = request.declarations.some((declaration) => declaration.target === 'codex.skill'
|
|
2602
|
+
|| declaration.target === 'claude.skill'
|
|
2603
|
+
|| declaration.target === 'agent-skills.skill');
|
|
2604
|
+
const hasSensitiveNonSkill = request.declarations.some((declaration) => declaration.target !== 'codex.skill'
|
|
2605
|
+
&& declaration.target !== 'claude.skill'
|
|
2606
|
+
&& declaration.target !== 'agent-skills.skill'
|
|
2607
|
+
&& declaration.refreshKeys.some((key) => configFieldContainsSensitiveValue(manifest.config?.fields?.[key])));
|
|
2608
|
+
if (!hasSkill || !hasSensitiveNonSkill)
|
|
2609
|
+
return [request];
|
|
2610
|
+
const skillDeclarations = request.declarations.filter((declaration) => declaration.target === 'codex.skill'
|
|
2611
|
+
|| declaration.target === 'claude.skill'
|
|
2612
|
+
|| declaration.target === 'agent-skills.skill');
|
|
2613
|
+
const nonSkillDeclarations = request.declarations.filter((declaration) => !skillDeclarations.includes(declaration));
|
|
2614
|
+
return [
|
|
2615
|
+
{ ...request, declarations: skillDeclarations },
|
|
2616
|
+
{ ...request, declarations: nonSkillDeclarations },
|
|
2617
|
+
];
|
|
2618
|
+
});
|
|
2619
|
+
return [...partitionedAwOwned, ...codexNative.values()];
|
|
2620
|
+
}
|
|
3456
2621
|
function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declarations, resolvedRuntimeImports, skillValidationTools) {
|
|
3457
2622
|
if (!renderEntry || declarations.length === 0)
|
|
3458
2623
|
return;
|
|
3459
|
-
const
|
|
2624
|
+
const requests = resolveArtifactRendererFixtureRequests(declarations, manifest);
|
|
3460
2625
|
const values = defaultConfigValues(manifest);
|
|
3461
2626
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'aw-artifact-render-'));
|
|
3462
2627
|
try {
|
|
3463
|
-
for (const
|
|
3464
|
-
const
|
|
2628
|
+
for (const [requestIndex, request] of requests.entries()) {
|
|
2629
|
+
const { kind, surface, scope, configScope, declarations: scopedDeclarations, } = request;
|
|
3465
2630
|
const distribution = surface === 'agent-skills' ? surface : undefined;
|
|
3466
2631
|
const platform = distribution ? undefined : surface;
|
|
3467
2632
|
const event = distribution ? 'materialize' : 'install';
|
|
3468
|
-
const
|
|
3469
|
-
|
|
3470
|
-
const
|
|
2633
|
+
const requestRoot = path.join(tempRoot, `${requestIndex}-${kind}-${surface}-${scope}`);
|
|
2634
|
+
const rendererAwHome = path.join(requestRoot, 'aw-home');
|
|
2635
|
+
const rendererCodexHome = path.join(requestRoot, 'codex-home');
|
|
2636
|
+
const rendererClaudeHome = path.join(requestRoot, 'claude-home');
|
|
2637
|
+
fs.mkdirSync(requestRoot, { recursive: true });
|
|
2638
|
+
const contextPath = path.join(requestRoot, 'context.json');
|
|
3471
2639
|
const runtimeImports = scopedDeclarations.flatMap((target) => target.runtimeImports.map((runtimeImport, importIndex) => {
|
|
3472
2640
|
const resolved = resolvedRuntimeImports?.artifactImports.get(authorRuntimeImportKey(target.id, importIndex));
|
|
3473
2641
|
if (!resolved)
|
|
3474
2642
|
return null;
|
|
3475
|
-
const importPath = path.join(
|
|
2643
|
+
const importPath = path.join(requestRoot, `runtime-${target.id}-${importIndex}`);
|
|
3476
2644
|
fs.writeFileSync(importPath, resolved.bytes);
|
|
3477
2645
|
return {
|
|
3478
2646
|
artifactId: target.id,
|
|
@@ -3483,6 +2651,37 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3483
2651
|
digest: resolved.digest,
|
|
3484
2652
|
};
|
|
3485
2653
|
}).filter(Boolean));
|
|
2654
|
+
const refreshKeys = [...new Set(scopedDeclarations.flatMap((target) => target.refreshKeys))];
|
|
2655
|
+
const scopedSchema = Object.fromEntries(refreshKeys.flatMap((key) => manifest.config?.fields?.[key] === undefined
|
|
2656
|
+
? []
|
|
2657
|
+
: [[key, manifest.config.fields[key]]]));
|
|
2658
|
+
const scopedValues = Object.fromEntries(refreshKeys.flatMap((key) => Object.prototype.hasOwnProperty.call(values, key) ? [[key, values[key]]] : []));
|
|
2659
|
+
try {
|
|
2660
|
+
for (const target of scopedDeclarations.filter((declaration) => declaration.runtimeConfig)) {
|
|
2661
|
+
const definition = normalizePluginConfigDefinition(manifest.config);
|
|
2662
|
+
createRuntimeConfigSnapshot({
|
|
2663
|
+
installedIdentity: {
|
|
2664
|
+
sourceId: 'authoring',
|
|
2665
|
+
sourceIdentity: 'authoring',
|
|
2666
|
+
pluginId: `authoring/${manifest.name}`,
|
|
2667
|
+
pluginName: manifest.name,
|
|
2668
|
+
pluginVersion: manifest.version,
|
|
2669
|
+
platform: target.platform,
|
|
2670
|
+
scope: target.delivery?.mode === 'codex-native' ? 'user' : scope,
|
|
2671
|
+
configScope,
|
|
2672
|
+
artifactId: target.id,
|
|
2673
|
+
skillKey: target.skillKey,
|
|
2674
|
+
},
|
|
2675
|
+
declaration: target.runtimeConfig,
|
|
2676
|
+
definition,
|
|
2677
|
+
values,
|
|
2678
|
+
});
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
catch (runtimeConfigError) {
|
|
2682
|
+
error(`scripts.artifacts.render fixture (${surface}/${scope}) runtime config failed: ${runtimeConfigError.message}`);
|
|
2683
|
+
continue;
|
|
2684
|
+
}
|
|
3486
2685
|
fs.writeFileSync(contextPath, JSON.stringify({
|
|
3487
2686
|
event,
|
|
3488
2687
|
surface: distribution
|
|
@@ -3498,29 +2697,28 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3498
2697
|
},
|
|
3499
2698
|
config: {
|
|
3500
2699
|
mode: 'artifact-render',
|
|
3501
|
-
targetScope:
|
|
3502
|
-
|
|
3503
|
-
values,
|
|
3504
|
-
sources: Object.fromEntries(Object.keys(
|
|
2700
|
+
targetScope: configScope,
|
|
2701
|
+
...(manifest.config?.fields === undefined ? {} : { schema: scopedSchema }),
|
|
2702
|
+
values: scopedValues,
|
|
2703
|
+
sources: Object.fromEntries(Object.keys(scopedValues).map((key) => [key, 'default'])),
|
|
3505
2704
|
issues: [],
|
|
3506
2705
|
},
|
|
3507
2706
|
changedConfigKeys: [],
|
|
2707
|
+
requestedArtifactIds: scopedDeclarations.map((target) => target.id),
|
|
3508
2708
|
previousArtifacts: [],
|
|
3509
|
-
artifactSources:
|
|
2709
|
+
artifactSources: declarations.filter((target) => (target.target === 'codex.skill' || target.target === 'claude.skill' || target.target === 'agent-skills.skill')
|
|
2710
|
+
&& (distribution ? target.distribution === distribution : target.platform === platform)
|
|
2711
|
+
&& target.scopes.includes(scope)).map((target) => ({
|
|
3510
2712
|
id: target.id,
|
|
3511
2713
|
target: target.target,
|
|
3512
2714
|
sourceDirs: target.sourceDirs.map((sourceDir) => path.join(pluginDir, normalizeRelativePath(sourceDir))),
|
|
3513
2715
|
})),
|
|
3514
2716
|
runtimeImports,
|
|
3515
2717
|
paths: {
|
|
3516
|
-
awHome:
|
|
3517
|
-
codexHome:
|
|
3518
|
-
claudeHome:
|
|
2718
|
+
awHome: rendererAwHome,
|
|
2719
|
+
codexHome: rendererCodexHome,
|
|
2720
|
+
claudeHome: rendererClaudeHome,
|
|
3519
2721
|
manifestPath: path.join(pluginDir, '.agents-plugin', 'plugin.json'),
|
|
3520
|
-
configStores: {
|
|
3521
|
-
user: path.join(tempRoot, 'config-user.json'),
|
|
3522
|
-
project: path.join(tempRoot, 'config-project.json'),
|
|
3523
|
-
},
|
|
3524
2722
|
},
|
|
3525
2723
|
renderer: {
|
|
3526
2724
|
runner: renderEntry.runner,
|
|
@@ -3530,14 +2728,17 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3530
2728
|
rendererDigest: 'author-validator-fixture',
|
|
3531
2729
|
},
|
|
3532
2730
|
}), 'utf8');
|
|
3533
|
-
const rendererHome = path.join(
|
|
3534
|
-
const rendererTemp = path.join(
|
|
2731
|
+
const rendererHome = path.join(requestRoot, 'home');
|
|
2732
|
+
const rendererTemp = path.join(requestRoot, 'tmp');
|
|
2733
|
+
for (const rendererStateHome of [rendererAwHome, rendererCodexHome, rendererClaudeHome]) {
|
|
2734
|
+
fs.mkdirSync(rendererStateHome, { recursive: true });
|
|
2735
|
+
}
|
|
3535
2736
|
fs.mkdirSync(rendererHome, { recursive: true });
|
|
3536
2737
|
fs.mkdirSync(rendererTemp, { recursive: true });
|
|
3537
2738
|
const rendererEnv = {
|
|
3538
|
-
AW_HOME:
|
|
3539
|
-
AW_CODEX_HOME:
|
|
3540
|
-
AW_CLAUDE_HOME:
|
|
2739
|
+
AW_HOME: rendererAwHome,
|
|
2740
|
+
AW_CODEX_HOME: rendererCodexHome,
|
|
2741
|
+
AW_CLAUDE_HOME: rendererClaudeHome,
|
|
3541
2742
|
PATH: process.env.PATH ?? process.env.Path ?? '',
|
|
3542
2743
|
AW_PLUGIN_CONTEXT: contextPath,
|
|
3543
2744
|
AW_PLUGIN_ROOT: pluginDir,
|
|
@@ -3618,16 +2819,14 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3618
2819
|
const scripts = expectRecord(manifest.scripts, 'scripts');
|
|
3619
2820
|
if (!scripts)
|
|
3620
2821
|
return null;
|
|
3621
|
-
validateAllowedKeys(scripts, 'scripts', ['
|
|
3622
|
-
if (scripts.timeoutMs !== undefined &&
|
|
3623
|
-
(!Number.isInteger(scripts.timeoutMs) ||
|
|
3624
|
-
scripts.timeoutMs < SCRIPT_TIMEOUT_MIN_MS ||
|
|
3625
|
-
scripts.timeoutMs > SCRIPT_TIMEOUT_MAX_MS)) {
|
|
3626
|
-
error(`scripts.timeoutMs must be an integer between ${SCRIPT_TIMEOUT_MIN_MS} and ${SCRIPT_TIMEOUT_MAX_MS}`);
|
|
3627
|
-
}
|
|
2822
|
+
validateAllowedKeys(scripts, 'scripts', ['preApply', 'helpers', 'node', 'artifacts', 'tests']);
|
|
3628
2823
|
const runtimeNodeEntries = [];
|
|
3629
2824
|
let artifactRenderEntry = null;
|
|
3630
2825
|
let packageMeta = null;
|
|
2826
|
+
const runtimeConfigSkillRoots = (manifest.artifacts?.targets ?? [])
|
|
2827
|
+
.filter((target) => target.runtimeConfig !== undefined)
|
|
2828
|
+
.flatMap((target) => target.sourceDirs ?? [])
|
|
2829
|
+
.map(normalizeRelativePath);
|
|
3631
2830
|
if (scripts.helpers !== undefined) {
|
|
3632
2831
|
const helpers = expectRecord(scripts.helpers, 'scripts.helpers');
|
|
3633
2832
|
if (helpers) {
|
|
@@ -3641,17 +2840,21 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3641
2840
|
runtimeNodeEntries.push({ path: helperPath, fieldName: 'scripts.helpers.config' });
|
|
3642
2841
|
}
|
|
3643
2842
|
}
|
|
3644
|
-
if (scripts.
|
|
3645
|
-
const
|
|
3646
|
-
if (
|
|
3647
|
-
validateAllowedKeys(
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
}
|
|
3653
|
-
validateLifecycleDefinition(pluginDir, definition, `scripts.lifecycle.${eventName}`, runtimeNodeEntries);
|
|
2843
|
+
if (scripts.preApply !== undefined) {
|
|
2844
|
+
const preApply = expectRecord(scripts.preApply, 'scripts.preApply');
|
|
2845
|
+
if (preApply) {
|
|
2846
|
+
validateAllowedKeys(preApply, 'scripts.preApply', ['path', 'timeoutMs']);
|
|
2847
|
+
const preApplyPath = normalizePluginScriptPath(pluginDir, preApply.path, 'scripts.preApply.path');
|
|
2848
|
+
validateDeclaredScriptFile(pluginDir, preApplyPath, 'scripts.preApply.path');
|
|
2849
|
+
if (preApplyPath && !isNodeRunnablePath(preApplyPath)) {
|
|
2850
|
+
error('scripts.preApply.path must use a Node runnable extension (.js, .mjs, or .cjs)');
|
|
3654
2851
|
}
|
|
2852
|
+
if (preApply.timeoutMs !== undefined && (!Number.isInteger(preApply.timeoutMs)
|
|
2853
|
+
|| preApply.timeoutMs < SCRIPT_TIMEOUT_MIN_MS || preApply.timeoutMs > SCRIPT_TIMEOUT_MAX_MS)) {
|
|
2854
|
+
error(`scripts.preApply.timeoutMs must be an integer between ${SCRIPT_TIMEOUT_MIN_MS} and ${SCRIPT_TIMEOUT_MAX_MS}`);
|
|
2855
|
+
}
|
|
2856
|
+
if (preApplyPath)
|
|
2857
|
+
runtimeNodeEntries.push({ path: preApplyPath, fieldName: 'scripts.preApply.path' });
|
|
3655
2858
|
}
|
|
3656
2859
|
}
|
|
3657
2860
|
if (scripts.node !== undefined) {
|
|
@@ -3671,6 +2874,7 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3671
2874
|
label: 'scripts.node.build import contract',
|
|
3672
2875
|
packageMeta,
|
|
3673
2876
|
allowDevDependencies: true,
|
|
2877
|
+
runtimeConfigSkillRoots,
|
|
3674
2878
|
});
|
|
3675
2879
|
}
|
|
3676
2880
|
}
|
|
@@ -3720,6 +2924,7 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3720
2924
|
label: 'scripts.tests.node import contract',
|
|
3721
2925
|
packageMeta,
|
|
3722
2926
|
allowDevDependencies: true,
|
|
2927
|
+
runtimeConfigSkillRoots,
|
|
3723
2928
|
});
|
|
3724
2929
|
}
|
|
3725
2930
|
}
|
|
@@ -3729,6 +2934,7 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3729
2934
|
label: 'runtime Node import contract',
|
|
3730
2935
|
packageMeta,
|
|
3731
2936
|
allowDevDependencies: false,
|
|
2937
|
+
runtimeConfigSkillRoots,
|
|
3732
2938
|
});
|
|
3733
2939
|
return artifactRenderEntry;
|
|
3734
2940
|
}
|
|
@@ -4130,8 +3336,6 @@ function printUsage() {
|
|
|
4130
3336
|
aw plugin validate <plugin-directory> [options]
|
|
4131
3337
|
|
|
4132
3338
|
Options:
|
|
4133
|
-
--catalog-min-version <semver>
|
|
4134
|
-
Validate compatibility.aw.minVersion against a target catalog.
|
|
4135
3339
|
--strict-agent-skills Allow only Agent Skills portable core frontmatter.
|
|
4136
3340
|
--claude-skill-profile <minimum|current>
|
|
4137
3341
|
Validate Claude fields at AW minimum 2.1.88 (default) or current docs.
|
|
@@ -4201,7 +3405,30 @@ async function validatePluginDir(pluginDir, options) {
|
|
|
4201
3405
|
}
|
|
4202
3406
|
const skillValidationTools = await tryLoadCliSkillValidationTools();
|
|
4203
3407
|
try {
|
|
4204
|
-
await cliManifestTools.validateSourcePluginManifest(pluginDir, manifest);
|
|
3408
|
+
const normalizedManifest = await cliManifestTools.validateSourcePluginManifest(pluginDir, manifest);
|
|
3409
|
+
// Renderer fixtures consume the same compiled config as installed runtime consumers.
|
|
3410
|
+
if (normalizedManifest.config)
|
|
3411
|
+
manifest.config = normalizedManifest.config;
|
|
3412
|
+
const requirements = await cliManifestTools.resolvePluginRequirements({
|
|
3413
|
+
pluginRoot: pluginDir,
|
|
3414
|
+
manifest: normalizedManifest,
|
|
3415
|
+
implementedCapabilities: cliManifestTools.implementedCapabilities,
|
|
3416
|
+
});
|
|
3417
|
+
if (requirements.unsupportedByCurrentAw.length > 0) {
|
|
3418
|
+
capabilityDiagnostic = {
|
|
3419
|
+
errorCode: 'awCapabilityUnsupported',
|
|
3420
|
+
missingCapabilities: requirements.unsupportedByCurrentAw,
|
|
3421
|
+
availableCapabilities: [...cliManifestTools.implementedCapabilities].sort(),
|
|
3422
|
+
};
|
|
3423
|
+
error(`[awCapabilityUnsupported] missingCapabilities=${JSON.stringify(requirements.unsupportedByCurrentAw)}`);
|
|
3424
|
+
}
|
|
3425
|
+
const satisfiedCapabilities = new Set([...requirements.declared, ...requirements.derived]);
|
|
3426
|
+
const advisoryObservations = requirements.observations.filter((observation) => (observation.code !== 'aw-command-detected'
|
|
3427
|
+
|| observation.capability === undefined
|
|
3428
|
+
|| !satisfiedCapabilities.has(observation.capability)));
|
|
3429
|
+
for (const observation of advisoryObservations) {
|
|
3430
|
+
warn(`[awCapabilityObservation] ${observation.path}: ${observation.message}`);
|
|
3431
|
+
}
|
|
4205
3432
|
}
|
|
4206
3433
|
catch (e) {
|
|
4207
3434
|
error(e.message);
|
|
@@ -4212,8 +3439,7 @@ async function validatePluginDir(pluginDir, options) {
|
|
|
4212
3439
|
validateNoBuildOutputDirs(pluginDir);
|
|
4213
3440
|
validateDeclaredNativeAssets(pluginDir, manifest);
|
|
4214
3441
|
validateCanonicalSkillLayout(pluginDir, manifest);
|
|
4215
|
-
|
|
4216
|
-
validatePluginEvalManifest(pluginDir, manifest);
|
|
3442
|
+
await validatePluginEvalManifest(pluginDir, manifest);
|
|
4217
3443
|
const artifactRenderEntry = validateScriptsLightweight(pluginDir, manifest);
|
|
4218
3444
|
const runtimeImports = await resolveAuthorRuntimeImports({
|
|
4219
3445
|
pluginDir,
|
|
@@ -4263,7 +3489,6 @@ function parseArgs(argv) {
|
|
|
4263
3489
|
const args = argv.slice(2);
|
|
4264
3490
|
const options = {
|
|
4265
3491
|
pluginDir: null,
|
|
4266
|
-
catalogMinVersion: null,
|
|
4267
3492
|
strictAgentSkills: false,
|
|
4268
3493
|
claudeSkillProfile: 'minimum',
|
|
4269
3494
|
versionBase: 'HEAD',
|
|
@@ -4320,18 +3545,6 @@ function parseArgs(argv) {
|
|
|
4320
3545
|
}
|
|
4321
3546
|
exitCliError(`Unknown argument: ${arg}`, options.json);
|
|
4322
3547
|
}
|
|
4323
|
-
if (arg === '--catalog-min-version') {
|
|
4324
|
-
const value = args[index + 1];
|
|
4325
|
-
if (!value || value.startsWith('--')) {
|
|
4326
|
-
exitCliError('--catalog-min-version requires a value.', options.json);
|
|
4327
|
-
}
|
|
4328
|
-
index += 1;
|
|
4329
|
-
if (!STRICT_SEMVER_PATTERN.test(value)) {
|
|
4330
|
-
exitCliError(`Invalid --catalog-min-version: expected a strict SemVer 2.0.0 string. Received ${JSON.stringify(value)}.`, options.json);
|
|
4331
|
-
}
|
|
4332
|
-
options.catalogMinVersion = value;
|
|
4333
|
-
continue;
|
|
4334
|
-
}
|
|
4335
3548
|
exitCliError(`Unknown option: ${arg}`, options.json);
|
|
4336
3549
|
}
|
|
4337
3550
|
return options;
|
|
@@ -4363,7 +3576,6 @@ async function main() {
|
|
|
4363
3576
|
const repoRoot = requestedWorkspaceRoot ?? discoverCanonicalRepoRoot(repoRootCandidate);
|
|
4364
3577
|
const gitRoot = discoverCanonicalRepoRoot(findRepoRootCandidate(resolved));
|
|
4365
3578
|
await validatePluginDir(resolved, {
|
|
4366
|
-
catalogMinVersion: args.catalogMinVersion,
|
|
4367
3579
|
strictAgentSkills: args.strictAgentSkills,
|
|
4368
3580
|
claudeSkillProfile: args.claudeSkillProfile,
|
|
4369
3581
|
versionBase: args.versionBase,
|
|
@@ -4377,9 +3589,12 @@ async function main() {
|
|
|
4377
3589
|
profile: args.strictAgentSkills ? 'agent-skills-portable-core' : 'aw-source',
|
|
4378
3590
|
claudeSkillProfile: args.claudeSkillProfile,
|
|
4379
3591
|
pluginDirectory: resolved,
|
|
3592
|
+
workspaceRoot: process.env.AW_AUTHORING_WORKSPACE_ROOT ?? repoRoot,
|
|
3593
|
+
workspaceRootResolvedFrom: process.env.AW_AUTHORING_WORKSPACE_ROOT_RESOLVED_FROM ?? 'explicit',
|
|
4380
3594
|
versionCheck: pluginVersionCheckResult,
|
|
4381
3595
|
warnings,
|
|
4382
3596
|
errors,
|
|
3597
|
+
...(capabilityDiagnostic ?? {}),
|
|
4383
3598
|
}, null, 2));
|
|
4384
3599
|
if (errors.length > 0) {
|
|
4385
3600
|
process.exit(1);
|