@enactprotocol/cli 1.2.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/package.json +34 -38
- package/src/commands/auth/index.ts +940 -0
- package/src/commands/cache/index.ts +361 -0
- package/src/commands/config/README.md +239 -0
- package/src/commands/config/index.ts +164 -0
- package/src/commands/env/README.md +197 -0
- package/src/commands/env/index.ts +392 -0
- package/src/commands/exec/README.md +110 -0
- package/src/commands/exec/index.ts +195 -0
- package/src/commands/get/index.ts +198 -0
- package/src/commands/index.ts +30 -0
- package/src/commands/inspect/index.ts +264 -0
- package/src/commands/install/README.md +146 -0
- package/src/commands/install/index.ts +682 -0
- package/src/commands/list/README.md +115 -0
- package/src/commands/list/index.ts +138 -0
- package/src/commands/publish/index.ts +350 -0
- package/src/commands/report/index.ts +366 -0
- package/src/commands/run/README.md +124 -0
- package/src/commands/run/index.ts +686 -0
- package/src/commands/search/index.ts +368 -0
- package/src/commands/setup/index.ts +274 -0
- package/src/commands/sign/index.ts +652 -0
- package/src/commands/trust/README.md +214 -0
- package/src/commands/trust/index.ts +453 -0
- package/src/commands/unyank/index.ts +107 -0
- package/src/commands/yank/index.ts +143 -0
- package/src/index.ts +96 -0
- package/src/types.ts +81 -0
- package/src/utils/errors.ts +409 -0
- package/src/utils/exit-codes.ts +159 -0
- package/src/utils/ignore.ts +147 -0
- package/src/utils/index.ts +107 -0
- package/src/utils/output.ts +242 -0
- package/src/utils/spinner.ts +214 -0
- package/tests/commands/auth.test.ts +217 -0
- package/tests/commands/cache.test.ts +286 -0
- package/tests/commands/config.test.ts +277 -0
- package/tests/commands/env.test.ts +293 -0
- package/tests/commands/exec.test.ts +112 -0
- package/tests/commands/get.test.ts +179 -0
- package/tests/commands/inspect.test.ts +201 -0
- package/tests/commands/install-integration.test.ts +343 -0
- package/tests/commands/install.test.ts +288 -0
- package/tests/commands/list.test.ts +160 -0
- package/tests/commands/publish.test.ts +186 -0
- package/tests/commands/report.test.ts +194 -0
- package/tests/commands/run.test.ts +231 -0
- package/tests/commands/search.test.ts +131 -0
- package/tests/commands/sign.test.ts +164 -0
- package/tests/commands/trust.test.ts +236 -0
- package/tests/commands/unyank.test.ts +114 -0
- package/tests/commands/yank.test.ts +154 -0
- package/tests/e2e.test.ts +554 -0
- package/tests/fixtures/calculator/enact.yaml +34 -0
- package/tests/fixtures/echo-tool/enact.md +31 -0
- package/tests/fixtures/env-tool/enact.yaml +19 -0
- package/tests/fixtures/greeter/enact.yaml +18 -0
- package/tests/fixtures/invalid-tool/enact.yaml +4 -0
- package/tests/index.test.ts +8 -0
- package/tests/types.test.ts +84 -0
- package/tests/utils/errors.test.ts +303 -0
- package/tests/utils/exit-codes.test.ts +189 -0
- package/tests/utils/ignore.test.ts +461 -0
- package/tests/utils/output.test.ts +126 -0
- package/tsconfig.json +17 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/dist/index.js +0 -231410
- package/dist/index.js.bak +0 -231409
- package/dist/web/static/app.js +0 -663
- package/dist/web/static/index.html +0 -117
- package/dist/web/static/style.css +0 -291
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the config command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
6
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import { configureConfigCommand } from "../../src/commands/config";
|
|
10
|
+
|
|
11
|
+
// Test fixtures directory
|
|
12
|
+
const FIXTURES_DIR = join(import.meta.dir, "..", "fixtures", "config-cmd");
|
|
13
|
+
|
|
14
|
+
describe("config command", () => {
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
mkdirSync(FIXTURES_DIR, { recursive: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
if (existsSync(FIXTURES_DIR)) {
|
|
21
|
+
rmSync(FIXTURES_DIR, { recursive: true, force: true });
|
|
22
|
+
mkdirSync(FIXTURES_DIR, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterAll(() => {
|
|
27
|
+
if (existsSync(FIXTURES_DIR)) {
|
|
28
|
+
rmSync(FIXTURES_DIR, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("command configuration", () => {
|
|
33
|
+
test("configures config command on program", () => {
|
|
34
|
+
const program = new Command();
|
|
35
|
+
configureConfigCommand(program);
|
|
36
|
+
|
|
37
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
38
|
+
expect(configCmd).toBeDefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("has correct description", () => {
|
|
42
|
+
const program = new Command();
|
|
43
|
+
configureConfigCommand(program);
|
|
44
|
+
|
|
45
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
46
|
+
expect(configCmd?.description()).toBe("Manage CLI configuration");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("has get subcommand", () => {
|
|
50
|
+
const program = new Command();
|
|
51
|
+
configureConfigCommand(program);
|
|
52
|
+
|
|
53
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
54
|
+
const getCmd = configCmd?.commands.find((cmd) => cmd.name() === "get");
|
|
55
|
+
expect(getCmd).toBeDefined();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("has set subcommand", () => {
|
|
59
|
+
const program = new Command();
|
|
60
|
+
configureConfigCommand(program);
|
|
61
|
+
|
|
62
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
63
|
+
const setCmd = configCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
64
|
+
expect(setCmd).toBeDefined();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("has list subcommand", () => {
|
|
68
|
+
const program = new Command();
|
|
69
|
+
configureConfigCommand(program);
|
|
70
|
+
|
|
71
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
72
|
+
const listCmd = configCmd?.commands.find((cmd) => cmd.name() === "list");
|
|
73
|
+
expect(listCmd).toBeDefined();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("config get subcommand", () => {
|
|
78
|
+
test("has key argument", () => {
|
|
79
|
+
const program = new Command();
|
|
80
|
+
configureConfigCommand(program);
|
|
81
|
+
|
|
82
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
83
|
+
const getCmd = configCmd?.commands.find((cmd) => cmd.name() === "get");
|
|
84
|
+
const args = getCmd?.registeredArguments ?? [];
|
|
85
|
+
expect(args.length).toBe(1);
|
|
86
|
+
expect(args[0]?.name()).toBe("key");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("has --json option", () => {
|
|
90
|
+
const program = new Command();
|
|
91
|
+
configureConfigCommand(program);
|
|
92
|
+
|
|
93
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
94
|
+
const getCmd = configCmd?.commands.find((cmd) => cmd.name() === "get");
|
|
95
|
+
const opts = getCmd?.options ?? [];
|
|
96
|
+
const jsonOpt = opts.find((o) => o.long === "--json");
|
|
97
|
+
expect(jsonOpt).toBeDefined();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("config set subcommand", () => {
|
|
102
|
+
test("has key argument", () => {
|
|
103
|
+
const program = new Command();
|
|
104
|
+
configureConfigCommand(program);
|
|
105
|
+
|
|
106
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
107
|
+
const setCmd = configCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
108
|
+
const args = setCmd?.registeredArguments ?? [];
|
|
109
|
+
expect(args.length).toBeGreaterThanOrEqual(1);
|
|
110
|
+
expect(args[0]?.name()).toBe("key");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("has value argument", () => {
|
|
114
|
+
const program = new Command();
|
|
115
|
+
configureConfigCommand(program);
|
|
116
|
+
|
|
117
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
118
|
+
const setCmd = configCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
119
|
+
const args = setCmd?.registeredArguments ?? [];
|
|
120
|
+
expect(args.length).toBeGreaterThanOrEqual(2);
|
|
121
|
+
expect(args[1]?.name()).toBe("value");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("has --json option", () => {
|
|
125
|
+
const program = new Command();
|
|
126
|
+
configureConfigCommand(program);
|
|
127
|
+
|
|
128
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
129
|
+
const setCmd = configCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
130
|
+
const opts = setCmd?.options ?? [];
|
|
131
|
+
const jsonOpt = opts.find((o) => o.long === "--json");
|
|
132
|
+
expect(jsonOpt).toBeDefined();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe("config list subcommand", () => {
|
|
137
|
+
test("has --json option", () => {
|
|
138
|
+
const program = new Command();
|
|
139
|
+
configureConfigCommand(program);
|
|
140
|
+
|
|
141
|
+
const configCmd = program.commands.find((cmd) => cmd.name() === "config");
|
|
142
|
+
const listCmd = configCmd?.commands.find((cmd) => cmd.name() === "list");
|
|
143
|
+
const opts = listCmd?.options ?? [];
|
|
144
|
+
const jsonOpt = opts.find((o) => o.long === "--json");
|
|
145
|
+
expect(jsonOpt).toBeDefined();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe("dot notation key parsing", () => {
|
|
150
|
+
test("parses simple keys", () => {
|
|
151
|
+
const key = "version";
|
|
152
|
+
const parts = key.split(".");
|
|
153
|
+
expect(parts).toEqual(["version"]);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("parses nested keys", () => {
|
|
157
|
+
const key = "trust.policy";
|
|
158
|
+
const parts = key.split(".");
|
|
159
|
+
expect(parts).toEqual(["trust", "policy"]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("parses deeply nested keys", () => {
|
|
163
|
+
const key = "registry.auth.token";
|
|
164
|
+
const parts = key.split(".");
|
|
165
|
+
expect(parts).toEqual(["registry", "auth", "token"]);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("gets value from nested object", () => {
|
|
169
|
+
const config = {
|
|
170
|
+
trust: {
|
|
171
|
+
policy: "warn",
|
|
172
|
+
publishers: ["alice"],
|
|
173
|
+
},
|
|
174
|
+
cache: {
|
|
175
|
+
maxSizeMb: 1024,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const getValue = (obj: Record<string, unknown>, key: string): unknown => {
|
|
180
|
+
const parts = key.split(".");
|
|
181
|
+
let current: unknown = obj;
|
|
182
|
+
for (const part of parts) {
|
|
183
|
+
if (current === null || typeof current !== "object") {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
current = (current as Record<string, unknown>)[part];
|
|
187
|
+
}
|
|
188
|
+
return current;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
expect(getValue(config, "trust.policy")).toBe("warn");
|
|
192
|
+
expect(getValue(config, "cache.maxSizeMb")).toBe(1024);
|
|
193
|
+
expect(getValue(config, "trust.publishers")).toEqual(["alice"]);
|
|
194
|
+
expect(getValue(config, "nonexistent")).toBeUndefined();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("sets value in nested object", () => {
|
|
198
|
+
const config: Record<string, unknown> = {
|
|
199
|
+
trust: {
|
|
200
|
+
policy: "warn",
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const setValue = (obj: Record<string, unknown>, key: string, value: unknown): void => {
|
|
205
|
+
const parts = key.split(".");
|
|
206
|
+
let current = obj;
|
|
207
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
208
|
+
const part = parts[i] as string;
|
|
209
|
+
if (!(part in current) || typeof current[part] !== "object") {
|
|
210
|
+
current[part] = {};
|
|
211
|
+
}
|
|
212
|
+
current = current[part] as Record<string, unknown>;
|
|
213
|
+
}
|
|
214
|
+
const lastPart = parts[parts.length - 1] as string;
|
|
215
|
+
current[lastPart] = value;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
setValue(config, "trust.policy", "strict");
|
|
219
|
+
expect((config.trust as Record<string, unknown>).policy).toBe("strict");
|
|
220
|
+
|
|
221
|
+
setValue(config, "newKey", "value");
|
|
222
|
+
expect(config.newKey).toBe("value");
|
|
223
|
+
|
|
224
|
+
setValue(config, "deep.nested.key", 123);
|
|
225
|
+
expect(((config.deep as Record<string, unknown>).nested as Record<string, unknown>).key).toBe(
|
|
226
|
+
123
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe("value parsing", () => {
|
|
232
|
+
test("parses JSON object values", () => {
|
|
233
|
+
const value = '{"enabled": true}';
|
|
234
|
+
const parsed = JSON.parse(value);
|
|
235
|
+
expect(parsed.enabled).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("parses JSON array values", () => {
|
|
239
|
+
const value = '["alice", "bob"]';
|
|
240
|
+
const parsed = JSON.parse(value);
|
|
241
|
+
expect(parsed).toEqual(["alice", "bob"]);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("parses number values", () => {
|
|
245
|
+
const value = "1024";
|
|
246
|
+
const parsed = JSON.parse(value);
|
|
247
|
+
expect(parsed).toBe(1024);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("parses boolean values", () => {
|
|
251
|
+
expect(JSON.parse("true")).toBe(true);
|
|
252
|
+
expect(JSON.parse("false")).toBe(false);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("keeps string values as-is when not valid JSON", () => {
|
|
256
|
+
const value = "simple-string";
|
|
257
|
+
let parsed: unknown = value;
|
|
258
|
+
try {
|
|
259
|
+
parsed = JSON.parse(value);
|
|
260
|
+
} catch {
|
|
261
|
+
// Keep as string
|
|
262
|
+
}
|
|
263
|
+
expect(parsed).toBe("simple-string");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("keeps URL values as strings", () => {
|
|
267
|
+
const value = "https://enact.tools";
|
|
268
|
+
let parsed: unknown = value;
|
|
269
|
+
try {
|
|
270
|
+
parsed = JSON.parse(value);
|
|
271
|
+
} catch {
|
|
272
|
+
// Keep as string
|
|
273
|
+
}
|
|
274
|
+
expect(parsed).toBe("https://enact.tools");
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the env command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
6
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import { configureEnvCommand } from "../../src/commands/env";
|
|
10
|
+
|
|
11
|
+
// Test fixtures directory
|
|
12
|
+
const FIXTURES_DIR = join(import.meta.dir, "..", "fixtures", "env-cmd");
|
|
13
|
+
|
|
14
|
+
describe("env command", () => {
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
mkdirSync(FIXTURES_DIR, { recursive: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
if (existsSync(FIXTURES_DIR)) {
|
|
21
|
+
rmSync(FIXTURES_DIR, { recursive: true, force: true });
|
|
22
|
+
mkdirSync(FIXTURES_DIR, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterAll(() => {
|
|
27
|
+
if (existsSync(FIXTURES_DIR)) {
|
|
28
|
+
rmSync(FIXTURES_DIR, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("command configuration", () => {
|
|
33
|
+
test("configures env command on program", () => {
|
|
34
|
+
const program = new Command();
|
|
35
|
+
configureEnvCommand(program);
|
|
36
|
+
|
|
37
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
38
|
+
expect(envCmd).toBeDefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("has correct description", () => {
|
|
42
|
+
const program = new Command();
|
|
43
|
+
configureEnvCommand(program);
|
|
44
|
+
|
|
45
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
46
|
+
expect(envCmd?.description()).toBe("Manage environment variables and secrets");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("has set subcommand", () => {
|
|
50
|
+
const program = new Command();
|
|
51
|
+
configureEnvCommand(program);
|
|
52
|
+
|
|
53
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
54
|
+
const setCmd = envCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
55
|
+
expect(setCmd).toBeDefined();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("has get subcommand", () => {
|
|
59
|
+
const program = new Command();
|
|
60
|
+
configureEnvCommand(program);
|
|
61
|
+
|
|
62
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
63
|
+
const getCmd = envCmd?.commands.find((cmd) => cmd.name() === "get");
|
|
64
|
+
expect(getCmd).toBeDefined();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("has list subcommand", () => {
|
|
68
|
+
const program = new Command();
|
|
69
|
+
configureEnvCommand(program);
|
|
70
|
+
|
|
71
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
72
|
+
const listCmd = envCmd?.commands.find((cmd) => cmd.name() === "list");
|
|
73
|
+
expect(listCmd).toBeDefined();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("has delete subcommand", () => {
|
|
77
|
+
const program = new Command();
|
|
78
|
+
configureEnvCommand(program);
|
|
79
|
+
|
|
80
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
81
|
+
const deleteCmd = envCmd?.commands.find((cmd) => cmd.name() === "delete");
|
|
82
|
+
expect(deleteCmd).toBeDefined();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("delete has rm alias", () => {
|
|
86
|
+
const program = new Command();
|
|
87
|
+
configureEnvCommand(program);
|
|
88
|
+
|
|
89
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
90
|
+
const deleteCmd = envCmd?.commands.find((cmd) => cmd.name() === "delete");
|
|
91
|
+
const aliases = deleteCmd?.aliases() ?? [];
|
|
92
|
+
expect(aliases).toContain("rm");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("env set subcommand", () => {
|
|
97
|
+
test("has key argument", () => {
|
|
98
|
+
const program = new Command();
|
|
99
|
+
configureEnvCommand(program);
|
|
100
|
+
|
|
101
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
102
|
+
const setCmd = envCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
103
|
+
const args = setCmd?.registeredArguments ?? [];
|
|
104
|
+
expect(args.length).toBeGreaterThanOrEqual(1);
|
|
105
|
+
expect(args[0]?.name()).toBe("key");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("has optional value argument", () => {
|
|
109
|
+
const program = new Command();
|
|
110
|
+
configureEnvCommand(program);
|
|
111
|
+
|
|
112
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
113
|
+
const setCmd = envCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
114
|
+
const args = setCmd?.registeredArguments ?? [];
|
|
115
|
+
expect(args.length).toBeGreaterThanOrEqual(2);
|
|
116
|
+
expect(args[1]?.name()).toBe("value");
|
|
117
|
+
expect(args[1]?.required).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("has --secret option", () => {
|
|
121
|
+
const program = new Command();
|
|
122
|
+
configureEnvCommand(program);
|
|
123
|
+
|
|
124
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
125
|
+
const setCmd = envCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
126
|
+
const opts = setCmd?.options ?? [];
|
|
127
|
+
const secretOpt = opts.find((o) => o.long === "--secret");
|
|
128
|
+
expect(secretOpt).toBeDefined();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("has --namespace option", () => {
|
|
132
|
+
const program = new Command();
|
|
133
|
+
configureEnvCommand(program);
|
|
134
|
+
|
|
135
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
136
|
+
const setCmd = envCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
137
|
+
const opts = setCmd?.options ?? [];
|
|
138
|
+
const namespaceOpt = opts.find((o) => o.long === "--namespace");
|
|
139
|
+
expect(namespaceOpt).toBeDefined();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("has --local option", () => {
|
|
143
|
+
const program = new Command();
|
|
144
|
+
configureEnvCommand(program);
|
|
145
|
+
|
|
146
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
147
|
+
const setCmd = envCmd?.commands.find((cmd) => cmd.name() === "set");
|
|
148
|
+
const opts = setCmd?.options ?? [];
|
|
149
|
+
const localOpt = opts.find((o) => o.long === "--local");
|
|
150
|
+
expect(localOpt).toBeDefined();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe("env get subcommand", () => {
|
|
155
|
+
test("has key argument", () => {
|
|
156
|
+
const program = new Command();
|
|
157
|
+
configureEnvCommand(program);
|
|
158
|
+
|
|
159
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
160
|
+
const getCmd = envCmd?.commands.find((cmd) => cmd.name() === "get");
|
|
161
|
+
const args = getCmd?.registeredArguments ?? [];
|
|
162
|
+
expect(args.length).toBe(1);
|
|
163
|
+
expect(args[0]?.name()).toBe("key");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("has --secret option", () => {
|
|
167
|
+
const program = new Command();
|
|
168
|
+
configureEnvCommand(program);
|
|
169
|
+
|
|
170
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
171
|
+
const getCmd = envCmd?.commands.find((cmd) => cmd.name() === "get");
|
|
172
|
+
const opts = getCmd?.options ?? [];
|
|
173
|
+
const secretOpt = opts.find((o) => o.long === "--secret");
|
|
174
|
+
expect(secretOpt).toBeDefined();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("has --namespace option", () => {
|
|
178
|
+
const program = new Command();
|
|
179
|
+
configureEnvCommand(program);
|
|
180
|
+
|
|
181
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
182
|
+
const getCmd = envCmd?.commands.find((cmd) => cmd.name() === "get");
|
|
183
|
+
const opts = getCmd?.options ?? [];
|
|
184
|
+
const namespaceOpt = opts.find((o) => o.long === "--namespace");
|
|
185
|
+
expect(namespaceOpt).toBeDefined();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("env list subcommand", () => {
|
|
190
|
+
test("has --secret option", () => {
|
|
191
|
+
const program = new Command();
|
|
192
|
+
configureEnvCommand(program);
|
|
193
|
+
|
|
194
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
195
|
+
const listCmd = envCmd?.commands.find((cmd) => cmd.name() === "list");
|
|
196
|
+
const opts = listCmd?.options ?? [];
|
|
197
|
+
const secretOpt = opts.find((o) => o.long === "--secret");
|
|
198
|
+
expect(secretOpt).toBeDefined();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("has --local option", () => {
|
|
202
|
+
const program = new Command();
|
|
203
|
+
configureEnvCommand(program);
|
|
204
|
+
|
|
205
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
206
|
+
const listCmd = envCmd?.commands.find((cmd) => cmd.name() === "list");
|
|
207
|
+
const opts = listCmd?.options ?? [];
|
|
208
|
+
const localOpt = opts.find((o) => o.long === "--local");
|
|
209
|
+
expect(localOpt).toBeDefined();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("has --global option", () => {
|
|
213
|
+
const program = new Command();
|
|
214
|
+
configureEnvCommand(program);
|
|
215
|
+
|
|
216
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
217
|
+
const listCmd = envCmd?.commands.find((cmd) => cmd.name() === "list");
|
|
218
|
+
const opts = listCmd?.options ?? [];
|
|
219
|
+
const globalOpt = opts.find((o) => o.long === "--global");
|
|
220
|
+
expect(globalOpt).toBeDefined();
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe("env delete subcommand", () => {
|
|
225
|
+
test("has key argument", () => {
|
|
226
|
+
const program = new Command();
|
|
227
|
+
configureEnvCommand(program);
|
|
228
|
+
|
|
229
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
230
|
+
const deleteCmd = envCmd?.commands.find((cmd) => cmd.name() === "delete");
|
|
231
|
+
const args = deleteCmd?.registeredArguments ?? [];
|
|
232
|
+
expect(args.length).toBe(1);
|
|
233
|
+
expect(args[0]?.name()).toBe("key");
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("has --secret option", () => {
|
|
237
|
+
const program = new Command();
|
|
238
|
+
configureEnvCommand(program);
|
|
239
|
+
|
|
240
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
241
|
+
const deleteCmd = envCmd?.commands.find((cmd) => cmd.name() === "delete");
|
|
242
|
+
const opts = deleteCmd?.options ?? [];
|
|
243
|
+
const secretOpt = opts.find((o) => o.long === "--secret");
|
|
244
|
+
expect(secretOpt).toBeDefined();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("has --namespace option", () => {
|
|
248
|
+
const program = new Command();
|
|
249
|
+
configureEnvCommand(program);
|
|
250
|
+
|
|
251
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
252
|
+
const deleteCmd = envCmd?.commands.find((cmd) => cmd.name() === "delete");
|
|
253
|
+
const opts = deleteCmd?.options ?? [];
|
|
254
|
+
const namespaceOpt = opts.find((o) => o.long === "--namespace");
|
|
255
|
+
expect(namespaceOpt).toBeDefined();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("has --local option", () => {
|
|
259
|
+
const program = new Command();
|
|
260
|
+
configureEnvCommand(program);
|
|
261
|
+
|
|
262
|
+
const envCmd = program.commands.find((cmd) => cmd.name() === "env");
|
|
263
|
+
const deleteCmd = envCmd?.commands.find((cmd) => cmd.name() === "delete");
|
|
264
|
+
const opts = deleteCmd?.options ?? [];
|
|
265
|
+
const localOpt = opts.find((o) => o.long === "--local");
|
|
266
|
+
expect(localOpt).toBeDefined();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe("secret vs env mode", () => {
|
|
271
|
+
test("secret mode requires namespace", () => {
|
|
272
|
+
const options = { secret: true, namespace: undefined };
|
|
273
|
+
const requiresNamespace = options.secret && !options.namespace;
|
|
274
|
+
expect(requiresNamespace).toBe(true);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("secret mode with namespace is valid", () => {
|
|
278
|
+
const options = { secret: true, namespace: "alice/api" };
|
|
279
|
+
const isValid = options.secret && options.namespace;
|
|
280
|
+
expect(isValid).toBeTruthy();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("env mode does not require namespace", () => {
|
|
284
|
+
const options = { secret: false };
|
|
285
|
+
expect(options.secret).toBe(false);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("local option applies to env mode", () => {
|
|
289
|
+
const options = { secret: false, local: true };
|
|
290
|
+
expect(options.local).toBe(true);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the exec command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
6
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import { configureExecCommand } from "../../src/commands/exec";
|
|
10
|
+
|
|
11
|
+
// Test fixtures directory
|
|
12
|
+
const FIXTURES_DIR = join(import.meta.dir, "..", "fixtures", "exec-cmd");
|
|
13
|
+
|
|
14
|
+
describe("exec command", () => {
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
mkdirSync(FIXTURES_DIR, { recursive: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
if (existsSync(FIXTURES_DIR)) {
|
|
21
|
+
rmSync(FIXTURES_DIR, { recursive: true, force: true });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("command configuration", () => {
|
|
26
|
+
test("configures exec command on program", () => {
|
|
27
|
+
const program = new Command();
|
|
28
|
+
configureExecCommand(program);
|
|
29
|
+
|
|
30
|
+
const execCmd = program.commands.find((cmd) => cmd.name() === "exec");
|
|
31
|
+
expect(execCmd).toBeDefined();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("has correct description", () => {
|
|
35
|
+
const program = new Command();
|
|
36
|
+
configureExecCommand(program);
|
|
37
|
+
|
|
38
|
+
const execCmd = program.commands.find((cmd) => cmd.name() === "exec");
|
|
39
|
+
expect(execCmd?.description()).toBe(
|
|
40
|
+
"Execute an arbitrary command in a tool's container environment"
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("accepts tool argument", () => {
|
|
45
|
+
const program = new Command();
|
|
46
|
+
configureExecCommand(program);
|
|
47
|
+
|
|
48
|
+
const execCmd = program.commands.find((cmd) => cmd.name() === "exec");
|
|
49
|
+
const args = execCmd?.registeredArguments ?? [];
|
|
50
|
+
expect(args.length).toBeGreaterThanOrEqual(1);
|
|
51
|
+
expect(args[0]?.name()).toBe("tool");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("accepts command argument", () => {
|
|
55
|
+
const program = new Command();
|
|
56
|
+
configureExecCommand(program);
|
|
57
|
+
|
|
58
|
+
const execCmd = program.commands.find((cmd) => cmd.name() === "exec");
|
|
59
|
+
const args = execCmd?.registeredArguments ?? [];
|
|
60
|
+
expect(args.length).toBeGreaterThanOrEqual(2);
|
|
61
|
+
expect(args[1]?.name()).toBe("command");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("has --timeout option", () => {
|
|
65
|
+
const program = new Command();
|
|
66
|
+
configureExecCommand(program);
|
|
67
|
+
|
|
68
|
+
const execCmd = program.commands.find((cmd) => cmd.name() === "exec");
|
|
69
|
+
const opts = execCmd?.options ?? [];
|
|
70
|
+
const timeoutOpt = opts.find((o) => o.long === "--timeout");
|
|
71
|
+
expect(timeoutOpt).toBeDefined();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("has --verbose option", () => {
|
|
75
|
+
const program = new Command();
|
|
76
|
+
configureExecCommand(program);
|
|
77
|
+
|
|
78
|
+
const execCmd = program.commands.find((cmd) => cmd.name() === "exec");
|
|
79
|
+
const opts = execCmd?.options ?? [];
|
|
80
|
+
const verboseOpt = opts.find((o) => o.long === "--verbose");
|
|
81
|
+
expect(verboseOpt).toBeDefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("has --json option", () => {
|
|
85
|
+
const program = new Command();
|
|
86
|
+
configureExecCommand(program);
|
|
87
|
+
|
|
88
|
+
const execCmd = program.commands.find((cmd) => cmd.name() === "exec");
|
|
89
|
+
const opts = execCmd?.options ?? [];
|
|
90
|
+
const jsonOpt = opts.find((o) => o.long === "--json");
|
|
91
|
+
expect(jsonOpt).toBeDefined();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("command parsing", () => {
|
|
96
|
+
test("command with spaces should be quoted", () => {
|
|
97
|
+
const command = 'echo "Hello World"';
|
|
98
|
+
expect(command).toContain(" ");
|
|
99
|
+
expect(command).toContain('"');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("complex shell commands", () => {
|
|
103
|
+
const command = "ls -la && cat enact.md";
|
|
104
|
+
expect(command).toContain("&&");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("command with pipes", () => {
|
|
108
|
+
const command = "cat file.txt | grep pattern | wc -l";
|
|
109
|
+
expect(command).toContain("|");
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|