@herdctl/core 0.0.1 → 0.0.2
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/config/__tests__/agent.test.js +31 -13
- package/dist/config/__tests__/agent.test.js.map +1 -1
- package/dist/config/__tests__/merge.test.js +9 -2
- package/dist/config/__tests__/merge.test.js.map +1 -1
- package/dist/config/__tests__/schema.test.js +350 -1
- package/dist/config/__tests__/schema.test.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +3 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +828 -24
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +118 -6
- package/dist/config/schema.js.map +1 -1
- package/dist/fleet-manager/__tests__/coverage.test.js +11 -332
- package/dist/fleet-manager/__tests__/coverage.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/errors.test.js +1 -49
- package/dist/fleet-manager/__tests__/errors.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/integration.test.js +109 -0
- package/dist/fleet-manager/__tests__/integration.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/reload.test.js +1 -1
- package/dist/fleet-manager/__tests__/reload.test.js.map +1 -1
- package/dist/fleet-manager/config-reload.d.ts +164 -0
- package/dist/fleet-manager/config-reload.d.ts.map +1 -0
- package/dist/fleet-manager/config-reload.js +445 -0
- package/dist/fleet-manager/config-reload.js.map +1 -0
- package/dist/fleet-manager/context.d.ts +76 -0
- package/dist/fleet-manager/context.d.ts.map +1 -0
- package/dist/fleet-manager/context.js +11 -0
- package/dist/fleet-manager/context.js.map +1 -0
- package/dist/fleet-manager/errors.d.ts +0 -25
- package/dist/fleet-manager/errors.d.ts.map +1 -1
- package/dist/fleet-manager/errors.js +0 -38
- package/dist/fleet-manager/errors.js.map +1 -1
- package/dist/fleet-manager/event-emitters.d.ts +123 -0
- package/dist/fleet-manager/event-emitters.d.ts.map +1 -0
- package/dist/fleet-manager/event-emitters.js +136 -0
- package/dist/fleet-manager/event-emitters.js.map +1 -0
- package/dist/fleet-manager/event-types.d.ts +0 -15
- package/dist/fleet-manager/event-types.d.ts.map +1 -1
- package/dist/fleet-manager/fleet-manager.d.ts +40 -653
- package/dist/fleet-manager/fleet-manager.d.ts.map +1 -1
- package/dist/fleet-manager/fleet-manager.js +95 -1720
- package/dist/fleet-manager/fleet-manager.js.map +1 -1
- package/dist/fleet-manager/index.d.ts +13 -2
- package/dist/fleet-manager/index.d.ts.map +1 -1
- package/dist/fleet-manager/index.js +19 -6
- package/dist/fleet-manager/index.js.map +1 -1
- package/dist/fleet-manager/job-control.d.ts +64 -0
- package/dist/fleet-manager/job-control.d.ts.map +1 -0
- package/dist/fleet-manager/job-control.js +296 -0
- package/dist/fleet-manager/job-control.js.map +1 -0
- package/dist/fleet-manager/log-streaming.d.ts +171 -0
- package/dist/fleet-manager/log-streaming.d.ts.map +1 -0
- package/dist/fleet-manager/log-streaming.js +503 -0
- package/dist/fleet-manager/log-streaming.js.map +1 -0
- package/dist/fleet-manager/schedule-executor.d.ts +63 -0
- package/dist/fleet-manager/schedule-executor.d.ts.map +1 -0
- package/dist/fleet-manager/schedule-executor.js +209 -0
- package/dist/fleet-manager/schedule-executor.js.map +1 -0
- package/dist/fleet-manager/schedule-management.d.ts +71 -0
- package/dist/fleet-manager/schedule-management.d.ts.map +1 -0
- package/dist/fleet-manager/schedule-management.js +171 -0
- package/dist/fleet-manager/schedule-management.js.map +1 -0
- package/dist/fleet-manager/status-queries.d.ts +105 -0
- package/dist/fleet-manager/status-queries.d.ts.map +1 -0
- package/dist/fleet-manager/status-queries.js +247 -0
- package/dist/fleet-manager/status-queries.js.map +1 -0
- package/dist/fleet-manager/types.d.ts +0 -39
- package/dist/fleet-manager/types.d.ts.map +1 -1
- package/dist/runner/__tests__/job-executor.test.js +206 -1
- package/dist/runner/__tests__/job-executor.test.js.map +1 -1
- package/dist/runner/job-executor.d.ts +9 -0
- package/dist/runner/job-executor.d.ts.map +1 -1
- package/dist/runner/job-executor.js +78 -4
- package/dist/runner/job-executor.js.map +1 -1
- package/dist/runner/types.d.ts +2 -0
- package/dist/runner/types.d.ts.map +1 -1
- package/dist/scheduler/__tests__/cron.test.d.ts +2 -0
- package/dist/scheduler/__tests__/cron.test.d.ts.map +1 -0
- package/dist/scheduler/__tests__/cron.test.js +867 -0
- package/dist/scheduler/__tests__/cron.test.js.map +1 -0
- package/dist/scheduler/__tests__/scheduler.test.js +164 -5
- package/dist/scheduler/__tests__/scheduler.test.js.map +1 -1
- package/dist/scheduler/cron.d.ts +126 -0
- package/dist/scheduler/cron.d.ts.map +1 -0
- package/dist/scheduler/cron.js +390 -0
- package/dist/scheduler/cron.js.map +1 -0
- package/dist/scheduler/errors.d.ts +81 -1
- package/dist/scheduler/errors.d.ts.map +1 -1
- package/dist/scheduler/errors.js +81 -6
- package/dist/scheduler/errors.js.map +1 -1
- package/dist/scheduler/index.d.ts +1 -0
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +2 -0
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/schedule-runner.d.ts +2 -2
- package/dist/scheduler/schedule-runner.d.ts.map +1 -1
- package/dist/scheduler/schedule-runner.js +20 -8
- package/dist/scheduler/schedule-runner.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts +4 -4
- package/dist/scheduler/scheduler.d.ts.map +1 -1
- package/dist/scheduler/scheduler.js +86 -20
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/types.d.ts +1 -1
- package/dist/scheduler/types.d.ts.map +1 -1
- package/dist/state/schemas/job-metadata.d.ts +2 -2
- package/package.json +33 -8
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-test.log +0 -219
- package/.turbo/turbo-typecheck.log +0 -4
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/coverage-final.json +0 -51
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -251
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/coverage/src/config/index.html +0 -191
- package/coverage/src/config/index.ts.html +0 -442
- package/coverage/src/config/interpolate.ts.html +0 -652
- package/coverage/src/config/loader.ts.html +0 -1501
- package/coverage/src/config/merge.ts.html +0 -823
- package/coverage/src/config/parser.ts.html +0 -1213
- package/coverage/src/config/schema.ts.html +0 -1123
- package/coverage/src/fleet-manager/errors.ts.html +0 -2326
- package/coverage/src/fleet-manager/event-types.ts.html +0 -1219
- package/coverage/src/fleet-manager/fleet-manager.ts.html +0 -7030
- package/coverage/src/fleet-manager/index.html +0 -206
- package/coverage/src/fleet-manager/index.ts.html +0 -469
- package/coverage/src/fleet-manager/job-manager.ts.html +0 -2074
- package/coverage/src/fleet-manager/job-queue.ts.html +0 -2479
- package/coverage/src/fleet-manager/types.ts.html +0 -2602
- package/coverage/src/index.html +0 -116
- package/coverage/src/index.ts.html +0 -181
- package/coverage/src/runner/errors.ts.html +0 -1006
- package/coverage/src/runner/index.html +0 -191
- package/coverage/src/runner/index.ts.html +0 -256
- package/coverage/src/runner/job-executor.ts.html +0 -1429
- package/coverage/src/runner/message-processor.ts.html +0 -1150
- package/coverage/src/runner/sdk-adapter.ts.html +0 -658
- package/coverage/src/runner/types.ts.html +0 -559
- package/coverage/src/scheduler/errors.ts.html +0 -388
- package/coverage/src/scheduler/index.html +0 -206
- package/coverage/src/scheduler/index.ts.html +0 -244
- package/coverage/src/scheduler/interval.ts.html +0 -652
- package/coverage/src/scheduler/schedule-runner.ts.html +0 -1411
- package/coverage/src/scheduler/schedule-state.ts.html +0 -718
- package/coverage/src/scheduler/scheduler.ts.html +0 -1795
- package/coverage/src/scheduler/types.ts.html +0 -733
- package/coverage/src/state/directory.ts.html +0 -736
- package/coverage/src/state/errors.ts.html +0 -376
- package/coverage/src/state/fleet-state.ts.html +0 -937
- package/coverage/src/state/index.html +0 -221
- package/coverage/src/state/index.ts.html +0 -322
- package/coverage/src/state/job-metadata.ts.html +0 -1420
- package/coverage/src/state/job-output.ts.html +0 -1033
- package/coverage/src/state/schemas/fleet-state.ts.html +0 -445
- package/coverage/src/state/schemas/index.html +0 -176
- package/coverage/src/state/schemas/index.ts.html +0 -286
- package/coverage/src/state/schemas/job-metadata.ts.html +0 -628
- package/coverage/src/state/schemas/job-output.ts.html +0 -616
- package/coverage/src/state/schemas/session-info.ts.html +0 -361
- package/coverage/src/state/session.ts.html +0 -844
- package/coverage/src/state/types.ts.html +0 -262
- package/coverage/src/state/utils/atomic.ts.html +0 -748
- package/coverage/src/state/utils/index.html +0 -146
- package/coverage/src/state/utils/index.ts.html +0 -103
- package/coverage/src/state/utils/reads.ts.html +0 -1621
- package/coverage/src/work-sources/adapters/github.ts.html +0 -3583
- package/coverage/src/work-sources/adapters/index.html +0 -131
- package/coverage/src/work-sources/adapters/index.ts.html +0 -277
- package/coverage/src/work-sources/errors.ts.html +0 -298
- package/coverage/src/work-sources/index.html +0 -176
- package/coverage/src/work-sources/index.ts.html +0 -529
- package/coverage/src/work-sources/manager.ts.html +0 -1324
- package/coverage/src/work-sources/registry.ts.html +0 -619
- package/coverage/src/work-sources/types.ts.html +0 -568
- package/dist/fleet-manager/__tests__/event-helpers.test.d.ts +0 -7
- package/dist/fleet-manager/__tests__/event-helpers.test.d.ts.map +0 -1
- package/dist/fleet-manager/__tests__/event-helpers.test.js +0 -368
- package/dist/fleet-manager/__tests__/event-helpers.test.js.map +0 -1
- package/src/config/__tests__/agent.test.ts +0 -864
- package/src/config/__tests__/interpolate.test.ts +0 -644
- package/src/config/__tests__/loader.test.ts +0 -784
- package/src/config/__tests__/merge.test.ts +0 -751
- package/src/config/__tests__/parser.test.ts +0 -533
- package/src/config/__tests__/schema.test.ts +0 -873
- package/src/config/index.ts +0 -119
- package/src/config/interpolate.ts +0 -189
- package/src/config/loader.ts +0 -472
- package/src/config/merge.ts +0 -246
- package/src/config/parser.ts +0 -376
- package/src/config/schema.ts +0 -346
- package/src/fleet-manager/__tests__/coverage.test.ts +0 -2869
- package/src/fleet-manager/__tests__/errors.test.ts +0 -660
- package/src/fleet-manager/__tests__/event-helpers.test.ts +0 -448
- package/src/fleet-manager/__tests__/integration.test.ts +0 -1209
- package/src/fleet-manager/__tests__/job-control.test.ts +0 -283
- package/src/fleet-manager/__tests__/job-manager.test.ts +0 -869
- package/src/fleet-manager/__tests__/job-queue.test.ts +0 -401
- package/src/fleet-manager/__tests__/reload.test.ts +0 -751
- package/src/fleet-manager/__tests__/status-queries.test.ts +0 -595
- package/src/fleet-manager/__tests__/trigger.test.ts +0 -601
- package/src/fleet-manager/errors.ts +0 -747
- package/src/fleet-manager/event-types.ts +0 -378
- package/src/fleet-manager/fleet-manager.ts +0 -2315
- package/src/fleet-manager/index.ts +0 -128
- package/src/fleet-manager/job-manager.ts +0 -663
- package/src/fleet-manager/job-queue.ts +0 -798
- package/src/fleet-manager/types.ts +0 -839
- package/src/index.ts +0 -32
- package/src/runner/__tests__/errors.test.ts +0 -382
- package/src/runner/__tests__/job-executor.test.ts +0 -1708
- package/src/runner/__tests__/message-processor.test.ts +0 -960
- package/src/runner/__tests__/sdk-adapter.test.ts +0 -626
- package/src/runner/errors.ts +0 -307
- package/src/runner/index.ts +0 -57
- package/src/runner/job-executor.ts +0 -448
- package/src/runner/message-processor.ts +0 -355
- package/src/runner/sdk-adapter.ts +0 -191
- package/src/runner/types.ts +0 -158
- package/src/scheduler/__tests__/errors.test.ts +0 -159
- package/src/scheduler/__tests__/interval.test.ts +0 -515
- package/src/scheduler/__tests__/schedule-runner.test.ts +0 -798
- package/src/scheduler/__tests__/schedule-state.test.ts +0 -671
- package/src/scheduler/__tests__/scheduler.test.ts +0 -1280
- package/src/scheduler/errors.ts +0 -101
- package/src/scheduler/index.ts +0 -53
- package/src/scheduler/interval.ts +0 -189
- package/src/scheduler/schedule-runner.ts +0 -442
- package/src/scheduler/schedule-state.ts +0 -211
- package/src/scheduler/scheduler.ts +0 -570
- package/src/scheduler/types.ts +0 -216
- package/src/state/__tests__/directory.test.ts +0 -595
- package/src/state/__tests__/fleet-state.test.ts +0 -868
- package/src/state/__tests__/job-metadata-schema.test.ts +0 -414
- package/src/state/__tests__/job-metadata.test.ts +0 -831
- package/src/state/__tests__/job-output.test.ts +0 -856
- package/src/state/__tests__/session-schema.test.ts +0 -378
- package/src/state/__tests__/session.test.ts +0 -604
- package/src/state/directory.ts +0 -217
- package/src/state/errors.ts +0 -97
- package/src/state/fleet-state.ts +0 -284
- package/src/state/index.ts +0 -79
- package/src/state/job-metadata.ts +0 -445
- package/src/state/job-output.ts +0 -316
- package/src/state/schemas/__tests__/job-output.test.ts +0 -338
- package/src/state/schemas/fleet-state.ts +0 -120
- package/src/state/schemas/index.ts +0 -67
- package/src/state/schemas/job-metadata.ts +0 -181
- package/src/state/schemas/job-output.ts +0 -177
- package/src/state/schemas/session-info.ts +0 -92
- package/src/state/session.ts +0 -253
- package/src/state/types.ts +0 -59
- package/src/state/utils/__tests__/atomic.test.ts +0 -723
- package/src/state/utils/__tests__/reads.test.ts +0 -1071
- package/src/state/utils/atomic.ts +0 -221
- package/src/state/utils/index.ts +0 -6
- package/src/state/utils/reads.ts +0 -512
- package/src/work-sources/__tests__/github.test.ts +0 -1800
- package/src/work-sources/__tests__/manager.test.ts +0 -529
- package/src/work-sources/__tests__/registry.test.ts +0 -477
- package/src/work-sources/__tests__/types.test.ts +0 -479
- package/src/work-sources/adapters/github.ts +0 -1166
- package/src/work-sources/adapters/index.ts +0 -64
- package/src/work-sources/errors.ts +0 -71
- package/src/work-sources/index.ts +0 -148
- package/src/work-sources/manager.ts +0 -413
- package/src/work-sources/registry.ts +0 -178
- package/src/work-sources/types.ts +0 -161
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -19
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
parseFleetConfig,
|
|
4
|
-
validateFleetConfig,
|
|
5
|
-
safeParseFleetConfig,
|
|
6
|
-
YamlSyntaxError,
|
|
7
|
-
SchemaValidationError,
|
|
8
|
-
ConfigError,
|
|
9
|
-
} from "../parser.js";
|
|
10
|
-
import type { FleetConfig } from "../schema.js";
|
|
11
|
-
|
|
12
|
-
describe("parseFleetConfig", () => {
|
|
13
|
-
describe("valid configurations", () => {
|
|
14
|
-
it("parses a minimal valid configuration", () => {
|
|
15
|
-
const yaml = `
|
|
16
|
-
version: 1
|
|
17
|
-
`;
|
|
18
|
-
const config = parseFleetConfig(yaml);
|
|
19
|
-
expect(config.version).toBe(1);
|
|
20
|
-
expect(config.agents).toEqual([]);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("parses an empty file with defaults", () => {
|
|
24
|
-
const yaml = "";
|
|
25
|
-
const config = parseFleetConfig(yaml);
|
|
26
|
-
expect(config.version).toBe(1);
|
|
27
|
-
expect(config.agents).toEqual([]);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("parses a complete configuration from SPEC.md example", () => {
|
|
31
|
-
const yaml = `
|
|
32
|
-
version: 1
|
|
33
|
-
|
|
34
|
-
defaults:
|
|
35
|
-
docker:
|
|
36
|
-
enabled: false
|
|
37
|
-
permissions:
|
|
38
|
-
mode: acceptEdits
|
|
39
|
-
allowed_tools:
|
|
40
|
-
- Read
|
|
41
|
-
- Edit
|
|
42
|
-
- Write
|
|
43
|
-
- Bash
|
|
44
|
-
- Glob
|
|
45
|
-
- Grep
|
|
46
|
-
work_source:
|
|
47
|
-
type: github
|
|
48
|
-
labels:
|
|
49
|
-
ready: "ready"
|
|
50
|
-
in_progress: "in-progress"
|
|
51
|
-
cleanup_in_progress: true
|
|
52
|
-
instances:
|
|
53
|
-
max_concurrent: 1
|
|
54
|
-
|
|
55
|
-
workspace:
|
|
56
|
-
root: ~/herdctl-workspace
|
|
57
|
-
auto_clone: true
|
|
58
|
-
clone_depth: 1
|
|
59
|
-
|
|
60
|
-
agents:
|
|
61
|
-
- path: ./agents/bragdoc-coder.yaml
|
|
62
|
-
- path: ./agents/bragdoc-marketer.yaml
|
|
63
|
-
- path: ./agents/turtle-content.yaml
|
|
64
|
-
|
|
65
|
-
chat:
|
|
66
|
-
discord:
|
|
67
|
-
enabled: true
|
|
68
|
-
token_env: DISCORD_BOT_TOKEN
|
|
69
|
-
`;
|
|
70
|
-
const config = parseFleetConfig(yaml);
|
|
71
|
-
|
|
72
|
-
expect(config.version).toBe(1);
|
|
73
|
-
expect(config.defaults?.docker?.enabled).toBe(false);
|
|
74
|
-
expect(config.defaults?.permissions?.mode).toBe("acceptEdits");
|
|
75
|
-
expect(config.defaults?.permissions?.allowed_tools).toContain("Read");
|
|
76
|
-
expect(config.defaults?.work_source?.type).toBe("github");
|
|
77
|
-
expect(config.defaults?.work_source?.labels?.ready).toBe("ready");
|
|
78
|
-
expect(config.defaults?.instances?.max_concurrent).toBe(1);
|
|
79
|
-
expect(config.workspace?.root).toBe("~/herdctl-workspace");
|
|
80
|
-
expect(config.workspace?.auto_clone).toBe(true);
|
|
81
|
-
expect(config.workspace?.clone_depth).toBe(1);
|
|
82
|
-
expect(config.agents).toHaveLength(3);
|
|
83
|
-
expect(config.agents[0].path).toBe("./agents/bragdoc-coder.yaml");
|
|
84
|
-
expect(config.chat?.discord?.enabled).toBe(true);
|
|
85
|
-
expect(config.chat?.discord?.token_env).toBe("DISCORD_BOT_TOKEN");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("parses configuration with fleet metadata", () => {
|
|
89
|
-
const yaml = `
|
|
90
|
-
version: 1
|
|
91
|
-
fleet:
|
|
92
|
-
name: my-fleet
|
|
93
|
-
description: A fleet of agents
|
|
94
|
-
`;
|
|
95
|
-
const config = parseFleetConfig(yaml);
|
|
96
|
-
expect(config.fleet?.name).toBe("my-fleet");
|
|
97
|
-
expect(config.fleet?.description).toBe("A fleet of agents");
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("parses configuration with webhooks", () => {
|
|
101
|
-
const yaml = `
|
|
102
|
-
version: 1
|
|
103
|
-
webhooks:
|
|
104
|
-
enabled: true
|
|
105
|
-
port: 8081
|
|
106
|
-
secret_env: WEBHOOK_SECRET
|
|
107
|
-
`;
|
|
108
|
-
const config = parseFleetConfig(yaml);
|
|
109
|
-
expect(config.webhooks?.enabled).toBe(true);
|
|
110
|
-
expect(config.webhooks?.port).toBe(8081);
|
|
111
|
-
expect(config.webhooks?.secret_env).toBe("WEBHOOK_SECRET");
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("parses configuration with docker settings", () => {
|
|
115
|
-
const yaml = `
|
|
116
|
-
version: 1
|
|
117
|
-
docker:
|
|
118
|
-
enabled: true
|
|
119
|
-
base_image: herdctl-base:latest
|
|
120
|
-
`;
|
|
121
|
-
const config = parseFleetConfig(yaml);
|
|
122
|
-
expect(config.docker?.enabled).toBe(true);
|
|
123
|
-
expect(config.docker?.base_image).toBe("herdctl-base:latest");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("applies default values correctly", () => {
|
|
127
|
-
const yaml = `
|
|
128
|
-
version: 1
|
|
129
|
-
workspace:
|
|
130
|
-
root: /tmp/workspace
|
|
131
|
-
`;
|
|
132
|
-
const config = parseFleetConfig(yaml);
|
|
133
|
-
expect(config.workspace?.auto_clone).toBe(true);
|
|
134
|
-
expect(config.workspace?.clone_depth).toBe(1);
|
|
135
|
-
expect(config.workspace?.default_branch).toBe("main");
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("parses all permission modes", () => {
|
|
139
|
-
const modes = ["default", "acceptEdits", "bypassPermissions", "plan"];
|
|
140
|
-
for (const mode of modes) {
|
|
141
|
-
const yaml = `
|
|
142
|
-
version: 1
|
|
143
|
-
defaults:
|
|
144
|
-
permissions:
|
|
145
|
-
mode: ${mode}
|
|
146
|
-
`;
|
|
147
|
-
const config = parseFleetConfig(yaml);
|
|
148
|
-
expect(config.defaults?.permissions?.mode).toBe(mode);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("parses bash permissions", () => {
|
|
153
|
-
const yaml = `
|
|
154
|
-
version: 1
|
|
155
|
-
defaults:
|
|
156
|
-
permissions:
|
|
157
|
-
bash:
|
|
158
|
-
allowed_commands:
|
|
159
|
-
- git
|
|
160
|
-
- npm
|
|
161
|
-
- pnpm
|
|
162
|
-
denied_patterns:
|
|
163
|
-
- "rm -rf /"
|
|
164
|
-
- "sudo *"
|
|
165
|
-
`;
|
|
166
|
-
const config = parseFleetConfig(yaml);
|
|
167
|
-
expect(config.defaults?.permissions?.bash?.allowed_commands).toContain(
|
|
168
|
-
"git"
|
|
169
|
-
);
|
|
170
|
-
expect(config.defaults?.permissions?.bash?.denied_patterns).toContain(
|
|
171
|
-
"rm -rf /"
|
|
172
|
-
);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it("parses denied_tools", () => {
|
|
176
|
-
const yaml = `
|
|
177
|
-
version: 1
|
|
178
|
-
defaults:
|
|
179
|
-
permissions:
|
|
180
|
-
denied_tools:
|
|
181
|
-
- WebSearch
|
|
182
|
-
`;
|
|
183
|
-
const config = parseFleetConfig(yaml);
|
|
184
|
-
expect(config.defaults?.permissions?.denied_tools).toContain("WebSearch");
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
describe("YAML syntax errors", () => {
|
|
189
|
-
it("throws YamlSyntaxError for invalid YAML", () => {
|
|
190
|
-
const yaml = `
|
|
191
|
-
version: 1
|
|
192
|
-
indentation: wrong
|
|
193
|
-
`;
|
|
194
|
-
expect(() => parseFleetConfig(yaml)).toThrow(YamlSyntaxError);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it("includes line and column info in YamlSyntaxError", () => {
|
|
198
|
-
const yaml = `version: 1
|
|
199
|
-
agents: [
|
|
200
|
-
unclosed array
|
|
201
|
-
`;
|
|
202
|
-
try {
|
|
203
|
-
parseFleetConfig(yaml);
|
|
204
|
-
expect.fail("Should have thrown");
|
|
205
|
-
} catch (error) {
|
|
206
|
-
expect(error).toBeInstanceOf(YamlSyntaxError);
|
|
207
|
-
const yamlError = error as YamlSyntaxError;
|
|
208
|
-
expect(yamlError.message).toContain("Invalid YAML syntax");
|
|
209
|
-
expect(yamlError.originalError).toBeDefined();
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("throws YamlSyntaxError for tabs in indentation", () => {
|
|
214
|
-
const yaml = "version: 1\nagents:\n\t- path: test.yaml";
|
|
215
|
-
expect(() => parseFleetConfig(yaml)).toThrow(YamlSyntaxError);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it("throws YamlSyntaxError for duplicate keys", () => {
|
|
219
|
-
const yaml = `
|
|
220
|
-
version: 1
|
|
221
|
-
version: 2
|
|
222
|
-
`;
|
|
223
|
-
expect(() => parseFleetConfig(yaml)).toThrow(YamlSyntaxError);
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
describe("schema validation errors", () => {
|
|
228
|
-
it("throws SchemaValidationError for invalid version type", () => {
|
|
229
|
-
const yaml = `
|
|
230
|
-
version: "not-a-number"
|
|
231
|
-
`;
|
|
232
|
-
expect(() => parseFleetConfig(yaml)).toThrow(SchemaValidationError);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it("throws SchemaValidationError for invalid permission mode", () => {
|
|
236
|
-
const yaml = `
|
|
237
|
-
version: 1
|
|
238
|
-
defaults:
|
|
239
|
-
permissions:
|
|
240
|
-
mode: invalid-mode
|
|
241
|
-
`;
|
|
242
|
-
expect(() => parseFleetConfig(yaml)).toThrow(SchemaValidationError);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("throws SchemaValidationError for invalid work source type", () => {
|
|
246
|
-
const yaml = `
|
|
247
|
-
version: 1
|
|
248
|
-
defaults:
|
|
249
|
-
work_source:
|
|
250
|
-
type: invalid-source
|
|
251
|
-
`;
|
|
252
|
-
expect(() => parseFleetConfig(yaml)).toThrow(SchemaValidationError);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it("throws SchemaValidationError for negative max_concurrent", () => {
|
|
256
|
-
const yaml = `
|
|
257
|
-
version: 1
|
|
258
|
-
defaults:
|
|
259
|
-
instances:
|
|
260
|
-
max_concurrent: -1
|
|
261
|
-
`;
|
|
262
|
-
expect(() => parseFleetConfig(yaml)).toThrow(SchemaValidationError);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it("throws SchemaValidationError for missing required workspace root", () => {
|
|
266
|
-
const yaml = `
|
|
267
|
-
version: 1
|
|
268
|
-
workspace:
|
|
269
|
-
auto_clone: true
|
|
270
|
-
`;
|
|
271
|
-
expect(() => parseFleetConfig(yaml)).toThrow(SchemaValidationError);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it("throws SchemaValidationError for invalid agent reference (missing path)", () => {
|
|
275
|
-
const yaml = `
|
|
276
|
-
version: 1
|
|
277
|
-
agents:
|
|
278
|
-
- name: invalid
|
|
279
|
-
`;
|
|
280
|
-
expect(() => parseFleetConfig(yaml)).toThrow(SchemaValidationError);
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it("includes path info in SchemaValidationError", () => {
|
|
284
|
-
const yaml = `
|
|
285
|
-
version: 1
|
|
286
|
-
defaults:
|
|
287
|
-
permissions:
|
|
288
|
-
mode: invalid
|
|
289
|
-
`;
|
|
290
|
-
try {
|
|
291
|
-
parseFleetConfig(yaml);
|
|
292
|
-
expect.fail("Should have thrown");
|
|
293
|
-
} catch (error) {
|
|
294
|
-
expect(error).toBeInstanceOf(SchemaValidationError);
|
|
295
|
-
const schemaError = error as SchemaValidationError;
|
|
296
|
-
expect(schemaError.issues).toHaveLength(1);
|
|
297
|
-
expect(schemaError.issues[0].path).toBe("defaults.permissions.mode");
|
|
298
|
-
expect(schemaError.message).toContain("defaults.permissions.mode");
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it("reports multiple validation errors", () => {
|
|
303
|
-
const yaml = `
|
|
304
|
-
version: "invalid"
|
|
305
|
-
defaults:
|
|
306
|
-
permissions:
|
|
307
|
-
mode: invalid
|
|
308
|
-
`;
|
|
309
|
-
try {
|
|
310
|
-
parseFleetConfig(yaml);
|
|
311
|
-
expect.fail("Should have thrown");
|
|
312
|
-
} catch (error) {
|
|
313
|
-
expect(error).toBeInstanceOf(SchemaValidationError);
|
|
314
|
-
const schemaError = error as SchemaValidationError;
|
|
315
|
-
expect(schemaError.issues.length).toBeGreaterThanOrEqual(2);
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it("throws SchemaValidationError for negative clone_depth", () => {
|
|
320
|
-
const yaml = `
|
|
321
|
-
version: 1
|
|
322
|
-
workspace:
|
|
323
|
-
root: /tmp
|
|
324
|
-
clone_depth: -5
|
|
325
|
-
`;
|
|
326
|
-
expect(() => parseFleetConfig(yaml)).toThrow(SchemaValidationError);
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
it("throws SchemaValidationError for non-integer clone_depth", () => {
|
|
330
|
-
const yaml = `
|
|
331
|
-
version: 1
|
|
332
|
-
workspace:
|
|
333
|
-
root: /tmp
|
|
334
|
-
clone_depth: 1.5
|
|
335
|
-
`;
|
|
336
|
-
expect(() => parseFleetConfig(yaml)).toThrow(SchemaValidationError);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
it("throws SchemaValidationError for invalid webhook port", () => {
|
|
340
|
-
const yaml = `
|
|
341
|
-
version: 1
|
|
342
|
-
webhooks:
|
|
343
|
-
enabled: true
|
|
344
|
-
port: -80
|
|
345
|
-
`;
|
|
346
|
-
expect(() => parseFleetConfig(yaml)).toThrow(SchemaValidationError);
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
describe("validateFleetConfig", () => {
|
|
352
|
-
it("validates a valid config object", () => {
|
|
353
|
-
const config = {
|
|
354
|
-
version: 1,
|
|
355
|
-
agents: [{ path: "./test.yaml" }],
|
|
356
|
-
};
|
|
357
|
-
const validated = validateFleetConfig(config);
|
|
358
|
-
expect(validated.version).toBe(1);
|
|
359
|
-
expect(validated.agents).toHaveLength(1);
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
it("throws SchemaValidationError for invalid object", () => {
|
|
363
|
-
const config = {
|
|
364
|
-
version: "invalid",
|
|
365
|
-
};
|
|
366
|
-
expect(() => validateFleetConfig(config)).toThrow(SchemaValidationError);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it("applies defaults to partial config", () => {
|
|
370
|
-
const config = {};
|
|
371
|
-
const validated = validateFleetConfig(config);
|
|
372
|
-
expect(validated.version).toBe(1);
|
|
373
|
-
expect(validated.agents).toEqual([]);
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
describe("safeParseFleetConfig", () => {
|
|
378
|
-
it("returns success for valid YAML", () => {
|
|
379
|
-
const yaml = `
|
|
380
|
-
version: 1
|
|
381
|
-
agents:
|
|
382
|
-
- path: ./test.yaml
|
|
383
|
-
`;
|
|
384
|
-
const result = safeParseFleetConfig(yaml);
|
|
385
|
-
expect(result.success).toBe(true);
|
|
386
|
-
if (result.success) {
|
|
387
|
-
expect(result.data.version).toBe(1);
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
it("returns error for invalid YAML syntax", () => {
|
|
392
|
-
const yaml = `
|
|
393
|
-
version: 1
|
|
394
|
-
bad: indentation
|
|
395
|
-
`;
|
|
396
|
-
const result = safeParseFleetConfig(yaml);
|
|
397
|
-
expect(result.success).toBe(false);
|
|
398
|
-
if (!result.success) {
|
|
399
|
-
expect(result.error).toBeInstanceOf(YamlSyntaxError);
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
it("returns error for schema validation failure", () => {
|
|
404
|
-
const yaml = `
|
|
405
|
-
version: "invalid"
|
|
406
|
-
`;
|
|
407
|
-
const result = safeParseFleetConfig(yaml);
|
|
408
|
-
expect(result.success).toBe(false);
|
|
409
|
-
if (!result.success) {
|
|
410
|
-
expect(result.error).toBeInstanceOf(SchemaValidationError);
|
|
411
|
-
}
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
it("wraps unexpected errors in ConfigError", () => {
|
|
415
|
-
// This is hard to trigger, but we test the interface works
|
|
416
|
-
const result = safeParseFleetConfig("");
|
|
417
|
-
expect(result.success).toBe(true);
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
describe("Error classes", () => {
|
|
422
|
-
it("ConfigError has correct name", () => {
|
|
423
|
-
const error = new ConfigError("test");
|
|
424
|
-
expect(error.name).toBe("ConfigError");
|
|
425
|
-
expect(error.message).toBe("test");
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it("YamlSyntaxError extends ConfigError", () => {
|
|
429
|
-
// Create a mock YAMLParseError
|
|
430
|
-
const mockError = {
|
|
431
|
-
message: "test error",
|
|
432
|
-
linePos: [{ line: 5, col: 10 }],
|
|
433
|
-
} as never;
|
|
434
|
-
|
|
435
|
-
const error = new YamlSyntaxError(mockError);
|
|
436
|
-
expect(error).toBeInstanceOf(ConfigError);
|
|
437
|
-
expect(error.name).toBe("YamlSyntaxError");
|
|
438
|
-
expect(error.line).toBe(5);
|
|
439
|
-
expect(error.column).toBe(10);
|
|
440
|
-
expect(error.message).toContain("line 5");
|
|
441
|
-
expect(error.message).toContain("column 10");
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
it("YamlSyntaxError handles missing position", () => {
|
|
445
|
-
const mockError = {
|
|
446
|
-
message: "test error",
|
|
447
|
-
linePos: undefined,
|
|
448
|
-
} as never;
|
|
449
|
-
|
|
450
|
-
const error = new YamlSyntaxError(mockError);
|
|
451
|
-
expect(error.line).toBeUndefined();
|
|
452
|
-
expect(error.column).toBeUndefined();
|
|
453
|
-
expect(error.message).toContain("Invalid YAML syntax");
|
|
454
|
-
expect(error.message).not.toContain("line");
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
it("YamlSyntaxError handles empty linePos array", () => {
|
|
458
|
-
const mockError = {
|
|
459
|
-
message: "test error",
|
|
460
|
-
linePos: [],
|
|
461
|
-
} as never;
|
|
462
|
-
|
|
463
|
-
const error = new YamlSyntaxError(mockError);
|
|
464
|
-
expect(error.line).toBeUndefined();
|
|
465
|
-
expect(error.column).toBeUndefined();
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
it("SchemaValidationError extends ConfigError", () => {
|
|
469
|
-
const mockZodError = {
|
|
470
|
-
issues: [
|
|
471
|
-
{ path: ["a", "b"], message: "test message", code: "invalid_type" },
|
|
472
|
-
],
|
|
473
|
-
} as never;
|
|
474
|
-
|
|
475
|
-
const error = new SchemaValidationError(mockZodError);
|
|
476
|
-
expect(error).toBeInstanceOf(ConfigError);
|
|
477
|
-
expect(error.name).toBe("SchemaValidationError");
|
|
478
|
-
expect(error.issues).toHaveLength(1);
|
|
479
|
-
expect(error.issues[0].path).toBe("a.b");
|
|
480
|
-
expect(error.issues[0].message).toBe("test message");
|
|
481
|
-
expect(error.message).toContain("a.b");
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
it("SchemaValidationError handles root path", () => {
|
|
485
|
-
const mockZodError = {
|
|
486
|
-
issues: [{ path: [], message: "root error", code: "invalid_type" }],
|
|
487
|
-
} as never;
|
|
488
|
-
|
|
489
|
-
const error = new SchemaValidationError(mockZodError);
|
|
490
|
-
expect(error.issues[0].path).toBe("(root)");
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
it("SchemaValidationError handles multiple issues", () => {
|
|
494
|
-
const mockZodError = {
|
|
495
|
-
issues: [
|
|
496
|
-
{ path: ["a"], message: "error 1", code: "invalid_type" },
|
|
497
|
-
{ path: ["b", "c"], message: "error 2", code: "invalid_type" },
|
|
498
|
-
],
|
|
499
|
-
} as never;
|
|
500
|
-
|
|
501
|
-
const error = new SchemaValidationError(mockZodError);
|
|
502
|
-
expect(error.issues).toHaveLength(2);
|
|
503
|
-
expect(error.message).toContain("a: error 1");
|
|
504
|
-
expect(error.message).toContain("b.c: error 2");
|
|
505
|
-
});
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
describe("type safety", () => {
|
|
509
|
-
it("returns properly typed FleetConfig", () => {
|
|
510
|
-
const yaml = `
|
|
511
|
-
version: 1
|
|
512
|
-
defaults:
|
|
513
|
-
permissions:
|
|
514
|
-
mode: acceptEdits
|
|
515
|
-
workspace:
|
|
516
|
-
root: /tmp
|
|
517
|
-
agents:
|
|
518
|
-
- path: ./test.yaml
|
|
519
|
-
`;
|
|
520
|
-
const config: FleetConfig = parseFleetConfig(yaml);
|
|
521
|
-
|
|
522
|
-
// TypeScript compilation verifies these types
|
|
523
|
-
const _version: number = config.version;
|
|
524
|
-
const _mode: string | undefined = config.defaults?.permissions?.mode;
|
|
525
|
-
const _root: string | undefined = config.workspace?.root;
|
|
526
|
-
const _path: string | undefined = config.agents[0]?.path;
|
|
527
|
-
|
|
528
|
-
expect(_version).toBe(1);
|
|
529
|
-
expect(_mode).toBe("acceptEdits");
|
|
530
|
-
expect(_root).toBe("/tmp");
|
|
531
|
-
expect(_path).toBe("./test.yaml");
|
|
532
|
-
});
|
|
533
|
-
});
|