@herdctl/core 0.0.1
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/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test.log +219 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/coverage-final.json +51 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +251 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/src/config/index.html +191 -0
- package/coverage/src/config/index.ts.html +442 -0
- package/coverage/src/config/interpolate.ts.html +652 -0
- package/coverage/src/config/loader.ts.html +1501 -0
- package/coverage/src/config/merge.ts.html +823 -0
- package/coverage/src/config/parser.ts.html +1213 -0
- package/coverage/src/config/schema.ts.html +1123 -0
- package/coverage/src/fleet-manager/errors.ts.html +2326 -0
- package/coverage/src/fleet-manager/event-types.ts.html +1219 -0
- package/coverage/src/fleet-manager/fleet-manager.ts.html +7030 -0
- package/coverage/src/fleet-manager/index.html +206 -0
- package/coverage/src/fleet-manager/index.ts.html +469 -0
- package/coverage/src/fleet-manager/job-manager.ts.html +2074 -0
- package/coverage/src/fleet-manager/job-queue.ts.html +2479 -0
- package/coverage/src/fleet-manager/types.ts.html +2602 -0
- package/coverage/src/index.html +116 -0
- package/coverage/src/index.ts.html +181 -0
- package/coverage/src/runner/errors.ts.html +1006 -0
- package/coverage/src/runner/index.html +191 -0
- package/coverage/src/runner/index.ts.html +256 -0
- package/coverage/src/runner/job-executor.ts.html +1429 -0
- package/coverage/src/runner/message-processor.ts.html +1150 -0
- package/coverage/src/runner/sdk-adapter.ts.html +658 -0
- package/coverage/src/runner/types.ts.html +559 -0
- package/coverage/src/scheduler/errors.ts.html +388 -0
- package/coverage/src/scheduler/index.html +206 -0
- package/coverage/src/scheduler/index.ts.html +244 -0
- package/coverage/src/scheduler/interval.ts.html +652 -0
- package/coverage/src/scheduler/schedule-runner.ts.html +1411 -0
- package/coverage/src/scheduler/schedule-state.ts.html +718 -0
- package/coverage/src/scheduler/scheduler.ts.html +1795 -0
- package/coverage/src/scheduler/types.ts.html +733 -0
- package/coverage/src/state/directory.ts.html +736 -0
- package/coverage/src/state/errors.ts.html +376 -0
- package/coverage/src/state/fleet-state.ts.html +937 -0
- package/coverage/src/state/index.html +221 -0
- package/coverage/src/state/index.ts.html +322 -0
- package/coverage/src/state/job-metadata.ts.html +1420 -0
- package/coverage/src/state/job-output.ts.html +1033 -0
- package/coverage/src/state/schemas/fleet-state.ts.html +445 -0
- package/coverage/src/state/schemas/index.html +176 -0
- package/coverage/src/state/schemas/index.ts.html +286 -0
- package/coverage/src/state/schemas/job-metadata.ts.html +628 -0
- package/coverage/src/state/schemas/job-output.ts.html +616 -0
- package/coverage/src/state/schemas/session-info.ts.html +361 -0
- package/coverage/src/state/session.ts.html +844 -0
- package/coverage/src/state/types.ts.html +262 -0
- package/coverage/src/state/utils/atomic.ts.html +748 -0
- package/coverage/src/state/utils/index.html +146 -0
- package/coverage/src/state/utils/index.ts.html +103 -0
- package/coverage/src/state/utils/reads.ts.html +1621 -0
- package/coverage/src/work-sources/adapters/github.ts.html +3583 -0
- package/coverage/src/work-sources/adapters/index.html +131 -0
- package/coverage/src/work-sources/adapters/index.ts.html +277 -0
- package/coverage/src/work-sources/errors.ts.html +298 -0
- package/coverage/src/work-sources/index.html +176 -0
- package/coverage/src/work-sources/index.ts.html +529 -0
- package/coverage/src/work-sources/manager.ts.html +1324 -0
- package/coverage/src/work-sources/registry.ts.html +619 -0
- package/coverage/src/work-sources/types.ts.html +568 -0
- package/dist/config/__tests__/agent.test.d.ts +2 -0
- package/dist/config/__tests__/agent.test.d.ts.map +1 -0
- package/dist/config/__tests__/agent.test.js +752 -0
- package/dist/config/__tests__/agent.test.js.map +1 -0
- package/dist/config/__tests__/interpolate.test.d.ts +2 -0
- package/dist/config/__tests__/interpolate.test.d.ts.map +1 -0
- package/dist/config/__tests__/interpolate.test.js +509 -0
- package/dist/config/__tests__/interpolate.test.js.map +1 -0
- package/dist/config/__tests__/loader.test.d.ts +2 -0
- package/dist/config/__tests__/loader.test.d.ts.map +1 -0
- package/dist/config/__tests__/loader.test.js +631 -0
- package/dist/config/__tests__/loader.test.js.map +1 -0
- package/dist/config/__tests__/merge.test.d.ts +2 -0
- package/dist/config/__tests__/merge.test.d.ts.map +1 -0
- package/dist/config/__tests__/merge.test.js +672 -0
- package/dist/config/__tests__/merge.test.js.map +1 -0
- package/dist/config/__tests__/parser.test.d.ts +2 -0
- package/dist/config/__tests__/parser.test.d.ts.map +1 -0
- package/dist/config/__tests__/parser.test.js +476 -0
- package/dist/config/__tests__/parser.test.js.map +1 -0
- package/dist/config/__tests__/schema.test.d.ts +2 -0
- package/dist/config/__tests__/schema.test.d.ts.map +1 -0
- package/dist/config/__tests__/schema.test.js +776 -0
- package/dist/config/__tests__/schema.test.js.map +1 -0
- package/dist/config/index.d.ts +11 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +26 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/interpolate.d.ts +76 -0
- package/dist/config/interpolate.d.ts.map +1 -0
- package/dist/config/interpolate.js +143 -0
- package/dist/config/interpolate.js.map +1 -0
- package/dist/config/loader.d.ts +147 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +336 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/merge.d.ts +84 -0
- package/dist/config/merge.d.ts.map +1 -0
- package/dist/config/merge.js +138 -0
- package/dist/config/merge.js.map +1 -0
- package/dist/config/parser.d.ts +143 -0
- package/dist/config/parser.d.ts.map +1 -0
- package/dist/config/parser.js +316 -0
- package/dist/config/parser.js.map +1 -0
- package/dist/config/schema.d.ts +1906 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +268 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/fleet-manager/__tests__/coverage.test.d.ts +13 -0
- package/dist/fleet-manager/__tests__/coverage.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/coverage.test.js +2282 -0
- package/dist/fleet-manager/__tests__/coverage.test.js.map +1 -0
- package/dist/fleet-manager/__tests__/errors.test.d.ts +7 -0
- package/dist/fleet-manager/__tests__/errors.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/errors.test.js +557 -0
- package/dist/fleet-manager/__tests__/errors.test.js.map +1 -0
- package/dist/fleet-manager/__tests__/event-helpers.test.d.ts +7 -0
- package/dist/fleet-manager/__tests__/event-helpers.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/event-helpers.test.js +368 -0
- package/dist/fleet-manager/__tests__/event-helpers.test.js.map +1 -0
- package/dist/fleet-manager/__tests__/integration.test.d.ts +11 -0
- package/dist/fleet-manager/__tests__/integration.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/integration.test.js +949 -0
- package/dist/fleet-manager/__tests__/integration.test.js.map +1 -0
- package/dist/fleet-manager/__tests__/job-control.test.d.ts +7 -0
- package/dist/fleet-manager/__tests__/job-control.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/job-control.test.js +215 -0
- package/dist/fleet-manager/__tests__/job-control.test.js.map +1 -0
- package/dist/fleet-manager/__tests__/job-manager.test.d.ts +7 -0
- package/dist/fleet-manager/__tests__/job-manager.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/job-manager.test.js +659 -0
- package/dist/fleet-manager/__tests__/job-manager.test.js.map +1 -0
- package/dist/fleet-manager/__tests__/job-queue.test.d.ts +5 -0
- package/dist/fleet-manager/__tests__/job-queue.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/job-queue.test.js +315 -0
- package/dist/fleet-manager/__tests__/job-queue.test.js.map +1 -0
- package/dist/fleet-manager/__tests__/reload.test.d.ts +7 -0
- package/dist/fleet-manager/__tests__/reload.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/reload.test.js +609 -0
- package/dist/fleet-manager/__tests__/reload.test.js.map +1 -0
- package/dist/fleet-manager/__tests__/status-queries.test.d.ts +7 -0
- package/dist/fleet-manager/__tests__/status-queries.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/status-queries.test.js +488 -0
- package/dist/fleet-manager/__tests__/status-queries.test.js.map +1 -0
- package/dist/fleet-manager/__tests__/trigger.test.d.ts +7 -0
- package/dist/fleet-manager/__tests__/trigger.test.d.ts.map +1 -0
- package/dist/fleet-manager/__tests__/trigger.test.js +471 -0
- package/dist/fleet-manager/__tests__/trigger.test.js.map +1 -0
- package/dist/fleet-manager/errors.d.ts +407 -0
- package/dist/fleet-manager/errors.d.ts.map +1 -0
- package/dist/fleet-manager/errors.js +569 -0
- package/dist/fleet-manager/errors.js.map +1 -0
- package/dist/fleet-manager/event-types.d.ts +302 -0
- package/dist/fleet-manager/event-types.d.ts.map +1 -0
- package/dist/fleet-manager/event-types.js +9 -0
- package/dist/fleet-manager/event-types.js.map +1 -0
- package/dist/fleet-manager/fleet-manager.d.ts +699 -0
- package/dist/fleet-manager/fleet-manager.d.ts.map +1 -0
- package/dist/fleet-manager/fleet-manager.js +1906 -0
- package/dist/fleet-manager/fleet-manager.js.map +1 -0
- package/dist/fleet-manager/index.d.ts +17 -0
- package/dist/fleet-manager/index.d.ts.map +1 -0
- package/dist/fleet-manager/index.js +29 -0
- package/dist/fleet-manager/index.js.map +1 -0
- package/dist/fleet-manager/job-manager.d.ts +271 -0
- package/dist/fleet-manager/job-manager.d.ts.map +1 -0
- package/dist/fleet-manager/job-manager.js +443 -0
- package/dist/fleet-manager/job-manager.js.map +1 -0
- package/dist/fleet-manager/job-queue.d.ts +422 -0
- package/dist/fleet-manager/job-queue.d.ts.map +1 -0
- package/dist/fleet-manager/job-queue.js +448 -0
- package/dist/fleet-manager/job-queue.js.map +1 -0
- package/dist/fleet-manager/types.d.ts +680 -0
- package/dist/fleet-manager/types.d.ts.map +1 -0
- package/dist/fleet-manager/types.js +8 -0
- package/dist/fleet-manager/types.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/runner/__tests__/errors.test.d.ts +2 -0
- package/dist/runner/__tests__/errors.test.d.ts.map +1 -0
- package/dist/runner/__tests__/errors.test.js +264 -0
- package/dist/runner/__tests__/errors.test.js.map +1 -0
- package/dist/runner/__tests__/job-executor.test.d.ts +2 -0
- package/dist/runner/__tests__/job-executor.test.d.ts.map +1 -0
- package/dist/runner/__tests__/job-executor.test.js +1345 -0
- package/dist/runner/__tests__/job-executor.test.js.map +1 -0
- package/dist/runner/__tests__/message-processor.test.d.ts +2 -0
- package/dist/runner/__tests__/message-processor.test.d.ts.map +1 -0
- package/dist/runner/__tests__/message-processor.test.js +768 -0
- package/dist/runner/__tests__/message-processor.test.js.map +1 -0
- package/dist/runner/__tests__/sdk-adapter.test.d.ts +2 -0
- package/dist/runner/__tests__/sdk-adapter.test.d.ts.map +1 -0
- package/dist/runner/__tests__/sdk-adapter.test.js +554 -0
- package/dist/runner/__tests__/sdk-adapter.test.js.map +1 -0
- package/dist/runner/errors.d.ts +121 -0
- package/dist/runner/errors.d.ts.map +1 -0
- package/dist/runner/errors.js +212 -0
- package/dist/runner/errors.js.map +1 -0
- package/dist/runner/index.d.ts +12 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/index.js +15 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/job-executor.d.ts +98 -0
- package/dist/runner/job-executor.d.ts.map +1 -0
- package/dist/runner/job-executor.js +333 -0
- package/dist/runner/job-executor.js.map +1 -0
- package/dist/runner/message-processor.d.ts +45 -0
- package/dist/runner/message-processor.d.ts.map +1 -0
- package/dist/runner/message-processor.js +294 -0
- package/dist/runner/message-processor.js.map +1 -0
- package/dist/runner/sdk-adapter.d.ts +60 -0
- package/dist/runner/sdk-adapter.d.ts.map +1 -0
- package/dist/runner/sdk-adapter.js +138 -0
- package/dist/runner/sdk-adapter.js.map +1 -0
- package/dist/runner/types.d.ts +135 -0
- package/dist/runner/types.d.ts.map +1 -0
- package/dist/runner/types.js +7 -0
- package/dist/runner/types.js.map +1 -0
- package/dist/scheduler/__tests__/errors.test.d.ts +2 -0
- package/dist/scheduler/__tests__/errors.test.d.ts.map +1 -0
- package/dist/scheduler/__tests__/errors.test.js +101 -0
- package/dist/scheduler/__tests__/errors.test.js.map +1 -0
- package/dist/scheduler/__tests__/interval.test.d.ts +2 -0
- package/dist/scheduler/__tests__/interval.test.d.ts.map +1 -0
- package/dist/scheduler/__tests__/interval.test.js +419 -0
- package/dist/scheduler/__tests__/interval.test.js.map +1 -0
- package/dist/scheduler/__tests__/schedule-runner.test.d.ts +2 -0
- package/dist/scheduler/__tests__/schedule-runner.test.d.ts.map +1 -0
- package/dist/scheduler/__tests__/schedule-runner.test.js +634 -0
- package/dist/scheduler/__tests__/schedule-runner.test.js.map +1 -0
- package/dist/scheduler/__tests__/schedule-state.test.d.ts +2 -0
- package/dist/scheduler/__tests__/schedule-state.test.d.ts.map +1 -0
- package/dist/scheduler/__tests__/schedule-state.test.js +572 -0
- package/dist/scheduler/__tests__/schedule-state.test.js.map +1 -0
- package/dist/scheduler/__tests__/scheduler.test.d.ts +2 -0
- package/dist/scheduler/__tests__/scheduler.test.d.ts.map +1 -0
- package/dist/scheduler/__tests__/scheduler.test.js +987 -0
- package/dist/scheduler/__tests__/scheduler.test.js.map +1 -0
- package/dist/scheduler/errors.d.ts +61 -0
- package/dist/scheduler/errors.d.ts.map +1 -0
- package/dist/scheduler/errors.js +81 -0
- package/dist/scheduler/errors.js.map +1 -0
- package/dist/scheduler/index.d.ts +13 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +17 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/interval.d.ts +64 -0
- package/dist/scheduler/interval.d.ts.map +1 -0
- package/dist/scheduler/interval.js +139 -0
- package/dist/scheduler/interval.js.map +1 -0
- package/dist/scheduler/schedule-runner.d.ts +149 -0
- package/dist/scheduler/schedule-runner.d.ts.map +1 -0
- package/dist/scheduler/schedule-runner.js +277 -0
- package/dist/scheduler/schedule-runner.js.map +1 -0
- package/dist/scheduler/schedule-state.d.ts +105 -0
- package/dist/scheduler/schedule-state.d.ts.map +1 -0
- package/dist/scheduler/schedule-state.js +151 -0
- package/dist/scheduler/schedule-state.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +138 -0
- package/dist/scheduler/scheduler.d.ts.map +1 -0
- package/dist/scheduler/scheduler.js +423 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/dist/scheduler/types.d.ts +160 -0
- package/dist/scheduler/types.d.ts.map +1 -0
- package/dist/scheduler/types.js +8 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/state/__tests__/directory.test.d.ts +2 -0
- package/dist/state/__tests__/directory.test.d.ts.map +1 -0
- package/dist/state/__tests__/directory.test.js +414 -0
- package/dist/state/__tests__/directory.test.js.map +1 -0
- package/dist/state/__tests__/fleet-state.test.d.ts +2 -0
- package/dist/state/__tests__/fleet-state.test.d.ts.map +1 -0
- package/dist/state/__tests__/fleet-state.test.js +696 -0
- package/dist/state/__tests__/fleet-state.test.js.map +1 -0
- package/dist/state/__tests__/job-metadata-schema.test.d.ts +2 -0
- package/dist/state/__tests__/job-metadata-schema.test.d.ts.map +1 -0
- package/dist/state/__tests__/job-metadata-schema.test.js +329 -0
- package/dist/state/__tests__/job-metadata-schema.test.js.map +1 -0
- package/dist/state/__tests__/job-metadata.test.d.ts +2 -0
- package/dist/state/__tests__/job-metadata.test.d.ts.map +1 -0
- package/dist/state/__tests__/job-metadata.test.js +667 -0
- package/dist/state/__tests__/job-metadata.test.js.map +1 -0
- package/dist/state/__tests__/job-output.test.d.ts +2 -0
- package/dist/state/__tests__/job-output.test.d.ts.map +1 -0
- package/dist/state/__tests__/job-output.test.js +672 -0
- package/dist/state/__tests__/job-output.test.js.map +1 -0
- package/dist/state/__tests__/session-schema.test.d.ts +2 -0
- package/dist/state/__tests__/session-schema.test.d.ts.map +1 -0
- package/dist/state/__tests__/session-schema.test.js +323 -0
- package/dist/state/__tests__/session-schema.test.js.map +1 -0
- package/dist/state/__tests__/session.test.d.ts +2 -0
- package/dist/state/__tests__/session.test.d.ts.map +1 -0
- package/dist/state/__tests__/session.test.js +468 -0
- package/dist/state/__tests__/session.test.js.map +1 -0
- package/dist/state/directory.d.ts +42 -0
- package/dist/state/directory.d.ts.map +1 -0
- package/dist/state/directory.js +170 -0
- package/dist/state/directory.js.map +1 -0
- package/dist/state/errors.d.ts +44 -0
- package/dist/state/errors.d.ts.map +1 -0
- package/dist/state/errors.js +82 -0
- package/dist/state/errors.js.map +1 -0
- package/dist/state/fleet-state.d.ts +126 -0
- package/dist/state/fleet-state.d.ts.map +1 -0
- package/dist/state/fleet-state.js +196 -0
- package/dist/state/fleet-state.js.map +1 -0
- package/dist/state/index.d.ts +21 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +30 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/job-metadata.d.ts +151 -0
- package/dist/state/job-metadata.d.ts.map +1 -0
- package/dist/state/job-metadata.js +287 -0
- package/dist/state/job-metadata.js.map +1 -0
- package/dist/state/job-output.d.ts +116 -0
- package/dist/state/job-output.d.ts.map +1 -0
- package/dist/state/job-output.js +218 -0
- package/dist/state/job-output.js.map +1 -0
- package/dist/state/schemas/__tests__/job-output.test.d.ts +2 -0
- package/dist/state/schemas/__tests__/job-output.test.d.ts.map +1 -0
- package/dist/state/schemas/__tests__/job-output.test.js +279 -0
- package/dist/state/schemas/__tests__/job-output.test.js.map +1 -0
- package/dist/state/schemas/fleet-state.d.ts +249 -0
- package/dist/state/schemas/fleet-state.d.ts.map +1 -0
- package/dist/state/schemas/fleet-state.js +97 -0
- package/dist/state/schemas/fleet-state.js.map +1 -0
- package/dist/state/schemas/index.d.ts +10 -0
- package/dist/state/schemas/index.d.ts.map +1 -0
- package/dist/state/schemas/index.js +10 -0
- package/dist/state/schemas/index.js.map +1 -0
- package/dist/state/schemas/job-metadata.d.ts +118 -0
- package/dist/state/schemas/job-metadata.d.ts.map +1 -0
- package/dist/state/schemas/job-metadata.js +123 -0
- package/dist/state/schemas/job-metadata.js.map +1 -0
- package/dist/state/schemas/job-output.d.ts +291 -0
- package/dist/state/schemas/job-output.d.ts.map +1 -0
- package/dist/state/schemas/job-output.js +132 -0
- package/dist/state/schemas/job-output.js.map +1 -0
- package/dist/state/schemas/session-info.d.ts +65 -0
- package/dist/state/schemas/session-info.d.ts.map +1 -0
- package/dist/state/schemas/session-info.js +58 -0
- package/dist/state/schemas/session-info.js.map +1 -0
- package/dist/state/session.d.ts +92 -0
- package/dist/state/session.d.ts.map +1 -0
- package/dist/state/session.js +173 -0
- package/dist/state/session.js.map +1 -0
- package/dist/state/types.d.ts +54 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +18 -0
- package/dist/state/types.js.map +1 -0
- package/dist/state/utils/__tests__/atomic.test.d.ts +2 -0
- package/dist/state/utils/__tests__/atomic.test.d.ts.map +1 -0
- package/dist/state/utils/__tests__/atomic.test.js +537 -0
- package/dist/state/utils/__tests__/atomic.test.js.map +1 -0
- package/dist/state/utils/__tests__/reads.test.d.ts +2 -0
- package/dist/state/utils/__tests__/reads.test.d.ts.map +1 -0
- package/dist/state/utils/__tests__/reads.test.js +792 -0
- package/dist/state/utils/__tests__/reads.test.js.map +1 -0
- package/dist/state/utils/atomic.d.ts +89 -0
- package/dist/state/utils/atomic.d.ts.map +1 -0
- package/dist/state/utils/atomic.js +157 -0
- package/dist/state/utils/atomic.js.map +1 -0
- package/dist/state/utils/index.d.ts +6 -0
- package/dist/state/utils/index.d.ts.map +1 -0
- package/dist/state/utils/index.js +6 -0
- package/dist/state/utils/index.js.map +1 -0
- package/dist/state/utils/reads.d.ts +196 -0
- package/dist/state/utils/reads.d.ts.map +1 -0
- package/dist/state/utils/reads.js +346 -0
- package/dist/state/utils/reads.js.map +1 -0
- package/dist/work-sources/__tests__/github.test.d.ts +2 -0
- package/dist/work-sources/__tests__/github.test.d.ts.map +1 -0
- package/dist/work-sources/__tests__/github.test.js +1334 -0
- package/dist/work-sources/__tests__/github.test.js.map +1 -0
- package/dist/work-sources/__tests__/manager.test.d.ts +2 -0
- package/dist/work-sources/__tests__/manager.test.d.ts.map +1 -0
- package/dist/work-sources/__tests__/manager.test.js +424 -0
- package/dist/work-sources/__tests__/manager.test.js.map +1 -0
- package/dist/work-sources/__tests__/registry.test.d.ts +2 -0
- package/dist/work-sources/__tests__/registry.test.d.ts.map +1 -0
- package/dist/work-sources/__tests__/registry.test.js +381 -0
- package/dist/work-sources/__tests__/registry.test.js.map +1 -0
- package/dist/work-sources/__tests__/types.test.d.ts +2 -0
- package/dist/work-sources/__tests__/types.test.d.ts.map +1 -0
- package/dist/work-sources/__tests__/types.test.js +406 -0
- package/dist/work-sources/__tests__/types.test.js.map +1 -0
- package/dist/work-sources/adapters/github.d.ts +290 -0
- package/dist/work-sources/adapters/github.d.ts.map +1 -0
- package/dist/work-sources/adapters/github.js +803 -0
- package/dist/work-sources/adapters/github.js.map +1 -0
- package/dist/work-sources/adapters/index.d.ts +10 -0
- package/dist/work-sources/adapters/index.d.ts.map +1 -0
- package/dist/work-sources/adapters/index.js +31 -0
- package/dist/work-sources/adapters/index.js.map +1 -0
- package/dist/work-sources/errors.d.ts +40 -0
- package/dist/work-sources/errors.d.ts.map +1 -0
- package/dist/work-sources/errors.js +54 -0
- package/dist/work-sources/errors.js.map +1 -0
- package/dist/work-sources/index.d.ts +105 -0
- package/dist/work-sources/index.d.ts.map +1 -0
- package/dist/work-sources/index.js +24 -0
- package/dist/work-sources/index.js.map +1 -0
- package/dist/work-sources/manager.d.ts +370 -0
- package/dist/work-sources/manager.d.ts.map +1 -0
- package/dist/work-sources/manager.js +61 -0
- package/dist/work-sources/manager.js.map +1 -0
- package/dist/work-sources/registry.d.ts +128 -0
- package/dist/work-sources/registry.d.ts.map +1 -0
- package/dist/work-sources/registry.js +132 -0
- package/dist/work-sources/registry.js.map +1 -0
- package/dist/work-sources/types.d.ts +127 -0
- package/dist/work-sources/types.d.ts.map +1 -0
- package/dist/work-sources/types.js +8 -0
- package/dist/work-sources/types.js.map +1 -0
- package/package.json +23 -0
- package/src/config/__tests__/agent.test.ts +864 -0
- package/src/config/__tests__/interpolate.test.ts +644 -0
- package/src/config/__tests__/loader.test.ts +784 -0
- package/src/config/__tests__/merge.test.ts +751 -0
- package/src/config/__tests__/parser.test.ts +533 -0
- package/src/config/__tests__/schema.test.ts +873 -0
- package/src/config/index.ts +119 -0
- package/src/config/interpolate.ts +189 -0
- package/src/config/loader.ts +472 -0
- package/src/config/merge.ts +246 -0
- package/src/config/parser.ts +376 -0
- package/src/config/schema.ts +346 -0
- package/src/fleet-manager/__tests__/coverage.test.ts +2869 -0
- package/src/fleet-manager/__tests__/errors.test.ts +660 -0
- package/src/fleet-manager/__tests__/event-helpers.test.ts +448 -0
- package/src/fleet-manager/__tests__/integration.test.ts +1209 -0
- package/src/fleet-manager/__tests__/job-control.test.ts +283 -0
- package/src/fleet-manager/__tests__/job-manager.test.ts +869 -0
- package/src/fleet-manager/__tests__/job-queue.test.ts +401 -0
- package/src/fleet-manager/__tests__/reload.test.ts +751 -0
- package/src/fleet-manager/__tests__/status-queries.test.ts +595 -0
- package/src/fleet-manager/__tests__/trigger.test.ts +601 -0
- package/src/fleet-manager/errors.ts +747 -0
- package/src/fleet-manager/event-types.ts +378 -0
- package/src/fleet-manager/fleet-manager.ts +2315 -0
- package/src/fleet-manager/index.ts +128 -0
- package/src/fleet-manager/job-manager.ts +663 -0
- package/src/fleet-manager/job-queue.ts +798 -0
- package/src/fleet-manager/types.ts +839 -0
- package/src/index.ts +32 -0
- package/src/runner/__tests__/errors.test.ts +382 -0
- package/src/runner/__tests__/job-executor.test.ts +1708 -0
- package/src/runner/__tests__/message-processor.test.ts +960 -0
- package/src/runner/__tests__/sdk-adapter.test.ts +626 -0
- package/src/runner/errors.ts +307 -0
- package/src/runner/index.ts +57 -0
- package/src/runner/job-executor.ts +448 -0
- package/src/runner/message-processor.ts +355 -0
- package/src/runner/sdk-adapter.ts +191 -0
- package/src/runner/types.ts +158 -0
- package/src/scheduler/__tests__/errors.test.ts +159 -0
- package/src/scheduler/__tests__/interval.test.ts +515 -0
- package/src/scheduler/__tests__/schedule-runner.test.ts +798 -0
- package/src/scheduler/__tests__/schedule-state.test.ts +671 -0
- package/src/scheduler/__tests__/scheduler.test.ts +1280 -0
- package/src/scheduler/errors.ts +101 -0
- package/src/scheduler/index.ts +53 -0
- package/src/scheduler/interval.ts +189 -0
- package/src/scheduler/schedule-runner.ts +442 -0
- package/src/scheduler/schedule-state.ts +211 -0
- package/src/scheduler/scheduler.ts +570 -0
- package/src/scheduler/types.ts +216 -0
- package/src/state/__tests__/directory.test.ts +595 -0
- package/src/state/__tests__/fleet-state.test.ts +868 -0
- package/src/state/__tests__/job-metadata-schema.test.ts +414 -0
- package/src/state/__tests__/job-metadata.test.ts +831 -0
- package/src/state/__tests__/job-output.test.ts +856 -0
- package/src/state/__tests__/session-schema.test.ts +378 -0
- package/src/state/__tests__/session.test.ts +604 -0
- package/src/state/directory.ts +217 -0
- package/src/state/errors.ts +97 -0
- package/src/state/fleet-state.ts +284 -0
- package/src/state/index.ts +79 -0
- package/src/state/job-metadata.ts +445 -0
- package/src/state/job-output.ts +316 -0
- package/src/state/schemas/__tests__/job-output.test.ts +338 -0
- package/src/state/schemas/fleet-state.ts +120 -0
- package/src/state/schemas/index.ts +67 -0
- package/src/state/schemas/job-metadata.ts +181 -0
- package/src/state/schemas/job-output.ts +177 -0
- package/src/state/schemas/session-info.ts +92 -0
- package/src/state/session.ts +253 -0
- package/src/state/types.ts +59 -0
- package/src/state/utils/__tests__/atomic.test.ts +723 -0
- package/src/state/utils/__tests__/reads.test.ts +1071 -0
- package/src/state/utils/atomic.ts +221 -0
- package/src/state/utils/index.ts +6 -0
- package/src/state/utils/reads.ts +512 -0
- package/src/work-sources/__tests__/github.test.ts +1800 -0
- package/src/work-sources/__tests__/manager.test.ts +529 -0
- package/src/work-sources/__tests__/registry.test.ts +477 -0
- package/src/work-sources/__tests__/types.test.ts +479 -0
- package/src/work-sources/adapters/github.ts +1166 -0
- package/src/work-sources/adapters/index.ts +64 -0
- package/src/work-sources/errors.ts +71 -0
- package/src/work-sources/index.ts +148 -0
- package/src/work-sources/manager.ts +413 -0
- package/src/work-sources/registry.ts +178 -0
- package/src/work-sources/types.ts +161 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +19 -0
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { mkdir, writeFile, rm, realpath } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import {
|
|
6
|
+
loadConfig,
|
|
7
|
+
safeLoadConfig,
|
|
8
|
+
findConfigFile,
|
|
9
|
+
CONFIG_FILE_NAMES,
|
|
10
|
+
ConfigNotFoundError,
|
|
11
|
+
AgentLoadError,
|
|
12
|
+
} from "../loader.js";
|
|
13
|
+
import { FileReadError, SchemaValidationError } from "../parser.js";
|
|
14
|
+
import { UndefinedVariableError } from "../interpolate.js";
|
|
15
|
+
|
|
16
|
+
// Helper to create a temp directory structure
|
|
17
|
+
async function createTempDir(): Promise<string> {
|
|
18
|
+
const baseDir = join(tmpdir(), `herdctl-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
19
|
+
await mkdir(baseDir, { recursive: true });
|
|
20
|
+
// Resolve to real path to handle macOS /var -> /private/var symlink
|
|
21
|
+
return await realpath(baseDir);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Helper to create a file
|
|
25
|
+
async function createFile(filePath: string, content: string): Promise<void> {
|
|
26
|
+
await mkdir(join(filePath, ".."), { recursive: true });
|
|
27
|
+
await writeFile(filePath, content, "utf-8");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("CONFIG_FILE_NAMES", () => {
|
|
31
|
+
it("includes herdctl.yaml and herdctl.yml", () => {
|
|
32
|
+
expect(CONFIG_FILE_NAMES).toContain("herdctl.yaml");
|
|
33
|
+
expect(CONFIG_FILE_NAMES).toContain("herdctl.yml");
|
|
34
|
+
expect(CONFIG_FILE_NAMES).toHaveLength(2);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("findConfigFile", () => {
|
|
39
|
+
let tempDir: string;
|
|
40
|
+
|
|
41
|
+
beforeEach(async () => {
|
|
42
|
+
tempDir = await createTempDir();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(async () => {
|
|
46
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("finds herdctl.yaml in the current directory", async () => {
|
|
50
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: 1");
|
|
51
|
+
|
|
52
|
+
const result = await findConfigFile(tempDir);
|
|
53
|
+
|
|
54
|
+
expect(result).not.toBeNull();
|
|
55
|
+
expect(result!.path).toBe(join(tempDir, "herdctl.yaml"));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("finds herdctl.yml in the current directory", async () => {
|
|
59
|
+
await createFile(join(tempDir, "herdctl.yml"), "version: 1");
|
|
60
|
+
|
|
61
|
+
const result = await findConfigFile(tempDir);
|
|
62
|
+
|
|
63
|
+
expect(result).not.toBeNull();
|
|
64
|
+
expect(result!.path).toBe(join(tempDir, "herdctl.yml"));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("prefers herdctl.yaml over herdctl.yml", async () => {
|
|
68
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: 1");
|
|
69
|
+
await createFile(join(tempDir, "herdctl.yml"), "version: 1");
|
|
70
|
+
|
|
71
|
+
const result = await findConfigFile(tempDir);
|
|
72
|
+
|
|
73
|
+
expect(result).not.toBeNull();
|
|
74
|
+
expect(result!.path).toBe(join(tempDir, "herdctl.yaml"));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("finds config file in parent directory", async () => {
|
|
78
|
+
const subDir = join(tempDir, "sub", "deep", "nested");
|
|
79
|
+
await mkdir(subDir, { recursive: true });
|
|
80
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: 1");
|
|
81
|
+
|
|
82
|
+
const result = await findConfigFile(subDir);
|
|
83
|
+
|
|
84
|
+
expect(result).not.toBeNull();
|
|
85
|
+
expect(result!.path).toBe(join(tempDir, "herdctl.yaml"));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("finds config file in intermediate parent directory", async () => {
|
|
89
|
+
const subDir = join(tempDir, "sub", "deep", "nested");
|
|
90
|
+
const intermediateDir = join(tempDir, "sub");
|
|
91
|
+
await mkdir(subDir, { recursive: true });
|
|
92
|
+
await createFile(join(intermediateDir, "herdctl.yaml"), "version: 1");
|
|
93
|
+
|
|
94
|
+
const result = await findConfigFile(subDir);
|
|
95
|
+
|
|
96
|
+
expect(result).not.toBeNull();
|
|
97
|
+
expect(result!.path).toBe(join(intermediateDir, "herdctl.yaml"));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("returns null when no config file is found", async () => {
|
|
101
|
+
const result = await findConfigFile(tempDir);
|
|
102
|
+
|
|
103
|
+
expect(result).toBeNull();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("returns searched paths when config is found", async () => {
|
|
107
|
+
const subDir = join(tempDir, "sub");
|
|
108
|
+
await mkdir(subDir, { recursive: true });
|
|
109
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: 1");
|
|
110
|
+
|
|
111
|
+
const result = await findConfigFile(subDir);
|
|
112
|
+
|
|
113
|
+
expect(result).not.toBeNull();
|
|
114
|
+
expect(result!.searchedPaths).toContain(join(subDir, "herdctl.yaml"));
|
|
115
|
+
expect(result!.searchedPaths).toContain(join(subDir, "herdctl.yml"));
|
|
116
|
+
expect(result!.searchedPaths).toContain(join(tempDir, "herdctl.yaml"));
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("ConfigNotFoundError", () => {
|
|
121
|
+
it("creates error with correct properties", () => {
|
|
122
|
+
const searchedPaths = ["/path/a", "/path/b"];
|
|
123
|
+
const error = new ConfigNotFoundError("/start/dir", searchedPaths);
|
|
124
|
+
|
|
125
|
+
expect(error.name).toBe("ConfigNotFoundError");
|
|
126
|
+
expect(error.startDirectory).toBe("/start/dir");
|
|
127
|
+
expect(error.searchedPaths).toEqual(searchedPaths);
|
|
128
|
+
expect(error.message).toContain("No herdctl configuration file found");
|
|
129
|
+
expect(error.message).toContain("/start/dir");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("AgentLoadError", () => {
|
|
134
|
+
it("creates error with correct properties", () => {
|
|
135
|
+
const cause = new Error("File not found");
|
|
136
|
+
const error = new AgentLoadError("./agents/test.yaml", cause, "test-agent");
|
|
137
|
+
|
|
138
|
+
expect(error.name).toBe("AgentLoadError");
|
|
139
|
+
expect(error.agentPath).toBe("./agents/test.yaml");
|
|
140
|
+
expect(error.agentName).toBe("test-agent");
|
|
141
|
+
expect(error.cause).toBe(cause);
|
|
142
|
+
expect(error.message).toContain("test.yaml");
|
|
143
|
+
expect(error.message).toContain("test-agent");
|
|
144
|
+
expect(error.message).toContain("File not found");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("creates error without agent name", () => {
|
|
148
|
+
const cause = new Error("File not found");
|
|
149
|
+
const error = new AgentLoadError("./agents/test.yaml", cause);
|
|
150
|
+
|
|
151
|
+
expect(error.agentName).toBeUndefined();
|
|
152
|
+
expect(error.message).not.toContain("(undefined)");
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("loadConfig", () => {
|
|
157
|
+
let tempDir: string;
|
|
158
|
+
|
|
159
|
+
beforeEach(async () => {
|
|
160
|
+
tempDir = await createTempDir();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
afterEach(async () => {
|
|
164
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("file discovery", () => {
|
|
168
|
+
it("loads config from explicit yaml file path", async () => {
|
|
169
|
+
const configPath = join(tempDir, "herdctl.yaml");
|
|
170
|
+
await createFile(configPath, "version: 1");
|
|
171
|
+
|
|
172
|
+
const result = await loadConfig(configPath);
|
|
173
|
+
|
|
174
|
+
expect(result.fleet.version).toBe(1);
|
|
175
|
+
expect(result.configPath).toBe(configPath);
|
|
176
|
+
expect(result.configDir).toBe(tempDir);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("loads config from explicit yml file path", async () => {
|
|
180
|
+
const configPath = join(tempDir, "herdctl.yml");
|
|
181
|
+
await createFile(configPath, "version: 1");
|
|
182
|
+
|
|
183
|
+
const result = await loadConfig(configPath);
|
|
184
|
+
|
|
185
|
+
expect(result.fleet.version).toBe(1);
|
|
186
|
+
expect(result.configPath).toBe(configPath);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("searches from directory when path is a directory", async () => {
|
|
190
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: 1");
|
|
191
|
+
|
|
192
|
+
const result = await loadConfig(tempDir);
|
|
193
|
+
|
|
194
|
+
expect(result.fleet.version).toBe(1);
|
|
195
|
+
expect(result.configPath).toBe(join(tempDir, "herdctl.yaml"));
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("throws ConfigNotFoundError when no config found in directory", async () => {
|
|
199
|
+
await expect(loadConfig(tempDir)).rejects.toThrow(ConfigNotFoundError);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("throws FileReadError when specified file does not exist", async () => {
|
|
203
|
+
const nonExistentPath = join(tempDir, "nonexistent.yaml");
|
|
204
|
+
|
|
205
|
+
await expect(loadConfig(nonExistentPath)).rejects.toThrow(FileReadError);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe("fleet configuration parsing", () => {
|
|
210
|
+
it("parses empty config with defaults", async () => {
|
|
211
|
+
await createFile(join(tempDir, "herdctl.yaml"), "");
|
|
212
|
+
|
|
213
|
+
const result = await loadConfig(tempDir);
|
|
214
|
+
|
|
215
|
+
expect(result.fleet.version).toBe(1);
|
|
216
|
+
expect(result.fleet.agents).toEqual([]);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("parses complete fleet configuration", async () => {
|
|
220
|
+
const config = `
|
|
221
|
+
version: 1
|
|
222
|
+
fleet:
|
|
223
|
+
name: test-fleet
|
|
224
|
+
description: A test fleet
|
|
225
|
+
defaults:
|
|
226
|
+
model: claude-sonnet-4-20250514
|
|
227
|
+
max_turns: 50
|
|
228
|
+
workspace:
|
|
229
|
+
root: ./workspace
|
|
230
|
+
`;
|
|
231
|
+
await createFile(join(tempDir, "herdctl.yaml"), config);
|
|
232
|
+
|
|
233
|
+
const result = await loadConfig(tempDir);
|
|
234
|
+
|
|
235
|
+
expect(result.fleet.fleet?.name).toBe("test-fleet");
|
|
236
|
+
expect(result.fleet.fleet?.description).toBe("A test fleet");
|
|
237
|
+
expect(result.fleet.defaults?.model).toBe("claude-sonnet-4-20250514");
|
|
238
|
+
expect(result.fleet.defaults?.max_turns).toBe(50);
|
|
239
|
+
expect(result.fleet.workspace?.root).toBe("./workspace");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("throws on invalid YAML syntax", async () => {
|
|
243
|
+
await createFile(join(tempDir, "herdctl.yaml"), "invalid: yaml: syntax:");
|
|
244
|
+
|
|
245
|
+
await expect(loadConfig(tempDir)).rejects.toThrow("Invalid YAML syntax");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("throws SchemaValidationError on invalid schema", async () => {
|
|
249
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: -1");
|
|
250
|
+
|
|
251
|
+
await expect(loadConfig(tempDir)).rejects.toThrow(SchemaValidationError);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe("agent loading", () => {
|
|
256
|
+
it("loads agents referenced in fleet config", async () => {
|
|
257
|
+
const fleetConfig = `
|
|
258
|
+
version: 1
|
|
259
|
+
agents:
|
|
260
|
+
- path: ./agents/test-agent.yaml
|
|
261
|
+
`;
|
|
262
|
+
const agentConfig = `
|
|
263
|
+
name: test-agent
|
|
264
|
+
description: A test agent
|
|
265
|
+
`;
|
|
266
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
267
|
+
await createFile(join(tempDir, "agents", "test-agent.yaml"), agentConfig);
|
|
268
|
+
|
|
269
|
+
const result = await loadConfig(tempDir);
|
|
270
|
+
|
|
271
|
+
expect(result.agents).toHaveLength(1);
|
|
272
|
+
expect(result.agents[0].name).toBe("test-agent");
|
|
273
|
+
expect(result.agents[0].description).toBe("A test agent");
|
|
274
|
+
expect(result.agents[0].configPath).toBe(join(tempDir, "agents", "test-agent.yaml"));
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("loads multiple agents", async () => {
|
|
278
|
+
const fleetConfig = `
|
|
279
|
+
version: 1
|
|
280
|
+
agents:
|
|
281
|
+
- path: ./agents/agent1.yaml
|
|
282
|
+
- path: ./agents/agent2.yaml
|
|
283
|
+
`;
|
|
284
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
285
|
+
await createFile(join(tempDir, "agents", "agent1.yaml"), "name: agent-one");
|
|
286
|
+
await createFile(join(tempDir, "agents", "agent2.yaml"), "name: agent-two");
|
|
287
|
+
|
|
288
|
+
const result = await loadConfig(tempDir);
|
|
289
|
+
|
|
290
|
+
expect(result.agents).toHaveLength(2);
|
|
291
|
+
expect(result.agents[0].name).toBe("agent-one");
|
|
292
|
+
expect(result.agents[1].name).toBe("agent-two");
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("throws AgentLoadError when agent file not found", async () => {
|
|
296
|
+
const fleetConfig = `
|
|
297
|
+
version: 1
|
|
298
|
+
agents:
|
|
299
|
+
- path: ./agents/nonexistent.yaml
|
|
300
|
+
`;
|
|
301
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
302
|
+
|
|
303
|
+
await expect(loadConfig(tempDir)).rejects.toThrow(AgentLoadError);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("throws AgentLoadError when agent YAML is invalid", async () => {
|
|
307
|
+
const fleetConfig = `
|
|
308
|
+
version: 1
|
|
309
|
+
agents:
|
|
310
|
+
- path: ./agents/invalid.yaml
|
|
311
|
+
`;
|
|
312
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
313
|
+
await createFile(join(tempDir, "agents", "invalid.yaml"), "invalid: yaml: syntax:");
|
|
314
|
+
|
|
315
|
+
await expect(loadConfig(tempDir)).rejects.toThrow(AgentLoadError);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it("throws AgentLoadError when agent schema is invalid", async () => {
|
|
319
|
+
const fleetConfig = `
|
|
320
|
+
version: 1
|
|
321
|
+
agents:
|
|
322
|
+
- path: ./agents/invalid.yaml
|
|
323
|
+
`;
|
|
324
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
325
|
+
// Missing required 'name' field
|
|
326
|
+
await createFile(join(tempDir, "agents", "invalid.yaml"), "description: no name");
|
|
327
|
+
|
|
328
|
+
await expect(loadConfig(tempDir)).rejects.toThrow(AgentLoadError);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("resolves absolute agent paths correctly", async () => {
|
|
332
|
+
const agentPath = join(tempDir, "elsewhere", "agent.yaml");
|
|
333
|
+
const fleetConfig = `
|
|
334
|
+
version: 1
|
|
335
|
+
agents:
|
|
336
|
+
- path: ${agentPath}
|
|
337
|
+
`;
|
|
338
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
339
|
+
await createFile(agentPath, "name: elsewhere-agent");
|
|
340
|
+
|
|
341
|
+
const result = await loadConfig(tempDir);
|
|
342
|
+
|
|
343
|
+
expect(result.agents[0].name).toBe("elsewhere-agent");
|
|
344
|
+
expect(result.agents[0].configPath).toBe(agentPath);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
describe("defaults merging", () => {
|
|
349
|
+
it("merges fleet defaults into agent config", async () => {
|
|
350
|
+
const fleetConfig = `
|
|
351
|
+
version: 1
|
|
352
|
+
defaults:
|
|
353
|
+
model: claude-sonnet-4-20250514
|
|
354
|
+
max_turns: 100
|
|
355
|
+
permission_mode: acceptEdits
|
|
356
|
+
agents:
|
|
357
|
+
- path: ./agents/test.yaml
|
|
358
|
+
`;
|
|
359
|
+
const agentConfig = `
|
|
360
|
+
name: test-agent
|
|
361
|
+
`;
|
|
362
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
363
|
+
await createFile(join(tempDir, "agents", "test.yaml"), agentConfig);
|
|
364
|
+
|
|
365
|
+
const result = await loadConfig(tempDir);
|
|
366
|
+
|
|
367
|
+
expect(result.agents[0].model).toBe("claude-sonnet-4-20250514");
|
|
368
|
+
expect(result.agents[0].max_turns).toBe(100);
|
|
369
|
+
expect(result.agents[0].permission_mode).toBe("acceptEdits");
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("agent values override fleet defaults", async () => {
|
|
373
|
+
const fleetConfig = `
|
|
374
|
+
version: 1
|
|
375
|
+
defaults:
|
|
376
|
+
model: claude-sonnet-4-20250514
|
|
377
|
+
max_turns: 100
|
|
378
|
+
agents:
|
|
379
|
+
- path: ./agents/test.yaml
|
|
380
|
+
`;
|
|
381
|
+
const agentConfig = `
|
|
382
|
+
name: test-agent
|
|
383
|
+
model: claude-opus-4-20250514
|
|
384
|
+
max_turns: 50
|
|
385
|
+
`;
|
|
386
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
387
|
+
await createFile(join(tempDir, "agents", "test.yaml"), agentConfig);
|
|
388
|
+
|
|
389
|
+
const result = await loadConfig(tempDir);
|
|
390
|
+
|
|
391
|
+
expect(result.agents[0].model).toBe("claude-opus-4-20250514");
|
|
392
|
+
expect(result.agents[0].max_turns).toBe(50);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("deep merges nested objects", async () => {
|
|
396
|
+
const fleetConfig = `
|
|
397
|
+
version: 1
|
|
398
|
+
defaults:
|
|
399
|
+
permissions:
|
|
400
|
+
mode: acceptEdits
|
|
401
|
+
allowed_tools:
|
|
402
|
+
- Read
|
|
403
|
+
- Write
|
|
404
|
+
agents:
|
|
405
|
+
- path: ./agents/test.yaml
|
|
406
|
+
`;
|
|
407
|
+
const agentConfig = `
|
|
408
|
+
name: test-agent
|
|
409
|
+
permissions:
|
|
410
|
+
allowed_tools:
|
|
411
|
+
- Bash
|
|
412
|
+
`;
|
|
413
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
414
|
+
await createFile(join(tempDir, "agents", "test.yaml"), agentConfig);
|
|
415
|
+
|
|
416
|
+
const result = await loadConfig(tempDir);
|
|
417
|
+
|
|
418
|
+
// Mode should come from defaults
|
|
419
|
+
expect(result.agents[0].permissions?.mode).toBe("acceptEdits");
|
|
420
|
+
// Arrays are replaced, not merged
|
|
421
|
+
expect(result.agents[0].permissions?.allowed_tools).toEqual(["Bash"]);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("skips merging when mergeDefaults is false", async () => {
|
|
425
|
+
const fleetConfig = `
|
|
426
|
+
version: 1
|
|
427
|
+
defaults:
|
|
428
|
+
model: claude-sonnet-4-20250514
|
|
429
|
+
agents:
|
|
430
|
+
- path: ./agents/test.yaml
|
|
431
|
+
`;
|
|
432
|
+
const agentConfig = `
|
|
433
|
+
name: test-agent
|
|
434
|
+
`;
|
|
435
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
436
|
+
await createFile(join(tempDir, "agents", "test.yaml"), agentConfig);
|
|
437
|
+
|
|
438
|
+
const result = await loadConfig(tempDir, { mergeDefaults: false });
|
|
439
|
+
|
|
440
|
+
expect(result.agents[0].model).toBeUndefined();
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
describe("environment interpolation", () => {
|
|
445
|
+
it("interpolates environment variables in fleet config", async () => {
|
|
446
|
+
const fleetConfig = `
|
|
447
|
+
version: 1
|
|
448
|
+
fleet:
|
|
449
|
+
name: \${FLEET_NAME}
|
|
450
|
+
`;
|
|
451
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
452
|
+
|
|
453
|
+
const result = await loadConfig(tempDir, {
|
|
454
|
+
env: { FLEET_NAME: "my-fleet" },
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
expect(result.fleet.fleet?.name).toBe("my-fleet");
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("interpolates environment variables in agent config", async () => {
|
|
461
|
+
const fleetConfig = `
|
|
462
|
+
version: 1
|
|
463
|
+
agents:
|
|
464
|
+
- path: ./agents/test.yaml
|
|
465
|
+
`;
|
|
466
|
+
const agentConfig = `
|
|
467
|
+
name: test-agent
|
|
468
|
+
model: \${MODEL_NAME}
|
|
469
|
+
`;
|
|
470
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
471
|
+
await createFile(join(tempDir, "agents", "test.yaml"), agentConfig);
|
|
472
|
+
|
|
473
|
+
const result = await loadConfig(tempDir, {
|
|
474
|
+
env: { MODEL_NAME: "claude-sonnet-4-20250514" },
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
expect(result.agents[0].model).toBe("claude-sonnet-4-20250514");
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it("uses default values for undefined variables", async () => {
|
|
481
|
+
const fleetConfig = `
|
|
482
|
+
version: 1
|
|
483
|
+
fleet:
|
|
484
|
+
name: \${FLEET_NAME:-default-fleet}
|
|
485
|
+
`;
|
|
486
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
487
|
+
|
|
488
|
+
const result = await loadConfig(tempDir, { env: {} });
|
|
489
|
+
|
|
490
|
+
expect(result.fleet.fleet?.name).toBe("default-fleet");
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it("throws UndefinedVariableError for undefined variables without default", async () => {
|
|
494
|
+
const fleetConfig = `
|
|
495
|
+
version: 1
|
|
496
|
+
fleet:
|
|
497
|
+
name: \${UNDEFINED_VAR}
|
|
498
|
+
`;
|
|
499
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
500
|
+
|
|
501
|
+
await expect(loadConfig(tempDir, { env: {} })).rejects.toThrow(UndefinedVariableError);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it("skips interpolation when interpolate is false", async () => {
|
|
505
|
+
const fleetConfig = `
|
|
506
|
+
version: 1
|
|
507
|
+
fleet:
|
|
508
|
+
name: \${FLEET_NAME}
|
|
509
|
+
`;
|
|
510
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
511
|
+
|
|
512
|
+
const result = await loadConfig(tempDir, {
|
|
513
|
+
interpolate: false,
|
|
514
|
+
env: { FLEET_NAME: "my-fleet" },
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
expect(result.fleet.fleet?.name).toBe("${FLEET_NAME}");
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
describe("result structure", () => {
|
|
522
|
+
it("returns correct structure with all fields", async () => {
|
|
523
|
+
const fleetConfig = `
|
|
524
|
+
version: 1
|
|
525
|
+
fleet:
|
|
526
|
+
name: test-fleet
|
|
527
|
+
agents:
|
|
528
|
+
- path: ./agents/test.yaml
|
|
529
|
+
`;
|
|
530
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
531
|
+
await createFile(join(tempDir, "agents", "test.yaml"), "name: test-agent");
|
|
532
|
+
|
|
533
|
+
const result = await loadConfig(tempDir);
|
|
534
|
+
|
|
535
|
+
expect(result).toHaveProperty("fleet");
|
|
536
|
+
expect(result).toHaveProperty("agents");
|
|
537
|
+
expect(result).toHaveProperty("configPath");
|
|
538
|
+
expect(result).toHaveProperty("configDir");
|
|
539
|
+
expect(result.configPath).toBe(join(tempDir, "herdctl.yaml"));
|
|
540
|
+
expect(result.configDir).toBe(tempDir);
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it("returns empty agents array when no agents defined", async () => {
|
|
544
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: 1");
|
|
545
|
+
|
|
546
|
+
const result = await loadConfig(tempDir);
|
|
547
|
+
|
|
548
|
+
expect(result.agents).toEqual([]);
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
describe("safeLoadConfig", () => {
|
|
554
|
+
let tempDir: string;
|
|
555
|
+
|
|
556
|
+
beforeEach(async () => {
|
|
557
|
+
tempDir = await createTempDir();
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
afterEach(async () => {
|
|
561
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("returns success result when config loads successfully", async () => {
|
|
565
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: 1");
|
|
566
|
+
|
|
567
|
+
const result = await safeLoadConfig(tempDir);
|
|
568
|
+
|
|
569
|
+
expect(result.success).toBe(true);
|
|
570
|
+
if (result.success) {
|
|
571
|
+
expect(result.data.fleet.version).toBe(1);
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it("returns failure result with ConfigNotFoundError", async () => {
|
|
576
|
+
const result = await safeLoadConfig(tempDir);
|
|
577
|
+
|
|
578
|
+
expect(result.success).toBe(false);
|
|
579
|
+
if (!result.success) {
|
|
580
|
+
expect(result.error).toBeInstanceOf(ConfigNotFoundError);
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it("returns failure result with FileReadError", async () => {
|
|
585
|
+
const result = await safeLoadConfig(join(tempDir, "nonexistent.yaml"));
|
|
586
|
+
|
|
587
|
+
expect(result.success).toBe(false);
|
|
588
|
+
if (!result.success) {
|
|
589
|
+
expect(result.error).toBeInstanceOf(FileReadError);
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it("returns failure result with SchemaValidationError", async () => {
|
|
594
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: invalid");
|
|
595
|
+
|
|
596
|
+
const result = await safeLoadConfig(tempDir);
|
|
597
|
+
|
|
598
|
+
expect(result.success).toBe(false);
|
|
599
|
+
if (!result.success) {
|
|
600
|
+
expect(result.error).toBeInstanceOf(SchemaValidationError);
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it("returns failure result with AgentLoadError", async () => {
|
|
605
|
+
const fleetConfig = `
|
|
606
|
+
version: 1
|
|
607
|
+
agents:
|
|
608
|
+
- path: ./nonexistent.yaml
|
|
609
|
+
`;
|
|
610
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
611
|
+
|
|
612
|
+
const result = await safeLoadConfig(tempDir);
|
|
613
|
+
|
|
614
|
+
expect(result.success).toBe(false);
|
|
615
|
+
if (!result.success) {
|
|
616
|
+
expect(result.error).toBeInstanceOf(AgentLoadError);
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it("wraps non-ConfigError errors", async () => {
|
|
621
|
+
// Create a file that will cause an unexpected error
|
|
622
|
+
// This is difficult to trigger, so we'll test the general behavior
|
|
623
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: 1");
|
|
624
|
+
|
|
625
|
+
const result = await safeLoadConfig(tempDir);
|
|
626
|
+
|
|
627
|
+
expect(result.success).toBe(true);
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
describe("auto-discovery from cwd", () => {
|
|
632
|
+
let tempDir: string;
|
|
633
|
+
let originalCwd: string;
|
|
634
|
+
|
|
635
|
+
beforeEach(async () => {
|
|
636
|
+
tempDir = await createTempDir();
|
|
637
|
+
originalCwd = process.cwd();
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
afterEach(async () => {
|
|
641
|
+
process.chdir(originalCwd);
|
|
642
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
it("finds config from current working directory when no path provided", async () => {
|
|
646
|
+
await createFile(join(tempDir, "herdctl.yaml"), "version: 1");
|
|
647
|
+
process.chdir(tempDir);
|
|
648
|
+
|
|
649
|
+
const result = await loadConfig();
|
|
650
|
+
|
|
651
|
+
expect(result.fleet.version).toBe(1);
|
|
652
|
+
expect(result.configPath).toBe(join(tempDir, "herdctl.yaml"));
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it("throws ConfigNotFoundError when no config in cwd hierarchy", async () => {
|
|
656
|
+
process.chdir(tempDir);
|
|
657
|
+
|
|
658
|
+
await expect(loadConfig()).rejects.toThrow(ConfigNotFoundError);
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
describe("integration scenarios", () => {
|
|
663
|
+
let tempDir: string;
|
|
664
|
+
|
|
665
|
+
beforeEach(async () => {
|
|
666
|
+
tempDir = await createTempDir();
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
afterEach(async () => {
|
|
670
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
it("handles a realistic fleet configuration", async () => {
|
|
674
|
+
const fleetConfig = `
|
|
675
|
+
version: 1
|
|
676
|
+
|
|
677
|
+
fleet:
|
|
678
|
+
name: example-fleet
|
|
679
|
+
description: An example herdctl fleet
|
|
680
|
+
|
|
681
|
+
defaults:
|
|
682
|
+
model: claude-sonnet-4-20250514
|
|
683
|
+
max_turns: 50
|
|
684
|
+
permission_mode: acceptEdits
|
|
685
|
+
permissions:
|
|
686
|
+
allowed_tools:
|
|
687
|
+
- Read
|
|
688
|
+
- Edit
|
|
689
|
+
- Write
|
|
690
|
+
- Bash
|
|
691
|
+
- Glob
|
|
692
|
+
- Grep
|
|
693
|
+
|
|
694
|
+
workspace:
|
|
695
|
+
root: ./workspace
|
|
696
|
+
auto_clone: true
|
|
697
|
+
clone_depth: 1
|
|
698
|
+
|
|
699
|
+
agents:
|
|
700
|
+
- path: ./agents/coder.yaml
|
|
701
|
+
- path: ./agents/reviewer.yaml
|
|
702
|
+
|
|
703
|
+
chat:
|
|
704
|
+
discord:
|
|
705
|
+
enabled: true
|
|
706
|
+
token_env: DISCORD_TOKEN
|
|
707
|
+
`;
|
|
708
|
+
|
|
709
|
+
const coderAgent = `
|
|
710
|
+
name: coder
|
|
711
|
+
description: Writes code based on issues
|
|
712
|
+
system_prompt: |
|
|
713
|
+
You are a coding assistant.
|
|
714
|
+
Write clean, maintainable code.
|
|
715
|
+
permissions:
|
|
716
|
+
allowed_tools:
|
|
717
|
+
- Read
|
|
718
|
+
- Edit
|
|
719
|
+
- Write
|
|
720
|
+
- Bash
|
|
721
|
+
`;
|
|
722
|
+
|
|
723
|
+
const reviewerAgent = `
|
|
724
|
+
name: reviewer
|
|
725
|
+
description: Reviews pull requests
|
|
726
|
+
model: claude-opus-4-20250514
|
|
727
|
+
max_turns: 25
|
|
728
|
+
system_prompt: |
|
|
729
|
+
You are a code reviewer.
|
|
730
|
+
Focus on code quality and security.
|
|
731
|
+
`;
|
|
732
|
+
|
|
733
|
+
await createFile(join(tempDir, "herdctl.yaml"), fleetConfig);
|
|
734
|
+
await createFile(join(tempDir, "agents", "coder.yaml"), coderAgent);
|
|
735
|
+
await createFile(join(tempDir, "agents", "reviewer.yaml"), reviewerAgent);
|
|
736
|
+
|
|
737
|
+
const result = await loadConfig(tempDir, {
|
|
738
|
+
env: { DISCORD_TOKEN: "test-token" },
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
// Fleet config
|
|
742
|
+
expect(result.fleet.fleet?.name).toBe("example-fleet");
|
|
743
|
+
expect(result.fleet.defaults?.model).toBe("claude-sonnet-4-20250514");
|
|
744
|
+
expect(result.fleet.workspace?.root).toBe("./workspace");
|
|
745
|
+
expect(result.fleet.chat?.discord?.enabled).toBe(true);
|
|
746
|
+
|
|
747
|
+
// Agents loaded
|
|
748
|
+
expect(result.agents).toHaveLength(2);
|
|
749
|
+
|
|
750
|
+
// Coder agent - inherits defaults
|
|
751
|
+
const coder = result.agents.find(a => a.name === "coder");
|
|
752
|
+
expect(coder).toBeDefined();
|
|
753
|
+
expect(coder!.model).toBe("claude-sonnet-4-20250514"); // from defaults
|
|
754
|
+
expect(coder!.max_turns).toBe(50); // from defaults
|
|
755
|
+
expect(coder!.permission_mode).toBe("acceptEdits"); // from defaults
|
|
756
|
+
expect(coder!.permissions?.allowed_tools).toEqual(["Read", "Edit", "Write", "Bash"]); // agent override
|
|
757
|
+
|
|
758
|
+
// Reviewer agent - overrides defaults
|
|
759
|
+
const reviewer = result.agents.find(a => a.name === "reviewer");
|
|
760
|
+
expect(reviewer).toBeDefined();
|
|
761
|
+
expect(reviewer!.model).toBe("claude-opus-4-20250514"); // agent override
|
|
762
|
+
expect(reviewer!.max_turns).toBe(25); // agent override
|
|
763
|
+
expect(reviewer!.permission_mode).toBe("acceptEdits"); // from defaults
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
it("handles nested directory structure", async () => {
|
|
767
|
+
const subDir = join(tempDir, "projects", "myproject", "src");
|
|
768
|
+
await mkdir(subDir, { recursive: true });
|
|
769
|
+
|
|
770
|
+
await createFile(join(tempDir, "projects", "myproject", "herdctl.yaml"), `
|
|
771
|
+
version: 1
|
|
772
|
+
agents:
|
|
773
|
+
- path: ./config/agent.yaml
|
|
774
|
+
`);
|
|
775
|
+
await createFile(join(tempDir, "projects", "myproject", "config", "agent.yaml"), `
|
|
776
|
+
name: nested-agent
|
|
777
|
+
`);
|
|
778
|
+
|
|
779
|
+
const result = await loadConfig(subDir);
|
|
780
|
+
|
|
781
|
+
expect(result.configPath).toBe(join(tempDir, "projects", "myproject", "herdctl.yaml"));
|
|
782
|
+
expect(result.agents[0].name).toBe("nested-agent");
|
|
783
|
+
});
|
|
784
|
+
});
|