@studio-foundation/runner 0.3.0-beta.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/ARCHITECTURE.md +53 -0
- package/LICENSE +663 -0
- package/README.md +125 -0
- package/configs/agents/analyst.agent.yaml +31 -0
- package/configs/agents/code-generator.agent.yaml +31 -0
- package/configs/agents/generic.agent.yaml +23 -0
- package/dist/__tests__/script-executor.test.d.ts +2 -0
- package/dist/__tests__/script-executor.test.d.ts.map +1 -0
- package/dist/__tests__/script-executor.test.js +173 -0
- package/dist/__tests__/script-executor.test.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/integration-loader.d.ts +20 -0
- package/dist/integrations/integration-loader.d.ts.map +1 -0
- package/dist/integrations/integration-loader.js +71 -0
- package/dist/integrations/integration-loader.js.map +1 -0
- package/dist/integrations/integration-loader.test.d.ts +2 -0
- package/dist/integrations/integration-loader.test.d.ts.map +1 -0
- package/dist/integrations/integration-loader.test.js +77 -0
- package/dist/integrations/integration-loader.test.js.map +1 -0
- package/dist/middleware/anonymization.d.ts +22 -0
- package/dist/middleware/anonymization.d.ts.map +1 -0
- package/dist/middleware/anonymization.js +37 -0
- package/dist/middleware/anonymization.js.map +1 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +10 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/mcp-client.d.ts +19 -0
- package/dist/plugins/mcp-client.d.ts.map +1 -0
- package/dist/plugins/mcp-client.js +126 -0
- package/dist/plugins/mcp-client.js.map +1 -0
- package/dist/plugins/mcp-client.test.d.ts +2 -0
- package/dist/plugins/mcp-client.test.d.ts.map +1 -0
- package/dist/plugins/mcp-client.test.js +160 -0
- package/dist/plugins/mcp-client.test.js.map +1 -0
- package/dist/plugins/oauth-provider.d.ts +27 -0
- package/dist/plugins/oauth-provider.d.ts.map +1 -0
- package/dist/plugins/oauth-provider.js +140 -0
- package/dist/plugins/oauth-provider.js.map +1 -0
- package/dist/plugins/oauth-provider.test.d.ts +2 -0
- package/dist/plugins/oauth-provider.test.d.ts.map +1 -0
- package/dist/plugins/oauth-provider.test.js +144 -0
- package/dist/plugins/oauth-provider.test.js.map +1 -0
- package/dist/plugins/plugin-loader.d.ts +29 -0
- package/dist/plugins/plugin-loader.d.ts.map +1 -0
- package/dist/plugins/plugin-loader.js +71 -0
- package/dist/plugins/plugin-loader.js.map +1 -0
- package/dist/plugins/plugin-loader.test.d.ts +2 -0
- package/dist/plugins/plugin-loader.test.d.ts.map +1 -0
- package/dist/plugins/plugin-loader.test.js +87 -0
- package/dist/plugins/plugin-loader.test.js.map +1 -0
- package/dist/prompt-builder.d.ts +47 -0
- package/dist/prompt-builder.d.ts.map +1 -0
- package/dist/prompt-builder.js +263 -0
- package/dist/prompt-builder.js.map +1 -0
- package/dist/prompt-builder.test.d.ts +2 -0
- package/dist/prompt-builder.test.d.ts.map +1 -0
- package/dist/prompt-builder.test.js +154 -0
- package/dist/prompt-builder.test.js.map +1 -0
- package/dist/providers/anthropic.d.ts +14 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +118 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/anthropic.test.d.ts +2 -0
- package/dist/providers/anthropic.test.d.ts.map +1 -0
- package/dist/providers/anthropic.test.js +76 -0
- package/dist/providers/anthropic.test.js.map +1 -0
- package/dist/providers/mock.d.ts +17 -0
- package/dist/providers/mock.d.ts.map +1 -0
- package/dist/providers/mock.js +39 -0
- package/dist/providers/mock.js.map +1 -0
- package/dist/providers/ollama.d.ts +18 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +155 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/ollama.test.d.ts +2 -0
- package/dist/providers/ollama.test.d.ts.map +1 -0
- package/dist/providers/ollama.test.js +123 -0
- package/dist/providers/ollama.test.js.map +1 -0
- package/dist/providers/openai-responses.d.ts +18 -0
- package/dist/providers/openai-responses.d.ts.map +1 -0
- package/dist/providers/openai-responses.js +175 -0
- package/dist/providers/openai-responses.js.map +1 -0
- package/dist/providers/openai.d.ts +16 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +132 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openai.test.d.ts +2 -0
- package/dist/providers/openai.test.d.ts.map +1 -0
- package/dist/providers/openai.test.js +51 -0
- package/dist/providers/openai.test.js.map +1 -0
- package/dist/providers/provider.d.ts +42 -0
- package/dist/providers/provider.d.ts.map +1 -0
- package/dist/providers/provider.js +10 -0
- package/dist/providers/provider.js.map +1 -0
- package/dist/providers/registry.d.ts +42 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +65 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/runner.d.ts +46 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +324 -0
- package/dist/runner.js.map +1 -0
- package/dist/runner.test.d.ts +2 -0
- package/dist/runner.test.d.ts.map +1 -0
- package/dist/runner.test.js +280 -0
- package/dist/runner.test.js.map +1 -0
- package/dist/script-executor.d.ts +11 -0
- package/dist/script-executor.d.ts.map +1 -0
- package/dist/script-executor.js +85 -0
- package/dist/script-executor.js.map +1 -0
- package/dist/tools/builtin/git.d.ts +6 -0
- package/dist/tools/builtin/git.d.ts.map +1 -0
- package/dist/tools/builtin/git.js +294 -0
- package/dist/tools/builtin/git.js.map +1 -0
- package/dist/tools/builtin/patch.d.ts +6 -0
- package/dist/tools/builtin/patch.d.ts.map +1 -0
- package/dist/tools/builtin/patch.js +238 -0
- package/dist/tools/builtin/patch.js.map +1 -0
- package/dist/tools/builtin/repo-manager.d.ts +6 -0
- package/dist/tools/builtin/repo-manager.d.ts.map +1 -0
- package/dist/tools/builtin/repo-manager.js +171 -0
- package/dist/tools/builtin/repo-manager.js.map +1 -0
- package/dist/tools/builtin/search.d.ts +6 -0
- package/dist/tools/builtin/search.d.ts.map +1 -0
- package/dist/tools/builtin/search.js +104 -0
- package/dist/tools/builtin/search.js.map +1 -0
- package/dist/tools/builtin/shell.d.ts +6 -0
- package/dist/tools/builtin/shell.d.ts.map +1 -0
- package/dist/tools/builtin/shell.js +78 -0
- package/dist/tools/builtin/shell.js.map +1 -0
- package/dist/tools/builtin/studio-run.d.ts +12 -0
- package/dist/tools/builtin/studio-run.d.ts.map +1 -0
- package/dist/tools/builtin/studio-run.js +61 -0
- package/dist/tools/builtin/studio-run.js.map +1 -0
- package/dist/tools/builtin/web-search.d.ts +7 -0
- package/dist/tools/builtin/web-search.d.ts.map +1 -0
- package/dist/tools/builtin/web-search.js +94 -0
- package/dist/tools/builtin/web-search.js.map +1 -0
- package/dist/tools/builtin/web-search.test.d.ts +5 -0
- package/dist/tools/builtin/web-search.test.d.ts.map +1 -0
- package/dist/tools/builtin/web-search.test.js +90 -0
- package/dist/tools/builtin/web-search.test.js.map +1 -0
- package/dist/tools/errors.d.ts +4 -0
- package/dist/tools/errors.d.ts.map +1 -0
- package/dist/tools/errors.js +11 -0
- package/dist/tools/errors.js.map +1 -0
- package/dist/tools/errors.test.d.ts +2 -0
- package/dist/tools/errors.test.d.ts.map +1 -0
- package/dist/tools/errors.test.js +14 -0
- package/dist/tools/errors.test.js.map +1 -0
- package/dist/tools/plugin-loader.d.ts +31 -0
- package/dist/tools/plugin-loader.d.ts.map +1 -0
- package/dist/tools/plugin-loader.js +183 -0
- package/dist/tools/plugin-loader.js.map +1 -0
- package/dist/tools/plugin-loader.test.d.ts +2 -0
- package/dist/tools/plugin-loader.test.d.ts.map +1 -0
- package/dist/tools/plugin-loader.test.js +116 -0
- package/dist/tools/plugin-loader.test.js.map +1 -0
- package/dist/tools/skills/skill-loader.d.ts +23 -0
- package/dist/tools/skills/skill-loader.d.ts.map +1 -0
- package/dist/tools/skills/skill-loader.js +58 -0
- package/dist/tools/skills/skill-loader.js.map +1 -0
- package/dist/tools/skills/skill-loader.test.d.ts +2 -0
- package/dist/tools/skills/skill-loader.test.d.ts.map +1 -0
- package/dist/tools/skills/skill-loader.test.js +92 -0
- package/dist/tools/skills/skill-loader.test.js.map +1 -0
- package/dist/tools/tool-executor.d.ts +12 -0
- package/dist/tools/tool-executor.d.ts.map +1 -0
- package/dist/tools/tool-executor.js +76 -0
- package/dist/tools/tool-executor.js.map +1 -0
- package/dist/tools/tool-executor.test.d.ts +2 -0
- package/dist/tools/tool-executor.test.d.ts.map +1 -0
- package/dist/tools/tool-executor.test.js +83 -0
- package/dist/tools/tool-executor.test.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +65 -0
- package/dist/tools/tool-registry.d.ts.map +1 -0
- package/dist/tools/tool-registry.js +112 -0
- package/dist/tools/tool-registry.js.map +1 -0
- package/dist/tools/yaml-executor.d.ts +22 -0
- package/dist/tools/yaml-executor.d.ts.map +1 -0
- package/dist/tools/yaml-executor.js +80 -0
- package/dist/tools/yaml-executor.js.map +1 -0
- package/dist/utils/race-signal.d.ts +6 -0
- package/dist/utils/race-signal.d.ts.map +1 -0
- package/dist/utils/race-signal.js +21 -0
- package/dist/utils/race-signal.js.map +1 -0
- package/dist/utils/race-signal.test.d.ts +2 -0
- package/dist/utils/race-signal.test.d.ts.map +1 -0
- package/dist/utils/race-signal.test.js +48 -0
- package/dist/utils/race-signal.test.js.map +1 -0
- package/package.json +36 -0
- package/src/__tests__/script-executor.test.ts +180 -0
- package/src/index.ts +68 -0
- package/src/integrations/integration-loader.test.ts +88 -0
- package/src/integrations/integration-loader.ts +68 -0
- package/src/middleware/anonymization.ts +38 -0
- package/src/plugins/index.ts +4 -0
- package/src/plugins/mcp-client.test.ts +148 -0
- package/src/plugins/mcp-client.ts +128 -0
- package/src/plugins/oauth-provider.test.ts +167 -0
- package/src/plugins/oauth-provider.ts +175 -0
- package/src/plugins/plugin-loader.test.ts +114 -0
- package/src/plugins/plugin-loader.ts +90 -0
- package/src/prompt-builder.test.ts +167 -0
- package/src/prompt-builder.ts +332 -0
- package/src/providers/anthropic.test.ts +101 -0
- package/src/providers/anthropic.ts +135 -0
- package/src/providers/mock.ts +57 -0
- package/src/providers/ollama.test.ts +166 -0
- package/src/providers/ollama.ts +152 -0
- package/src/providers/openai-responses.ts +212 -0
- package/src/providers/openai.test.ts +67 -0
- package/src/providers/openai.ts +139 -0
- package/src/providers/provider.ts +54 -0
- package/src/providers/registry.ts +77 -0
- package/src/runner.test.ts +343 -0
- package/src/runner.ts +396 -0
- package/src/script-executor.ts +107 -0
- package/src/tools/builtin/git.ts +311 -0
- package/src/tools/builtin/patch.ts +257 -0
- package/src/tools/builtin/repo-manager.ts +142 -0
- package/src/tools/builtin/search.ts +108 -0
- package/src/tools/builtin/shell.ts +82 -0
- package/src/tools/builtin/studio-run.ts +73 -0
- package/src/tools/builtin/web-search.test.ts +122 -0
- package/src/tools/builtin/web-search.ts +101 -0
- package/src/tools/errors.test.ts +12 -0
- package/src/tools/errors.ts +6 -0
- package/src/tools/plugin-loader.test.ts +130 -0
- package/src/tools/plugin-loader.ts +203 -0
- package/src/tools/skills/README.md +49 -0
- package/src/tools/skills/skill-loader.test.ts +106 -0
- package/src/tools/skills/skill-loader.ts +62 -0
- package/src/tools/tool-executor.test.ts +88 -0
- package/src/tools/tool-executor.ts +84 -0
- package/src/tools/tool-registry.ts +130 -0
- package/src/tools/yaml-executor.ts +120 -0
- package/src/utils/race-signal.test.ts +50 -0
- package/src/utils/race-signal.ts +17 -0
- package/templates/integrations/linear.integration.yaml +35 -0
- package/templates/integrations/slack.integration.yaml +22 -0
- package/templates/integrations/webhook.integration.yaml +17 -0
- package/templates/tools/git.tool.yaml +80 -0
- package/templates/tools/repo-manager.tool.yaml +64 -0
- package/templates/tools/search.tool.yaml +22 -0
- package/templates/tools/shell.tool.yaml +19 -0
- package/templates/tools/web-search.tool.yaml +24 -0
- package/tests/anonymization-middleware.test.ts +61 -0
- package/tests/anthropic.test.ts +87 -0
- package/tests/apply-patch.test.ts +355 -0
- package/tests/fixtures/tools/test-builtin.tool.yaml +14 -0
- package/tests/fixtures/tools/test-shell.tool.yaml +19 -0
- package/tests/mock-provider.test.ts +104 -0
- package/tests/openai.test.ts +72 -0
- package/tests/plugin-loader.test.ts +54 -0
- package/tests/prompt-builder.test.ts +468 -0
- package/tests/runner-anonymization.test.ts +89 -0
- package/tests/runner.test.ts +885 -0
- package/tests/studio-run.test.ts +94 -0
- package/tests/tool-executor.test.ts +115 -0
- package/tests/tool-registry.test.ts +84 -0
- package/tests/yaml-executor.test.ts +76 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const skill_loader_js_1 = require("./skill-loader.js");
|
|
7
|
+
const TMP = (0, node_path_1.join)('/tmp', '.studio-runner-skill-loader-test-' + Date.now());
|
|
8
|
+
const SKILLS_DIR = (0, node_path_1.join)(TMP, 'skills');
|
|
9
|
+
(0, vitest_1.describe)('loadSkill', () => {
|
|
10
|
+
(0, vitest_1.beforeAll)(async () => {
|
|
11
|
+
await (0, promises_1.mkdir)(SKILLS_DIR, { recursive: true });
|
|
12
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(SKILLS_DIR, 'commit-conventions.skill.md'), '# Commit Conventions\n\nUse conventional commits.');
|
|
13
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(SKILLS_DIR, 'react-patterns.skill.md'), '# React Patterns\n\nPrefer hooks.');
|
|
14
|
+
});
|
|
15
|
+
(0, vitest_1.afterAll)(async () => {
|
|
16
|
+
await (0, promises_1.rm)(TMP, { recursive: true, force: true });
|
|
17
|
+
});
|
|
18
|
+
(0, vitest_1.it)('returns SkillContent for an existing skill file', async () => {
|
|
19
|
+
const skill = await (0, skill_loader_js_1.loadSkill)('commit-conventions', TMP);
|
|
20
|
+
(0, vitest_1.expect)(skill).toEqual({
|
|
21
|
+
name: 'commit-conventions',
|
|
22
|
+
content: '# Commit Conventions\n\nUse conventional commits.',
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
(0, vitest_1.it)('returns null and warns for a missing skill file', async () => {
|
|
26
|
+
const warnSpy = vitest_1.vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
27
|
+
const skill = await (0, skill_loader_js_1.loadSkill)('nonexistent', TMP);
|
|
28
|
+
(0, vitest_1.expect)(skill).toBeNull();
|
|
29
|
+
(0, vitest_1.expect)(warnSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('nonexistent'));
|
|
30
|
+
warnSpy.mockRestore();
|
|
31
|
+
});
|
|
32
|
+
(0, vitest_1.it)('returns null when studioDir has no skills/ subdirectory', async () => {
|
|
33
|
+
const warnSpy = vitest_1.vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
34
|
+
const skill = await (0, skill_loader_js_1.loadSkill)('commit-conventions', '/tmp/nonexistent-studio-dir-xyz');
|
|
35
|
+
(0, vitest_1.expect)(skill).toBeNull();
|
|
36
|
+
warnSpy.mockRestore();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
(0, vitest_1.describe)('loadSkills', () => {
|
|
40
|
+
(0, vitest_1.beforeAll)(async () => {
|
|
41
|
+
await (0, promises_1.mkdir)(SKILLS_DIR, { recursive: true }).catch(() => { });
|
|
42
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(SKILLS_DIR, 'commit-conventions.skill.md'), '# Commit Conventions\n\nUse conventional commits.').catch(() => { });
|
|
43
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(SKILLS_DIR, 'react-patterns.skill.md'), '# React Patterns\n\nPrefer hooks.').catch(() => { });
|
|
44
|
+
});
|
|
45
|
+
(0, vitest_1.it)('loads all named skills from .studio/skills/', async () => {
|
|
46
|
+
const skills = await (0, skill_loader_js_1.loadSkills)(['commit-conventions', 'react-patterns'], TMP);
|
|
47
|
+
(0, vitest_1.expect)(skills).toHaveLength(2);
|
|
48
|
+
(0, vitest_1.expect)(skills[0]).toEqual({ name: 'commit-conventions', content: '# Commit Conventions\n\nUse conventional commits.' });
|
|
49
|
+
(0, vitest_1.expect)(skills[1]).toEqual({ name: 'react-patterns', content: '# React Patterns\n\nPrefer hooks.' });
|
|
50
|
+
});
|
|
51
|
+
(0, vitest_1.it)('skips missing skills and returns the ones that exist', async () => {
|
|
52
|
+
const warnSpy = vitest_1.vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
53
|
+
const skills = await (0, skill_loader_js_1.loadSkills)(['commit-conventions', 'missing-skill'], TMP);
|
|
54
|
+
(0, vitest_1.expect)(skills).toHaveLength(1);
|
|
55
|
+
(0, vitest_1.expect)(skills[0].name).toBe('commit-conventions');
|
|
56
|
+
(0, vitest_1.expect)(warnSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('missing-skill'));
|
|
57
|
+
warnSpy.mockRestore();
|
|
58
|
+
});
|
|
59
|
+
(0, vitest_1.it)('returns empty array when names list is empty', async () => {
|
|
60
|
+
const skills = await (0, skill_loader_js_1.loadSkills)([], TMP);
|
|
61
|
+
(0, vitest_1.expect)(skills).toHaveLength(0);
|
|
62
|
+
});
|
|
63
|
+
(0, vitest_1.it)('returns empty array when studioDir has no skills/ directory', async () => {
|
|
64
|
+
const warnSpy = vitest_1.vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
65
|
+
const skills = await (0, skill_loader_js_1.loadSkills)(['commit-conventions'], '/tmp/nonexistent-studio-xyz');
|
|
66
|
+
(0, vitest_1.expect)(skills).toHaveLength(0);
|
|
67
|
+
warnSpy.mockRestore();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
(0, vitest_1.describe)('validateSkillManifest', () => {
|
|
71
|
+
(0, vitest_1.it)('returns true for a valid skill manifest', () => {
|
|
72
|
+
(0, vitest_1.expect)((0, skill_loader_js_1.validateSkillManifest)({ name: 'my-skill', content: '# My Skill\n\nContent here.' })).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
(0, vitest_1.it)('throws when manifest is missing name', () => {
|
|
75
|
+
(0, vitest_1.expect)(() => (0, skill_loader_js_1.validateSkillManifest)({ content: 'some content' })).toThrow();
|
|
76
|
+
});
|
|
77
|
+
(0, vitest_1.it)('throws when manifest is missing content', () => {
|
|
78
|
+
(0, vitest_1.expect)(() => (0, skill_loader_js_1.validateSkillManifest)({ name: 'my-skill' })).toThrow();
|
|
79
|
+
});
|
|
80
|
+
(0, vitest_1.it)('throws when manifest is not an object', () => {
|
|
81
|
+
(0, vitest_1.expect)(() => (0, skill_loader_js_1.validateSkillManifest)(null)).toThrow();
|
|
82
|
+
(0, vitest_1.expect)(() => (0, skill_loader_js_1.validateSkillManifest)('string')).toThrow();
|
|
83
|
+
(0, vitest_1.expect)(() => (0, skill_loader_js_1.validateSkillManifest)(42)).toThrow();
|
|
84
|
+
});
|
|
85
|
+
(0, vitest_1.it)('throws when name is not a string', () => {
|
|
86
|
+
(0, vitest_1.expect)(() => (0, skill_loader_js_1.validateSkillManifest)({ name: 123, content: 'content' })).toThrow();
|
|
87
|
+
});
|
|
88
|
+
(0, vitest_1.it)('throws when content is not a string', () => {
|
|
89
|
+
(0, vitest_1.expect)(() => (0, skill_loader_js_1.validateSkillManifest)({ name: 'skill', content: [] })).toThrow();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=skill-loader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-loader.test.js","sourceRoot":"","sources":["../../../src/tools/skills/skill-loader.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ToolCall } from '@studio-foundation/contracts';
|
|
2
|
+
import { ToolRegistry } from './tool-registry.js';
|
|
3
|
+
export declare class ToolExecutor {
|
|
4
|
+
private registry;
|
|
5
|
+
constructor(registry: ToolRegistry);
|
|
6
|
+
execute(toolCall: {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
arguments: Record<string, unknown>;
|
|
10
|
+
}): Promise<ToolCall>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=tool-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-executor.d.ts","sourceRoot":"","sources":["../../src/tools/tool-executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AA4BlD,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAEpC,OAAO,CAAC,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;CAmD7G"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToolExecutor = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Validate LLM-provided arguments against the tool's JSON Schema.
|
|
6
|
+
* Returns an error string if invalid, null if OK.
|
|
7
|
+
*/
|
|
8
|
+
function validateArgs(toolName, schema, args) {
|
|
9
|
+
const properties = schema.properties ?? {};
|
|
10
|
+
const required = schema.required ?? [];
|
|
11
|
+
const declared = new Set(Object.keys(properties));
|
|
12
|
+
const missing = required.filter(p => !(p in args));
|
|
13
|
+
if (missing.length > 0) {
|
|
14
|
+
return `Tool ${toolName}: missing required parameter${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}`;
|
|
15
|
+
}
|
|
16
|
+
const unknown = Object.keys(args).filter(p => !declared.has(p));
|
|
17
|
+
if (unknown.length > 0) {
|
|
18
|
+
return `Tool ${toolName}: unknown parameter${unknown.length > 1 ? 's' : ''}: ${unknown.join(', ')} (declared: ${[...declared].join(', ')})`;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
class ToolExecutor {
|
|
23
|
+
registry;
|
|
24
|
+
constructor(registry) {
|
|
25
|
+
this.registry = registry;
|
|
26
|
+
}
|
|
27
|
+
async execute(toolCall) {
|
|
28
|
+
const tool = this.registry.get(toolCall.name);
|
|
29
|
+
if (!tool) {
|
|
30
|
+
return {
|
|
31
|
+
id: toolCall.id,
|
|
32
|
+
name: toolCall.name,
|
|
33
|
+
arguments: toolCall.arguments,
|
|
34
|
+
error: `Tool not found: ${toolCall.name}`
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Validate arguments against the tool's parameter schema
|
|
38
|
+
const validationError = validateArgs(toolCall.name, tool.parameters, toolCall.arguments);
|
|
39
|
+
if (validationError) {
|
|
40
|
+
return {
|
|
41
|
+
id: toolCall.id,
|
|
42
|
+
name: toolCall.name,
|
|
43
|
+
arguments: toolCall.arguments,
|
|
44
|
+
error: validationError,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const toolResult = await tool.execute(toolCall.arguments);
|
|
49
|
+
if (!toolResult.success) {
|
|
50
|
+
return {
|
|
51
|
+
id: toolCall.id,
|
|
52
|
+
name: toolCall.name,
|
|
53
|
+
arguments: toolCall.arguments,
|
|
54
|
+
error: toolResult.error || 'Tool execution failed'
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
id: toolCall.id,
|
|
59
|
+
name: toolCall.name,
|
|
60
|
+
arguments: toolCall.arguments,
|
|
61
|
+
result: toolResult.output
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
66
|
+
return {
|
|
67
|
+
id: toolCall.id,
|
|
68
|
+
name: toolCall.name,
|
|
69
|
+
arguments: toolCall.arguments,
|
|
70
|
+
error: errorMessage
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.ToolExecutor = ToolExecutor;
|
|
76
|
+
//# sourceMappingURL=tool-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-executor.js","sourceRoot":"","sources":["../../src/tools/tool-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-executor.test.d.ts","sourceRoot":"","sources":["../../src/tools/tool-executor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const tool_executor_js_1 = require("./tool-executor.js");
|
|
5
|
+
const tool_registry_js_1 = require("./tool-registry.js");
|
|
6
|
+
function makeRegistry(paramSchema) {
|
|
7
|
+
const registry = new tool_registry_js_1.ToolRegistry();
|
|
8
|
+
registry.register({
|
|
9
|
+
name: 'test-tool',
|
|
10
|
+
description: 'A test tool',
|
|
11
|
+
parameters: paramSchema,
|
|
12
|
+
execute: vitest_1.vi.fn().mockResolvedValue({ success: true, output: 'ok' }),
|
|
13
|
+
});
|
|
14
|
+
return registry;
|
|
15
|
+
}
|
|
16
|
+
const SCHEMA_WITH_REQUIRED = {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
query: { type: 'string', description: 'Search query' },
|
|
20
|
+
},
|
|
21
|
+
required: ['query'],
|
|
22
|
+
};
|
|
23
|
+
const SCHEMA_NO_REQUIRED = {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
query: { type: 'string', description: 'Search query' },
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
(0, vitest_1.describe)('ToolExecutor — argument validation', () => {
|
|
30
|
+
(0, vitest_1.it)('returns error for missing required parameter', async () => {
|
|
31
|
+
const executor = new tool_executor_js_1.ToolExecutor(makeRegistry(SCHEMA_WITH_REQUIRED));
|
|
32
|
+
const result = await executor.execute({
|
|
33
|
+
id: '1',
|
|
34
|
+
name: 'test-tool',
|
|
35
|
+
arguments: {},
|
|
36
|
+
});
|
|
37
|
+
(0, vitest_1.expect)(result.error).toMatch(/missing required parameter.*query/);
|
|
38
|
+
(0, vitest_1.expect)(result.result).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
(0, vitest_1.it)('returns error for unknown parameter', async () => {
|
|
41
|
+
const executor = new tool_executor_js_1.ToolExecutor(makeRegistry(SCHEMA_WITH_REQUIRED));
|
|
42
|
+
const result = await executor.execute({
|
|
43
|
+
id: '2',
|
|
44
|
+
name: 'test-tool',
|
|
45
|
+
arguments: { query: 'hello', pattern: 'oops' },
|
|
46
|
+
});
|
|
47
|
+
(0, vitest_1.expect)(result.error).toMatch(/unknown parameter.*pattern/);
|
|
48
|
+
(0, vitest_1.expect)(result.error).toMatch(/declared: query/);
|
|
49
|
+
(0, vitest_1.expect)(result.result).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
(0, vitest_1.it)('executes successfully with correct required parameters', async () => {
|
|
52
|
+
const registry = makeRegistry(SCHEMA_WITH_REQUIRED);
|
|
53
|
+
const executor = new tool_executor_js_1.ToolExecutor(registry);
|
|
54
|
+
const result = await executor.execute({
|
|
55
|
+
id: '3',
|
|
56
|
+
name: 'test-tool',
|
|
57
|
+
arguments: { query: 'ramen' },
|
|
58
|
+
});
|
|
59
|
+
(0, vitest_1.expect)(result.error).toBeUndefined();
|
|
60
|
+
(0, vitest_1.expect)(result.result).toBe('ok');
|
|
61
|
+
});
|
|
62
|
+
(0, vitest_1.it)('executes successfully with no required parameters and empty args', async () => {
|
|
63
|
+
const executor = new tool_executor_js_1.ToolExecutor(makeRegistry(SCHEMA_NO_REQUIRED));
|
|
64
|
+
const result = await executor.execute({
|
|
65
|
+
id: '4',
|
|
66
|
+
name: 'test-tool',
|
|
67
|
+
arguments: {},
|
|
68
|
+
});
|
|
69
|
+
(0, vitest_1.expect)(result.error).toBeUndefined();
|
|
70
|
+
(0, vitest_1.expect)(result.result).toBe('ok');
|
|
71
|
+
});
|
|
72
|
+
(0, vitest_1.it)('executes successfully with optional parameter provided', async () => {
|
|
73
|
+
const executor = new tool_executor_js_1.ToolExecutor(makeRegistry(SCHEMA_NO_REQUIRED));
|
|
74
|
+
const result = await executor.execute({
|
|
75
|
+
id: '5',
|
|
76
|
+
name: 'test-tool',
|
|
77
|
+
arguments: { query: 'ramen' },
|
|
78
|
+
});
|
|
79
|
+
(0, vitest_1.expect)(result.error).toBeUndefined();
|
|
80
|
+
(0, vitest_1.expect)(result.result).toBe('ok');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=tool-executor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-executor.test.js","sourceRoot":"","sources":["../../src/tools/tool-executor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registry - manages tool definitions and execution
|
|
3
|
+
*/
|
|
4
|
+
import { ToolDefinition } from '@studio-foundation/contracts';
|
|
5
|
+
/** Normalize tool name: dots → hyphens so both conventions work */
|
|
6
|
+
export declare function normalizeToolName(name: string): string;
|
|
7
|
+
export interface ToolResult {
|
|
8
|
+
success: boolean;
|
|
9
|
+
output: unknown;
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface Tool {
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
parameters: Record<string, unknown>;
|
|
16
|
+
execute(args: Record<string, unknown>): Promise<ToolResult>;
|
|
17
|
+
}
|
|
18
|
+
export declare class ToolRegistry {
|
|
19
|
+
private tools;
|
|
20
|
+
private toolToPlugin;
|
|
21
|
+
private pluginSnippets;
|
|
22
|
+
/**
|
|
23
|
+
* Register a single tool (no plugin metadata).
|
|
24
|
+
*/
|
|
25
|
+
register(tool: Tool): void;
|
|
26
|
+
/**
|
|
27
|
+
* Register all tools belonging to a plugin.
|
|
28
|
+
* If promptSnippet is provided it will be returned by getActiveSnippets()
|
|
29
|
+
* whenever any tool from this plugin is in the registry.
|
|
30
|
+
*/
|
|
31
|
+
registerPlugin(pluginName: string, tools: Tool[], promptSnippet?: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get tool by name
|
|
34
|
+
*/
|
|
35
|
+
get(name: string): Tool | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Check if tool exists
|
|
38
|
+
*/
|
|
39
|
+
has(name: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* List all tools
|
|
42
|
+
*/
|
|
43
|
+
list(): Tool[];
|
|
44
|
+
/**
|
|
45
|
+
* Convert tools to LLM tool definitions format
|
|
46
|
+
*/
|
|
47
|
+
toToolDefinitions(): ToolDefinition[];
|
|
48
|
+
/**
|
|
49
|
+
* Return prompt snippets for all plugins that have at least one tool
|
|
50
|
+
* currently in this registry.
|
|
51
|
+
*/
|
|
52
|
+
getActiveSnippets(): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Create a new registry filtered to specific tool names.
|
|
55
|
+
* Normalizes dots to hyphens so both "repo_manager.write_file"
|
|
56
|
+
* and "repo_manager-write_file" match the registered name.
|
|
57
|
+
* Plugin snippet metadata is carried over for included tools.
|
|
58
|
+
*/
|
|
59
|
+
filter(allowedTools: string[]): ToolRegistry;
|
|
60
|
+
/**
|
|
61
|
+
* Create a full copy of this registry (all tools + plugin metadata).
|
|
62
|
+
*/
|
|
63
|
+
clone(): ToolRegistry;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=tool-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC7D;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,cAAc,CAAkC;IAExD;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAI1B;;;;OAIG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAU/E;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAInC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,IAAI,IAAI,IAAI,EAAE;IAId;;OAEG;IACH,iBAAiB,IAAI,cAAc,EAAE;IAQrC;;;OAGG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAW7B;;;;;OAKG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,YAAY;IAmB5C;;OAEG;IACH,KAAK,IAAI,YAAY;CAGtB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tool registry - manages tool definitions and execution
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ToolRegistry = void 0;
|
|
7
|
+
exports.normalizeToolName = normalizeToolName;
|
|
8
|
+
/** Normalize tool name: dots → hyphens so both conventions work */
|
|
9
|
+
function normalizeToolName(name) {
|
|
10
|
+
return name.replace(/\./g, '-');
|
|
11
|
+
}
|
|
12
|
+
class ToolRegistry {
|
|
13
|
+
tools = new Map();
|
|
14
|
+
toolToPlugin = new Map(); // normalized name → plugin name
|
|
15
|
+
pluginSnippets = new Map(); // plugin name → snippet
|
|
16
|
+
/**
|
|
17
|
+
* Register a single tool (no plugin metadata).
|
|
18
|
+
*/
|
|
19
|
+
register(tool) {
|
|
20
|
+
this.tools.set(tool.name, tool);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register all tools belonging to a plugin.
|
|
24
|
+
* If promptSnippet is provided it will be returned by getActiveSnippets()
|
|
25
|
+
* whenever any tool from this plugin is in the registry.
|
|
26
|
+
*/
|
|
27
|
+
registerPlugin(pluginName, tools, promptSnippet) {
|
|
28
|
+
for (const tool of tools) {
|
|
29
|
+
this.register(tool);
|
|
30
|
+
this.toolToPlugin.set(normalizeToolName(tool.name), pluginName);
|
|
31
|
+
}
|
|
32
|
+
if (promptSnippet) {
|
|
33
|
+
this.pluginSnippets.set(pluginName, promptSnippet);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get tool by name
|
|
38
|
+
*/
|
|
39
|
+
get(name) {
|
|
40
|
+
return this.tools.get(name);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if tool exists
|
|
44
|
+
*/
|
|
45
|
+
has(name) {
|
|
46
|
+
return this.tools.has(name);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* List all tools
|
|
50
|
+
*/
|
|
51
|
+
list() {
|
|
52
|
+
return Array.from(this.tools.values());
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Convert tools to LLM tool definitions format
|
|
56
|
+
*/
|
|
57
|
+
toToolDefinitions() {
|
|
58
|
+
return this.list().map(tool => ({
|
|
59
|
+
name: tool.name,
|
|
60
|
+
description: tool.description,
|
|
61
|
+
parameters: tool.parameters
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Return prompt snippets for all plugins that have at least one tool
|
|
66
|
+
* currently in this registry.
|
|
67
|
+
*/
|
|
68
|
+
getActiveSnippets() {
|
|
69
|
+
const activePlugins = new Set();
|
|
70
|
+
for (const toolName of this.tools.keys()) {
|
|
71
|
+
const plugin = this.toolToPlugin.get(normalizeToolName(toolName));
|
|
72
|
+
if (plugin)
|
|
73
|
+
activePlugins.add(plugin);
|
|
74
|
+
}
|
|
75
|
+
return Array.from(activePlugins)
|
|
76
|
+
.map(p => this.pluginSnippets.get(p))
|
|
77
|
+
.filter((s) => s !== undefined);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create a new registry filtered to specific tool names.
|
|
81
|
+
* Normalizes dots to hyphens so both "repo_manager.write_file"
|
|
82
|
+
* and "repo_manager-write_file" match the registered name.
|
|
83
|
+
* Plugin snippet metadata is carried over for included tools.
|
|
84
|
+
*/
|
|
85
|
+
filter(allowedTools) {
|
|
86
|
+
const filtered = new ToolRegistry();
|
|
87
|
+
for (const toolName of allowedTools) {
|
|
88
|
+
const tool = this.tools.get(toolName)
|
|
89
|
+
?? this.tools.get(normalizeToolName(toolName));
|
|
90
|
+
if (tool) {
|
|
91
|
+
filtered.register(tool);
|
|
92
|
+
// Carry over plugin metadata so getActiveSnippets() works on filtered registry
|
|
93
|
+
const pluginName = this.toolToPlugin.get(normalizeToolName(tool.name));
|
|
94
|
+
if (pluginName) {
|
|
95
|
+
filtered.toolToPlugin.set(normalizeToolName(tool.name), pluginName);
|
|
96
|
+
const snippet = this.pluginSnippets.get(pluginName);
|
|
97
|
+
if (snippet)
|
|
98
|
+
filtered.pluginSnippets.set(pluginName, snippet);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return filtered;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create a full copy of this registry (all tools + plugin metadata).
|
|
106
|
+
*/
|
|
107
|
+
clone() {
|
|
108
|
+
return this.filter(Array.from(this.tools.keys()));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.ToolRegistry = ToolRegistry;
|
|
112
|
+
//# sourceMappingURL=tool-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-registry.js","sourceRoot":"","sources":["../../src/tools/tool-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ParseOutputFormat } from '@studio-foundation/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Render a shell command template with parameter substitution.
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* {{param}} → stringify value (empty string if undefined)
|
|
7
|
+
* {{#if param}}...{{/if}} → include block only when param is truthy
|
|
8
|
+
* {{#if param}}...{{else}}...{{/if}} → if/else block
|
|
9
|
+
* {{param | join 'sep'}} → join array with separator
|
|
10
|
+
* {{param | json}} → JSON.stringify(value)
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderTemplate(template: string, params: Record<string, unknown>): string;
|
|
13
|
+
export interface ShellResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
output: unknown;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Execute a rendered shell command and parse the output.
|
|
20
|
+
*/
|
|
21
|
+
export declare function executeShellCommand(command: string, parseOutput: ParseOutputFormat | undefined, workingDir: string, timeoutMs?: number, env?: Record<string, string>): Promise<ShellResult>;
|
|
22
|
+
//# sourceMappingURL=yaml-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml-executor.d.ts","sourceRoot":"","sources":["../../src/tools/yaml-executor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAItE;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CA+CR;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,iBAAiB,YAAS,EACvC,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,MAAe,EAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC,WAAW,CAAC,CAmCtB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderTemplate = renderTemplate;
|
|
4
|
+
exports.executeShellCommand = executeShellCommand;
|
|
5
|
+
// runner/src/tools/yaml-executor.ts
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const node_util_1 = require("node:util");
|
|
8
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
9
|
+
/**
|
|
10
|
+
* Render a shell command template with parameter substitution.
|
|
11
|
+
*
|
|
12
|
+
* Supports:
|
|
13
|
+
* {{param}} → stringify value (empty string if undefined)
|
|
14
|
+
* {{#if param}}...{{/if}} → include block only when param is truthy
|
|
15
|
+
* {{#if param}}...{{else}}...{{/if}} → if/else block
|
|
16
|
+
* {{param | join 'sep'}} → join array with separator
|
|
17
|
+
* {{param | json}} → JSON.stringify(value)
|
|
18
|
+
*/
|
|
19
|
+
function renderTemplate(template, params) {
|
|
20
|
+
let result = template;
|
|
21
|
+
// {{#if param}}...{{else}}...{{/if}} blocks (with else)
|
|
22
|
+
result = result.replace(/\{\{#if (\w+)\}\}([\s\S]*?)\{\{else\}\}([\s\S]*?)\{\{\/if\}\}/g, (_, key, trueBranch, falseBranch) => params[key] ? trueBranch : falseBranch);
|
|
23
|
+
// {{#if param}}...{{/if}} blocks (without else)
|
|
24
|
+
result = result.replace(/\{\{#if (\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_, key, inner) => (params[key] ? inner : ''));
|
|
25
|
+
// {{param | join 'sep'}} filter
|
|
26
|
+
result = result.replace(/\{\{(\w+)\s*\|\s*join\s+'([^']*)'\}\}/g, (_, key, sep) => {
|
|
27
|
+
const value = params[key];
|
|
28
|
+
return Array.isArray(value) ? value.join(sep) : String(value ?? '');
|
|
29
|
+
});
|
|
30
|
+
// {{param | join "sep"}} filter (double quotes variant)
|
|
31
|
+
result = result.replace(/\{\{(\w+)\s*\|\s*join\s+"([^"]*)"\}\}/g, (_, key, sep) => {
|
|
32
|
+
const value = params[key];
|
|
33
|
+
return Array.isArray(value) ? value.join(sep) : String(value ?? '');
|
|
34
|
+
});
|
|
35
|
+
// {{param | json}} filter
|
|
36
|
+
result = result.replace(/\{\{(\w+)\s*\|\s*json\}\}/g, (_, key) => JSON.stringify(params[key] ?? null));
|
|
37
|
+
// Plain {{param}} substitution
|
|
38
|
+
result = result.replace(/\{\{(\w+)\}\}/g, (_, key) => (params[key] === undefined ? '' : String(params[key])));
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Execute a rendered shell command and parse the output.
|
|
43
|
+
*/
|
|
44
|
+
async function executeShellCommand(command, parseOutput = 'text', workingDir, timeoutMs = 30_000, env) {
|
|
45
|
+
try {
|
|
46
|
+
const { stdout } = await execFileAsync('sh', ['-c', command], {
|
|
47
|
+
cwd: workingDir,
|
|
48
|
+
timeout: timeoutMs,
|
|
49
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
50
|
+
...(env ? { env: { ...process.env, ...env } } : {}),
|
|
51
|
+
});
|
|
52
|
+
const raw = stdout.trim();
|
|
53
|
+
if (parseOutput === 'json') {
|
|
54
|
+
try {
|
|
55
|
+
return { success: true, output: JSON.parse(raw) };
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
output: undefined,
|
|
61
|
+
error: `Failed to parse JSON output: ${raw.slice(0, 200)}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { success: true, output: raw };
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
const e = err;
|
|
69
|
+
// When claude -p exits non-zero, the JSON result is still in stdout.
|
|
70
|
+
// Try to parse it so the runner gets structured error info instead of "Command failed".
|
|
71
|
+
if (parseOutput === 'json' && e.stdout?.trim()) {
|
|
72
|
+
try {
|
|
73
|
+
return { success: false, output: JSON.parse(e.stdout.trim()), error: e.stderr?.trim() || undefined };
|
|
74
|
+
}
|
|
75
|
+
catch { /* fall through to default error */ }
|
|
76
|
+
}
|
|
77
|
+
return { success: false, output: undefined, error: e.stderr?.trim() || e.message || 'Command failed' };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=yaml-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml-executor.js","sourceRoot":"","sources":["../../src/tools/yaml-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Race a promise against an AbortSignal.
|
|
3
|
+
* Rejects with DOMException('Aborted', 'AbortError') if the signal fires first.
|
|
4
|
+
*/
|
|
5
|
+
export declare function raceSignal<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
6
|
+
//# sourceMappingURL=race-signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"race-signal.d.ts","sourceRoot":"","sources":["../../src/utils/race-signal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAYnF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.raceSignal = raceSignal;
|
|
4
|
+
/**
|
|
5
|
+
* Race a promise against an AbortSignal.
|
|
6
|
+
* Rejects with DOMException('Aborted', 'AbortError') if the signal fires first.
|
|
7
|
+
*/
|
|
8
|
+
function raceSignal(promise, signal) {
|
|
9
|
+
if (!signal)
|
|
10
|
+
return promise;
|
|
11
|
+
if (signal.aborted)
|
|
12
|
+
return Promise.reject(new DOMException('Aborted', 'AbortError'));
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const onAbort = () => reject(new DOMException('Aborted', 'AbortError'));
|
|
15
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
16
|
+
promise
|
|
17
|
+
.then((v) => { signal.removeEventListener('abort', onAbort); resolve(v); })
|
|
18
|
+
.catch((e) => { signal.removeEventListener('abort', onAbort); reject(e); });
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=race-signal.js.map
|