@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.
- 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 -231612
- package/dist/index.js.bak +0 -231611
- 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,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>;
|