@enactprotocol/cli 2.2.2 → 2.3.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/README.md +4 -37
- package/dist/commands/cache/index.js +5 -5
- package/dist/commands/cache/index.js.map +1 -1
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +2 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/inspect/index.d.ts.map +1 -1
- package/dist/commands/inspect/index.js +3 -2
- package/dist/commands/inspect/index.js.map +1 -1
- package/dist/commands/install/index.d.ts +1 -1
- package/dist/commands/install/index.d.ts.map +1 -1
- package/dist/commands/install/index.js +64 -4
- package/dist/commands/install/index.js.map +1 -1
- package/dist/commands/learn/index.d.ts.map +1 -1
- package/dist/commands/learn/index.js +54 -0
- package/dist/commands/learn/index.js.map +1 -1
- package/dist/commands/list/index.d.ts +1 -1
- package/dist/commands/list/index.d.ts.map +1 -1
- package/dist/commands/list/index.js +7 -3
- package/dist/commands/list/index.js.map +1 -1
- package/dist/commands/run/index.d.ts.map +1 -1
- package/dist/commands/run/index.js +142 -41
- package/dist/commands/run/index.js.map +1 -1
- package/dist/commands/serve/index.d.ts +9 -0
- package/dist/commands/serve/index.d.ts.map +1 -0
- package/dist/commands/serve/index.js +24 -0
- package/dist/commands/serve/index.js.map +1 -0
- package/dist/commands/validate/index.d.ts.map +1 -1
- package/dist/commands/validate/index.js +7 -37
- package/dist/commands/validate/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/utils/errors.js +2 -2
- package/package.json +7 -6
- package/src/commands/cache/index.ts +5 -5
- package/src/commands/env/README.md +1 -1
- package/src/commands/index.ts +3 -0
- package/src/commands/inspect/index.ts +3 -2
- package/src/commands/install/README.md +2 -2
- package/src/commands/install/index.ts +98 -4
- package/src/commands/learn/index.ts +67 -0
- package/src/commands/list/index.ts +12 -3
- package/src/commands/run/README.md +1 -1
- package/src/commands/run/index.ts +195 -48
- package/src/commands/serve/index.ts +26 -0
- package/src/commands/validate/index.ts +7 -42
- package/src/index.ts +5 -1
- package/src/utils/errors.ts +2 -2
- package/tests/commands/cache.test.ts +2 -2
- package/tests/commands/install-integration.test.ts +11 -12
- package/tests/commands/publish.test.ts +12 -2
- package/tests/commands/run.test.ts +3 -1
- package/tests/commands/serve.test.ts +82 -0
- package/tests/commands/sign.test.ts +1 -1
- package/tests/e2e.test.ts +56 -34
- package/tests/fixtures/calculator/skill.yaml +38 -0
- package/tests/fixtures/echo-tool/SKILL.md +3 -10
- package/tests/fixtures/env-tool/{enact.yaml → skill.yaml} +0 -6
- package/tests/fixtures/greeter/skill.yaml +22 -0
- package/tests/utils/ignore.test.ts +3 -1
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/tests/fixtures/calculator/enact.yaml +0 -34
- package/tests/fixtures/greeter/enact.yaml +0 -18
- /package/tests/fixtures/invalid-tool/{enact.yaml → skill.yaml} +0 -0
|
@@ -46,7 +46,7 @@ describe("install integration", () => {
|
|
|
46
46
|
mkdirSync(join(TEST_GLOBAL_HOME, ".enact", "cache"), { recursive: true });
|
|
47
47
|
|
|
48
48
|
// Create sample tool source
|
|
49
|
-
writeFileSync(join(TEST_TOOL_SRC, "
|
|
49
|
+
writeFileSync(join(TEST_TOOL_SRC, "skill.yaml"), SAMPLE_MANIFEST);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
beforeEach(() => {
|
|
@@ -92,7 +92,7 @@ describe("install integration", () => {
|
|
|
92
92
|
|
|
93
93
|
// Verify installation
|
|
94
94
|
expect(existsSync(destPath)).toBe(true);
|
|
95
|
-
expect(existsSync(join(destPath, "
|
|
95
|
+
expect(existsSync(join(destPath, "skill.yaml"))).toBe(true);
|
|
96
96
|
|
|
97
97
|
// Verify manifest can be loaded from destination
|
|
98
98
|
const installedManifest = loadManifestFromDir(destPath);
|
|
@@ -118,7 +118,7 @@ describe("install integration", () => {
|
|
|
118
118
|
// Create v2 source
|
|
119
119
|
const v2Source = join(TEST_BASE, "source-tool-v2");
|
|
120
120
|
mkdirSync(v2Source, { recursive: true });
|
|
121
|
-
writeFileSync(join(v2Source, "
|
|
121
|
+
writeFileSync(join(v2Source, "skill.yaml"), SAMPLE_MANIFEST_V2);
|
|
122
122
|
|
|
123
123
|
// Simulate force overwrite
|
|
124
124
|
rmSync(destPath, { recursive: true, force: true });
|
|
@@ -164,15 +164,14 @@ describe("install integration", () => {
|
|
|
164
164
|
expect(getInstalledVersion("test/sample-tool", "project", TEST_PROJECT)).toBe("2.0.0");
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
-
test("global install extracts to
|
|
167
|
+
test("global install extracts to skills path", async () => {
|
|
168
168
|
const { getToolCachePath } = await import("@enactprotocol/shared");
|
|
169
169
|
|
|
170
|
-
// Verify
|
|
171
|
-
const
|
|
172
|
-
expect(
|
|
173
|
-
expect(
|
|
174
|
-
expect(
|
|
175
|
-
expect(cachePath).toContain("v1.0.0");
|
|
170
|
+
// Verify skill path structure (~/.agent/skills/{name}/, no version subdir)
|
|
171
|
+
const skillPath = getToolCachePath("test/sample-tool", "1.0.0");
|
|
172
|
+
expect(skillPath).toContain(".agent");
|
|
173
|
+
expect(skillPath).toContain("skills");
|
|
174
|
+
expect(skillPath).toContain("test/sample-tool");
|
|
176
175
|
});
|
|
177
176
|
|
|
178
177
|
test("listInstalledTools returns installed tools from registry", async () => {
|
|
@@ -241,7 +240,7 @@ describe("install integration", () => {
|
|
|
241
240
|
const cachePath = join(TEST_GLOBAL_HOME, ".enact", "cache", "test", "cached-tool", "v1.0.0");
|
|
242
241
|
mkdirSync(cachePath, { recursive: true });
|
|
243
242
|
writeFileSync(
|
|
244
|
-
join(cachePath, "
|
|
243
|
+
join(cachePath, "skill.yaml"),
|
|
245
244
|
`
|
|
246
245
|
name: test/cached-tool
|
|
247
246
|
version: 1.0.0
|
|
@@ -252,7 +251,7 @@ command: echo "cached"
|
|
|
252
251
|
|
|
253
252
|
// The resolver should be able to find this tool once it's registered
|
|
254
253
|
// Note: Full resolver testing is in resolver.test.ts
|
|
255
|
-
expect(existsSync(join(cachePath, "
|
|
254
|
+
expect(existsSync(join(cachePath, "skill.yaml"))).toBe(true);
|
|
256
255
|
});
|
|
257
256
|
});
|
|
258
257
|
});
|
|
@@ -107,11 +107,19 @@ describe("publish command", () => {
|
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
describe("manifest file detection", () => {
|
|
110
|
-
test("identifies
|
|
110
|
+
test("identifies manifest files", () => {
|
|
111
111
|
const isManifest = (filename: string): boolean => {
|
|
112
|
-
return
|
|
112
|
+
return (
|
|
113
|
+
filename === "skill.yaml" ||
|
|
114
|
+
filename === "skill.yml" ||
|
|
115
|
+
filename === "enact.yaml" ||
|
|
116
|
+
filename === "enact.yml" ||
|
|
117
|
+
filename === "enact.md"
|
|
118
|
+
);
|
|
113
119
|
};
|
|
114
120
|
|
|
121
|
+
expect(isManifest("skill.yaml")).toBe(true);
|
|
122
|
+
expect(isManifest("skill.yml")).toBe(true);
|
|
115
123
|
expect(isManifest("enact.yaml")).toBe(true);
|
|
116
124
|
expect(isManifest("enact.yml")).toBe(true);
|
|
117
125
|
expect(isManifest("enact.md")).toBe(true);
|
|
@@ -124,6 +132,8 @@ describe("publish command", () => {
|
|
|
124
132
|
return lastDot === -1 ? "" : filename.slice(lastDot + 1);
|
|
125
133
|
};
|
|
126
134
|
|
|
135
|
+
expect(getExtension("skill.yaml")).toBe("yaml");
|
|
136
|
+
expect(getExtension("skill.yml")).toBe("yml");
|
|
127
137
|
expect(getExtension("enact.yaml")).toBe("yaml");
|
|
128
138
|
expect(getExtension("enact.yml")).toBe("yml");
|
|
129
139
|
expect(getExtension("enact.md")).toBe("md");
|
|
@@ -38,7 +38,9 @@ describe("run command", () => {
|
|
|
38
38
|
configureRunCommand(program);
|
|
39
39
|
|
|
40
40
|
const runCmd = program.commands.find((cmd) => cmd.name() === "run");
|
|
41
|
-
expect(runCmd?.description()).toBe(
|
|
41
|
+
expect(runCmd?.description()).toBe(
|
|
42
|
+
"Execute a tool or action with its manifest-defined command"
|
|
43
|
+
);
|
|
42
44
|
});
|
|
43
45
|
|
|
44
46
|
test("accepts tool argument", () => {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the serve command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { configureServeCommand } from "../../src/commands/serve";
|
|
8
|
+
|
|
9
|
+
describe("serve command", () => {
|
|
10
|
+
describe("command configuration", () => {
|
|
11
|
+
test("configures serve command on program", () => {
|
|
12
|
+
const program = new Command();
|
|
13
|
+
configureServeCommand(program);
|
|
14
|
+
|
|
15
|
+
const serveCmd = program.commands.find((cmd) => cmd.name() === "serve");
|
|
16
|
+
expect(serveCmd).toBeDefined();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("has correct description", () => {
|
|
20
|
+
const program = new Command();
|
|
21
|
+
configureServeCommand(program);
|
|
22
|
+
|
|
23
|
+
const serveCmd = program.commands.find((cmd) => cmd.name() === "serve");
|
|
24
|
+
expect(serveCmd?.description()).toBe("Start a self-hosted Enact registry server");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("options", () => {
|
|
29
|
+
test("has --port option with default 3000", () => {
|
|
30
|
+
const program = new Command();
|
|
31
|
+
configureServeCommand(program);
|
|
32
|
+
|
|
33
|
+
const serveCmd = program.commands.find((cmd) => cmd.name() === "serve");
|
|
34
|
+
const opts = serveCmd?.options ?? [];
|
|
35
|
+
const portOpt = opts.find((o) => o.long === "--port");
|
|
36
|
+
expect(portOpt).toBeDefined();
|
|
37
|
+
expect(portOpt?.defaultValue).toBe("3000");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("has -p short option for port", () => {
|
|
41
|
+
const program = new Command();
|
|
42
|
+
configureServeCommand(program);
|
|
43
|
+
|
|
44
|
+
const serveCmd = program.commands.find((cmd) => cmd.name() === "serve");
|
|
45
|
+
const opts = serveCmd?.options ?? [];
|
|
46
|
+
const portOpt = opts.find((o) => o.short === "-p");
|
|
47
|
+
expect(portOpt).toBeDefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("has --data option with default ./registry-data", () => {
|
|
51
|
+
const program = new Command();
|
|
52
|
+
configureServeCommand(program);
|
|
53
|
+
|
|
54
|
+
const serveCmd = program.commands.find((cmd) => cmd.name() === "serve");
|
|
55
|
+
const opts = serveCmd?.options ?? [];
|
|
56
|
+
const dataOpt = opts.find((o) => o.long === "--data");
|
|
57
|
+
expect(dataOpt).toBeDefined();
|
|
58
|
+
expect(dataOpt?.defaultValue).toBe("./registry-data");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("has -d short option for data", () => {
|
|
62
|
+
const program = new Command();
|
|
63
|
+
configureServeCommand(program);
|
|
64
|
+
|
|
65
|
+
const serveCmd = program.commands.find((cmd) => cmd.name() === "serve");
|
|
66
|
+
const opts = serveCmd?.options ?? [];
|
|
67
|
+
const dataOpt = opts.find((o) => o.short === "-d");
|
|
68
|
+
expect(dataOpt).toBeDefined();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("has --host option with default 0.0.0.0", () => {
|
|
72
|
+
const program = new Command();
|
|
73
|
+
configureServeCommand(program);
|
|
74
|
+
|
|
75
|
+
const serveCmd = program.commands.find((cmd) => cmd.name() === "serve");
|
|
76
|
+
const opts = serveCmd?.options ?? [];
|
|
77
|
+
const hostOpt = opts.find((o) => o.long === "--host");
|
|
78
|
+
expect(hostOpt).toBeDefined();
|
|
79
|
+
expect(hostOpt?.defaultValue).toBe("0.0.0.0");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
package/tests/e2e.test.ts
CHANGED
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
type ToolManifest,
|
|
18
18
|
applyDefaults,
|
|
19
19
|
loadManifestFromDir,
|
|
20
|
+
prepareActionCommand,
|
|
20
21
|
prepareCommand,
|
|
22
|
+
scriptToAction,
|
|
21
23
|
toolNameToPath,
|
|
22
24
|
tryResolveTool,
|
|
23
25
|
validateInputs,
|
|
@@ -77,7 +79,7 @@ describe("E2E: Fixture Validation", () => {
|
|
|
77
79
|
expect(loaded?.manifest.name).toBe("test/greeter");
|
|
78
80
|
expect(loaded?.manifest.version).toBe("1.0.0");
|
|
79
81
|
expect(loaded?.manifest.from).toBe("alpine:latest");
|
|
80
|
-
expect(loaded?.manifest.
|
|
82
|
+
expect(loaded?.manifest.scripts?.greet).toBeDefined();
|
|
81
83
|
});
|
|
82
84
|
|
|
83
85
|
test("echo-tool fixture has valid Markdown manifest", () => {
|
|
@@ -88,15 +90,12 @@ describe("E2E: Fixture Validation", () => {
|
|
|
88
90
|
expect(loaded?.format).toBe("md");
|
|
89
91
|
});
|
|
90
92
|
|
|
91
|
-
test("calculator fixture has valid manifest with
|
|
93
|
+
test("calculator fixture has valid manifest with scripts", () => {
|
|
92
94
|
const loaded = loadManifestFromDir(CALCULATOR_TOOL);
|
|
93
95
|
expect(loaded).not.toBeNull();
|
|
94
96
|
expect(loaded?.manifest.name).toBe("test/calculator");
|
|
95
97
|
expect(loaded?.manifest.from).toBe("python:3.12-alpine");
|
|
96
|
-
expect(loaded?.manifest.
|
|
97
|
-
expect(loaded?.manifest.inputSchema?.required).toContain("operation");
|
|
98
|
-
expect(loaded?.manifest.inputSchema?.required).toContain("a");
|
|
99
|
-
expect(loaded?.manifest.inputSchema?.required).toContain("b");
|
|
98
|
+
expect(loaded?.manifest.scripts?.calculate).toBeDefined();
|
|
100
99
|
});
|
|
101
100
|
|
|
102
101
|
test("invalid-tool fixture is missing required fields", () => {
|
|
@@ -126,7 +125,9 @@ describe("E2E: Tool Installation Flow", () => {
|
|
|
126
125
|
|
|
127
126
|
expect(manifest.name).toBe("test/greeter");
|
|
128
127
|
expect(existsSync(destPath)).toBe(true);
|
|
129
|
-
expect(
|
|
128
|
+
expect(
|
|
129
|
+
existsSync(join(destPath, "enact.yaml")) || existsSync(join(destPath, "skill.yaml"))
|
|
130
|
+
).toBe(true);
|
|
130
131
|
|
|
131
132
|
// Verify installed manifest can be loaded
|
|
132
133
|
const reloaded = loadManifestFromDir(destPath);
|
|
@@ -243,39 +244,41 @@ describe("E2E: Input Validation Flow", () => {
|
|
|
243
244
|
});
|
|
244
245
|
|
|
245
246
|
test("validates greeter with default input", () => {
|
|
246
|
-
//
|
|
247
|
-
const
|
|
248
|
-
const
|
|
247
|
+
// Convert script to action to get its inputSchema
|
|
248
|
+
const action = scriptToAction("greet", greeterManifest.scripts!.greet!);
|
|
249
|
+
const withDefaults = applyDefaults({}, action.inputSchema);
|
|
250
|
+
const result = validateInputs(withDefaults, action.inputSchema);
|
|
249
251
|
expect(result.valid).toBe(true);
|
|
250
252
|
// Should have default applied
|
|
251
253
|
expect(withDefaults.name).toBe("World");
|
|
252
254
|
});
|
|
253
255
|
|
|
254
256
|
test("validates greeter with custom input", () => {
|
|
255
|
-
const
|
|
257
|
+
const action = scriptToAction("greet", greeterManifest.scripts!.greet!);
|
|
258
|
+
const result = validateInputs({ name: "Alice" }, action.inputSchema);
|
|
256
259
|
expect(result.valid).toBe(true);
|
|
257
260
|
expect(result.coercedValues?.name).toBe("Alice");
|
|
258
261
|
});
|
|
259
262
|
|
|
260
263
|
test("validates calculator with all required inputs", () => {
|
|
261
|
-
const
|
|
264
|
+
const action = scriptToAction("calculate", calculatorManifest.scripts!.calculate!);
|
|
265
|
+
const result = validateInputs({ operation: "add", a: 5, b: 3 }, action.inputSchema);
|
|
262
266
|
expect(result.valid).toBe(true);
|
|
263
267
|
});
|
|
264
268
|
|
|
265
269
|
test("fails validation when required input is missing", () => {
|
|
270
|
+
const action = scriptToAction("calculate", calculatorManifest.scripts!.calculate!);
|
|
266
271
|
const result = validateInputs(
|
|
267
272
|
{ operation: "add", a: 5 }, // missing 'b'
|
|
268
|
-
|
|
273
|
+
action.inputSchema
|
|
269
274
|
);
|
|
270
275
|
expect(result.valid).toBe(false);
|
|
271
276
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
272
277
|
});
|
|
273
278
|
|
|
274
279
|
test("fails validation with invalid enum value", () => {
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
calculatorManifest.inputSchema
|
|
278
|
-
);
|
|
280
|
+
const action = scriptToAction("calculate", calculatorManifest.scripts!.calculate!);
|
|
281
|
+
const result = validateInputs({ operation: "invalid", a: 5, b: 3 }, action.inputSchema);
|
|
279
282
|
expect(result.valid).toBe(false);
|
|
280
283
|
});
|
|
281
284
|
});
|
|
@@ -291,11 +294,15 @@ describe("E2E: Command Preparation Flow", () => {
|
|
|
291
294
|
expect(preparedStr).not.toContain("${name}");
|
|
292
295
|
});
|
|
293
296
|
|
|
294
|
-
test("prepares calculator command with multiple inputs", () => {
|
|
297
|
+
test("prepares calculator command with multiple inputs (scripts)", () => {
|
|
295
298
|
const loaded = loadManifestFromDir(CALCULATOR_TOOL);
|
|
296
|
-
const
|
|
299
|
+
const action = scriptToAction("calculate", loaded!.manifest.scripts!.calculate!);
|
|
297
300
|
|
|
298
|
-
const prepared =
|
|
301
|
+
const prepared = prepareActionCommand(
|
|
302
|
+
action.command as string[],
|
|
303
|
+
{ operation: "add", a: 5, b: 3 },
|
|
304
|
+
action.inputSchema
|
|
305
|
+
);
|
|
299
306
|
const preparedStr = prepared.join(" ");
|
|
300
307
|
|
|
301
308
|
expect(preparedStr).toContain("add");
|
|
@@ -472,7 +479,7 @@ describe("E2E: Full Workflow", () => {
|
|
|
472
479
|
}
|
|
473
480
|
});
|
|
474
481
|
|
|
475
|
-
test("complete install -> resolve -> validate -> prepare flow", () => {
|
|
482
|
+
test("complete install -> resolve -> validate -> prepare flow (scripts)", () => {
|
|
476
483
|
// 1. Install tool
|
|
477
484
|
const destBase = join(tempDir, ".enact", "tools");
|
|
478
485
|
const { manifest } = installTool(GREETER_TOOL, destBase);
|
|
@@ -482,18 +489,23 @@ describe("E2E: Full Workflow", () => {
|
|
|
482
489
|
const resolution = tryResolveTool("test/greeter", { startDir: tempDir });
|
|
483
490
|
expect(resolution).not.toBeNull();
|
|
484
491
|
|
|
485
|
-
// 3.
|
|
486
|
-
const
|
|
492
|
+
// 3. Convert script to action and validate
|
|
493
|
+
const action = scriptToAction("greet", manifest.scripts!.greet!);
|
|
494
|
+
const validation = validateInputs({ name: "TestUser" }, action.inputSchema);
|
|
487
495
|
expect(validation.valid).toBe(true);
|
|
488
496
|
|
|
489
|
-
// 4. Prepare command
|
|
490
|
-
const prepared =
|
|
497
|
+
// 4. Prepare action command
|
|
498
|
+
const prepared = prepareActionCommand(
|
|
499
|
+
action.command as string[],
|
|
500
|
+
validation.coercedValues!,
|
|
501
|
+
action.inputSchema
|
|
502
|
+
);
|
|
491
503
|
const preparedStr = prepared.join(" ");
|
|
492
504
|
expect(preparedStr).toContain("TestUser");
|
|
493
505
|
expect(preparedStr).toContain("Hello");
|
|
494
506
|
});
|
|
495
507
|
|
|
496
|
-
test("complete calculator workflow", () => {
|
|
508
|
+
test("complete calculator workflow (scripts)", () => {
|
|
497
509
|
// 1. Install tool
|
|
498
510
|
const destBase = join(tempDir, ".enact", "tools");
|
|
499
511
|
const { manifest } = installTool(CALCULATOR_TOOL, destBase);
|
|
@@ -502,7 +514,9 @@ describe("E2E: Full Workflow", () => {
|
|
|
502
514
|
const resolution = tryResolveTool("test/calculator", { startDir: tempDir });
|
|
503
515
|
expect(resolution).not.toBeNull();
|
|
504
516
|
|
|
505
|
-
// 3.
|
|
517
|
+
// 3. Convert script to action and validate multiple operations
|
|
518
|
+
const action = scriptToAction("calculate", manifest.scripts!.calculate!);
|
|
519
|
+
|
|
506
520
|
const operations = [
|
|
507
521
|
{ operation: "add", a: 10, b: 5 },
|
|
508
522
|
{ operation: "subtract", a: 10, b: 5 },
|
|
@@ -511,10 +525,14 @@ describe("E2E: Full Workflow", () => {
|
|
|
511
525
|
];
|
|
512
526
|
|
|
513
527
|
for (const inputs of operations) {
|
|
514
|
-
const validation = validateInputs(inputs,
|
|
528
|
+
const validation = validateInputs(inputs, action.inputSchema);
|
|
515
529
|
expect(validation.valid).toBe(true);
|
|
516
530
|
|
|
517
|
-
const prepared =
|
|
531
|
+
const prepared = prepareActionCommand(
|
|
532
|
+
action.command as string[],
|
|
533
|
+
validation.coercedValues!,
|
|
534
|
+
action.inputSchema
|
|
535
|
+
);
|
|
518
536
|
const preparedStr = prepared.join(" ");
|
|
519
537
|
expect(preparedStr).toContain(inputs.operation);
|
|
520
538
|
expect(preparedStr).toContain(String(inputs.a));
|
|
@@ -522,7 +540,7 @@ describe("E2E: Full Workflow", () => {
|
|
|
522
540
|
}
|
|
523
541
|
});
|
|
524
542
|
|
|
525
|
-
test("handles markdown tool workflow", () => {
|
|
543
|
+
test("handles markdown tool workflow (scripts)", () => {
|
|
526
544
|
// Install markdown-based tool
|
|
527
545
|
const destBase = join(tempDir, ".enact", "tools");
|
|
528
546
|
const { manifest } = installTool(ECHO_TOOL, destBase);
|
|
@@ -532,12 +550,16 @@ describe("E2E: Full Workflow", () => {
|
|
|
532
550
|
const resolution = tryResolveTool("test/echo-tool", { startDir: tempDir });
|
|
533
551
|
expect(resolution).not.toBeNull();
|
|
534
552
|
|
|
535
|
-
//
|
|
536
|
-
const
|
|
553
|
+
// Convert script to action, validate, prepare
|
|
554
|
+
const action = scriptToAction("echo", manifest.scripts!.echo!);
|
|
555
|
+
const validation = validateInputs({ text: "Hello from markdown tool!" }, action.inputSchema);
|
|
537
556
|
expect(validation.valid).toBe(true);
|
|
538
557
|
|
|
539
|
-
|
|
540
|
-
|
|
558
|
+
const prepared = prepareActionCommand(
|
|
559
|
+
action.command as string[],
|
|
560
|
+
validation.coercedValues!,
|
|
561
|
+
action.inputSchema
|
|
562
|
+
);
|
|
541
563
|
const preparedStr = prepared.join(" ");
|
|
542
564
|
expect(preparedStr).toContain("Hello from markdown tool!");
|
|
543
565
|
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
enact: "2.0.0"
|
|
2
|
+
name: test/calculator
|
|
3
|
+
version: "2.0.0"
|
|
4
|
+
description: A calculator tool that performs basic math operations
|
|
5
|
+
from: python:3.12-alpine
|
|
6
|
+
|
|
7
|
+
scripts:
|
|
8
|
+
calculate:
|
|
9
|
+
command: "python3 -c \"import json; print(json.dumps({'result': eval('{{a}} + {{b}}' if '{{operation}}'=='add' else '{{a}} - {{b}}' if '{{operation}}'=='subtract' else '{{a}} * {{b}}' if '{{operation}}'=='multiply' else '{{a}} / {{b}}'), 'operation': '{{operation}}'}))\""
|
|
10
|
+
inputSchema:
|
|
11
|
+
type: object
|
|
12
|
+
properties:
|
|
13
|
+
operation:
|
|
14
|
+
type: string
|
|
15
|
+
enum:
|
|
16
|
+
- add
|
|
17
|
+
- subtract
|
|
18
|
+
- multiply
|
|
19
|
+
- divide
|
|
20
|
+
description: The math operation to perform
|
|
21
|
+
a:
|
|
22
|
+
type: number
|
|
23
|
+
description: First operand
|
|
24
|
+
b:
|
|
25
|
+
type: number
|
|
26
|
+
description: Second operand
|
|
27
|
+
required:
|
|
28
|
+
- operation
|
|
29
|
+
- a
|
|
30
|
+
- b
|
|
31
|
+
|
|
32
|
+
outputSchema:
|
|
33
|
+
type: object
|
|
34
|
+
properties:
|
|
35
|
+
result:
|
|
36
|
+
type: number
|
|
37
|
+
operation:
|
|
38
|
+
type: string
|
|
@@ -4,15 +4,8 @@ name: test/echo-tool
|
|
|
4
4
|
version: "1.0.0"
|
|
5
5
|
description: A tool that echoes its input for testing
|
|
6
6
|
from: alpine:latest
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
type: object
|
|
10
|
-
properties:
|
|
11
|
-
text:
|
|
12
|
-
type: string
|
|
13
|
-
description: Text to echo
|
|
14
|
-
required:
|
|
15
|
-
- text
|
|
7
|
+
scripts:
|
|
8
|
+
echo: "echo '{\"output\":\"{{text}}\"}'"
|
|
16
9
|
outputSchema:
|
|
17
10
|
type: object
|
|
18
11
|
properties:
|
|
@@ -27,5 +20,5 @@ A simple tool that echoes back the input text. Used for testing.
|
|
|
27
20
|
## Usage
|
|
28
21
|
|
|
29
22
|
```bash
|
|
30
|
-
enact run test/echo-tool --input text="Hello"
|
|
23
|
+
enact run test/echo-tool:echo --input text="Hello"
|
|
31
24
|
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
enact: "2.0.0"
|
|
2
|
+
name: test/greeter
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
description: A simple greeting tool for testing
|
|
5
|
+
from: alpine:latest
|
|
6
|
+
|
|
7
|
+
scripts:
|
|
8
|
+
greet:
|
|
9
|
+
command: "echo '{\"message\":\"Hello, {{name}}!\"}'"
|
|
10
|
+
inputSchema:
|
|
11
|
+
type: object
|
|
12
|
+
properties:
|
|
13
|
+
name:
|
|
14
|
+
type: string
|
|
15
|
+
description: Name to greet
|
|
16
|
+
default: World
|
|
17
|
+
|
|
18
|
+
outputSchema:
|
|
19
|
+
type: object
|
|
20
|
+
properties:
|
|
21
|
+
message:
|
|
22
|
+
type: string
|
|
@@ -280,7 +280,9 @@ dist `;
|
|
|
280
280
|
expect(shouldIgnore("lib/utils.js", "utils.js")).toBe(false);
|
|
281
281
|
});
|
|
282
282
|
|
|
283
|
-
test("allows
|
|
283
|
+
test("allows manifest files", () => {
|
|
284
|
+
expect(shouldIgnore("skill.yaml", "skill.yaml")).toBe(false);
|
|
285
|
+
expect(shouldIgnore("skill.yml", "skill.yml")).toBe(false);
|
|
284
286
|
expect(shouldIgnore("enact.md", "enact.md")).toBe(false);
|
|
285
287
|
expect(shouldIgnore("enact.yaml", "enact.yaml")).toBe(false);
|
|
286
288
|
expect(shouldIgnore("enact.yml", "enact.yml")).toBe(false);
|