@matthias-hausberger/beige 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/LICENSE.md +8 -0
- package/README.md +183 -0
- package/dist/channels/registry.d.ts +14 -0
- package/dist/channels/registry.d.ts.map +1 -0
- package/dist/channels/registry.js +14 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/telegram.d.ts +92 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +469 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/tui.d.ts +8 -0
- package/dist/channels/tui.d.ts.map +1 -0
- package/dist/channels/tui.js +574 -0
- package/dist/channels/tui.js.map +1 -0
- package/dist/cli.d.ts +23 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +571 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/loader.d.ts +6 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +103 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/loader.spec.d.ts +2 -0
- package/dist/config/loader.spec.d.ts.map +1 -0
- package/dist/config/loader.spec.js +195 -0
- package/dist/config/loader.spec.js.map +1 -0
- package/dist/config/schema.d.ts +107 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +42 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/schema.spec.d.ts +2 -0
- package/dist/config/schema.spec.d.ts.map +1 -0
- package/dist/config/schema.spec.js +180 -0
- package/dist/config/schema.spec.js.map +1 -0
- package/dist/gateway/agent-manager.d.ts +138 -0
- package/dist/gateway/agent-manager.d.ts.map +1 -0
- package/dist/gateway/agent-manager.js +532 -0
- package/dist/gateway/agent-manager.js.map +1 -0
- package/dist/gateway/api.d.ts +43 -0
- package/dist/gateway/api.d.ts.map +1 -0
- package/dist/gateway/api.js +256 -0
- package/dist/gateway/api.js.map +1 -0
- package/dist/gateway/api.spec.d.ts +2 -0
- package/dist/gateway/api.spec.d.ts.map +1 -0
- package/dist/gateway/api.spec.js +256 -0
- package/dist/gateway/api.spec.js.map +1 -0
- package/dist/gateway/audit.d.ts +38 -0
- package/dist/gateway/audit.d.ts.map +1 -0
- package/dist/gateway/audit.js +82 -0
- package/dist/gateway/audit.js.map +1 -0
- package/dist/gateway/audit.spec.d.ts +2 -0
- package/dist/gateway/audit.spec.d.ts.map +1 -0
- package/dist/gateway/audit.spec.js +212 -0
- package/dist/gateway/audit.spec.js.map +1 -0
- package/dist/gateway/gateway.d.ts +27 -0
- package/dist/gateway/gateway.d.ts.map +1 -0
- package/dist/gateway/gateway.js +158 -0
- package/dist/gateway/gateway.js.map +1 -0
- package/dist/gateway/policy.d.ts +27 -0
- package/dist/gateway/policy.d.ts.map +1 -0
- package/dist/gateway/policy.js +40 -0
- package/dist/gateway/policy.js.map +1 -0
- package/dist/gateway/policy.spec.d.ts +2 -0
- package/dist/gateway/policy.spec.d.ts.map +1 -0
- package/dist/gateway/policy.spec.js +121 -0
- package/dist/gateway/policy.spec.js.map +1 -0
- package/dist/gateway/provider-health.d.ts +83 -0
- package/dist/gateway/provider-health.d.ts.map +1 -0
- package/dist/gateway/provider-health.js +219 -0
- package/dist/gateway/provider-health.js.map +1 -0
- package/dist/gateway/provider-health.spec.d.ts +2 -0
- package/dist/gateway/provider-health.spec.d.ts.map +1 -0
- package/dist/gateway/provider-health.spec.js +278 -0
- package/dist/gateway/provider-health.spec.js.map +1 -0
- package/dist/gateway/session-settings.d.ts +62 -0
- package/dist/gateway/session-settings.d.ts.map +1 -0
- package/dist/gateway/session-settings.js +91 -0
- package/dist/gateway/session-settings.js.map +1 -0
- package/dist/gateway/session-settings.spec.d.ts +2 -0
- package/dist/gateway/session-settings.spec.d.ts.map +1 -0
- package/dist/gateway/session-settings.spec.js +141 -0
- package/dist/gateway/session-settings.spec.js.map +1 -0
- package/dist/gateway/sessions.d.ts +68 -0
- package/dist/gateway/sessions.d.ts.map +1 -0
- package/dist/gateway/sessions.js +177 -0
- package/dist/gateway/sessions.js.map +1 -0
- package/dist/gateway/sessions.spec.d.ts +2 -0
- package/dist/gateway/sessions.spec.d.ts.map +1 -0
- package/dist/gateway/sessions.spec.js +190 -0
- package/dist/gateway/sessions.spec.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +39 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +144 -0
- package/dist/install.js.map +1 -0
- package/dist/sandbox/manager.d.ts +63 -0
- package/dist/sandbox/manager.d.ts.map +1 -0
- package/dist/sandbox/manager.js +294 -0
- package/dist/sandbox/manager.js.map +1 -0
- package/dist/skills/index.d.ts +2 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +2 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/registry.d.ts +11 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/registry.js +86 -0
- package/dist/skills/registry.js.map +1 -0
- package/dist/skills/registry.spec.d.ts +2 -0
- package/dist/skills/registry.spec.d.ts.map +1 -0
- package/dist/skills/registry.spec.js +220 -0
- package/dist/skills/registry.spec.js.map +1 -0
- package/dist/socket/protocol.d.ts +21 -0
- package/dist/socket/protocol.d.ts.map +1 -0
- package/dist/socket/protocol.js +7 -0
- package/dist/socket/protocol.js.map +1 -0
- package/dist/socket/protocol.spec.d.ts +2 -0
- package/dist/socket/protocol.spec.d.ts.map +1 -0
- package/dist/socket/protocol.spec.js +135 -0
- package/dist/socket/protocol.spec.js.map +1 -0
- package/dist/socket/server.d.ts +21 -0
- package/dist/socket/server.d.ts.map +1 -0
- package/dist/socket/server.js +133 -0
- package/dist/socket/server.js.map +1 -0
- package/dist/socket/server.spec.d.ts +2 -0
- package/dist/socket/server.spec.d.ts.map +1 -0
- package/dist/socket/server.spec.js +333 -0
- package/dist/socket/server.spec.js.map +1 -0
- package/dist/test/fixtures.d.ts +47 -0
- package/dist/test/fixtures.d.ts.map +1 -0
- package/dist/test/fixtures.js +144 -0
- package/dist/test/fixtures.js.map +1 -0
- package/dist/toolkit/index.d.ts +4 -0
- package/dist/toolkit/index.d.ts.map +1 -0
- package/dist/toolkit/index.js +4 -0
- package/dist/toolkit/index.js.map +1 -0
- package/dist/toolkit/installer.d.ts +26 -0
- package/dist/toolkit/installer.d.ts.map +1 -0
- package/dist/toolkit/installer.js +247 -0
- package/dist/toolkit/installer.js.map +1 -0
- package/dist/toolkit/registry.d.ts +19 -0
- package/dist/toolkit/registry.d.ts.map +1 -0
- package/dist/toolkit/registry.js +119 -0
- package/dist/toolkit/registry.js.map +1 -0
- package/dist/toolkit/registry.spec.d.ts +2 -0
- package/dist/toolkit/registry.spec.d.ts.map +1 -0
- package/dist/toolkit/registry.spec.js +194 -0
- package/dist/toolkit/registry.spec.js.map +1 -0
- package/dist/toolkit/schema.d.ts +61 -0
- package/dist/toolkit/schema.d.ts.map +1 -0
- package/dist/toolkit/schema.js +116 -0
- package/dist/toolkit/schema.js.map +1 -0
- package/dist/toolkit/schema.spec.d.ts +2 -0
- package/dist/toolkit/schema.spec.d.ts.map +1 -0
- package/dist/toolkit/schema.spec.js +202 -0
- package/dist/toolkit/schema.spec.js.map +1 -0
- package/dist/tools/core.d.ts +10 -0
- package/dist/tools/core.d.ts.map +1 -0
- package/dist/tools/core.js +246 -0
- package/dist/tools/core.js.map +1 -0
- package/dist/tools/core.spec.d.ts +2 -0
- package/dist/tools/core.spec.d.ts.map +1 -0
- package/dist/tools/core.spec.js +315 -0
- package/dist/tools/core.spec.js.map +1 -0
- package/dist/tools/registry.d.ts +15 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +62 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/registry.spec.d.ts +2 -0
- package/dist/tools/registry.spec.d.ts.map +1 -0
- package/dist/tools/registry.spec.js +228 -0
- package/dist/tools/registry.spec.js.map +1 -0
- package/dist/tools/runner.d.ts +25 -0
- package/dist/tools/runner.d.ts.map +1 -0
- package/dist/tools/runner.js +35 -0
- package/dist/tools/runner.js.map +1 -0
- package/dist/tools/runner.spec.d.ts +2 -0
- package/dist/tools/runner.spec.d.ts.map +1 -0
- package/dist/tools/runner.spec.js +129 -0
- package/dist/tools/runner.spec.js.map +1 -0
- package/dist/types/session.d.ts +8 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +23 -0
- package/dist/types/session.js.map +1 -0
- package/package.json +76 -0
- package/tools/README.md +1 -0
- package/tools/kv/README.md +150 -0
- package/tools/kv/index.ts +149 -0
- package/tools/kv/tool.json +23 -0
- package/tools/message/README.md +53 -0
- package/tools/message/index.ts +183 -0
- package/tools/message/tool.json +11 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { resolve, dirname } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import JSON5 from "json5";
|
|
5
|
+
import { validateConfig } from "./schema.js";
|
|
6
|
+
const TOOLKIT_MARK = "_toolkit";
|
|
7
|
+
function loadToolkitRegistry() {
|
|
8
|
+
const registryPath = resolve(homedir(), ".beige", "toolkit-registry.json");
|
|
9
|
+
if (!existsSync(registryPath)) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const content = readFileSync(registryPath, "utf-8");
|
|
14
|
+
return JSON.parse(content);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function mergeToolkitTools(config, configDir) {
|
|
21
|
+
const registry = loadToolkitRegistry();
|
|
22
|
+
if (!registry) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
for (const [toolkitName, toolkit] of Object.entries(registry.toolkits)) {
|
|
26
|
+
for (const toolName of toolkit.tools) {
|
|
27
|
+
if (config.tools[toolName]) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const toolPath = resolve(toolkit.path, "tools", toolName);
|
|
31
|
+
const toolConfig = {
|
|
32
|
+
path: toolPath,
|
|
33
|
+
target: "gateway",
|
|
34
|
+
[TOOLKIT_MARK]: toolkitName,
|
|
35
|
+
};
|
|
36
|
+
config.tools[toolName] = toolConfig;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve environment variable references in strings.
|
|
42
|
+
* Supports ${VAR_NAME} syntax. Throws if a referenced var is not set.
|
|
43
|
+
*/
|
|
44
|
+
function resolveEnvVars(value) {
|
|
45
|
+
if (typeof value === "string") {
|
|
46
|
+
return value.replace(/\$\{([^}]+)\}/g, (match, varName) => {
|
|
47
|
+
const envValue = process.env[varName];
|
|
48
|
+
if (envValue === undefined) {
|
|
49
|
+
throw new Error(`Environment variable '${varName}' is not set (referenced in config)`);
|
|
50
|
+
}
|
|
51
|
+
return envValue;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
return value.map(resolveEnvVars);
|
|
56
|
+
}
|
|
57
|
+
if (value !== null && typeof value === "object") {
|
|
58
|
+
const result = {};
|
|
59
|
+
for (const [k, v] of Object.entries(value)) {
|
|
60
|
+
result[k] = resolveEnvVars(v);
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve relative tool paths against the config file directory.
|
|
68
|
+
*/
|
|
69
|
+
function resolveToolPaths(config, configDir) {
|
|
70
|
+
for (const tool of Object.values(config.tools)) {
|
|
71
|
+
if (tool.path && !tool.path.startsWith("/")) {
|
|
72
|
+
tool.path = resolve(configDir, tool.path);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolve relative skill paths against the config file directory.
|
|
78
|
+
*/
|
|
79
|
+
function resolveSkillPaths(config, configDir) {
|
|
80
|
+
if (!config.skills)
|
|
81
|
+
return;
|
|
82
|
+
for (const skill of Object.values(config.skills)) {
|
|
83
|
+
if (skill.path && !skill.path.startsWith("/")) {
|
|
84
|
+
skill.path = resolve(configDir, skill.path);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Load and validate a beige config file (JSON5 format).
|
|
90
|
+
*/
|
|
91
|
+
export function loadConfig(configPath) {
|
|
92
|
+
const absolutePath = resolve(configPath);
|
|
93
|
+
const configDir = dirname(absolutePath);
|
|
94
|
+
const raw = readFileSync(absolutePath, "utf-8");
|
|
95
|
+
const parsed = JSON5.parse(raw);
|
|
96
|
+
const resolved = resolveEnvVars(parsed);
|
|
97
|
+
const config = validateConfig(resolved);
|
|
98
|
+
resolveToolPaths(config, configDir);
|
|
99
|
+
resolveSkillPaths(config, configDir);
|
|
100
|
+
mergeToolkitTools(config, configDir);
|
|
101
|
+
return config;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.spec.d.ts","sourceRoot":"","sources":["../../src/config/loader.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { writeFileSync, mkdirSync, rmSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
import { loadConfig } from "./loader.js";
|
|
6
|
+
describe("loadConfig", () => {
|
|
7
|
+
let tempDir;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
tempDir = join(tmpdir(), `beige-test-${Date.now()}`);
|
|
10
|
+
mkdirSync(tempDir, { recursive: true });
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
14
|
+
});
|
|
15
|
+
describe("JSON5 parsing", () => {
|
|
16
|
+
it("parses valid JSON5 with comments", () => {
|
|
17
|
+
const configPath = join(tempDir, "config.json5");
|
|
18
|
+
writeFileSync(configPath, `
|
|
19
|
+
{
|
|
20
|
+
// This is a comment
|
|
21
|
+
llm: {
|
|
22
|
+
providers: {
|
|
23
|
+
anthropic: { apiKey: "test-key" },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
tools: {},
|
|
27
|
+
agents: {
|
|
28
|
+
/* Multi-line
|
|
29
|
+
comment */
|
|
30
|
+
assistant: {
|
|
31
|
+
model: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
32
|
+
tools: [],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
channels: {},
|
|
36
|
+
}
|
|
37
|
+
`);
|
|
38
|
+
const config = loadConfig(configPath);
|
|
39
|
+
expect(config.llm.providers.anthropic.apiKey).toBe("test-key");
|
|
40
|
+
expect(config.agents.assistant).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
it("parses unquoted keys", () => {
|
|
43
|
+
const configPath = join(tempDir, "config.json5");
|
|
44
|
+
writeFileSync(configPath, `
|
|
45
|
+
{
|
|
46
|
+
llm: { providers: { anthropic: { apiKey: "test" } } },
|
|
47
|
+
tools: {},
|
|
48
|
+
agents: { assistant: { model: { provider: "anthropic", model: "claude" }, tools: [] } },
|
|
49
|
+
channels: {},
|
|
50
|
+
}
|
|
51
|
+
`);
|
|
52
|
+
const config = loadConfig(configPath);
|
|
53
|
+
expect(config.llm.providers.anthropic).toBeDefined();
|
|
54
|
+
});
|
|
55
|
+
it("parses trailing commas", () => {
|
|
56
|
+
const configPath = join(tempDir, "config.json5");
|
|
57
|
+
writeFileSync(configPath, `
|
|
58
|
+
{
|
|
59
|
+
llm: { providers: { anthropic: { apiKey: "test", }, }, },
|
|
60
|
+
tools: {},
|
|
61
|
+
agents: {
|
|
62
|
+
assistant: { model: { provider: "anthropic", model: "claude", }, tools: [], },
|
|
63
|
+
},
|
|
64
|
+
channels: {},
|
|
65
|
+
}
|
|
66
|
+
`);
|
|
67
|
+
const config = loadConfig(configPath);
|
|
68
|
+
expect(config.llm.providers.anthropic.apiKey).toBe("test");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe("environment variable resolution", () => {
|
|
72
|
+
it("resolves ${ENV_VAR} references", () => {
|
|
73
|
+
process.env.TEST_API_KEY = "my-secret-key";
|
|
74
|
+
const configPath = join(tempDir, "config.json5");
|
|
75
|
+
writeFileSync(configPath, `
|
|
76
|
+
{
|
|
77
|
+
llm: { providers: { anthropic: { apiKey: "\${TEST_API_KEY}" } } },
|
|
78
|
+
tools: {},
|
|
79
|
+
agents: { assistant: { model: { provider: "anthropic", model: "claude" }, tools: [] } },
|
|
80
|
+
channels: {},
|
|
81
|
+
}
|
|
82
|
+
`);
|
|
83
|
+
const config = loadConfig(configPath);
|
|
84
|
+
expect(config.llm.providers.anthropic.apiKey).toBe("my-secret-key");
|
|
85
|
+
delete process.env.TEST_API_KEY;
|
|
86
|
+
});
|
|
87
|
+
it("resolves env vars in nested objects", () => {
|
|
88
|
+
process.env.TEST_TOKEN = "bot-token-123";
|
|
89
|
+
const configPath = join(tempDir, "config.json5");
|
|
90
|
+
writeFileSync(configPath, `
|
|
91
|
+
{
|
|
92
|
+
llm: { providers: { anthropic: { apiKey: "test" } } },
|
|
93
|
+
tools: {},
|
|
94
|
+
agents: { assistant: { model: { provider: "anthropic", model: "claude" }, tools: [] } },
|
|
95
|
+
channels: {
|
|
96
|
+
telegram: {
|
|
97
|
+
enabled: true,
|
|
98
|
+
token: "\${TEST_TOKEN}",
|
|
99
|
+
allowedUsers: [123],
|
|
100
|
+
agentMapping: { default: "assistant" },
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
`);
|
|
105
|
+
const config = loadConfig(configPath);
|
|
106
|
+
expect(config.channels.telegram?.token).toBe("bot-token-123");
|
|
107
|
+
delete process.env.TEST_TOKEN;
|
|
108
|
+
});
|
|
109
|
+
it("throws when env var is not set", () => {
|
|
110
|
+
delete process.env.NONEXISTENT_VAR;
|
|
111
|
+
const configPath = join(tempDir, "config.json5");
|
|
112
|
+
writeFileSync(configPath, `
|
|
113
|
+
{
|
|
114
|
+
llm: { providers: { anthropic: { apiKey: "\${NONEXISTENT_VAR}" } } },
|
|
115
|
+
tools: {},
|
|
116
|
+
agents: { assistant: { model: { provider: "anthropic", model: "claude" }, tools: [] } },
|
|
117
|
+
channels: {},
|
|
118
|
+
}
|
|
119
|
+
`);
|
|
120
|
+
expect(() => loadConfig(configPath)).toThrow("NONEXISTENT_VAR");
|
|
121
|
+
});
|
|
122
|
+
it("resolves env vars in arrays", () => {
|
|
123
|
+
process.env.ALLOWED_USER = "999888";
|
|
124
|
+
const configPath = join(tempDir, "config.json5");
|
|
125
|
+
writeFileSync(configPath, `
|
|
126
|
+
{
|
|
127
|
+
llm: { providers: { anthropic: { apiKey: "test" } } },
|
|
128
|
+
tools: {},
|
|
129
|
+
agents: { assistant: { model: { provider: "anthropic", model: "claude" }, tools: [] } },
|
|
130
|
+
channels: {
|
|
131
|
+
telegram: {
|
|
132
|
+
enabled: true,
|
|
133
|
+
token: "test",
|
|
134
|
+
allowedUsers: [123, "\${ALLOWED_USER}"],
|
|
135
|
+
agentMapping: { default: "assistant" },
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
`);
|
|
140
|
+
const config = loadConfig(configPath);
|
|
141
|
+
// Note: JSON5 parses strings in number arrays as strings
|
|
142
|
+
// The actual validation would convert these
|
|
143
|
+
expect(config.channels.telegram?.allowedUsers).toContain("999888");
|
|
144
|
+
delete process.env.ALLOWED_USER;
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe("path resolution", () => {
|
|
148
|
+
it("resolves relative tool paths against config directory", () => {
|
|
149
|
+
const configPath = join(tempDir, "config.json5");
|
|
150
|
+
writeFileSync(configPath, `
|
|
151
|
+
{
|
|
152
|
+
llm: { providers: { anthropic: { apiKey: "test" } } },
|
|
153
|
+
tools: {
|
|
154
|
+
mytool: { path: "./tools/mytool", target: "gateway" },
|
|
155
|
+
},
|
|
156
|
+
agents: { assistant: { model: { provider: "anthropic", model: "claude" }, tools: ["mytool"] } },
|
|
157
|
+
channels: {},
|
|
158
|
+
}
|
|
159
|
+
`);
|
|
160
|
+
const config = loadConfig(configPath);
|
|
161
|
+
expect(config.tools.mytool.path).toBe(join(tempDir, "tools/mytool"));
|
|
162
|
+
});
|
|
163
|
+
it("keeps absolute tool paths unchanged", () => {
|
|
164
|
+
const configPath = join(tempDir, "config.json5");
|
|
165
|
+
writeFileSync(configPath, `
|
|
166
|
+
{
|
|
167
|
+
llm: { providers: { anthropic: { apiKey: "test" } } },
|
|
168
|
+
tools: {
|
|
169
|
+
mytool: { path: "/absolute/path/to/tool", target: "gateway" },
|
|
170
|
+
},
|
|
171
|
+
agents: { assistant: { model: { provider: "anthropic", model: "claude" }, tools: ["mytool"] } },
|
|
172
|
+
channels: {},
|
|
173
|
+
}
|
|
174
|
+
`);
|
|
175
|
+
const config = loadConfig(configPath);
|
|
176
|
+
expect(config.tools.mytool.path).toBe("/absolute/path/to/tool");
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe("validation", () => {
|
|
180
|
+
it("validates config schema after loading", () => {
|
|
181
|
+
const configPath = join(tempDir, "config.json5");
|
|
182
|
+
writeFileSync(configPath, `
|
|
183
|
+
{
|
|
184
|
+
llm: { providers: {} },
|
|
185
|
+
tools: {},
|
|
186
|
+
agents: {},
|
|
187
|
+
channels: {},
|
|
188
|
+
}
|
|
189
|
+
`);
|
|
190
|
+
// Should not throw - empty providers/agents is valid
|
|
191
|
+
expect(() => loadConfig(configPath)).not.toThrow();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
//# sourceMappingURL=loader.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.spec.js","sourceRoot":"","sources":["../../src/config/loader.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beige configuration schema.
|
|
3
|
+
* Config is loaded from a YAML file. Environment variables are resolved at load time.
|
|
4
|
+
*/
|
|
5
|
+
export interface BeigeConfig {
|
|
6
|
+
llm: LLMConfig;
|
|
7
|
+
tools: Record<string, ToolConfig>;
|
|
8
|
+
skills?: Record<string, SkillConfig>;
|
|
9
|
+
agents: Record<string, AgentConfig>;
|
|
10
|
+
gateway?: GatewayServerConfig;
|
|
11
|
+
channels: ChannelsConfig;
|
|
12
|
+
}
|
|
13
|
+
export interface LLMConfig {
|
|
14
|
+
providers: Record<string, LLMProviderConfig>;
|
|
15
|
+
}
|
|
16
|
+
export interface LLMProviderConfig {
|
|
17
|
+
apiKey: string;
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
api?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ToolConfig {
|
|
22
|
+
/** Path to the tool package directory (relative to config file) */
|
|
23
|
+
path: string;
|
|
24
|
+
/** Where the tool handler executes */
|
|
25
|
+
target: "gateway" | "sandbox";
|
|
26
|
+
/** Arbitrary tool-specific configuration */
|
|
27
|
+
config?: Record<string, unknown>;
|
|
28
|
+
/** Internal: marks tools auto-loaded from toolkits */
|
|
29
|
+
_toolkit?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface SkillConfig {
|
|
32
|
+
/** Path to the skill package directory (relative to config file) */
|
|
33
|
+
path: string;
|
|
34
|
+
}
|
|
35
|
+
export interface SkillManifest {
|
|
36
|
+
name: string;
|
|
37
|
+
description: string;
|
|
38
|
+
/** Default: "README.md" */
|
|
39
|
+
contextFile?: string;
|
|
40
|
+
requires?: {
|
|
41
|
+
tools?: string[];
|
|
42
|
+
skills?: string[];
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export interface AgentConfig {
|
|
46
|
+
model: ModelRef;
|
|
47
|
+
/** Fallback models to try if the primary fails (after retries exhausted) */
|
|
48
|
+
fallbackModels?: ModelRef[];
|
|
49
|
+
/** List of tool names from the tools registry that this agent can use */
|
|
50
|
+
tools: string[];
|
|
51
|
+
/** List of skill names from the skills registry that this agent can use */
|
|
52
|
+
skills?: string[];
|
|
53
|
+
sandbox?: SandboxConfig;
|
|
54
|
+
}
|
|
55
|
+
export interface ModelRef {
|
|
56
|
+
provider: string;
|
|
57
|
+
model: string;
|
|
58
|
+
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high";
|
|
59
|
+
}
|
|
60
|
+
export interface SandboxConfig {
|
|
61
|
+
image?: string;
|
|
62
|
+
extraMounts?: Record<string, string>;
|
|
63
|
+
extraEnv?: Record<string, string>;
|
|
64
|
+
}
|
|
65
|
+
export interface GatewayServerConfig {
|
|
66
|
+
host?: string;
|
|
67
|
+
port?: number;
|
|
68
|
+
logFile?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface ChannelsConfig {
|
|
71
|
+
telegram?: TelegramChannelConfig;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Channel-level default settings. These can be overridden per-session
|
|
75
|
+
* by the user (e.g. via Telegram commands or TUI slash commands).
|
|
76
|
+
*/
|
|
77
|
+
export interface ChannelDefaultSettings {
|
|
78
|
+
/**
|
|
79
|
+
* If true, the channel is notified whenever the agent calls a tool
|
|
80
|
+
* (e.g. "🔧 exec: ls -la"). Default: false.
|
|
81
|
+
*/
|
|
82
|
+
verbose?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* If true, responses are streamed to the user in real-time (message updates
|
|
85
|
+
* as the LLM generates). If false, the full response is sent once complete.
|
|
86
|
+
* Default: true.
|
|
87
|
+
*/
|
|
88
|
+
streaming?: boolean;
|
|
89
|
+
}
|
|
90
|
+
export interface TelegramChannelConfig {
|
|
91
|
+
enabled: boolean;
|
|
92
|
+
token: string;
|
|
93
|
+
allowedUsers: number[];
|
|
94
|
+
agentMapping: {
|
|
95
|
+
default: string;
|
|
96
|
+
};
|
|
97
|
+
/** Channel-level setting defaults (overridable per-session). */
|
|
98
|
+
defaults?: ChannelDefaultSettings;
|
|
99
|
+
}
|
|
100
|
+
export interface ToolManifest {
|
|
101
|
+
name: string;
|
|
102
|
+
description: string;
|
|
103
|
+
commands?: string[];
|
|
104
|
+
target: "gateway" | "sandbox";
|
|
105
|
+
}
|
|
106
|
+
export declare function validateConfig(config: unknown): BeigeConfig;
|
|
107
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beige configuration schema.
|
|
3
|
+
* Config is loaded from a YAML file. Environment variables are resolved at load time.
|
|
4
|
+
*/
|
|
5
|
+
export function validateConfig(config) {
|
|
6
|
+
const c = config;
|
|
7
|
+
if (!c.llm?.providers || typeof c.llm.providers !== "object") {
|
|
8
|
+
throw new Error("Config: llm.providers is required");
|
|
9
|
+
}
|
|
10
|
+
if (!c.tools || typeof c.tools !== "object") {
|
|
11
|
+
throw new Error("Config: tools is required");
|
|
12
|
+
}
|
|
13
|
+
if (!c.agents || typeof c.agents !== "object") {
|
|
14
|
+
throw new Error("Config: agents is required");
|
|
15
|
+
}
|
|
16
|
+
// Validate agent tool references
|
|
17
|
+
for (const [agentName, agent] of Object.entries(c.agents)) {
|
|
18
|
+
if (!agent.model?.provider || !agent.model?.model) {
|
|
19
|
+
throw new Error(`Config: agents.${agentName}.model requires provider and model`);
|
|
20
|
+
}
|
|
21
|
+
for (const toolName of agent.tools) {
|
|
22
|
+
if (!c.tools[toolName]) {
|
|
23
|
+
throw new Error(`Config: agent '${agentName}' references unknown tool '${toolName}'`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Validate agent skill references
|
|
27
|
+
for (const skillName of agent.skills ?? []) {
|
|
28
|
+
if (!c.skills?.[skillName]) {
|
|
29
|
+
throw new Error(`Config: agent '${agentName}' references unknown skill '${skillName}'`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Validate channel agent references
|
|
34
|
+
if (c.channels?.telegram?.enabled) {
|
|
35
|
+
const defaultAgent = c.channels.telegram.agentMapping?.default;
|
|
36
|
+
if (defaultAgent && !c.agents[defaultAgent]) {
|
|
37
|
+
throw new Error(`Config: telegram.agentMapping.default references unknown agent '${defaultAgent}'`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return c;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAyHH,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,MAAM,CAAC,GAAG,MAAqB,CAAC;IAEhC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAED,iCAAiC;IACjC,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,kBAAkB,SAAS,oCAAoC,CAAC,CAAC;QACnF,CAAC;QACD,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACnC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CACb,kBAAkB,SAAS,8BAA8B,QAAQ,GAAG,CACrE,CAAC;YACJ,CAAC;QACH,CAAC;QACD,kCAAkC;QAClC,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YAC3C,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3B,MAAM,IAAI,KAAK,CACb,kBAAkB,SAAS,+BAA+B,SAAS,GAAG,CACvE,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,oCAAoC;IACpC,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;QAClC,MAAM,YAAY,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;QAC/D,IAAI,YAAY,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CACb,mEAAmE,YAAY,GAAG,CACnF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.spec.d.ts","sourceRoot":"","sources":["../../src/config/schema.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { validateConfig } from "./schema.js";
|
|
3
|
+
import { createMinimalConfig, createFullConfig } from "../test/fixtures.js";
|
|
4
|
+
describe("validateConfig", () => {
|
|
5
|
+
describe("valid configs", () => {
|
|
6
|
+
it("accepts minimal valid config", () => {
|
|
7
|
+
const config = createMinimalConfig();
|
|
8
|
+
expect(() => validateConfig(config)).not.toThrow();
|
|
9
|
+
});
|
|
10
|
+
it("accepts full config with tools and multiple agents", () => {
|
|
11
|
+
const config = createFullConfig();
|
|
12
|
+
expect(() => validateConfig(config)).not.toThrow();
|
|
13
|
+
});
|
|
14
|
+
it("accepts config with fallback models", () => {
|
|
15
|
+
const config = createMinimalConfig({
|
|
16
|
+
agents: {
|
|
17
|
+
assistant: {
|
|
18
|
+
model: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
19
|
+
fallbackModels: [
|
|
20
|
+
{ provider: "openai", model: "gpt-4o" },
|
|
21
|
+
],
|
|
22
|
+
tools: [],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
expect(() => validateConfig(config)).not.toThrow();
|
|
27
|
+
});
|
|
28
|
+
it("accepts config with thinking level", () => {
|
|
29
|
+
const config = createMinimalConfig({
|
|
30
|
+
agents: {
|
|
31
|
+
assistant: {
|
|
32
|
+
model: {
|
|
33
|
+
provider: "anthropic",
|
|
34
|
+
model: "claude-sonnet-4-20250514",
|
|
35
|
+
thinkingLevel: "high",
|
|
36
|
+
},
|
|
37
|
+
tools: [],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
expect(() => validateConfig(config)).not.toThrow();
|
|
42
|
+
});
|
|
43
|
+
it("accepts config with sandbox options", () => {
|
|
44
|
+
const config = createMinimalConfig({
|
|
45
|
+
agents: {
|
|
46
|
+
assistant: {
|
|
47
|
+
model: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
48
|
+
tools: [],
|
|
49
|
+
sandbox: {
|
|
50
|
+
image: "custom-sandbox:v1",
|
|
51
|
+
extraMounts: { "/host/path": "/container/path" },
|
|
52
|
+
extraEnv: { DEBUG: "true" },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
expect(() => validateConfig(config)).not.toThrow();
|
|
58
|
+
});
|
|
59
|
+
it("accepts config with gateway settings", () => {
|
|
60
|
+
const config = createMinimalConfig({
|
|
61
|
+
gateway: {
|
|
62
|
+
host: "0.0.0.0",
|
|
63
|
+
port: 8080,
|
|
64
|
+
logFile: "/var/log/beige.log",
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
expect(() => validateConfig(config)).not.toThrow();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("missing required fields", () => {
|
|
71
|
+
it("throws when llm.providers is missing", () => {
|
|
72
|
+
const config = { ...createMinimalConfig(), llm: {} };
|
|
73
|
+
expect(() => validateConfig(config)).toThrow("llm.providers is required");
|
|
74
|
+
});
|
|
75
|
+
it("throws when tools is missing", () => {
|
|
76
|
+
const { tools, ...config } = createMinimalConfig();
|
|
77
|
+
expect(() => validateConfig(config)).toThrow("tools is required");
|
|
78
|
+
});
|
|
79
|
+
it("throws when agents is missing", () => {
|
|
80
|
+
const { agents, ...config } = createMinimalConfig();
|
|
81
|
+
expect(() => validateConfig(config)).toThrow("agents is required");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe("agent validation", () => {
|
|
85
|
+
it("throws when agent model.provider is missing", () => {
|
|
86
|
+
const config = createMinimalConfig({
|
|
87
|
+
agents: {
|
|
88
|
+
assistant: {
|
|
89
|
+
model: { provider: "", model: "claude-sonnet-4-20250514" },
|
|
90
|
+
tools: [],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
expect(() => validateConfig(config)).toThrow("model requires provider");
|
|
95
|
+
});
|
|
96
|
+
it("throws when agent model.model is missing", () => {
|
|
97
|
+
const config = createMinimalConfig({
|
|
98
|
+
agents: {
|
|
99
|
+
assistant: {
|
|
100
|
+
model: { provider: "anthropic", model: "" },
|
|
101
|
+
tools: [],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
expect(() => validateConfig(config)).toThrow("model requires provider and model");
|
|
106
|
+
});
|
|
107
|
+
it("throws when agent references unknown tool", () => {
|
|
108
|
+
const config = createMinimalConfig({
|
|
109
|
+
agents: {
|
|
110
|
+
assistant: {
|
|
111
|
+
model: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
112
|
+
tools: ["nonexistent-tool"],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
expect(() => validateConfig(config)).toThrow("unknown tool 'nonexistent-tool'");
|
|
117
|
+
});
|
|
118
|
+
it("throws when agent references unknown skill", () => {
|
|
119
|
+
const config = createMinimalConfig({
|
|
120
|
+
skills: {
|
|
121
|
+
"code-review": { path: "/skills/code-review" },
|
|
122
|
+
},
|
|
123
|
+
agents: {
|
|
124
|
+
assistant: {
|
|
125
|
+
model: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
126
|
+
tools: [],
|
|
127
|
+
skills: ["nonexistent-skill"],
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
expect(() => validateConfig(config)).toThrow("unknown skill 'nonexistent-skill'");
|
|
132
|
+
});
|
|
133
|
+
it("accepts config with skills", () => {
|
|
134
|
+
const config = createMinimalConfig({
|
|
135
|
+
skills: {
|
|
136
|
+
"code-review": { path: "/skills/code-review" },
|
|
137
|
+
"testing": { path: "/skills/testing" },
|
|
138
|
+
},
|
|
139
|
+
agents: {
|
|
140
|
+
assistant: {
|
|
141
|
+
model: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
142
|
+
tools: [],
|
|
143
|
+
skills: ["code-review", "testing"],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
expect(() => validateConfig(config)).not.toThrow();
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
describe("channel validation", () => {
|
|
151
|
+
it("throws when telegram agentMapping.default references unknown agent", () => {
|
|
152
|
+
const config = createMinimalConfig({
|
|
153
|
+
channels: {
|
|
154
|
+
telegram: {
|
|
155
|
+
enabled: true,
|
|
156
|
+
token: "test-token",
|
|
157
|
+
allowedUsers: [123],
|
|
158
|
+
agentMapping: { default: "nonexistent-agent" },
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
expect(() => validateConfig(config)).toThrow("unknown agent 'nonexistent-agent'");
|
|
163
|
+
});
|
|
164
|
+
it("accepts disabled telegram channel even with invalid agent mapping", () => {
|
|
165
|
+
const config = createMinimalConfig({
|
|
166
|
+
channels: {
|
|
167
|
+
telegram: {
|
|
168
|
+
enabled: false,
|
|
169
|
+
token: "test-token",
|
|
170
|
+
allowedUsers: [123],
|
|
171
|
+
agentMapping: { default: "nonexistent-agent" },
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
// Disabled channels are not validated for agent references
|
|
176
|
+
expect(() => validateConfig(config)).not.toThrow();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
//# sourceMappingURL=schema.spec.js.map
|