@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,107 @@
1
+ /**
2
+ * enact unyank command
3
+ *
4
+ * Restore a previously yanked tool version.
5
+ */
6
+
7
+ import { createApiClient, unyankVersion } from "@enactprotocol/api";
8
+ import { getSecret } from "@enactprotocol/secrets";
9
+ import type { Command } from "commander";
10
+ import type { CommandContext, GlobalOptions } from "../../types";
11
+ import { dim, error, formatError, info, json, keyValue, newline, success } from "../../utils";
12
+
13
+ /** Auth namespace for token storage */
14
+ const AUTH_NAMESPACE = "enact:auth";
15
+ const ACCESS_TOKEN_KEY = "access_token";
16
+
17
+ interface UnyankOptions extends GlobalOptions {}
18
+
19
+ /**
20
+ * Parse tool@version syntax
21
+ */
22
+ function parseToolSpec(spec: string): { name: string; version: string } {
23
+ const atIndex = spec.lastIndexOf("@");
24
+ if (atIndex === -1 || atIndex === 0) {
25
+ throw new Error(
26
+ `Invalid tool specification: ${spec}\nExpected format: tool-name@version (e.g., alice/utils/greeter@1.0.0)`
27
+ );
28
+ }
29
+
30
+ return {
31
+ name: spec.slice(0, atIndex),
32
+ version: spec.slice(atIndex + 1),
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Unyank command handler
38
+ */
39
+ async function unyankHandler(
40
+ toolSpec: string,
41
+ options: UnyankOptions,
42
+ _ctx: CommandContext
43
+ ): Promise<void> {
44
+ // Parse tool@version
45
+ const { name, version } = parseToolSpec(toolSpec);
46
+
47
+ // Check for auth token
48
+ const authToken = await getSecret(AUTH_NAMESPACE, ACCESS_TOKEN_KEY);
49
+ if (!authToken) {
50
+ error("Not authenticated. Please run: enact auth login");
51
+ process.exit(1);
52
+ }
53
+
54
+ const client = createApiClient();
55
+ client.setAuthToken(authToken);
56
+
57
+ info(`Restoring ${name}@${version}...`);
58
+ newline();
59
+
60
+ try {
61
+ const result = await unyankVersion(client, name, version);
62
+
63
+ if (options.json) {
64
+ json({
65
+ yanked: result.yanked,
66
+ name,
67
+ version: result.version,
68
+ unyankedAt: result.unyankedAt.toISOString(),
69
+ });
70
+ return;
71
+ }
72
+
73
+ success(`Restored ${name}@${version}`);
74
+ keyValue("Unyanked At", result.unyankedAt.toISOString());
75
+ newline();
76
+ dim("The version is now visible in version listings again.");
77
+ } catch (err) {
78
+ error(`Failed to restore version: ${formatError(err)}`);
79
+ process.exit(1);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Configure the unyank command
85
+ */
86
+ export function configureUnyankCommand(program: Command): void {
87
+ program
88
+ .command("unyank <tool@version>")
89
+ .description("Restore a previously yanked tool version")
90
+ .option("-v, --verbose", "Show detailed output")
91
+ .option("--json", "Output as JSON")
92
+ .action(async (toolSpec: string, options: UnyankOptions) => {
93
+ const ctx: CommandContext = {
94
+ cwd: process.cwd(),
95
+ options,
96
+ isCI: Boolean(process.env.CI),
97
+ isInteractive: process.stdout.isTTY ?? false,
98
+ };
99
+
100
+ try {
101
+ await unyankHandler(toolSpec, options, ctx);
102
+ } catch (err) {
103
+ error(formatError(err));
104
+ process.exit(1);
105
+ }
106
+ });
107
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * enact yank command
3
+ *
4
+ * Yank a published tool version from the registry.
5
+ * Yanked versions remain downloadable but are excluded from version listings
6
+ * and show warnings to users.
7
+ */
8
+
9
+ import { createApiClient, yankVersion } from "@enactprotocol/api";
10
+ import { getSecret } from "@enactprotocol/secrets";
11
+ import type { Command } from "commander";
12
+ import type { CommandContext, GlobalOptions } from "../../types";
13
+ import {
14
+ dim,
15
+ error,
16
+ formatError,
17
+ info,
18
+ json,
19
+ keyValue,
20
+ newline,
21
+ success,
22
+ warning,
23
+ } from "../../utils";
24
+
25
+ /** Auth namespace for token storage */
26
+ const AUTH_NAMESPACE = "enact:auth";
27
+ const ACCESS_TOKEN_KEY = "access_token";
28
+
29
+ interface YankOptions extends GlobalOptions {
30
+ reason?: string;
31
+ replacement?: string;
32
+ }
33
+
34
+ /**
35
+ * Parse tool@version syntax
36
+ */
37
+ function parseToolSpec(spec: string): { name: string; version: string } {
38
+ const atIndex = spec.lastIndexOf("@");
39
+ if (atIndex === -1 || atIndex === 0) {
40
+ throw new Error(
41
+ `Invalid tool specification: ${spec}\nExpected format: tool-name@version (e.g., alice/utils/greeter@1.0.0)`
42
+ );
43
+ }
44
+
45
+ return {
46
+ name: spec.slice(0, atIndex),
47
+ version: spec.slice(atIndex + 1),
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Yank command handler
53
+ */
54
+ async function yankHandler(
55
+ toolSpec: string,
56
+ options: YankOptions,
57
+ _ctx: CommandContext
58
+ ): Promise<void> {
59
+ // Parse tool@version
60
+ const { name, version } = parseToolSpec(toolSpec);
61
+
62
+ // Check for auth token
63
+ const authToken = await getSecret(AUTH_NAMESPACE, ACCESS_TOKEN_KEY);
64
+ if (!authToken) {
65
+ error("Not authenticated. Please run: enact auth login");
66
+ process.exit(1);
67
+ }
68
+
69
+ const client = createApiClient();
70
+ client.setAuthToken(authToken);
71
+
72
+ info(`Yanking ${name}@${version}...`);
73
+
74
+ if (options.reason) {
75
+ dim(`Reason: ${options.reason}`);
76
+ }
77
+ if (options.replacement) {
78
+ dim(`Replacement: ${options.replacement}`);
79
+ }
80
+ newline();
81
+
82
+ try {
83
+ const result = await yankVersion(client, name, version, {
84
+ reason: options.reason,
85
+ replacementVersion: options.replacement,
86
+ });
87
+
88
+ if (options.json) {
89
+ json({
90
+ yanked: result.yanked,
91
+ name,
92
+ version: result.version,
93
+ reason: result.reason,
94
+ replacementVersion: result.replacementVersion,
95
+ yankedAt: result.yankedAt.toISOString(),
96
+ });
97
+ return;
98
+ }
99
+
100
+ success(`Yanked ${name}@${version}`);
101
+ keyValue("Yanked At", result.yankedAt.toISOString());
102
+ if (result.reason) {
103
+ keyValue("Reason", result.reason);
104
+ }
105
+ if (result.replacementVersion) {
106
+ keyValue("Replacement", result.replacementVersion);
107
+ }
108
+ newline();
109
+ warning("Note: Yanked versions can still be downloaded but show warnings.");
110
+ dim("Use 'enact unyank' to restore the version.");
111
+ } catch (err) {
112
+ error(`Failed to yank version: ${formatError(err)}`);
113
+ process.exit(1);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Configure the yank command
119
+ */
120
+ export function configureYankCommand(program: Command): void {
121
+ program
122
+ .command("yank <tool@version>")
123
+ .description("Yank a published tool version from the registry")
124
+ .option("-r, --reason <reason>", "Reason for yanking (e.g., security issue)")
125
+ .option("--replacement <version>", "Recommend a replacement version (e.g., 1.2.1)")
126
+ .option("-v, --verbose", "Show detailed output")
127
+ .option("--json", "Output as JSON")
128
+ .action(async (toolSpec: string, options: YankOptions) => {
129
+ const ctx: CommandContext = {
130
+ cwd: process.cwd(),
131
+ options,
132
+ isCI: Boolean(process.env.CI),
133
+ isInteractive: process.stdout.isTTY ?? false,
134
+ };
135
+
136
+ try {
137
+ await yankHandler(toolSpec, options, ctx);
138
+ } catch (err) {
139
+ error(formatError(err));
140
+ process.exit(1);
141
+ }
142
+ });
143
+ }
package/src/index.ts ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * @enactprotocol/cli
5
+ *
6
+ * Command-line interface for Enact.
7
+ * User-facing commands for tool execution, discovery, and management.
8
+ */
9
+
10
+ import { Command } from "commander";
11
+ import {
12
+ configureAuthCommand,
13
+ configureCacheCommand,
14
+ configureConfigCommand,
15
+ configureEnvCommand,
16
+ configureExecCommand,
17
+ configureGetCommand,
18
+ configureInspectCommand,
19
+ configureInstallCommand,
20
+ configureListCommand,
21
+ configurePublishCommand,
22
+ configureReportCommand,
23
+ configureRunCommand,
24
+ configureSearchCommand,
25
+ configureSetupCommand,
26
+ configureSignCommand,
27
+ configureTrustCommand,
28
+ configureUnyankCommand,
29
+ configureYankCommand,
30
+ } from "./commands";
31
+ import { error, formatError } from "./utils";
32
+
33
+ export const version = "0.1.0";
34
+
35
+ // Export types for external use
36
+ export type { GlobalOptions, CommandContext } from "./types";
37
+
38
+ // Main CLI entry point
39
+ async function main() {
40
+ const program = new Command();
41
+
42
+ program
43
+ .name("enact")
44
+ .description("Enact - Verified, portable protocol for AI-executable tools")
45
+ .version(version)
46
+ .option("--json", "Output as JSON")
47
+ .option("-v, --verbose", "Enable verbose output");
48
+
49
+ // Configure all commands
50
+ configureSetupCommand(program);
51
+ configureRunCommand(program);
52
+ configureExecCommand(program);
53
+ configureInstallCommand(program);
54
+ configureListCommand(program);
55
+ configureEnvCommand(program);
56
+ configureTrustCommand(program);
57
+ configureConfigCommand(program);
58
+
59
+ // Registry commands (Phase 8)
60
+ configureSearchCommand(program);
61
+ configureGetCommand(program);
62
+ configurePublishCommand(program);
63
+ configureAuthCommand(program);
64
+ configureCacheCommand(program);
65
+
66
+ // CLI solidification commands (Phase 9)
67
+ configureSignCommand(program);
68
+ configureReportCommand(program);
69
+ configureInspectCommand(program);
70
+
71
+ // API v2 migration commands
72
+ configureYankCommand(program);
73
+ configureUnyankCommand(program);
74
+
75
+ // Global error handler
76
+ program.exitOverride((err) => {
77
+ if (err.code === "commander.help" || err.code === "commander.version") {
78
+ process.exit(0);
79
+ }
80
+ throw err;
81
+ });
82
+
83
+ try {
84
+ await program.parseAsync(process.argv);
85
+ } catch (err) {
86
+ error(formatError(err));
87
+ process.exit(1);
88
+ }
89
+ }
90
+
91
+ if (import.meta.main) {
92
+ main().catch((err) => {
93
+ error(formatError(err));
94
+ process.exit(1);
95
+ });
96
+ }
package/src/types.ts ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * CLI Types
3
+ */
4
+
5
+ import type { Command } from "commander";
6
+
7
+ /**
8
+ * Global CLI options available to all commands
9
+ */
10
+ export interface GlobalOptions {
11
+ /** Enable verbose output */
12
+ verbose?: boolean;
13
+ /** Output as JSON */
14
+ json?: boolean;
15
+ /** Suppress all output except errors */
16
+ quiet?: boolean;
17
+ /** Run without making changes (preview mode) */
18
+ dryRun?: boolean;
19
+ }
20
+
21
+ /**
22
+ * Context passed to command handlers
23
+ */
24
+ export interface CommandContext {
25
+ /** Current working directory */
26
+ cwd: string;
27
+ /** Global options */
28
+ options: GlobalOptions;
29
+ /** Whether running in CI environment */
30
+ isCI: boolean;
31
+ /** Whether running interactively (TTY) */
32
+ isInteractive: boolean;
33
+ }
34
+
35
+ /**
36
+ * Command handler function signature
37
+ */
38
+ export type CommandHandler<T = Record<string, unknown>> = (
39
+ args: T,
40
+ context: CommandContext
41
+ ) => Promise<void>;
42
+
43
+ /**
44
+ * Command definition for registration
45
+ */
46
+ export interface CommandDefinition {
47
+ /** Command name */
48
+ name: string;
49
+ /** Command description */
50
+ description: string;
51
+ /** Command aliases */
52
+ aliases?: string[];
53
+ /** Configure the command (add options, arguments, subcommands) */
54
+ configure: (cmd: Command) => void;
55
+ }
56
+
57
+ /**
58
+ * Result from a command execution
59
+ */
60
+ export interface CommandResult {
61
+ success: boolean;
62
+ message?: string;
63
+ data?: unknown;
64
+ }
65
+
66
+ /**
67
+ * Exit codes
68
+ */
69
+ export const ExitCode = {
70
+ Success: 0,
71
+ Error: 1,
72
+ InvalidArgs: 2,
73
+ NotFound: 3,
74
+ PermissionDenied: 4,
75
+ Cancelled: 130, // Standard for Ctrl+C
76
+ } as const;
77
+
78
+ export type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];
79
+
80
+ // Legacy placeholder
81
+ export type CLIConfig = Record<string, unknown>;