@enactprotocol/cli 1.2.13 → 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.
Files changed (73) hide show
  1. package/README.md +88 -0
  2. package/package.json +34 -38
  3. package/src/commands/auth/index.ts +940 -0
  4. package/src/commands/cache/index.ts +361 -0
  5. package/src/commands/config/README.md +239 -0
  6. package/src/commands/config/index.ts +164 -0
  7. package/src/commands/env/README.md +197 -0
  8. package/src/commands/env/index.ts +392 -0
  9. package/src/commands/exec/README.md +110 -0
  10. package/src/commands/exec/index.ts +195 -0
  11. package/src/commands/get/index.ts +198 -0
  12. package/src/commands/index.ts +30 -0
  13. package/src/commands/inspect/index.ts +264 -0
  14. package/src/commands/install/README.md +146 -0
  15. package/src/commands/install/index.ts +682 -0
  16. package/src/commands/list/README.md +115 -0
  17. package/src/commands/list/index.ts +138 -0
  18. package/src/commands/publish/index.ts +350 -0
  19. package/src/commands/report/index.ts +366 -0
  20. package/src/commands/run/README.md +124 -0
  21. package/src/commands/run/index.ts +686 -0
  22. package/src/commands/search/index.ts +368 -0
  23. package/src/commands/setup/index.ts +274 -0
  24. package/src/commands/sign/index.ts +652 -0
  25. package/src/commands/trust/README.md +214 -0
  26. package/src/commands/trust/index.ts +453 -0
  27. package/src/commands/unyank/index.ts +107 -0
  28. package/src/commands/yank/index.ts +143 -0
  29. package/src/index.ts +96 -0
  30. package/src/types.ts +81 -0
  31. package/src/utils/errors.ts +409 -0
  32. package/src/utils/exit-codes.ts +159 -0
  33. package/src/utils/ignore.ts +147 -0
  34. package/src/utils/index.ts +107 -0
  35. package/src/utils/output.ts +242 -0
  36. package/src/utils/spinner.ts +214 -0
  37. package/tests/commands/auth.test.ts +217 -0
  38. package/tests/commands/cache.test.ts +286 -0
  39. package/tests/commands/config.test.ts +277 -0
  40. package/tests/commands/env.test.ts +293 -0
  41. package/tests/commands/exec.test.ts +112 -0
  42. package/tests/commands/get.test.ts +179 -0
  43. package/tests/commands/inspect.test.ts +201 -0
  44. package/tests/commands/install-integration.test.ts +343 -0
  45. package/tests/commands/install.test.ts +288 -0
  46. package/tests/commands/list.test.ts +160 -0
  47. package/tests/commands/publish.test.ts +186 -0
  48. package/tests/commands/report.test.ts +194 -0
  49. package/tests/commands/run.test.ts +231 -0
  50. package/tests/commands/search.test.ts +131 -0
  51. package/tests/commands/sign.test.ts +164 -0
  52. package/tests/commands/trust.test.ts +236 -0
  53. package/tests/commands/unyank.test.ts +114 -0
  54. package/tests/commands/yank.test.ts +154 -0
  55. package/tests/e2e.test.ts +554 -0
  56. package/tests/fixtures/calculator/enact.yaml +34 -0
  57. package/tests/fixtures/echo-tool/enact.md +31 -0
  58. package/tests/fixtures/env-tool/enact.yaml +19 -0
  59. package/tests/fixtures/greeter/enact.yaml +18 -0
  60. package/tests/fixtures/invalid-tool/enact.yaml +4 -0
  61. package/tests/index.test.ts +8 -0
  62. package/tests/types.test.ts +84 -0
  63. package/tests/utils/errors.test.ts +303 -0
  64. package/tests/utils/exit-codes.test.ts +189 -0
  65. package/tests/utils/ignore.test.ts +461 -0
  66. package/tests/utils/output.test.ts +126 -0
  67. package/tsconfig.json +17 -0
  68. package/tsconfig.tsbuildinfo +1 -0
  69. package/dist/index.js +0 -231612
  70. package/dist/index.js.bak +0 -231611
  71. package/dist/web/static/app.js +0 -663
  72. package/dist/web/static/index.html +0 -117
  73. package/dist/web/static/style.css +0 -291
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Tests for the get command
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { Command } from "commander";
7
+ import { configureGetCommand } from "../../src/commands/get";
8
+
9
+ describe("get command", () => {
10
+ describe("command configuration", () => {
11
+ test("configures get command on program", () => {
12
+ const program = new Command();
13
+ configureGetCommand(program);
14
+
15
+ const getCmd = program.commands.find((cmd) => cmd.name() === "get");
16
+ expect(getCmd).toBeDefined();
17
+ });
18
+
19
+ test("has correct description", () => {
20
+ const program = new Command();
21
+ configureGetCommand(program);
22
+
23
+ const getCmd = program.commands.find((cmd) => cmd.name() === "get");
24
+ expect(getCmd?.description()).toBe("Show detailed information about a tool");
25
+ });
26
+
27
+ test("has info as alias", () => {
28
+ const program = new Command();
29
+ configureGetCommand(program);
30
+
31
+ const getCmd = program.commands.find((cmd) => cmd.name() === "get");
32
+ expect(getCmd?.aliases()).toContain("info");
33
+ });
34
+
35
+ test("accepts tool argument", () => {
36
+ const program = new Command();
37
+ configureGetCommand(program);
38
+
39
+ const getCmd = program.commands.find((cmd) => cmd.name() === "get");
40
+ const args = getCmd?.registeredArguments ?? [];
41
+ expect(args.length).toBeGreaterThan(0);
42
+ expect(args[0]?.name()).toBe("tool");
43
+ });
44
+
45
+ test("has --version option", () => {
46
+ const program = new Command();
47
+ configureGetCommand(program);
48
+
49
+ const getCmd = program.commands.find((cmd) => cmd.name() === "get");
50
+ const opts = getCmd?.options ?? [];
51
+ const versionOpt = opts.find((o) => o.long === "--version");
52
+ expect(versionOpt).toBeDefined();
53
+ });
54
+
55
+ test("has -v short option for version", () => {
56
+ const program = new Command();
57
+ configureGetCommand(program);
58
+
59
+ const getCmd = program.commands.find((cmd) => cmd.name() === "get");
60
+ const opts = getCmd?.options ?? [];
61
+ const versionOpt = opts.find((o) => o.short === "-v");
62
+ expect(versionOpt).toBeDefined();
63
+ });
64
+
65
+ test("has --json option", () => {
66
+ const program = new Command();
67
+ configureGetCommand(program);
68
+
69
+ const getCmd = program.commands.find((cmd) => cmd.name() === "get");
70
+ const opts = getCmd?.options ?? [];
71
+ const jsonOpt = opts.find((o) => o.long === "--json");
72
+ expect(jsonOpt).toBeDefined();
73
+ });
74
+
75
+ test("has --verbose option", () => {
76
+ const program = new Command();
77
+ configureGetCommand(program);
78
+
79
+ const getCmd = program.commands.find((cmd) => cmd.name() === "get");
80
+ const opts = getCmd?.options ?? [];
81
+ const verboseOpt = opts.find((o) => o.long === "--verbose");
82
+ expect(verboseOpt).toBeDefined();
83
+ });
84
+ });
85
+
86
+ describe("tool name parsing", () => {
87
+ test("parses simple tool name", () => {
88
+ const toolName = "my-tool";
89
+ expect(toolName).not.toContain("@");
90
+ expect(toolName).not.toContain("/");
91
+ });
92
+
93
+ test("parses scoped tool name", () => {
94
+ const toolName = "@org/my-tool";
95
+ expect(toolName.startsWith("@")).toBe(true);
96
+ const parts = toolName.slice(1).split("/");
97
+ expect(parts[0]).toBe("org");
98
+ expect(parts[1]).toBe("my-tool");
99
+ });
100
+
101
+ test("parses tool name with version", () => {
102
+ const input = "my-tool@1.2.3";
103
+ const atIndex = input.lastIndexOf("@");
104
+ // For unscoped tools, @ separator for version
105
+ if (!input.startsWith("@")) {
106
+ const toolName = input.slice(0, atIndex);
107
+ const version = input.slice(atIndex + 1);
108
+ expect(toolName).toBe("my-tool");
109
+ expect(version).toBe("1.2.3");
110
+ }
111
+ });
112
+
113
+ test("parses scoped tool name with version", () => {
114
+ const input = "@org/my-tool@1.2.3";
115
+ // For scoped packages, split by /
116
+ const slashIndex = input.indexOf("/");
117
+ const org = input.slice(1, slashIndex);
118
+ const rest = input.slice(slashIndex + 1);
119
+ const atIndex = rest.indexOf("@");
120
+ const toolName = atIndex === -1 ? rest : rest.slice(0, atIndex);
121
+ const version = atIndex === -1 ? undefined : rest.slice(atIndex + 1);
122
+
123
+ expect(org).toBe("org");
124
+ expect(toolName).toBe("my-tool");
125
+ expect(version).toBe("1.2.3");
126
+ });
127
+ });
128
+
129
+ describe("date formatting", () => {
130
+ test("formats ISO date string", () => {
131
+ const formatDate = (dateStr: string): string => {
132
+ const date = new Date(dateStr);
133
+ return date.toLocaleDateString("en-US", {
134
+ year: "numeric",
135
+ month: "short",
136
+ day: "numeric",
137
+ });
138
+ };
139
+
140
+ const result = formatDate("2024-01-15T10:30:00Z");
141
+ expect(result).toContain("Jan");
142
+ expect(result).toContain("15");
143
+ expect(result).toContain("2024");
144
+ });
145
+
146
+ test("handles invalid date gracefully", () => {
147
+ const formatDate = (dateStr: string): string => {
148
+ const date = new Date(dateStr);
149
+ if (Number.isNaN(date.getTime())) return "Unknown";
150
+ return date.toLocaleDateString("en-US", {
151
+ year: "numeric",
152
+ month: "short",
153
+ day: "numeric",
154
+ });
155
+ };
156
+
157
+ expect(formatDate("invalid")).toBe("Unknown");
158
+ });
159
+ });
160
+
161
+ describe("trust status display", () => {
162
+ test("displays trusted status correctly", () => {
163
+ const getTrustLabel = (trusted: boolean): string => {
164
+ return trusted ? "✓ Verified" : "⚠ Unverified";
165
+ };
166
+
167
+ expect(getTrustLabel(true)).toContain("Verified");
168
+ expect(getTrustLabel(false)).toContain("Unverified");
169
+ });
170
+
171
+ test("trust level enum values", () => {
172
+ const trustLevels = ["none", "publisher", "auditor", "full"];
173
+ expect(trustLevels).toContain("none");
174
+ expect(trustLevels).toContain("publisher");
175
+ expect(trustLevels).toContain("auditor");
176
+ expect(trustLevels).toContain("full");
177
+ });
178
+ });
179
+ });
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Tests for the inspect command
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { Command } from "commander";
7
+ import { configureInspectCommand } from "../../src/commands/inspect";
8
+
9
+ describe("inspect command", () => {
10
+ describe("command configuration", () => {
11
+ test("configures inspect command on program", () => {
12
+ const program = new Command();
13
+ configureInspectCommand(program);
14
+
15
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
16
+ expect(inspectCmd).toBeDefined();
17
+ });
18
+
19
+ test("has correct description", () => {
20
+ const program = new Command();
21
+ configureInspectCommand(program);
22
+
23
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
24
+ expect(inspectCmd?.description()).toBe(
25
+ "Open a tool's page in browser for inspection (use --download to save locally)"
26
+ );
27
+ });
28
+
29
+ test("accepts tool argument", () => {
30
+ const program = new Command();
31
+ configureInspectCommand(program);
32
+
33
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
34
+ const args = inspectCmd?.registeredArguments ?? [];
35
+ expect(args.length).toBeGreaterThanOrEqual(1);
36
+ });
37
+
38
+ test("has --download option", () => {
39
+ const program = new Command();
40
+ configureInspectCommand(program);
41
+
42
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
43
+ const opts = inspectCmd?.options ?? [];
44
+ const downloadOpt = opts.find((o) => o.long === "--download");
45
+ expect(downloadOpt).toBeDefined();
46
+ });
47
+
48
+ test("has -d short option for download", () => {
49
+ const program = new Command();
50
+ configureInspectCommand(program);
51
+
52
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
53
+ const opts = inspectCmd?.options ?? [];
54
+ const downloadOpt = opts.find((o) => o.short === "-d");
55
+ expect(downloadOpt).toBeDefined();
56
+ });
57
+
58
+ test("has --output option", () => {
59
+ const program = new Command();
60
+ configureInspectCommand(program);
61
+
62
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
63
+ const opts = inspectCmd?.options ?? [];
64
+ const outputOpt = opts.find((o) => o.long === "--output");
65
+ expect(outputOpt).toBeDefined();
66
+ });
67
+
68
+ test("has -o short option for output", () => {
69
+ const program = new Command();
70
+ configureInspectCommand(program);
71
+
72
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
73
+ const opts = inspectCmd?.options ?? [];
74
+ const outputOpt = opts.find((o) => o.short === "-o");
75
+ expect(outputOpt).toBeDefined();
76
+ });
77
+
78
+ test("has --force option", () => {
79
+ const program = new Command();
80
+ configureInspectCommand(program);
81
+
82
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
83
+ const opts = inspectCmd?.options ?? [];
84
+ const forceOpt = opts.find((o) => o.long === "--force");
85
+ expect(forceOpt).toBeDefined();
86
+ });
87
+
88
+ test("has -f short option for force", () => {
89
+ const program = new Command();
90
+ configureInspectCommand(program);
91
+
92
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
93
+ const opts = inspectCmd?.options ?? [];
94
+ const forceOpt = opts.find((o) => o.short === "-f");
95
+ expect(forceOpt).toBeDefined();
96
+ });
97
+
98
+ test("has --verbose option", () => {
99
+ const program = new Command();
100
+ configureInspectCommand(program);
101
+
102
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
103
+ const opts = inspectCmd?.options ?? [];
104
+ const verboseOpt = opts.find((o) => o.long === "--verbose");
105
+ expect(verboseOpt).toBeDefined();
106
+ });
107
+
108
+ test("has --json option", () => {
109
+ const program = new Command();
110
+ configureInspectCommand(program);
111
+
112
+ const inspectCmd = program.commands.find((cmd) => cmd.name() === "inspect");
113
+ const opts = inspectCmd?.options ?? [];
114
+ const jsonOpt = opts.find((o) => o.long === "--json");
115
+ expect(jsonOpt).toBeDefined();
116
+ });
117
+ });
118
+
119
+ describe("tool spec parsing", () => {
120
+ // Helper function that mirrors the command's parseToolSpec
121
+ const parseToolSpec = (spec: string): { name: string; version: string | undefined } => {
122
+ const match = spec.match(/^(@[^@/]+\/[^@]+|[^@]+)(?:@(.+))?$/);
123
+ if (match?.[1]) {
124
+ return {
125
+ name: match[1],
126
+ version: match[2],
127
+ };
128
+ }
129
+ return { name: spec, version: undefined };
130
+ };
131
+
132
+ test("parses tool name without version", () => {
133
+ const result = parseToolSpec("alice/utils/greeter");
134
+ expect(result.name).toBe("alice/utils/greeter");
135
+ expect(result.version).toBeUndefined();
136
+ });
137
+
138
+ test("parses tool name with version", () => {
139
+ const result = parseToolSpec("alice/utils/greeter@1.0.0");
140
+ expect(result.name).toBe("alice/utils/greeter");
141
+ expect(result.version).toBe("1.0.0");
142
+ });
143
+
144
+ test("parses scoped package without version", () => {
145
+ const result = parseToolSpec("@scope/package");
146
+ expect(result.name).toBe("@scope/package");
147
+ expect(result.version).toBeUndefined();
148
+ });
149
+
150
+ test("parses scoped package with version", () => {
151
+ const result = parseToolSpec("@scope/package@2.0.0");
152
+ expect(result.name).toBe("@scope/package");
153
+ expect(result.version).toBe("2.0.0");
154
+ });
155
+ });
156
+
157
+ describe("bytes formatting", () => {
158
+ // Helper function that mirrors the command's formatBytes
159
+ const formatBytes = (bytes: number): string => {
160
+ if (bytes === 0) return "0 B";
161
+ const k = 1024;
162
+ const sizes = ["B", "KB", "MB", "GB"];
163
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
164
+ return `${(bytes / k ** i).toFixed(1)} ${sizes[i]}`;
165
+ };
166
+
167
+ test("formats zero bytes", () => {
168
+ expect(formatBytes(0)).toBe("0 B");
169
+ });
170
+
171
+ test("formats bytes", () => {
172
+ expect(formatBytes(500)).toBe("500.0 B");
173
+ });
174
+
175
+ test("formats kilobytes", () => {
176
+ expect(formatBytes(1024)).toBe("1.0 KB");
177
+ });
178
+
179
+ test("formats megabytes", () => {
180
+ expect(formatBytes(1024 * 1024)).toBe("1.0 MB");
181
+ });
182
+
183
+ test("formats gigabytes", () => {
184
+ expect(formatBytes(1024 * 1024 * 1024)).toBe("1.0 GB");
185
+ });
186
+ });
187
+
188
+ describe("output path logic", () => {
189
+ test("default output uses tool name in current directory", () => {
190
+ const toolName = "alice/utils/greeter";
191
+ const defaultOutput = toolName.split("/").pop();
192
+ expect(defaultOutput).toBe("greeter");
193
+ });
194
+
195
+ test("handles nested tool names", () => {
196
+ const toolName = "org/namespace/subspace/tool";
197
+ const defaultOutput = toolName.split("/").pop();
198
+ expect(defaultOutput).toBe("tool");
199
+ });
200
+ });
201
+ });