@lobu/core 6.1.1 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/agent-policy-harden.test.d.ts +11 -0
- package/dist/__tests__/agent-policy-harden.test.d.ts.map +1 -0
- package/dist/__tests__/agent-policy-harden.test.js +216 -0
- package/dist/__tests__/agent-policy-harden.test.js.map +1 -0
- package/dist/__tests__/agent-store.test.d.ts +8 -0
- package/dist/__tests__/agent-store.test.d.ts.map +1 -0
- package/dist/__tests__/agent-store.test.js +38 -0
- package/dist/__tests__/agent-store.test.js.map +1 -0
- package/dist/__tests__/command-registry.test.d.ts +8 -0
- package/dist/__tests__/command-registry.test.d.ts.map +1 -0
- package/dist/__tests__/command-registry.test.js +188 -0
- package/dist/__tests__/command-registry.test.js.map +1 -0
- package/dist/__tests__/encryption-key-validation.test.d.ts +2 -0
- package/dist/__tests__/encryption-key-validation.test.d.ts.map +1 -0
- package/dist/__tests__/encryption-key-validation.test.js +61 -0
- package/dist/__tests__/encryption-key-validation.test.js.map +1 -0
- package/dist/__tests__/encryption.test.js +2 -0
- package/dist/__tests__/encryption.test.js.map +1 -1
- package/dist/__tests__/errors.test.js +0 -36
- package/dist/__tests__/errors.test.js.map +1 -1
- package/dist/__tests__/guardrails-harden.test.d.ts +16 -0
- package/dist/__tests__/guardrails-harden.test.d.ts.map +1 -0
- package/dist/__tests__/guardrails-harden.test.js +328 -0
- package/dist/__tests__/guardrails-harden.test.js.map +1 -0
- package/dist/__tests__/instruction-provider.test.d.ts +8 -0
- package/dist/__tests__/instruction-provider.test.d.ts.map +1 -0
- package/dist/__tests__/instruction-provider.test.js +129 -0
- package/dist/__tests__/instruction-provider.test.js.map +1 -0
- package/dist/__tests__/lobu-toml-schema-harden.test.d.ts +10 -0
- package/dist/__tests__/lobu-toml-schema-harden.test.d.ts.map +1 -0
- package/dist/__tests__/lobu-toml-schema-harden.test.js +722 -0
- package/dist/__tests__/lobu-toml-schema-harden.test.js.map +1 -0
- package/dist/__tests__/lobu-toml-schema.test.js +40 -5
- package/dist/__tests__/lobu-toml-schema.test.js.map +1 -1
- package/dist/__tests__/network-domains.test.d.ts +9 -0
- package/dist/__tests__/network-domains.test.d.ts.map +1 -0
- package/dist/__tests__/network-domains.test.js +97 -0
- package/dist/__tests__/network-domains.test.js.map +1 -0
- package/dist/__tests__/sanitize.test.js +36 -5
- package/dist/__tests__/sanitize.test.js.map +1 -1
- package/dist/__tests__/utils-env.test.d.ts +8 -0
- package/dist/__tests__/utils-env.test.d.ts.map +1 -0
- package/dist/__tests__/utils-env.test.js +125 -0
- package/dist/__tests__/utils-env.test.js.map +1 -0
- package/dist/__tests__/utils-json.test.d.ts +8 -0
- package/dist/__tests__/utils-json.test.d.ts.map +1 -0
- package/dist/__tests__/utils-json.test.js +114 -0
- package/dist/__tests__/utils-json.test.js.map +1 -0
- package/dist/__tests__/utils-urls.test.d.ts +7 -0
- package/dist/__tests__/utils-urls.test.d.ts.map +1 -0
- package/dist/__tests__/utils-urls.test.js +37 -0
- package/dist/__tests__/utils-urls.test.js.map +1 -0
- package/dist/__tests__/worker-auth.test.js +32 -0
- package/dist/__tests__/worker-auth.test.js.map +1 -1
- package/dist/agent-policy.d.ts.map +1 -1
- package/dist/agent-policy.js +2 -5
- package/dist/agent-policy.js.map +1 -1
- package/dist/agent-store.d.ts +14 -0
- package/dist/agent-store.d.ts.map +1 -1
- package/dist/agent-store.js.map +1 -1
- package/dist/capabilities.d.ts +12 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +85 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/command-registry.d.ts +4 -0
- package/dist/command-registry.d.ts.map +1 -1
- package/dist/command-registry.js +11 -1
- package/dist/command-registry.js.map +1 -1
- package/dist/errors.d.ts +2 -40
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +17 -72
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lobu-toml-schema.d.ts +23 -8
- package/dist/lobu-toml-schema.d.ts.map +1 -1
- package/dist/lobu-toml-schema.js +31 -5
- package/dist/lobu-toml-schema.js.map +1 -1
- package/dist/logger.d.ts +0 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +4 -7
- package/dist/logger.js.map +1 -1
- package/dist/modules.d.ts.map +1 -1
- package/dist/modules.js +8 -12
- package/dist/modules.js.map +1 -1
- package/dist/utils/encryption.d.ts +2 -0
- package/dist/utils/encryption.d.ts.map +1 -1
- package/dist/utils/encryption.js +41 -11
- package/dist/utils/encryption.js.map +1 -1
- package/dist/utils/json.d.ts +1 -6
- package/dist/utils/json.d.ts.map +1 -1
- package/dist/utils/json.js +5 -23
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +29 -5
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/sanitize.d.ts +0 -24
- package/dist/utils/sanitize.d.ts.map +1 -1
- package/dist/utils/sanitize.js +61 -29
- package/dist/utils/sanitize.js.map +1 -1
- package/dist/worker/auth.d.ts +13 -5
- package/dist/worker/auth.d.ts.map +1 -1
- package/dist/worker/auth.js +45 -28
- package/dist/worker/auth.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hardened edge-case tests for lobu-toml-schema.ts.
|
|
4
|
+
*
|
|
5
|
+
* The existing lobu-toml-schema.test.ts covers preview and memory.
|
|
6
|
+
* This file covers: agent ID validation, provider mutual-exclusion,
|
|
7
|
+
* pre_approved tool pattern enforcement, network config, egress,
|
|
8
|
+
* platform name regex, and unknown/wrong-type fields.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
const bun_test_1 = require("bun:test");
|
|
12
|
+
const smol_toml_1 = require("smol-toml");
|
|
13
|
+
const lobu_toml_schema_1 = require("../lobu-toml-schema");
|
|
14
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
15
|
+
function valid(toml) {
|
|
16
|
+
return lobu_toml_schema_1.lobuConfigSchema.safeParse((0, smol_toml_1.parse)(toml));
|
|
17
|
+
}
|
|
18
|
+
function validResult(toml) {
|
|
19
|
+
const r = valid(toml);
|
|
20
|
+
if (!r.success)
|
|
21
|
+
throw new Error(r.error.toString());
|
|
22
|
+
return r.data;
|
|
23
|
+
}
|
|
24
|
+
const BASE = `
|
|
25
|
+
[agents.triage]
|
|
26
|
+
name = "Triage"
|
|
27
|
+
dir = "./agents/triage"
|
|
28
|
+
`;
|
|
29
|
+
// ── Agent ID validation ──────────────────────────────────────────────────────
|
|
30
|
+
(0, bun_test_1.describe)("lobu.toml agent ID validation", () => {
|
|
31
|
+
(0, bun_test_1.test)("accepts lowercase-alphanumeric-hyphen agent id", () => {
|
|
32
|
+
const result = valid(`
|
|
33
|
+
[agents.my-agent]
|
|
34
|
+
name = "My Agent"
|
|
35
|
+
dir = "./agents/my-agent"
|
|
36
|
+
`);
|
|
37
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
(0, bun_test_1.test)("rejects agent id starting with a hyphen", () => {
|
|
40
|
+
// smol-toml would parse this as a weird key; zod regex should reject it
|
|
41
|
+
const raw = (0, smol_toml_1.parse)(`
|
|
42
|
+
[agents.my-agent]
|
|
43
|
+
name = "OK"
|
|
44
|
+
dir = "./"
|
|
45
|
+
`);
|
|
46
|
+
// Simulate a bad key by patching the parsed object directly
|
|
47
|
+
const bad = { agents: { "-bad": { name: "Bad", dir: "./" } } };
|
|
48
|
+
const result = lobu_toml_schema_1.lobuConfigSchema.safeParse(bad);
|
|
49
|
+
(0, bun_test_1.expect)(result.success).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
(0, bun_test_1.test)("rejects agent id with uppercase letters", () => {
|
|
52
|
+
const bad = { agents: { MyAgent: { name: "Bad", dir: "./" } } };
|
|
53
|
+
const result = lobu_toml_schema_1.lobuConfigSchema.safeParse(bad);
|
|
54
|
+
(0, bun_test_1.expect)(result.success).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
(0, bun_test_1.test)("rejects agent id with spaces", () => {
|
|
57
|
+
const bad = { agents: { "my agent": { name: "Bad", dir: "./" } } };
|
|
58
|
+
const result = lobu_toml_schema_1.lobuConfigSchema.safeParse(bad);
|
|
59
|
+
(0, bun_test_1.expect)(result.success).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
(0, bun_test_1.test)("accepts agent id with numbers mid-string", () => {
|
|
62
|
+
const result = valid(`
|
|
63
|
+
[agents.agent2go]
|
|
64
|
+
name = "A"
|
|
65
|
+
dir = "./"
|
|
66
|
+
`);
|
|
67
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
(0, bun_test_1.test)("requires at least one agent", () => {
|
|
70
|
+
const result = lobu_toml_schema_1.lobuConfigSchema.safeParse({ agents: {} });
|
|
71
|
+
// An empty agents record is technically valid schema-wise (record allows empty)
|
|
72
|
+
// but flags real gaps — we verify it does NOT crash
|
|
73
|
+
(0, bun_test_1.expect)(typeof result.success).toBe("boolean");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
// ── Missing required fields ──────────────────────────────────────────────────
|
|
77
|
+
(0, bun_test_1.describe)("lobu.toml required fields", () => {
|
|
78
|
+
(0, bun_test_1.test)("rejects agent entry missing name", () => {
|
|
79
|
+
const bad = { agents: { triage: { dir: "./" } } };
|
|
80
|
+
(0, bun_test_1.expect)(lobu_toml_schema_1.lobuConfigSchema.safeParse(bad).success).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
(0, bun_test_1.test)("rejects agent entry missing dir", () => {
|
|
83
|
+
const bad = { agents: { triage: { name: "Triage" } } };
|
|
84
|
+
(0, bun_test_1.expect)(lobu_toml_schema_1.lobuConfigSchema.safeParse(bad).success).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
(0, bun_test_1.test)("rejects top-level missing agents key", () => {
|
|
87
|
+
(0, bun_test_1.expect)(lobu_toml_schema_1.lobuConfigSchema.safeParse({}).success).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
(0, bun_test_1.test)("accepts optional description field", () => {
|
|
90
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
91
|
+
agents: {
|
|
92
|
+
triage: { name: "Triage", description: "Handles stuff", dir: "./" },
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
96
|
+
if (r.success) {
|
|
97
|
+
(0, bun_test_1.expect)(r.data.agents.triage?.description).toBe("Handles stuff");
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
// ── Provider key / secret_ref mutual exclusion ───────────────────────────────
|
|
102
|
+
(0, bun_test_1.describe)("lobu.toml provider mutual exclusion", () => {
|
|
103
|
+
(0, bun_test_1.test)("accepts provider with only key", () => {
|
|
104
|
+
const result = valid(`${BASE}
|
|
105
|
+
[[agents.triage.providers]]
|
|
106
|
+
id = "anthropic"
|
|
107
|
+
key = "sk-ant-xxx"
|
|
108
|
+
`);
|
|
109
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
(0, bun_test_1.test)("accepts provider with only secret_ref", () => {
|
|
112
|
+
const result = valid(`${BASE}
|
|
113
|
+
[[agents.triage.providers]]
|
|
114
|
+
id = "anthropic"
|
|
115
|
+
secret_ref = "lobu_secret_abc123"
|
|
116
|
+
`);
|
|
117
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
(0, bun_test_1.test)("rejects provider with both key and secret_ref", () => {
|
|
120
|
+
const result = valid(`${BASE}
|
|
121
|
+
[[agents.triage.providers]]
|
|
122
|
+
id = "anthropic"
|
|
123
|
+
key = "sk-ant-xxx"
|
|
124
|
+
secret_ref = "lobu_secret_abc123"
|
|
125
|
+
`);
|
|
126
|
+
(0, bun_test_1.expect)(result.success).toBe(false);
|
|
127
|
+
if (!result.success) {
|
|
128
|
+
const messages = result.error.issues.map((i) => i.message).join(" ");
|
|
129
|
+
(0, bun_test_1.expect)(messages).toContain("at most one");
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
(0, bun_test_1.test)("accepts provider with neither key nor secret_ref (env-var fallback)", () => {
|
|
133
|
+
const result = valid(`${BASE}
|
|
134
|
+
[[agents.triage.providers]]
|
|
135
|
+
id = "openai"
|
|
136
|
+
`);
|
|
137
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
// ── tools.pre_approved pattern validation ────────────────────────────────────
|
|
141
|
+
(0, bun_test_1.describe)("lobu.toml tools.pre_approved patterns", () => {
|
|
142
|
+
(0, bun_test_1.test)("accepts a valid specific tool path", () => {
|
|
143
|
+
const result = valid(`${BASE}
|
|
144
|
+
[agents.triage.tools]
|
|
145
|
+
pre_approved = ["/mcp/gmail/tools/send_email"]
|
|
146
|
+
`);
|
|
147
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
(0, bun_test_1.test)("accepts a wildcard tool path", () => {
|
|
150
|
+
const result = valid(`${BASE}
|
|
151
|
+
[agents.triage.tools]
|
|
152
|
+
pre_approved = ["/mcp/linear/tools/*"]
|
|
153
|
+
`);
|
|
154
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
(0, bun_test_1.test)("accepts mixed specific and wildcard patterns", () => {
|
|
157
|
+
const result = valid(`${BASE}
|
|
158
|
+
[agents.triage.tools]
|
|
159
|
+
pre_approved = ["/mcp/gmail/tools/send_email", "/mcp/linear/tools/*"]
|
|
160
|
+
`);
|
|
161
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
(0, bun_test_1.test)("rejects a bare tool name (no leading slash)", () => {
|
|
164
|
+
const result = valid(`${BASE}
|
|
165
|
+
[agents.triage.tools]
|
|
166
|
+
pre_approved = ["gmail"]
|
|
167
|
+
`);
|
|
168
|
+
(0, bun_test_1.expect)(result.success).toBe(false);
|
|
169
|
+
if (!result.success) {
|
|
170
|
+
const msgs = result.error.issues.map((i) => i.message).join(" ");
|
|
171
|
+
(0, bun_test_1.expect)(msgs).toMatch(/pre_approved/i);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
(0, bun_test_1.test)("rejects missing /tools/ segment", () => {
|
|
175
|
+
const result = valid(`${BASE}
|
|
176
|
+
[agents.triage.tools]
|
|
177
|
+
pre_approved = ["/mcp/gmail/send_email"]
|
|
178
|
+
`);
|
|
179
|
+
(0, bun_test_1.expect)(result.success).toBe(false);
|
|
180
|
+
});
|
|
181
|
+
(0, bun_test_1.test)("rejects double wildcard pattern", () => {
|
|
182
|
+
const result = valid(`${BASE}
|
|
183
|
+
[agents.triage.tools]
|
|
184
|
+
pre_approved = ["/mcp/gmail/tools/**"]
|
|
185
|
+
`);
|
|
186
|
+
(0, bun_test_1.expect)(result.success).toBe(false);
|
|
187
|
+
});
|
|
188
|
+
(0, bun_test_1.test)("rejects URL-style pattern", () => {
|
|
189
|
+
const result = valid(`${BASE}
|
|
190
|
+
[agents.triage.tools]
|
|
191
|
+
pre_approved = ["https://example.com/mcp/tools/read"]
|
|
192
|
+
`);
|
|
193
|
+
(0, bun_test_1.expect)(result.success).toBe(false);
|
|
194
|
+
});
|
|
195
|
+
(0, bun_test_1.test)("accepts mcp id with underscores and dashes", () => {
|
|
196
|
+
const result = valid(`${BASE}
|
|
197
|
+
[agents.triage.tools]
|
|
198
|
+
pre_approved = ["/mcp/my_mcp-server/tools/do_thing"]
|
|
199
|
+
`);
|
|
200
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
(0, bun_test_1.test)("rejects mcp id with dots", () => {
|
|
203
|
+
const result = valid(`${BASE}
|
|
204
|
+
[agents.triage.tools]
|
|
205
|
+
pre_approved = ["/mcp/my.mcp/tools/do_thing"]
|
|
206
|
+
`);
|
|
207
|
+
(0, bun_test_1.expect)(result.success).toBe(false);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
// ── tools.allowed / denied / strict ─────────────────────────────────────────
|
|
211
|
+
(0, bun_test_1.describe)("lobu.toml tools.allowed / denied / strict", () => {
|
|
212
|
+
(0, bun_test_1.test)("accepts allowed/denied/strict combination", () => {
|
|
213
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
214
|
+
agents: {
|
|
215
|
+
triage: {
|
|
216
|
+
name: "T",
|
|
217
|
+
dir: "./",
|
|
218
|
+
tools: {
|
|
219
|
+
allowed: ["Bash(git:*)", "mcp__github__*"],
|
|
220
|
+
denied: ["Bash(rm:*)"],
|
|
221
|
+
strict: true,
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
227
|
+
if (r.success) {
|
|
228
|
+
(0, bun_test_1.expect)(r.data.agents.triage?.tools?.strict).toBe(true);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
(0, bun_test_1.test)("rejects non-boolean strict", () => {
|
|
232
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
233
|
+
agents: {
|
|
234
|
+
triage: {
|
|
235
|
+
name: "T",
|
|
236
|
+
dir: "./",
|
|
237
|
+
tools: { strict: "yes" },
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
// ── egress config ─────────────────────────────────────────────────────────────
|
|
245
|
+
(0, bun_test_1.describe)("lobu.toml egress config", () => {
|
|
246
|
+
(0, bun_test_1.test)("accepts extra_policy and judge_model", () => {
|
|
247
|
+
const result = valid(`${BASE}
|
|
248
|
+
[agents.triage.egress]
|
|
249
|
+
extra_policy = "Never exfiltrate tokens."
|
|
250
|
+
judge_model = "claude-haiku-4-5-20251001"
|
|
251
|
+
`);
|
|
252
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
253
|
+
if (result.success) {
|
|
254
|
+
(0, bun_test_1.expect)(result.data.agents.triage?.egress?.extra_policy).toBe("Never exfiltrate tokens.");
|
|
255
|
+
(0, bun_test_1.expect)(result.data.agents.triage?.egress?.judge_model).toBe("claude-haiku-4-5-20251001");
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
(0, bun_test_1.test)("accepts egress with no fields (all optional)", () => {
|
|
259
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
260
|
+
agents: { triage: { name: "T", dir: "./", egress: {} } },
|
|
261
|
+
});
|
|
262
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
263
|
+
});
|
|
264
|
+
(0, bun_test_1.test)("rejects non-string extra_policy", () => {
|
|
265
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
266
|
+
agents: {
|
|
267
|
+
triage: {
|
|
268
|
+
name: "T",
|
|
269
|
+
dir: "./",
|
|
270
|
+
egress: { extra_policy: 42 },
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
// ── network config ────────────────────────────────────────────────────────────
|
|
278
|
+
(0, bun_test_1.describe)("lobu.toml network config", () => {
|
|
279
|
+
(0, bun_test_1.test)("normalizes *.example.com to .example.com in allowed", () => {
|
|
280
|
+
const result = valid(`${BASE}
|
|
281
|
+
[agents.triage.network]
|
|
282
|
+
allowed = ["*.example.com", "api.github.com"]
|
|
283
|
+
`);
|
|
284
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
285
|
+
if (result.success) {
|
|
286
|
+
(0, bun_test_1.expect)(result.data.agents.triage?.network?.allowed).toContain(".example.com");
|
|
287
|
+
(0, bun_test_1.expect)(result.data.agents.triage?.network?.allowed).toContain("api.github.com");
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
(0, bun_test_1.test)("deduplicates domain patterns in allowed", () => {
|
|
291
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
292
|
+
agents: {
|
|
293
|
+
triage: {
|
|
294
|
+
name: "T",
|
|
295
|
+
dir: "./",
|
|
296
|
+
network: { allowed: ["api.github.com", "api.github.com"] },
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
301
|
+
if (r.success) {
|
|
302
|
+
(0, bun_test_1.expect)(r.data.agents.triage?.network?.allowed).toEqual([
|
|
303
|
+
"api.github.com",
|
|
304
|
+
]);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
(0, bun_test_1.test)("accepts judge entries as strings", () => {
|
|
308
|
+
const result = valid(`${BASE}
|
|
309
|
+
[agents.triage.network]
|
|
310
|
+
allowed = ["api.example.com"]
|
|
311
|
+
judge = ["*.slack.com"]
|
|
312
|
+
`);
|
|
313
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
314
|
+
if (result.success) {
|
|
315
|
+
(0, bun_test_1.expect)(result.data.agents.triage?.network?.judge).toEqual([
|
|
316
|
+
{ domain: "*.slack.com" },
|
|
317
|
+
]);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
(0, bun_test_1.test)("accepts judge entries as objects with named policy", () => {
|
|
321
|
+
const result = valid(`${BASE}
|
|
322
|
+
[agents.triage.network]
|
|
323
|
+
[[agents.triage.network.judge]]
|
|
324
|
+
domain = "*.slack.com"
|
|
325
|
+
judge = "strict"
|
|
326
|
+
|
|
327
|
+
[agents.triage.network.judges]
|
|
328
|
+
strict = "Only GET requests."
|
|
329
|
+
`);
|
|
330
|
+
(0, bun_test_1.expect)(result.success).toBe(true);
|
|
331
|
+
if (result.success) {
|
|
332
|
+
const judge = result.data.agents.triage?.network?.judge?.[0];
|
|
333
|
+
(0, bun_test_1.expect)(judge?.domain).toBe("*.slack.com");
|
|
334
|
+
(0, bun_test_1.expect)(judge?.judge).toBe("strict");
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
(0, bun_test_1.test)("lowercases domain patterns in denied", () => {
|
|
338
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
339
|
+
agents: {
|
|
340
|
+
triage: {
|
|
341
|
+
name: "T",
|
|
342
|
+
dir: "./",
|
|
343
|
+
network: { denied: ["MALICIOUS.COM"] },
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
348
|
+
if (r.success) {
|
|
349
|
+
(0, bun_test_1.expect)(r.data.agents.triage?.network?.denied).toContain("malicious.com");
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
// ── platform config ───────────────────────────────────────────────────────────
|
|
354
|
+
(0, bun_test_1.describe)("lobu.toml platform config", () => {
|
|
355
|
+
(0, bun_test_1.test)("accepts valid platform with all fields", () => {
|
|
356
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
357
|
+
agents: {
|
|
358
|
+
triage: {
|
|
359
|
+
name: "T",
|
|
360
|
+
dir: "./",
|
|
361
|
+
platforms: [
|
|
362
|
+
{
|
|
363
|
+
type: "telegram",
|
|
364
|
+
name: "main",
|
|
365
|
+
config: { botToken: "$BOT_TOKEN" },
|
|
366
|
+
channels: ["123456", "789012"],
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
373
|
+
});
|
|
374
|
+
(0, bun_test_1.test)("rejects platform name with uppercase", () => {
|
|
375
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
376
|
+
agents: {
|
|
377
|
+
triage: {
|
|
378
|
+
name: "T",
|
|
379
|
+
dir: "./",
|
|
380
|
+
platforms: [
|
|
381
|
+
{
|
|
382
|
+
type: "slack",
|
|
383
|
+
name: "MyWorkspace",
|
|
384
|
+
config: { botToken: "xoxb-..." },
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
391
|
+
if (!r.success) {
|
|
392
|
+
const msgs = r.error.issues.map((i) => i.message).join(" ");
|
|
393
|
+
(0, bun_test_1.expect)(msgs).toMatch(/lowercase/i);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
(0, bun_test_1.test)("rejects platform name starting with hyphen", () => {
|
|
397
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
398
|
+
agents: {
|
|
399
|
+
triage: {
|
|
400
|
+
name: "T",
|
|
401
|
+
dir: "./",
|
|
402
|
+
platforms: [
|
|
403
|
+
{
|
|
404
|
+
type: "slack",
|
|
405
|
+
name: "-bad",
|
|
406
|
+
config: { botToken: "xoxb-..." },
|
|
407
|
+
},
|
|
408
|
+
],
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
413
|
+
});
|
|
414
|
+
(0, bun_test_1.test)("accepts platform without optional name", () => {
|
|
415
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
416
|
+
agents: {
|
|
417
|
+
triage: {
|
|
418
|
+
name: "T",
|
|
419
|
+
dir: "./",
|
|
420
|
+
platforms: [{ type: "telegram", config: { botToken: "$BOT_TOKEN" } }],
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
// ── mcp server config ─────────────────────────────────────────────────────────
|
|
428
|
+
(0, bun_test_1.describe)("lobu.toml mcp server config", () => {
|
|
429
|
+
(0, bun_test_1.test)("accepts streamable-http type", () => {
|
|
430
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
431
|
+
agents: {
|
|
432
|
+
triage: {
|
|
433
|
+
name: "T",
|
|
434
|
+
dir: "./",
|
|
435
|
+
skills: {
|
|
436
|
+
mcp: {
|
|
437
|
+
github: {
|
|
438
|
+
type: "streamable-http",
|
|
439
|
+
url: "https://mcp.github.com",
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
447
|
+
});
|
|
448
|
+
(0, bun_test_1.test)("accepts stdio type with command and args", () => {
|
|
449
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
450
|
+
agents: {
|
|
451
|
+
triage: {
|
|
452
|
+
name: "T",
|
|
453
|
+
dir: "./",
|
|
454
|
+
skills: {
|
|
455
|
+
mcp: {
|
|
456
|
+
local: {
|
|
457
|
+
type: "stdio",
|
|
458
|
+
command: "npx",
|
|
459
|
+
args: ["-y", "@mcp/pkg"],
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
});
|
|
466
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
467
|
+
});
|
|
468
|
+
(0, bun_test_1.test)("rejects unknown mcp type", () => {
|
|
469
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
470
|
+
agents: {
|
|
471
|
+
triage: {
|
|
472
|
+
name: "T",
|
|
473
|
+
dir: "./",
|
|
474
|
+
skills: { mcp: { bad: { type: "websocket", url: "ws://..." } } },
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
479
|
+
});
|
|
480
|
+
(0, bun_test_1.test)("accepts auth_scope user", () => {
|
|
481
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
482
|
+
agents: {
|
|
483
|
+
triage: {
|
|
484
|
+
name: "T",
|
|
485
|
+
dir: "./",
|
|
486
|
+
skills: {
|
|
487
|
+
mcp: {
|
|
488
|
+
svc: {
|
|
489
|
+
type: "streamable-http",
|
|
490
|
+
url: "https://svc.example.com",
|
|
491
|
+
auth_scope: "user",
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
499
|
+
});
|
|
500
|
+
(0, bun_test_1.test)("accepts auth_scope channel", () => {
|
|
501
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
502
|
+
agents: {
|
|
503
|
+
triage: {
|
|
504
|
+
name: "T",
|
|
505
|
+
dir: "./",
|
|
506
|
+
skills: {
|
|
507
|
+
mcp: {
|
|
508
|
+
svc: {
|
|
509
|
+
type: "streamable-http",
|
|
510
|
+
url: "https://svc.example.com",
|
|
511
|
+
auth_scope: "channel",
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
519
|
+
});
|
|
520
|
+
(0, bun_test_1.test)("rejects unknown auth_scope", () => {
|
|
521
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
522
|
+
agents: {
|
|
523
|
+
triage: {
|
|
524
|
+
name: "T",
|
|
525
|
+
dir: "./",
|
|
526
|
+
skills: {
|
|
527
|
+
mcp: {
|
|
528
|
+
svc: {
|
|
529
|
+
type: "streamable-http",
|
|
530
|
+
url: "https://svc.example.com",
|
|
531
|
+
auth_scope: "org",
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
// ── guardrails list ──────────────────────────────────────────────────────────
|
|
542
|
+
(0, bun_test_1.describe)("lobu.toml guardrails", () => {
|
|
543
|
+
(0, bun_test_1.test)("accepts a guardrails array of strings", () => {
|
|
544
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
545
|
+
agents: {
|
|
546
|
+
triage: {
|
|
547
|
+
name: "T",
|
|
548
|
+
dir: "./",
|
|
549
|
+
guardrails: ["prompt-injection", "secret-scan"],
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
554
|
+
if (r.success) {
|
|
555
|
+
(0, bun_test_1.expect)(r.data.agents.triage?.guardrails).toEqual([
|
|
556
|
+
"prompt-injection",
|
|
557
|
+
"secret-scan",
|
|
558
|
+
]);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
(0, bun_test_1.test)("rejects non-string guardrail entry", () => {
|
|
562
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
563
|
+
agents: {
|
|
564
|
+
triage: {
|
|
565
|
+
name: "T",
|
|
566
|
+
dir: "./",
|
|
567
|
+
guardrails: [42],
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
572
|
+
});
|
|
573
|
+
(0, bun_test_1.test)("accepts empty guardrails array", () => {
|
|
574
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
575
|
+
agents: { triage: { name: "T", dir: "./", guardrails: [] } },
|
|
576
|
+
});
|
|
577
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
// ── worker config ─────────────────────────────────────────────────────────────
|
|
581
|
+
(0, bun_test_1.describe)("lobu.toml worker config", () => {
|
|
582
|
+
(0, bun_test_1.test)("accepts nix_packages list", () => {
|
|
583
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
584
|
+
agents: {
|
|
585
|
+
triage: {
|
|
586
|
+
name: "T",
|
|
587
|
+
dir: "./",
|
|
588
|
+
worker: { nix_packages: ["python311", "ffmpeg"] },
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
});
|
|
592
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
593
|
+
if (r.success) {
|
|
594
|
+
(0, bun_test_1.expect)(r.data.agents.triage?.worker?.nix_packages).toEqual([
|
|
595
|
+
"python311",
|
|
596
|
+
"ffmpeg",
|
|
597
|
+
]);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
(0, bun_test_1.test)("accepts empty worker config", () => {
|
|
601
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
602
|
+
agents: { triage: { name: "T", dir: "./", worker: {} } },
|
|
603
|
+
});
|
|
604
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
// ── memory strict mode ────────────────────────────────────────────────────────
|
|
608
|
+
(0, bun_test_1.describe)("lobu.toml memory strict mode", () => {
|
|
609
|
+
(0, bun_test_1.test)("rejects unknown memory key", () => {
|
|
610
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
611
|
+
agents: { triage: { name: "T", dir: "./" } },
|
|
612
|
+
memory: { enabled: true, unknown_field: "oops" },
|
|
613
|
+
});
|
|
614
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
615
|
+
});
|
|
616
|
+
(0, bun_test_1.test)("accepts all known memory fields", () => {
|
|
617
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
618
|
+
agents: { triage: { name: "T", dir: "./" } },
|
|
619
|
+
memory: {
|
|
620
|
+
enabled: true,
|
|
621
|
+
org: "dev",
|
|
622
|
+
organization_id: "org-uuid-123",
|
|
623
|
+
name: "Dev Org",
|
|
624
|
+
description: "For dev use",
|
|
625
|
+
visibility: "private",
|
|
626
|
+
models: "./models",
|
|
627
|
+
data: "./data",
|
|
628
|
+
connectors: "./connectors",
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
632
|
+
});
|
|
633
|
+
(0, bun_test_1.test)("rejects invalid visibility value", () => {
|
|
634
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
635
|
+
agents: { triage: { name: "T", dir: "./" } },
|
|
636
|
+
memory: { visibility: "protected" },
|
|
637
|
+
});
|
|
638
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
// ── preview code_ttl_minutes max ─────────────────────────────────────────────
|
|
642
|
+
(0, bun_test_1.describe)("lobu.toml preview code_ttl_minutes bounds", () => {
|
|
643
|
+
(0, bun_test_1.test)("accepts code_ttl_minutes = 60", () => {
|
|
644
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
645
|
+
agents: {
|
|
646
|
+
triage: {
|
|
647
|
+
name: "T",
|
|
648
|
+
dir: "./",
|
|
649
|
+
preview: { slack: { enabled: true, code_ttl_minutes: 60 } },
|
|
650
|
+
},
|
|
651
|
+
},
|
|
652
|
+
});
|
|
653
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
654
|
+
});
|
|
655
|
+
(0, bun_test_1.test)("rejects code_ttl_minutes = 61 (exceeds max)", () => {
|
|
656
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
657
|
+
agents: {
|
|
658
|
+
triage: {
|
|
659
|
+
name: "T",
|
|
660
|
+
dir: "./",
|
|
661
|
+
preview: { slack: { enabled: true, code_ttl_minutes: 61 } },
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
});
|
|
665
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
666
|
+
});
|
|
667
|
+
(0, bun_test_1.test)("rejects code_ttl_minutes = 0 (not positive)", () => {
|
|
668
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
669
|
+
agents: {
|
|
670
|
+
triage: {
|
|
671
|
+
name: "T",
|
|
672
|
+
dir: "./",
|
|
673
|
+
preview: { slack: { enabled: true, code_ttl_minutes: 0 } },
|
|
674
|
+
},
|
|
675
|
+
},
|
|
676
|
+
});
|
|
677
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
678
|
+
});
|
|
679
|
+
(0, bun_test_1.test)("rejects non-integer code_ttl_minutes", () => {
|
|
680
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
681
|
+
agents: {
|
|
682
|
+
triage: {
|
|
683
|
+
name: "T",
|
|
684
|
+
dir: "./",
|
|
685
|
+
preview: { slack: { enabled: true, code_ttl_minutes: 1.5 } },
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
});
|
|
689
|
+
(0, bun_test_1.expect)(r.success).toBe(false);
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
// ── default value coercions ───────────────────────────────────────────────────
|
|
693
|
+
(0, bun_test_1.describe)("lobu.toml default value coercions", () => {
|
|
694
|
+
(0, bun_test_1.test)("providers defaults to []", () => {
|
|
695
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
696
|
+
agents: { triage: { name: "T", dir: "./" } },
|
|
697
|
+
});
|
|
698
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
699
|
+
if (r.success) {
|
|
700
|
+
(0, bun_test_1.expect)(r.data.agents.triage?.providers).toEqual([]);
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
(0, bun_test_1.test)("platforms defaults to []", () => {
|
|
704
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
705
|
+
agents: { triage: { name: "T", dir: "./" } },
|
|
706
|
+
});
|
|
707
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
708
|
+
if (r.success) {
|
|
709
|
+
(0, bun_test_1.expect)(r.data.agents.triage?.platforms).toEqual([]);
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
(0, bun_test_1.test)("skills defaults to {}", () => {
|
|
713
|
+
const r = lobu_toml_schema_1.lobuConfigSchema.safeParse({
|
|
714
|
+
agents: { triage: { name: "T", dir: "./" } },
|
|
715
|
+
});
|
|
716
|
+
(0, bun_test_1.expect)(r.success).toBe(true);
|
|
717
|
+
if (r.success) {
|
|
718
|
+
(0, bun_test_1.expect)(r.data.agents.triage?.skills).toEqual({});
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
});
|
|
722
|
+
//# sourceMappingURL=lobu-toml-schema-harden.test.js.map
|