enact-cli 1.0.5 → 1.0.7
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/dist/index.js +1171 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7050,7 +7050,7 @@ var require_public_api = __commonJS((exports) => {
|
|
|
7050
7050
|
});
|
|
7051
7051
|
|
|
7052
7052
|
// src/index.ts
|
|
7053
|
-
var
|
|
7053
|
+
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
7054
7054
|
import { parseArgs } from "util";
|
|
7055
7055
|
|
|
7056
7056
|
// node_modules/@clack/prompts/dist/index.mjs
|
|
@@ -7782,9 +7782,11 @@ ${import_picocolors3.default.bold("Usage:")}
|
|
|
7782
7782
|
|
|
7783
7783
|
${import_picocolors3.default.bold("Commands:")}
|
|
7784
7784
|
${import_picocolors3.default.green("auth")} Manage authentication (login, logout, status, token)
|
|
7785
|
+
${import_picocolors3.default.green("exec")} Execute a tool by fetching and running it
|
|
7785
7786
|
${import_picocolors3.default.green("init")} Create a new tool definition
|
|
7786
7787
|
${import_picocolors3.default.green("publish")} Publish a tool to the registry
|
|
7787
7788
|
${import_picocolors3.default.green("search")} Search for tools in the registry
|
|
7789
|
+
${import_picocolors3.default.green("sign")} Sign and verify tools with cryptographic signatures
|
|
7788
7790
|
${import_picocolors3.default.green("remote")} Manage remote servers (add, list, remove)
|
|
7789
7791
|
${import_picocolors3.default.green("user")} User operations (get public key)
|
|
7790
7792
|
|
|
@@ -7793,11 +7795,13 @@ ${import_picocolors3.default.bold("Global Options:")}
|
|
|
7793
7795
|
${import_picocolors3.default.yellow("--version, -v")} Show version information
|
|
7794
7796
|
|
|
7795
7797
|
${import_picocolors3.default.bold("Examples:")}
|
|
7796
|
-
${import_picocolors3.default.cyan("enact")}
|
|
7797
|
-
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("search")} ${import_picocolors3.default.yellow("--tags")} web,api
|
|
7798
|
-
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("
|
|
7799
|
-
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("
|
|
7800
|
-
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("
|
|
7798
|
+
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.dim("# Interactive mode")}
|
|
7799
|
+
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("search")} ${import_picocolors3.default.yellow("--tags")} web,api ${import_picocolors3.default.dim("# Search tools by tags")}
|
|
7800
|
+
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("exec")} enact/text/slugify ${import_picocolors3.default.dim("# Execute a tool")}
|
|
7801
|
+
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("sign")} verify my-tool.yaml ${import_picocolors3.default.dim("# Verify tool signatures")}
|
|
7802
|
+
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} my-tool.yaml ${import_picocolors3.default.dim("# Publish a tool")}
|
|
7803
|
+
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("auth")} login ${import_picocolors3.default.dim("# Login with OAuth")}
|
|
7804
|
+
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("init")} ${import_picocolors3.default.yellow("--minimal")} ${import_picocolors3.default.dim("# Create minimal tool template")}
|
|
7801
7805
|
|
|
7802
7806
|
${import_picocolors3.default.bold("More Help:")}
|
|
7803
7807
|
${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("<command>")} ${import_picocolors3.default.yellow("--help")} ${import_picocolors3.default.dim("# Show command-specific help")}
|
|
@@ -9442,6 +9446,7 @@ Options:
|
|
|
9442
9446
|
--limit <number> Maximum number of results (default: 20)
|
|
9443
9447
|
--tags <tags> Filter by tags (comma-separated)
|
|
9444
9448
|
--format <format> Output format: table, json, list (default: table)
|
|
9449
|
+
--json Output results as JSON (shorthand for --format json)
|
|
9445
9450
|
--author <author> Filter by author
|
|
9446
9451
|
|
|
9447
9452
|
Examples:
|
|
@@ -9449,6 +9454,7 @@ Examples:
|
|
|
9449
9454
|
enact search formatter --tags cli,text
|
|
9450
9455
|
enact search --author myorg
|
|
9451
9456
|
enact search prettier --limit 5 --format json
|
|
9457
|
+
enact search prettier --limit 5 --json
|
|
9452
9458
|
`);
|
|
9453
9459
|
return;
|
|
9454
9460
|
}
|
|
@@ -9820,6 +9826,1102 @@ ${import_picocolors9.default.bold("EXAMPLES")}
|
|
|
9820
9826
|
`);
|
|
9821
9827
|
}
|
|
9822
9828
|
|
|
9829
|
+
// src/commands/exec.ts
|
|
9830
|
+
import { spawn } from "child_process";
|
|
9831
|
+
import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
|
|
9832
|
+
import { resolve } from "path";
|
|
9833
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
9834
|
+
|
|
9835
|
+
// src/security/sign.ts
|
|
9836
|
+
import * as crypto from "crypto";
|
|
9837
|
+
import * as fs from "fs";
|
|
9838
|
+
import * as path from "path";
|
|
9839
|
+
function createCanonicalToolDefinition(tool) {
|
|
9840
|
+
const canonical = {};
|
|
9841
|
+
const orderedFields = [
|
|
9842
|
+
"name",
|
|
9843
|
+
"description",
|
|
9844
|
+
"command",
|
|
9845
|
+
"protocol_version",
|
|
9846
|
+
"version",
|
|
9847
|
+
"timeout",
|
|
9848
|
+
"tags",
|
|
9849
|
+
"input_schema",
|
|
9850
|
+
"output_schema",
|
|
9851
|
+
"annotations",
|
|
9852
|
+
"env_vars",
|
|
9853
|
+
"examples",
|
|
9854
|
+
"resources",
|
|
9855
|
+
"doc",
|
|
9856
|
+
"authors",
|
|
9857
|
+
"enact"
|
|
9858
|
+
];
|
|
9859
|
+
for (const field of orderedFields) {
|
|
9860
|
+
if (tool[field] !== undefined) {
|
|
9861
|
+
canonical[field] = tool[field];
|
|
9862
|
+
}
|
|
9863
|
+
}
|
|
9864
|
+
const remainingFields = Object.keys(tool).filter((key) => !orderedFields.includes(key)).sort();
|
|
9865
|
+
for (const field of remainingFields) {
|
|
9866
|
+
if (tool[field] !== undefined) {
|
|
9867
|
+
canonical[field] = tool[field];
|
|
9868
|
+
}
|
|
9869
|
+
}
|
|
9870
|
+
return canonical;
|
|
9871
|
+
}
|
|
9872
|
+
function createCanonicalToolJson(toolData) {
|
|
9873
|
+
const toolRecord = {
|
|
9874
|
+
name: toolData.name,
|
|
9875
|
+
description: toolData.description,
|
|
9876
|
+
command: toolData.command,
|
|
9877
|
+
protocol_version: toolData.protocol_version,
|
|
9878
|
+
version: toolData.version,
|
|
9879
|
+
timeout: toolData.timeout,
|
|
9880
|
+
tags: toolData.tags,
|
|
9881
|
+
input_schema: toolData.input_schema,
|
|
9882
|
+
output_schema: toolData.output_schema,
|
|
9883
|
+
annotations: toolData.annotations,
|
|
9884
|
+
env_vars: toolData.env_vars,
|
|
9885
|
+
examples: toolData.examples,
|
|
9886
|
+
resources: toolData.resources,
|
|
9887
|
+
doc: toolData.doc,
|
|
9888
|
+
authors: toolData.authors,
|
|
9889
|
+
enact: toolData.enact || "1.0.0"
|
|
9890
|
+
};
|
|
9891
|
+
const canonical = createCanonicalToolDefinition(toolRecord);
|
|
9892
|
+
return JSON.stringify(canonical, Object.keys(canonical).sort());
|
|
9893
|
+
}
|
|
9894
|
+
var DEFAULT_POLICY = {
|
|
9895
|
+
minimumSignatures: 1,
|
|
9896
|
+
allowedAlgorithms: ["sha256"]
|
|
9897
|
+
};
|
|
9898
|
+
var TRUSTED_KEYS_DIR = path.join(process.env.HOME || ".", ".enact", "trusted-keys");
|
|
9899
|
+
function getTrustedPublicKeysMap() {
|
|
9900
|
+
const trustedKeys = new Map;
|
|
9901
|
+
if (fs.existsSync(TRUSTED_KEYS_DIR)) {
|
|
9902
|
+
try {
|
|
9903
|
+
const files = fs.readdirSync(TRUSTED_KEYS_DIR);
|
|
9904
|
+
for (const file of files) {
|
|
9905
|
+
if (file.endsWith(".pem")) {
|
|
9906
|
+
const keyPath = path.join(TRUSTED_KEYS_DIR, file);
|
|
9907
|
+
const pemContent = fs.readFileSync(keyPath, "utf8");
|
|
9908
|
+
const base64Key = pemToBase64(pemContent);
|
|
9909
|
+
trustedKeys.set(base64Key, pemContent);
|
|
9910
|
+
}
|
|
9911
|
+
}
|
|
9912
|
+
} catch (error) {
|
|
9913
|
+
console.error(`Error reading trusted keys: ${error.message}`);
|
|
9914
|
+
}
|
|
9915
|
+
}
|
|
9916
|
+
return trustedKeys;
|
|
9917
|
+
}
|
|
9918
|
+
function pemToBase64(pem) {
|
|
9919
|
+
return pem.replace(/-----BEGIN PUBLIC KEY-----/, "").replace(/-----END PUBLIC KEY-----/, "").replace(/\s/g, "");
|
|
9920
|
+
}
|
|
9921
|
+
function base64ToPem(base64) {
|
|
9922
|
+
return `-----BEGIN PUBLIC KEY-----
|
|
9923
|
+
${base64.match(/.{1,64}/g)?.join(`
|
|
9924
|
+
`)}
|
|
9925
|
+
-----END PUBLIC KEY-----`;
|
|
9926
|
+
}
|
|
9927
|
+
async function signTool(toolPath, privateKeyPath, publicKeyPath, signerInfo, outputPath) {
|
|
9928
|
+
const toolYaml = fs.readFileSync(toolPath, "utf8");
|
|
9929
|
+
const privateKey = fs.readFileSync(privateKeyPath, "utf8");
|
|
9930
|
+
const publicKeyPem = fs.readFileSync(publicKeyPath, "utf8");
|
|
9931
|
+
const tool = $parse(toolYaml);
|
|
9932
|
+
const toolForSigning = { ...tool };
|
|
9933
|
+
delete toolForSigning.signatures;
|
|
9934
|
+
const canonicalJson = createCanonicalToolJson(toolForSigning);
|
|
9935
|
+
console.log("=== SIGNING DEBUG (WEBAPP COMPATIBLE) ===");
|
|
9936
|
+
console.log("Tool for signing:", JSON.stringify(toolForSigning, null, 2));
|
|
9937
|
+
console.log("Canonical JSON (webapp format):", canonicalJson);
|
|
9938
|
+
console.log("Canonical JSON length:", canonicalJson.length);
|
|
9939
|
+
console.log("==========================================");
|
|
9940
|
+
const toolHashBytes = await hashTool(toolForSigning);
|
|
9941
|
+
const { webcrypto } = await import("node:crypto");
|
|
9942
|
+
const privateKeyData = crypto.createPrivateKey({
|
|
9943
|
+
key: privateKey,
|
|
9944
|
+
format: "pem",
|
|
9945
|
+
type: "pkcs8"
|
|
9946
|
+
}).export({ format: "der", type: "pkcs8" });
|
|
9947
|
+
const privateKeyObj = await webcrypto.subtle.importKey("pkcs8", privateKeyData, { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
|
|
9948
|
+
const signatureArrayBuffer = await webcrypto.subtle.sign({ name: "ECDSA", hash: { name: "SHA-256" } }, privateKeyObj, toolHashBytes);
|
|
9949
|
+
const signature = new Uint8Array(signatureArrayBuffer);
|
|
9950
|
+
const signatureB64 = Buffer.from(signature).toString("base64");
|
|
9951
|
+
console.log("Generated signature (Web Crypto API):", signatureB64);
|
|
9952
|
+
console.log("Signature length:", signature.length, "bytes (should be 64 for P-256)");
|
|
9953
|
+
const publicKeyBase64 = pemToBase64(publicKeyPem);
|
|
9954
|
+
if (!tool.signatures) {
|
|
9955
|
+
tool.signatures = {};
|
|
9956
|
+
}
|
|
9957
|
+
tool.signatures[publicKeyBase64] = {
|
|
9958
|
+
algorithm: "sha256",
|
|
9959
|
+
type: "ecdsa-p256",
|
|
9960
|
+
signer: signerInfo.id,
|
|
9961
|
+
created: new Date().toISOString(),
|
|
9962
|
+
value: signatureB64,
|
|
9963
|
+
...signerInfo.role && { role: signerInfo.role }
|
|
9964
|
+
};
|
|
9965
|
+
const signedToolYaml = $stringify(tool);
|
|
9966
|
+
if (outputPath) {
|
|
9967
|
+
fs.writeFileSync(outputPath, signedToolYaml);
|
|
9968
|
+
}
|
|
9969
|
+
return signedToolYaml;
|
|
9970
|
+
}
|
|
9971
|
+
async function hashTool(tool) {
|
|
9972
|
+
const canonical = createCanonicalToolDefinition(tool);
|
|
9973
|
+
const { signature, ...toolForSigning } = canonical;
|
|
9974
|
+
const canonicalJson = JSON.stringify(toolForSigning, Object.keys(toolForSigning).sort());
|
|
9975
|
+
console.log("\uD83D\uDD0D Canonical JSON for hashing:", canonicalJson);
|
|
9976
|
+
console.log("\uD83D\uDD0D Canonical JSON length:", canonicalJson.length);
|
|
9977
|
+
const encoder = new TextEncoder;
|
|
9978
|
+
const data = encoder.encode(canonicalJson);
|
|
9979
|
+
const { webcrypto } = await import("node:crypto");
|
|
9980
|
+
const hashBuffer = await webcrypto.subtle.digest("SHA-256", data);
|
|
9981
|
+
const hashBytes = new Uint8Array(hashBuffer);
|
|
9982
|
+
console.log("\uD83D\uDD0D SHA-256 hash length:", hashBytes.length, "bytes (should be 32)");
|
|
9983
|
+
return hashBytes;
|
|
9984
|
+
}
|
|
9985
|
+
async function verifyToolSignature(toolObject, signatureB64, publicKeyObj) {
|
|
9986
|
+
try {
|
|
9987
|
+
const toolHash = await hashTool(toolObject);
|
|
9988
|
+
const signatureBytes = new Uint8Array(atob(signatureB64).split("").map((char) => char.charCodeAt(0)));
|
|
9989
|
+
console.log("\uD83D\uDD0D Tool hash byte length:", toolHash.length, "(should be 32 for SHA-256)");
|
|
9990
|
+
console.log("\uD83D\uDD0D Signature bytes length:", signatureBytes.length, "(should be 64 for P-256)");
|
|
9991
|
+
const { webcrypto } = await import("node:crypto");
|
|
9992
|
+
const isValid = await webcrypto.subtle.verify({ name: "ECDSA", hash: { name: "SHA-256" } }, publicKeyObj, signatureBytes, toolHash);
|
|
9993
|
+
console.log("\uD83C\uDFAF Web Crypto API verification result:", isValid);
|
|
9994
|
+
return isValid;
|
|
9995
|
+
} catch (error) {
|
|
9996
|
+
console.error("❌ Verification error:", error);
|
|
9997
|
+
return false;
|
|
9998
|
+
}
|
|
9999
|
+
}
|
|
10000
|
+
async function verifyTool(toolYaml, policy = DEFAULT_POLICY) {
|
|
10001
|
+
const errors2 = [];
|
|
10002
|
+
const verifiedSigners = [];
|
|
10003
|
+
try {
|
|
10004
|
+
const trustedKeys = getTrustedPublicKeysMap();
|
|
10005
|
+
if (trustedKeys.size === 0) {
|
|
10006
|
+
return {
|
|
10007
|
+
isValid: false,
|
|
10008
|
+
message: "No trusted public keys available",
|
|
10009
|
+
validSignatures: 0,
|
|
10010
|
+
totalSignatures: 0,
|
|
10011
|
+
verifiedSigners: [],
|
|
10012
|
+
errors: ["No trusted keys configured"]
|
|
10013
|
+
};
|
|
10014
|
+
}
|
|
10015
|
+
if (process.env.DEBUG) {
|
|
10016
|
+
console.log("Trusted keys available:");
|
|
10017
|
+
for (const [key, pem] of trustedKeys.entries()) {
|
|
10018
|
+
console.log(` Key: ${key.substring(0, 20)}...`);
|
|
10019
|
+
}
|
|
10020
|
+
}
|
|
10021
|
+
const tool = typeof toolYaml === "string" ? $parse(toolYaml) : toolYaml;
|
|
10022
|
+
if (!tool.signatures || Object.keys(tool.signatures).length === 0) {
|
|
10023
|
+
return {
|
|
10024
|
+
isValid: false,
|
|
10025
|
+
message: "No signatures found in the tool",
|
|
10026
|
+
validSignatures: 0,
|
|
10027
|
+
totalSignatures: 0,
|
|
10028
|
+
verifiedSigners: [],
|
|
10029
|
+
errors: ["No signatures found"]
|
|
10030
|
+
};
|
|
10031
|
+
}
|
|
10032
|
+
const totalSignatures = Object.keys(tool.signatures).length;
|
|
10033
|
+
const toolForVerification = { ...tool };
|
|
10034
|
+
delete toolForVerification.signatures;
|
|
10035
|
+
const toolHashBytes = await hashTool(toolForVerification);
|
|
10036
|
+
if (true) {
|
|
10037
|
+
console.log("=== VERIFICATION DEBUG (WEBAPP COMPATIBLE) ===");
|
|
10038
|
+
console.log("Original tool signature field:", Object.keys(tool.signatures || {}));
|
|
10039
|
+
console.log("Tool before removing signatures:", JSON.stringify(tool, null, 2));
|
|
10040
|
+
console.log("Tool for verification:", JSON.stringify(toolForVerification, null, 2));
|
|
10041
|
+
console.log("Tool hash bytes length:", toolHashBytes.length, "(should be 32 for SHA-256)");
|
|
10042
|
+
console.log("==============================================");
|
|
10043
|
+
}
|
|
10044
|
+
let validSignatures = 0;
|
|
10045
|
+
for (const [publicKeyBase64, signatureData] of Object.entries(tool.signatures)) {
|
|
10046
|
+
try {
|
|
10047
|
+
if (policy.allowedAlgorithms && !policy.allowedAlgorithms.includes(signatureData.algorithm)) {
|
|
10048
|
+
errors2.push(`Signature by ${signatureData.signer}: unsupported algorithm ${signatureData.algorithm}`);
|
|
10049
|
+
continue;
|
|
10050
|
+
}
|
|
10051
|
+
if (policy.trustedSigners && !policy.trustedSigners.includes(signatureData.signer)) {
|
|
10052
|
+
errors2.push(`Signature by ${signatureData.signer}: signer not in trusted list`);
|
|
10053
|
+
continue;
|
|
10054
|
+
}
|
|
10055
|
+
const publicKeyPem = trustedKeys.get(publicKeyBase64);
|
|
10056
|
+
if (!publicKeyPem) {
|
|
10057
|
+
const reconstructedPem = base64ToPem(publicKeyBase64);
|
|
10058
|
+
if (!trustedKeys.has(pemToBase64(reconstructedPem))) {
|
|
10059
|
+
errors2.push(`Signature by ${signatureData.signer}: public key not trusted`);
|
|
10060
|
+
continue;
|
|
10061
|
+
}
|
|
10062
|
+
}
|
|
10063
|
+
if (process.env.DEBUG) {
|
|
10064
|
+
console.log("Looking for public key:", publicKeyBase64);
|
|
10065
|
+
console.log("Key found in trusted keys:", !!publicKeyPem);
|
|
10066
|
+
}
|
|
10067
|
+
let isValid2 = false;
|
|
10068
|
+
try {
|
|
10069
|
+
const publicKeyToUse = publicKeyPem || base64ToPem(publicKeyBase64);
|
|
10070
|
+
if (process.env.DEBUG) {
|
|
10071
|
+
console.log("Signature base64:", signatureData.value);
|
|
10072
|
+
console.log("Signature buffer length (should be 64):", Buffer.from(signatureData.value, "base64").length);
|
|
10073
|
+
console.log("Public key base64:", publicKeyBase64);
|
|
10074
|
+
}
|
|
10075
|
+
if (signatureData.type === "ecdsa-p256") {
|
|
10076
|
+
const { webcrypto } = await import("node:crypto");
|
|
10077
|
+
const publicKeyData = crypto.createPublicKey({
|
|
10078
|
+
key: publicKeyToUse,
|
|
10079
|
+
format: "pem",
|
|
10080
|
+
type: "spki"
|
|
10081
|
+
}).export({ format: "der", type: "spki" });
|
|
10082
|
+
const publicKeyObj = await webcrypto.subtle.importKey("spki", publicKeyData, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
|
|
10083
|
+
isValid2 = await verifyToolSignature(toolForVerification, signatureData.value, publicKeyObj);
|
|
10084
|
+
if (process.env.DEBUG) {
|
|
10085
|
+
console.log("Web Crypto API verification result (webapp compatible):", isValid2);
|
|
10086
|
+
}
|
|
10087
|
+
} else {
|
|
10088
|
+
const verify = crypto.createVerify("SHA256");
|
|
10089
|
+
const canonicalJson = createCanonicalToolJson(toolForVerification);
|
|
10090
|
+
verify.update(canonicalJson, "utf8");
|
|
10091
|
+
const signature = Buffer.from(signatureData.value, "base64");
|
|
10092
|
+
isValid2 = verify.verify(publicKeyToUse, signature);
|
|
10093
|
+
}
|
|
10094
|
+
} catch (verifyError) {
|
|
10095
|
+
errors2.push(`Signature by ${signatureData.signer}: verification error - ${verifyError.message}`);
|
|
10096
|
+
continue;
|
|
10097
|
+
}
|
|
10098
|
+
if (isValid2) {
|
|
10099
|
+
validSignatures++;
|
|
10100
|
+
verifiedSigners.push({
|
|
10101
|
+
signer: signatureData.signer,
|
|
10102
|
+
role: signatureData.role,
|
|
10103
|
+
keyId: publicKeyBase64.substring(0, 8)
|
|
10104
|
+
});
|
|
10105
|
+
} else {
|
|
10106
|
+
errors2.push(`Signature by ${signatureData.signer}: cryptographic verification failed`);
|
|
10107
|
+
}
|
|
10108
|
+
} catch (error) {
|
|
10109
|
+
errors2.push(`Signature by ${signatureData.signer}: verification error - ${error.message}`);
|
|
10110
|
+
}
|
|
10111
|
+
}
|
|
10112
|
+
const policyErrors = [];
|
|
10113
|
+
if (policy.minimumSignatures && validSignatures < policy.minimumSignatures) {
|
|
10114
|
+
policyErrors.push(`Policy requires ${policy.minimumSignatures} signatures, but only ${validSignatures} valid`);
|
|
10115
|
+
}
|
|
10116
|
+
if (policy.requireRoles && policy.requireRoles.length > 0) {
|
|
10117
|
+
const verifiedRoles = verifiedSigners.map((s) => s.role).filter(Boolean);
|
|
10118
|
+
const missingRoles = policy.requireRoles.filter((role) => !verifiedRoles.includes(role));
|
|
10119
|
+
if (missingRoles.length > 0) {
|
|
10120
|
+
policyErrors.push(`Policy requires roles: ${missingRoles.join(", ")}`);
|
|
10121
|
+
}
|
|
10122
|
+
}
|
|
10123
|
+
const isValid = policyErrors.length === 0 && validSignatures > 0;
|
|
10124
|
+
const allErrors = [...errors2, ...policyErrors];
|
|
10125
|
+
let message;
|
|
10126
|
+
if (isValid) {
|
|
10127
|
+
message = `Tool "${tool.name}" verified with ${validSignatures}/${totalSignatures} valid signatures`;
|
|
10128
|
+
if (verifiedSigners.length > 0) {
|
|
10129
|
+
const signerInfo = verifiedSigners.map((s) => `${s.signer}${s.role ? ` (${s.role})` : ""}`).join(", ");
|
|
10130
|
+
message += ` from: ${signerInfo}`;
|
|
10131
|
+
}
|
|
10132
|
+
} else {
|
|
10133
|
+
message = `Tool "${tool.name}" verification failed: ${allErrors[0] || "Unknown error"}`;
|
|
10134
|
+
}
|
|
10135
|
+
return {
|
|
10136
|
+
isValid,
|
|
10137
|
+
message,
|
|
10138
|
+
validSignatures,
|
|
10139
|
+
totalSignatures,
|
|
10140
|
+
verifiedSigners,
|
|
10141
|
+
errors: allErrors
|
|
10142
|
+
};
|
|
10143
|
+
} catch (error) {
|
|
10144
|
+
return {
|
|
10145
|
+
isValid: false,
|
|
10146
|
+
message: `Verification error: ${error.message}`,
|
|
10147
|
+
validSignatures: 0,
|
|
10148
|
+
totalSignatures: 0,
|
|
10149
|
+
verifiedSigners: [],
|
|
10150
|
+
errors: [error.message]
|
|
10151
|
+
};
|
|
10152
|
+
}
|
|
10153
|
+
}
|
|
10154
|
+
var VERIFICATION_POLICIES = {
|
|
10155
|
+
PERMISSIVE: {
|
|
10156
|
+
minimumSignatures: 1,
|
|
10157
|
+
allowedAlgorithms: ["sha256"]
|
|
10158
|
+
},
|
|
10159
|
+
ENTERPRISE: {
|
|
10160
|
+
minimumSignatures: 2,
|
|
10161
|
+
requireRoles: ["author", "reviewer"],
|
|
10162
|
+
allowedAlgorithms: ["sha256"]
|
|
10163
|
+
},
|
|
10164
|
+
PARANOID: {
|
|
10165
|
+
minimumSignatures: 3,
|
|
10166
|
+
requireRoles: ["author", "reviewer", "approver"],
|
|
10167
|
+
allowedAlgorithms: ["sha256"]
|
|
10168
|
+
}
|
|
10169
|
+
};
|
|
10170
|
+
|
|
10171
|
+
// src/commands/exec.ts
|
|
10172
|
+
async function loadLocalTool(filePath) {
|
|
10173
|
+
const resolvedPath = resolve(filePath);
|
|
10174
|
+
if (!existsSync7(resolvedPath)) {
|
|
10175
|
+
throw new Error(`Tool file not found: ${resolvedPath}`);
|
|
10176
|
+
}
|
|
10177
|
+
try {
|
|
10178
|
+
const fileContent = readFileSync3(resolvedPath, "utf8");
|
|
10179
|
+
const toolData = $parse(fileContent);
|
|
10180
|
+
const toolDefinition = {
|
|
10181
|
+
...toolData,
|
|
10182
|
+
raw_content: fileContent
|
|
10183
|
+
};
|
|
10184
|
+
if (!toolDefinition.name) {
|
|
10185
|
+
throw new Error("Tool must have a name");
|
|
10186
|
+
}
|
|
10187
|
+
if (!toolDefinition.command) {
|
|
10188
|
+
throw new Error("Tool must have a command");
|
|
10189
|
+
}
|
|
10190
|
+
return toolDefinition;
|
|
10191
|
+
} catch (error) {
|
|
10192
|
+
if (error instanceof $YAMLParseError) {
|
|
10193
|
+
throw new Error(`Invalid YAML in tool file: ${error.message}`);
|
|
10194
|
+
}
|
|
10195
|
+
throw error;
|
|
10196
|
+
}
|
|
10197
|
+
}
|
|
10198
|
+
function isLocalToolPath(toolIdentifier) {
|
|
10199
|
+
return toolIdentifier.includes("/") && (toolIdentifier.endsWith(".yaml") || toolIdentifier.endsWith(".yml") || existsSync7(resolve(toolIdentifier)));
|
|
10200
|
+
}
|
|
10201
|
+
async function handleExecCommand(args, options) {
|
|
10202
|
+
if (options.help) {
|
|
10203
|
+
console.log(`
|
|
10204
|
+
Usage: enact exec <tool-name-or-path> [options]
|
|
10205
|
+
|
|
10206
|
+
Execute an Enact tool by fetching its definition from the registry or loading from a local file.
|
|
10207
|
+
|
|
10208
|
+
Arguments:
|
|
10209
|
+
tool-name-or-path Name of the tool (e.g., "enact/text/slugify") or path to local YAML file
|
|
10210
|
+
|
|
10211
|
+
Options:
|
|
10212
|
+
--help, -h Show this help message
|
|
10213
|
+
--input <data> Input data as JSON string or stdin
|
|
10214
|
+
--params <params> Parameters as JSON object
|
|
10215
|
+
--timeout <time> Override tool timeout (Go duration format: 30s, 5m, 1h)
|
|
10216
|
+
--dry Show command that would be executed without running it
|
|
10217
|
+
--verbose, -v Show detailed execution information
|
|
10218
|
+
--skip-verification Skip signature verification (not recommended)
|
|
10219
|
+
--verify-policy Verification policy: permissive, enterprise, paranoid (default: permissive)
|
|
10220
|
+
--force Force execution even if signature verification fails
|
|
10221
|
+
|
|
10222
|
+
Security Options:
|
|
10223
|
+
permissive Require 1+ valid signatures from trusted keys (default)
|
|
10224
|
+
enterprise Require author + reviewer signatures
|
|
10225
|
+
paranoid Require author + reviewer + approver signatures
|
|
10226
|
+
|
|
10227
|
+
Examples:
|
|
10228
|
+
enact exec enact/text/slugify --input "Hello World"
|
|
10229
|
+
enact exec org/ai/review --params '{"file": "README.md"}' --verify-policy enterprise
|
|
10230
|
+
enact exec ./my-tool.yaml --input "test data"
|
|
10231
|
+
enact exec untrusted/tool --skip-verification # Not recommended
|
|
10232
|
+
`);
|
|
10233
|
+
return;
|
|
10234
|
+
}
|
|
10235
|
+
let toolIdentifier = args[0];
|
|
10236
|
+
if (!toolIdentifier) {
|
|
10237
|
+
Ie(import_picocolors10.default.bgMagenta(import_picocolors10.default.white(" Execute Enact Tool ")));
|
|
10238
|
+
toolIdentifier = await he({
|
|
10239
|
+
message: "Enter the tool name or path to execute:",
|
|
10240
|
+
placeholder: "e.g., enact/text/slugify, ./my-tool.yaml",
|
|
10241
|
+
validate: (value) => {
|
|
10242
|
+
if (!value.trim())
|
|
10243
|
+
return "Please enter a tool name or file path";
|
|
10244
|
+
const trimmed = value.trim();
|
|
10245
|
+
if (isLocalToolPath(trimmed)) {
|
|
10246
|
+
return;
|
|
10247
|
+
}
|
|
10248
|
+
if (!/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_\/-]+$/.test(trimmed)) {
|
|
10249
|
+
return "Tool name must follow hierarchical format: org/category/tool-name, or be a path to a YAML file";
|
|
10250
|
+
}
|
|
10251
|
+
return;
|
|
10252
|
+
}
|
|
10253
|
+
});
|
|
10254
|
+
if (!toolIdentifier) {
|
|
10255
|
+
Se(import_picocolors10.default.yellow("Execution cancelled"));
|
|
10256
|
+
return;
|
|
10257
|
+
}
|
|
10258
|
+
}
|
|
10259
|
+
const isLocalFile = isLocalToolPath(toolIdentifier);
|
|
10260
|
+
const spinner = Y2();
|
|
10261
|
+
spinner.start(isLocalFile ? "Loading local tool definition..." : "Fetching tool definition...");
|
|
10262
|
+
let toolDefinition;
|
|
10263
|
+
try {
|
|
10264
|
+
if (isLocalFile) {
|
|
10265
|
+
toolDefinition = await loadLocalTool(toolIdentifier);
|
|
10266
|
+
spinner.stop("Local tool definition loaded");
|
|
10267
|
+
} else {
|
|
10268
|
+
toolDefinition = await enactApi.getTool(toolIdentifier);
|
|
10269
|
+
spinner.stop("Tool definition fetched");
|
|
10270
|
+
}
|
|
10271
|
+
} catch (error) {
|
|
10272
|
+
spinner.stop(isLocalFile ? "Failed to load local tool" : "Failed to fetch tool definition");
|
|
10273
|
+
if (!isLocalFile && error instanceof EnactApiError && error.statusCode === 404) {
|
|
10274
|
+
Se(import_picocolors10.default.red(`✗ Tool "${toolIdentifier}" not found`));
|
|
10275
|
+
} else {
|
|
10276
|
+
Se(import_picocolors10.default.red(`✗ Failed to ${isLocalFile ? "load" : "fetch"} tool: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
10277
|
+
}
|
|
10278
|
+
return;
|
|
10279
|
+
}
|
|
10280
|
+
if (!options.skipVerification) {
|
|
10281
|
+
spinner.start("Verifying tool signatures...");
|
|
10282
|
+
try {
|
|
10283
|
+
const policyName = (options.verifyPolicy || "permissive").toUpperCase();
|
|
10284
|
+
const policy = VERIFICATION_POLICIES[policyName] || VERIFICATION_POLICIES.PERMISSIVE;
|
|
10285
|
+
if (options.verbose) {
|
|
10286
|
+
console.log(import_picocolors10.default.cyan(`
|
|
10287
|
+
\uD83D\uDD10 Using verification policy: ${policyName.toLowerCase()}`));
|
|
10288
|
+
if (policy.minimumSignatures)
|
|
10289
|
+
console.log(` - Minimum signatures: ${policy.minimumSignatures}`);
|
|
10290
|
+
}
|
|
10291
|
+
let toolForVerification;
|
|
10292
|
+
if (isLocalFile) {
|
|
10293
|
+
toolForVerification = toolDefinition.raw_content;
|
|
10294
|
+
} else if (toolDefinition.raw_content && toolDefinition.signatures) {
|
|
10295
|
+
const originalTool = JSON.parse(toolDefinition.raw_content);
|
|
10296
|
+
if (!originalTool.enact) {
|
|
10297
|
+
originalTool.enact = "1.0.0";
|
|
10298
|
+
}
|
|
10299
|
+
toolForVerification = {
|
|
10300
|
+
...originalTool,
|
|
10301
|
+
signatures: toolDefinition.signatures
|
|
10302
|
+
};
|
|
10303
|
+
} else {
|
|
10304
|
+
toolForVerification = toolDefinition;
|
|
10305
|
+
if (!toolForVerification.enact) {
|
|
10306
|
+
toolForVerification.enact = "1.0.0";
|
|
10307
|
+
}
|
|
10308
|
+
}
|
|
10309
|
+
const verification = await verifyTool(toolForVerification, policy);
|
|
10310
|
+
spinner.stop("Signature verification completed");
|
|
10311
|
+
if (verification.isValid) {
|
|
10312
|
+
console.log(import_picocolors10.default.green(`✅ ${verification.message}`));
|
|
10313
|
+
if (options.verbose && verification.verifiedSigners.length > 0) {
|
|
10314
|
+
console.log(import_picocolors10.default.cyan(`
|
|
10315
|
+
\uD83D\uDD12 Verified signers:`));
|
|
10316
|
+
verification.verifiedSigners.forEach((signer) => {
|
|
10317
|
+
console.log(` - ${signer.signer}${signer.role ? ` (${signer.role})` : ""} [${signer.keyId}]`);
|
|
10318
|
+
});
|
|
10319
|
+
}
|
|
10320
|
+
} else {
|
|
10321
|
+
console.log(import_picocolors10.default.red(`❌ ${verification.message}`));
|
|
10322
|
+
if (verification.errors.length > 0) {
|
|
10323
|
+
console.log(import_picocolors10.default.red(`
|
|
10324
|
+
Verification errors:`));
|
|
10325
|
+
verification.errors.forEach((error) => {
|
|
10326
|
+
console.log(import_picocolors10.default.red(` - ${error}`));
|
|
10327
|
+
});
|
|
10328
|
+
}
|
|
10329
|
+
if (!options.force) {
|
|
10330
|
+
const shouldContinue = await ye({
|
|
10331
|
+
message: "Tool signature verification failed. Continue anyway?",
|
|
10332
|
+
initialValue: false
|
|
10333
|
+
});
|
|
10334
|
+
if (!shouldContinue) {
|
|
10335
|
+
Se(import_picocolors10.default.yellow("Execution cancelled for security"));
|
|
10336
|
+
return;
|
|
10337
|
+
}
|
|
10338
|
+
} else {
|
|
10339
|
+
console.log(import_picocolors10.default.yellow("⚠️ Proceeding due to --force flag"));
|
|
10340
|
+
}
|
|
10341
|
+
}
|
|
10342
|
+
} catch (error) {
|
|
10343
|
+
spinner.stop("Verification failed");
|
|
10344
|
+
console.log(import_picocolors10.default.red(`❌ Signature verification error: ${error.message}`));
|
|
10345
|
+
if (!options.force) {
|
|
10346
|
+
const shouldContinue = await ye({
|
|
10347
|
+
message: "Signature verification failed with error. Continue anyway?",
|
|
10348
|
+
initialValue: false
|
|
10349
|
+
});
|
|
10350
|
+
if (!shouldContinue) {
|
|
10351
|
+
Se(import_picocolors10.default.yellow("Execution cancelled for security"));
|
|
10352
|
+
return;
|
|
10353
|
+
}
|
|
10354
|
+
}
|
|
10355
|
+
}
|
|
10356
|
+
} else {
|
|
10357
|
+
console.log(import_picocolors10.default.yellow("⚠️ Signature verification skipped - tool may be untrusted"));
|
|
10358
|
+
}
|
|
10359
|
+
if (options.verbose) {
|
|
10360
|
+
console.log(import_picocolors10.default.cyan(`
|
|
10361
|
+
\uD83D\uDCCB Tool Information:`));
|
|
10362
|
+
console.log(`Name: ${toolDefinition.name}`);
|
|
10363
|
+
console.log(`Description: ${toolDefinition.description}`);
|
|
10364
|
+
console.log(`Command: ${toolDefinition.command}`);
|
|
10365
|
+
if (toolDefinition.timeout)
|
|
10366
|
+
console.log(`Timeout: ${toolDefinition.timeout}`);
|
|
10367
|
+
if (toolDefinition.tags)
|
|
10368
|
+
console.log(`Tags: ${toolDefinition.tags.join(", ")}`);
|
|
10369
|
+
if (toolDefinition.signatures) {
|
|
10370
|
+
const sigCount = Object.keys(toolDefinition.signatures).length;
|
|
10371
|
+
console.log(`Signatures: ${sigCount} signature(s) found`);
|
|
10372
|
+
} else {
|
|
10373
|
+
console.log(`Signatures: No signatures found`);
|
|
10374
|
+
}
|
|
10375
|
+
}
|
|
10376
|
+
let params = {};
|
|
10377
|
+
if (options.params) {
|
|
10378
|
+
try {
|
|
10379
|
+
params = JSON.parse(options.params);
|
|
10380
|
+
} catch (error) {
|
|
10381
|
+
Se(import_picocolors10.default.red(`✗ Invalid JSON in --params: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
10382
|
+
return;
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10385
|
+
if (options.input) {
|
|
10386
|
+
try {
|
|
10387
|
+
const inputData = JSON.parse(options.input);
|
|
10388
|
+
params = { ...params, ...inputData };
|
|
10389
|
+
} catch {
|
|
10390
|
+
params.input = options.input;
|
|
10391
|
+
}
|
|
10392
|
+
}
|
|
10393
|
+
const remainingArgs = args.slice(1);
|
|
10394
|
+
for (const arg of remainingArgs) {
|
|
10395
|
+
if (arg.includes("=")) {
|
|
10396
|
+
const [key, ...valueParts] = arg.split("=");
|
|
10397
|
+
const value = valueParts.join("=");
|
|
10398
|
+
const cleanValue = value.replace(/^["']|["']$/g, "");
|
|
10399
|
+
params[key] = cleanValue;
|
|
10400
|
+
}
|
|
10401
|
+
}
|
|
10402
|
+
if (toolDefinition.inputSchema && Object.keys(params).length === 0) {
|
|
10403
|
+
const needsParams = await ye({
|
|
10404
|
+
message: "This tool requires parameters. Would you like to provide them interactively?",
|
|
10405
|
+
initialValue: true
|
|
10406
|
+
});
|
|
10407
|
+
if (needsParams) {
|
|
10408
|
+
params = await collectParametersInteractively(toolDefinition.inputSchema);
|
|
10409
|
+
}
|
|
10410
|
+
}
|
|
10411
|
+
const command = await buildCommand(toolDefinition.command, params);
|
|
10412
|
+
if (options.dry) {
|
|
10413
|
+
console.log(import_picocolors10.default.cyan(`
|
|
10414
|
+
\uD83D\uDD0D Command that would be executed:`));
|
|
10415
|
+
console.log(import_picocolors10.default.white(command));
|
|
10416
|
+
console.log(import_picocolors10.default.cyan(`
|
|
10417
|
+
Environment variables:`));
|
|
10418
|
+
if (toolDefinition.env) {
|
|
10419
|
+
Object.entries(toolDefinition.env).forEach(([key, config]) => {
|
|
10420
|
+
const value = process.env[key] || config.default || "<not set>";
|
|
10421
|
+
console.log(` ${key}=${value}`);
|
|
10422
|
+
});
|
|
10423
|
+
} else {
|
|
10424
|
+
console.log(" (none required)");
|
|
10425
|
+
}
|
|
10426
|
+
return;
|
|
10427
|
+
}
|
|
10428
|
+
if (toolDefinition.env) {
|
|
10429
|
+
const missingEnvVars = await checkEnvironmentVariables(toolDefinition.env);
|
|
10430
|
+
if (missingEnvVars.length > 0) {
|
|
10431
|
+
Se(import_picocolors10.default.red(`✗ Missing required environment variables: ${missingEnvVars.join(", ")}`));
|
|
10432
|
+
return;
|
|
10433
|
+
}
|
|
10434
|
+
}
|
|
10435
|
+
const timeout = options.timeout || toolDefinition.timeout || "30s";
|
|
10436
|
+
await executeCommand(command, timeout, options.verbose);
|
|
10437
|
+
if (!isLocalFile) {
|
|
10438
|
+
try {
|
|
10439
|
+
await enactApi.logToolUsage(toolIdentifier, {
|
|
10440
|
+
action: "execute",
|
|
10441
|
+
metadata: {
|
|
10442
|
+
hasParams: Object.keys(params).length > 0,
|
|
10443
|
+
timeout,
|
|
10444
|
+
verificationSkipped: options.skipVerification || false,
|
|
10445
|
+
verificationPolicy: options.verifyPolicy || "permissive",
|
|
10446
|
+
timestamp: new Date().toISOString()
|
|
10447
|
+
}
|
|
10448
|
+
});
|
|
10449
|
+
} catch (error) {
|
|
10450
|
+
if (options.verbose) {
|
|
10451
|
+
console.log(import_picocolors10.default.yellow("⚠ Failed to log usage statistics"));
|
|
10452
|
+
}
|
|
10453
|
+
}
|
|
10454
|
+
}
|
|
10455
|
+
}
|
|
10456
|
+
async function collectParametersInteractively(inputSchema) {
|
|
10457
|
+
const params = {};
|
|
10458
|
+
if (inputSchema.properties) {
|
|
10459
|
+
for (const [key, schema] of Object.entries(inputSchema.properties)) {
|
|
10460
|
+
const prop = schema;
|
|
10461
|
+
const isRequired = inputSchema.required?.includes(key) || false;
|
|
10462
|
+
let value;
|
|
10463
|
+
if (prop.type === "string") {
|
|
10464
|
+
value = await he({
|
|
10465
|
+
message: `Enter ${key}:`,
|
|
10466
|
+
placeholder: prop.description || `Value for ${key}`,
|
|
10467
|
+
validate: isRequired ? (val) => val.trim() ? undefined : `${key} is required` : undefined
|
|
10468
|
+
});
|
|
10469
|
+
} else if (prop.type === "number" || prop.type === "integer") {
|
|
10470
|
+
value = await he({
|
|
10471
|
+
message: `Enter ${key} (number):`,
|
|
10472
|
+
placeholder: prop.description || `Number value for ${key}`,
|
|
10473
|
+
validate: (val) => {
|
|
10474
|
+
if (!val.trim() && isRequired)
|
|
10475
|
+
return `${key} is required`;
|
|
10476
|
+
if (val.trim() && isNaN(Number(val)))
|
|
10477
|
+
return "Must be a valid number";
|
|
10478
|
+
return;
|
|
10479
|
+
}
|
|
10480
|
+
});
|
|
10481
|
+
if (value)
|
|
10482
|
+
value = Number(value);
|
|
10483
|
+
} else if (prop.type === "boolean") {
|
|
10484
|
+
value = await ye({
|
|
10485
|
+
message: `${key}:`,
|
|
10486
|
+
initialValue: false
|
|
10487
|
+
});
|
|
10488
|
+
} else {
|
|
10489
|
+
value = await he({
|
|
10490
|
+
message: `Enter ${key} (JSON):`,
|
|
10491
|
+
placeholder: prop.description || `JSON value for ${key}`,
|
|
10492
|
+
validate: (val) => {
|
|
10493
|
+
if (!val.trim() && isRequired)
|
|
10494
|
+
return `${key} is required`;
|
|
10495
|
+
if (val.trim()) {
|
|
10496
|
+
try {
|
|
10497
|
+
JSON.parse(val);
|
|
10498
|
+
} catch {
|
|
10499
|
+
return "Must be valid JSON";
|
|
10500
|
+
}
|
|
10501
|
+
}
|
|
10502
|
+
return;
|
|
10503
|
+
}
|
|
10504
|
+
});
|
|
10505
|
+
if (value) {
|
|
10506
|
+
try {
|
|
10507
|
+
value = JSON.parse(value);
|
|
10508
|
+
} catch {}
|
|
10509
|
+
}
|
|
10510
|
+
}
|
|
10511
|
+
if (value !== null && value !== undefined && value !== "") {
|
|
10512
|
+
params[key] = value;
|
|
10513
|
+
}
|
|
10514
|
+
}
|
|
10515
|
+
}
|
|
10516
|
+
return params;
|
|
10517
|
+
}
|
|
10518
|
+
async function buildCommand(template, params) {
|
|
10519
|
+
let command = template;
|
|
10520
|
+
for (const [key, value] of Object.entries(params)) {
|
|
10521
|
+
const placeholder = `\${${key}}`;
|
|
10522
|
+
const escapedValue = typeof value === "string" ? value.replace(/'/g, `'"'"'`) : String(value);
|
|
10523
|
+
command = command.replace(new RegExp(placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), escapedValue);
|
|
10524
|
+
}
|
|
10525
|
+
return command;
|
|
10526
|
+
}
|
|
10527
|
+
async function checkEnvironmentVariables(envConfig) {
|
|
10528
|
+
const missing = [];
|
|
10529
|
+
for (const [key, config] of Object.entries(envConfig)) {
|
|
10530
|
+
if (config.required && !process.env[key] && !config.default) {
|
|
10531
|
+
missing.push(key);
|
|
10532
|
+
}
|
|
10533
|
+
}
|
|
10534
|
+
return missing;
|
|
10535
|
+
}
|
|
10536
|
+
async function executeCommand(command, timeout, verbose = false) {
|
|
10537
|
+
return new Promise((resolve2, reject) => {
|
|
10538
|
+
if (verbose) {
|
|
10539
|
+
console.log(import_picocolors10.default.cyan(`
|
|
10540
|
+
\uD83D\uDE80 Executing command:`));
|
|
10541
|
+
console.log(import_picocolors10.default.white(command));
|
|
10542
|
+
}
|
|
10543
|
+
const spinner = Y2();
|
|
10544
|
+
spinner.start("Executing tool...");
|
|
10545
|
+
const timeoutMs = parseTimeout(timeout);
|
|
10546
|
+
const child = spawn("sh", ["-c", command], {
|
|
10547
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
10548
|
+
timeout: timeoutMs
|
|
10549
|
+
});
|
|
10550
|
+
let stdout = "";
|
|
10551
|
+
let stderr = "";
|
|
10552
|
+
child.stdout.on("data", (data) => {
|
|
10553
|
+
stdout += data.toString();
|
|
10554
|
+
});
|
|
10555
|
+
child.stderr.on("data", (data) => {
|
|
10556
|
+
stderr += data.toString();
|
|
10557
|
+
});
|
|
10558
|
+
child.on("close", (code) => {
|
|
10559
|
+
spinner.stop("Execution completed");
|
|
10560
|
+
if (code === 0) {
|
|
10561
|
+
console.log(import_picocolors10.default.green(`
|
|
10562
|
+
✅ Tool executed successfully`));
|
|
10563
|
+
if (stdout.trim()) {
|
|
10564
|
+
console.log(import_picocolors10.default.cyan(`
|
|
10565
|
+
\uD83D\uDCE4 Output:`));
|
|
10566
|
+
console.log(stdout.trim());
|
|
10567
|
+
}
|
|
10568
|
+
resolve2();
|
|
10569
|
+
} else {
|
|
10570
|
+
console.log(import_picocolors10.default.red(`
|
|
10571
|
+
❌ Tool execution failed (exit code: ${code})`));
|
|
10572
|
+
if (stderr.trim()) {
|
|
10573
|
+
console.log(import_picocolors10.default.red(`
|
|
10574
|
+
\uD83D\uDCE4 Error output:`));
|
|
10575
|
+
console.log(stderr.trim());
|
|
10576
|
+
}
|
|
10577
|
+
if (stdout.trim()) {
|
|
10578
|
+
console.log(import_picocolors10.default.yellow(`
|
|
10579
|
+
\uD83D\uDCE4 Standard output:`));
|
|
10580
|
+
console.log(stdout.trim());
|
|
10581
|
+
}
|
|
10582
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
10583
|
+
}
|
|
10584
|
+
});
|
|
10585
|
+
child.on("error", (error) => {
|
|
10586
|
+
spinner.stop("Execution failed");
|
|
10587
|
+
console.log(import_picocolors10.default.red(`
|
|
10588
|
+
❌ Failed to execute command: ${error.message}`));
|
|
10589
|
+
reject(error);
|
|
10590
|
+
});
|
|
10591
|
+
});
|
|
10592
|
+
}
|
|
10593
|
+
function parseTimeout(timeout) {
|
|
10594
|
+
const match = timeout.match(/^(\d+)([smh])$/);
|
|
10595
|
+
if (!match)
|
|
10596
|
+
return 30000;
|
|
10597
|
+
const [, value, unit] = match;
|
|
10598
|
+
const num = parseInt(value);
|
|
10599
|
+
switch (unit) {
|
|
10600
|
+
case "s":
|
|
10601
|
+
return num * 1000;
|
|
10602
|
+
case "m":
|
|
10603
|
+
return num * 60 * 1000;
|
|
10604
|
+
case "h":
|
|
10605
|
+
return num * 60 * 60 * 1000;
|
|
10606
|
+
default:
|
|
10607
|
+
return 30000;
|
|
10608
|
+
}
|
|
10609
|
+
}
|
|
10610
|
+
|
|
10611
|
+
// src/commands/sign.ts
|
|
10612
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
10613
|
+
import * as fs2 from "fs";
|
|
10614
|
+
import * as path2 from "path";
|
|
10615
|
+
async function handleSignCommand(args, options) {
|
|
10616
|
+
if (options.help) {
|
|
10617
|
+
console.log(`
|
|
10618
|
+
Usage: enact sign <subcommand> [options]
|
|
10619
|
+
|
|
10620
|
+
Manage tool signatures and verification.
|
|
10621
|
+
|
|
10622
|
+
Subcommands:
|
|
10623
|
+
verify <tool-path> [policy] Verify tool signatures
|
|
10624
|
+
list-keys List trusted public keys
|
|
10625
|
+
sign <tool-path> Sign a tool (requires private key)
|
|
10626
|
+
|
|
10627
|
+
Options:
|
|
10628
|
+
--help, -h Show this help message
|
|
10629
|
+
--policy <policy> Verification policy: permissive, enterprise, paranoid
|
|
10630
|
+
--private-key <path> Path to private key for signing
|
|
10631
|
+
--role <role> Role for signature: author, reviewer, approver
|
|
10632
|
+
--signer <name> Signer identifier
|
|
10633
|
+
--verbose, -v Show detailed information
|
|
10634
|
+
|
|
10635
|
+
Verification Policies:
|
|
10636
|
+
permissive Require 1+ valid signatures from trusted keys (default)
|
|
10637
|
+
enterprise Require author + reviewer signatures
|
|
10638
|
+
paranoid Require author + reviewer + approver signatures
|
|
10639
|
+
|
|
10640
|
+
Examples:
|
|
10641
|
+
enact sign verify my-tool.yaml
|
|
10642
|
+
enact sign verify my-tool.yaml enterprise
|
|
10643
|
+
enact sign list-keys
|
|
10644
|
+
enact sign sign my-tool.yaml --private-key ~/.enact/private.pem --role author
|
|
10645
|
+
`);
|
|
10646
|
+
return;
|
|
10647
|
+
}
|
|
10648
|
+
const subcommand = args[0];
|
|
10649
|
+
if (!subcommand) {
|
|
10650
|
+
Ie(import_picocolors11.default.bgBlue(import_picocolors11.default.white(" Enact Tool Signing ")));
|
|
10651
|
+
const action = await ve({
|
|
10652
|
+
message: "What would you like to do?",
|
|
10653
|
+
options: [
|
|
10654
|
+
{ value: "verify", label: "\uD83D\uDD0D Verify tool signatures" },
|
|
10655
|
+
{ value: "list-keys", label: "\uD83D\uDD11 List trusted keys" },
|
|
10656
|
+
{ value: "sign", label: "✍️ Sign a tool" },
|
|
10657
|
+
{ value: "help", label: "❓ Show help" }
|
|
10658
|
+
]
|
|
10659
|
+
});
|
|
10660
|
+
if (action === null) {
|
|
10661
|
+
Se(import_picocolors11.default.yellow("Operation cancelled"));
|
|
10662
|
+
return;
|
|
10663
|
+
}
|
|
10664
|
+
if (action === "help") {
|
|
10665
|
+
await handleSignCommand(["help"], { help: true });
|
|
10666
|
+
return;
|
|
10667
|
+
}
|
|
10668
|
+
if (action === "verify") {
|
|
10669
|
+
await handleVerifyCommand([], options);
|
|
10670
|
+
return;
|
|
10671
|
+
}
|
|
10672
|
+
if (action === "list-keys") {
|
|
10673
|
+
await handleListKeysCommand([], options);
|
|
10674
|
+
return;
|
|
10675
|
+
}
|
|
10676
|
+
if (action === "sign") {
|
|
10677
|
+
await handleSignToolCommand([], options);
|
|
10678
|
+
return;
|
|
10679
|
+
}
|
|
10680
|
+
return;
|
|
10681
|
+
}
|
|
10682
|
+
switch (subcommand.toLowerCase()) {
|
|
10683
|
+
case "verify":
|
|
10684
|
+
await handleVerifyCommand(args.slice(1), options);
|
|
10685
|
+
break;
|
|
10686
|
+
case "list-keys":
|
|
10687
|
+
await handleListKeysCommand(args.slice(1), options);
|
|
10688
|
+
break;
|
|
10689
|
+
case "sign":
|
|
10690
|
+
await handleSignToolCommand(args.slice(1), options);
|
|
10691
|
+
break;
|
|
10692
|
+
default:
|
|
10693
|
+
console.error(import_picocolors11.default.red(`Unknown subcommand: ${subcommand}`));
|
|
10694
|
+
console.log(import_picocolors11.default.yellow('Use "enact sign --help" for available commands'));
|
|
10695
|
+
process.exit(1);
|
|
10696
|
+
}
|
|
10697
|
+
}
|
|
10698
|
+
async function handleVerifyCommand(args, options) {
|
|
10699
|
+
let toolPath = args[0];
|
|
10700
|
+
let policyName = args[1] || options.policy || "permissive";
|
|
10701
|
+
if (!toolPath) {
|
|
10702
|
+
Ie(import_picocolors11.default.bgGreen(import_picocolors11.default.white(" Verify Tool Signatures ")));
|
|
10703
|
+
toolPath = await he({
|
|
10704
|
+
message: "Enter path to tool YAML file:",
|
|
10705
|
+
placeholder: "./my-tool.yaml",
|
|
10706
|
+
validate: (value) => {
|
|
10707
|
+
if (!value.trim())
|
|
10708
|
+
return "Please enter a file path";
|
|
10709
|
+
if (!fs2.existsSync(value.trim()))
|
|
10710
|
+
return "File does not exist";
|
|
10711
|
+
return;
|
|
10712
|
+
}
|
|
10713
|
+
});
|
|
10714
|
+
if (!toolPath) {
|
|
10715
|
+
Se(import_picocolors11.default.yellow("Verification cancelled"));
|
|
10716
|
+
return;
|
|
10717
|
+
}
|
|
10718
|
+
policyName = await ve({
|
|
10719
|
+
message: "Select verification policy:",
|
|
10720
|
+
options: [
|
|
10721
|
+
{ value: "permissive", label: "Permissive - Require 1+ valid signatures (default)" },
|
|
10722
|
+
{ value: "enterprise", label: "Enterprise - Require author + reviewer signatures" },
|
|
10723
|
+
{ value: "paranoid", label: "Paranoid - Require author + reviewer + approver signatures" }
|
|
10724
|
+
]
|
|
10725
|
+
});
|
|
10726
|
+
}
|
|
10727
|
+
const policyKey = policyName.toUpperCase();
|
|
10728
|
+
const policy = VERIFICATION_POLICIES[policyKey] || VERIFICATION_POLICIES.PERMISSIVE;
|
|
10729
|
+
if (options.verbose) {
|
|
10730
|
+
console.log(import_picocolors11.default.cyan(`
|
|
10731
|
+
\uD83D\uDCCB Verification Details:`));
|
|
10732
|
+
console.log(`Tool: ${toolPath}`);
|
|
10733
|
+
console.log(`Policy: ${policyName}`);
|
|
10734
|
+
if (policy.minimumSignatures)
|
|
10735
|
+
console.log(`Minimum signatures: ${policy.minimumSignatures}`);
|
|
10736
|
+
if (policy.requireRoles)
|
|
10737
|
+
console.log(`Required roles: ${policy.requireRoles.join(", ")}`);
|
|
10738
|
+
}
|
|
10739
|
+
try {
|
|
10740
|
+
const spinner = Y2();
|
|
10741
|
+
spinner.start("Verifying tool signatures...");
|
|
10742
|
+
const toolYaml = fs2.readFileSync(toolPath, "utf8");
|
|
10743
|
+
const result = await verifyTool(toolYaml, policy);
|
|
10744
|
+
spinner.stop("Verification completed");
|
|
10745
|
+
if (result.isValid) {
|
|
10746
|
+
console.log(import_picocolors11.default.green(`
|
|
10747
|
+
✅ VERIFICATION PASSED`));
|
|
10748
|
+
console.log(import_picocolors11.default.green(`${result.message}`));
|
|
10749
|
+
} else {
|
|
10750
|
+
console.log(import_picocolors11.default.red(`
|
|
10751
|
+
❌ VERIFICATION FAILED`));
|
|
10752
|
+
console.log(import_picocolors11.default.red(`${result.message}`));
|
|
10753
|
+
}
|
|
10754
|
+
console.log(import_picocolors11.default.cyan(`
|
|
10755
|
+
\uD83D\uDCCA Signature Summary:`));
|
|
10756
|
+
console.log(`Valid signatures: ${result.validSignatures}/${result.totalSignatures}`);
|
|
10757
|
+
if (result.verifiedSigners.length > 0) {
|
|
10758
|
+
console.log(import_picocolors11.default.cyan(`
|
|
10759
|
+
\uD83D\uDD12 Verified signers:`));
|
|
10760
|
+
result.verifiedSigners.forEach((signer) => {
|
|
10761
|
+
console.log(` - ${signer.signer}${signer.role ? ` (${signer.role})` : ""} [${signer.keyId}]`);
|
|
10762
|
+
});
|
|
10763
|
+
}
|
|
10764
|
+
if (result.errors.length > 0) {
|
|
10765
|
+
console.log(import_picocolors11.default.yellow(`
|
|
10766
|
+
⚠️ Issues found:`));
|
|
10767
|
+
result.errors.forEach((error) => console.log(` - ${error}`));
|
|
10768
|
+
}
|
|
10769
|
+
if (!result.isValid) {
|
|
10770
|
+
Se(import_picocolors11.default.red("Tool verification failed"));
|
|
10771
|
+
process.exit(1);
|
|
10772
|
+
} else {
|
|
10773
|
+
Se(import_picocolors11.default.green("Tool verification passed"));
|
|
10774
|
+
}
|
|
10775
|
+
} catch (error) {
|
|
10776
|
+
console.error(import_picocolors11.default.red(`
|
|
10777
|
+
❌ Error verifying tool: ${error.message}`));
|
|
10778
|
+
process.exit(1);
|
|
10779
|
+
}
|
|
10780
|
+
}
|
|
10781
|
+
async function handleListKeysCommand(args, options) {
|
|
10782
|
+
Ie(import_picocolors11.default.bgCyan(import_picocolors11.default.white(" Trusted Public Keys ")));
|
|
10783
|
+
try {
|
|
10784
|
+
const trustedKeys = getTrustedPublicKeysMap();
|
|
10785
|
+
if (trustedKeys.size === 0) {
|
|
10786
|
+
console.log(import_picocolors11.default.yellow(`
|
|
10787
|
+
\uD83D\uDCED No trusted keys found`));
|
|
10788
|
+
console.log(import_picocolors11.default.dim("Add trusted keys to: ~/.enact/trusted-keys/"));
|
|
10789
|
+
return;
|
|
10790
|
+
}
|
|
10791
|
+
console.log(import_picocolors11.default.cyan(`
|
|
10792
|
+
\uD83D\uDD11 Found ${trustedKeys.size} trusted key(s):
|
|
10793
|
+
`));
|
|
10794
|
+
let keyIndex = 1;
|
|
10795
|
+
for (const [base64Key, pemContent] of trustedKeys.entries()) {
|
|
10796
|
+
const keyId = base64Key.substring(0, 16);
|
|
10797
|
+
const shortKey = base64Key.substring(0, 32) + "...";
|
|
10798
|
+
console.log(`${keyIndex}. Key ID: ${import_picocolors11.default.green(keyId)}`);
|
|
10799
|
+
console.log(` Preview: ${import_picocolors11.default.dim(shortKey)}`);
|
|
10800
|
+
if (options.verbose) {
|
|
10801
|
+
console.log(` Full PEM:
|
|
10802
|
+
${import_picocolors11.default.dim(pemContent)}`);
|
|
10803
|
+
}
|
|
10804
|
+
console.log("");
|
|
10805
|
+
keyIndex++;
|
|
10806
|
+
}
|
|
10807
|
+
if (!options.verbose) {
|
|
10808
|
+
console.log(import_picocolors11.default.dim("Use --verbose to see full PEM content"));
|
|
10809
|
+
}
|
|
10810
|
+
const trustedKeysDir = path2.join(process.env.HOME || ".", ".enact", "trusted-keys");
|
|
10811
|
+
console.log(import_picocolors11.default.cyan(`\uD83D\uDCC1 Trusted keys directory: ${trustedKeysDir}`));
|
|
10812
|
+
Se(import_picocolors11.default.green(`Listed ${trustedKeys.size} trusted keys`));
|
|
10813
|
+
} catch (error) {
|
|
10814
|
+
console.error(import_picocolors11.default.red(`
|
|
10815
|
+
❌ Error listing keys: ${error.message}`));
|
|
10816
|
+
process.exit(1);
|
|
10817
|
+
}
|
|
10818
|
+
}
|
|
10819
|
+
async function handleSignToolCommand(args, options) {
|
|
10820
|
+
let toolPath = args[0];
|
|
10821
|
+
let privateKeyPath = options.privateKey;
|
|
10822
|
+
let role = options.role;
|
|
10823
|
+
let signerName = options.signer;
|
|
10824
|
+
if (!toolPath) {
|
|
10825
|
+
Ie(import_picocolors11.default.bgMagenta(import_picocolors11.default.white(" Sign Tool ")));
|
|
10826
|
+
toolPath = await he({
|
|
10827
|
+
message: "Enter path to tool YAML file:",
|
|
10828
|
+
placeholder: "./my-tool.yaml",
|
|
10829
|
+
validate: (value) => {
|
|
10830
|
+
if (!value.trim())
|
|
10831
|
+
return "Please enter a file path";
|
|
10832
|
+
if (!fs2.existsSync(value.trim()))
|
|
10833
|
+
return "File does not exist";
|
|
10834
|
+
return;
|
|
10835
|
+
}
|
|
10836
|
+
});
|
|
10837
|
+
if (!toolPath) {
|
|
10838
|
+
Se(import_picocolors11.default.yellow("Signing cancelled"));
|
|
10839
|
+
return;
|
|
10840
|
+
}
|
|
10841
|
+
}
|
|
10842
|
+
if (!privateKeyPath) {
|
|
10843
|
+
privateKeyPath = await he({
|
|
10844
|
+
message: "Enter path to private key PEM file:",
|
|
10845
|
+
placeholder: "~/.enact/private.pem",
|
|
10846
|
+
validate: (value) => {
|
|
10847
|
+
if (!value.trim())
|
|
10848
|
+
return "Please enter a private key path";
|
|
10849
|
+
const expandedPath = value.replace(/^~/, process.env.HOME || "");
|
|
10850
|
+
if (!fs2.existsSync(expandedPath))
|
|
10851
|
+
return "Private key file does not exist";
|
|
10852
|
+
return;
|
|
10853
|
+
}
|
|
10854
|
+
});
|
|
10855
|
+
if (!privateKeyPath) {
|
|
10856
|
+
Se(import_picocolors11.default.yellow("Signing cancelled"));
|
|
10857
|
+
return;
|
|
10858
|
+
}
|
|
10859
|
+
}
|
|
10860
|
+
const expandedPrivateKeyPath = privateKeyPath.replace(/^~/, process.env.HOME || "");
|
|
10861
|
+
const publicKeyPath = expandedPrivateKeyPath.replace(/private\.pem$/, "public.pem").replace(/-private\.pem$/, "-public.pem");
|
|
10862
|
+
if (!fs2.existsSync(publicKeyPath)) {
|
|
10863
|
+
console.error(import_picocolors11.default.red(`
|
|
10864
|
+
❌ Public key not found at: ${publicKeyPath}`));
|
|
10865
|
+
console.log(import_picocolors11.default.yellow("Expected public key file alongside private key"));
|
|
10866
|
+
return;
|
|
10867
|
+
}
|
|
10868
|
+
if (!role) {
|
|
10869
|
+
role = await ve({
|
|
10870
|
+
message: "Select your role for this signature:",
|
|
10871
|
+
options: [
|
|
10872
|
+
{ value: "author", label: "Author - Original creator of the tool" },
|
|
10873
|
+
{ value: "reviewer", label: "Reviewer - Code reviewer/auditor" },
|
|
10874
|
+
{ value: "approver", label: "Approver - Final approver for production use" }
|
|
10875
|
+
]
|
|
10876
|
+
});
|
|
10877
|
+
}
|
|
10878
|
+
if (!signerName) {
|
|
10879
|
+
signerName = await he({
|
|
10880
|
+
message: "Enter your signer identifier:",
|
|
10881
|
+
placeholder: "your-username or email@domain.com",
|
|
10882
|
+
validate: (value) => {
|
|
10883
|
+
if (!value.trim())
|
|
10884
|
+
return "Please enter a signer identifier";
|
|
10885
|
+
return;
|
|
10886
|
+
}
|
|
10887
|
+
});
|
|
10888
|
+
if (!signerName) {
|
|
10889
|
+
Se(import_picocolors11.default.yellow("Signing cancelled"));
|
|
10890
|
+
return;
|
|
10891
|
+
}
|
|
10892
|
+
}
|
|
10893
|
+
try {
|
|
10894
|
+
const spinner = Y2();
|
|
10895
|
+
spinner.start("Signing tool...");
|
|
10896
|
+
const signedYaml = await signTool(toolPath, expandedPrivateKeyPath, publicKeyPath, { id: signerName, role }, toolPath);
|
|
10897
|
+
spinner.stop("Tool signed successfully");
|
|
10898
|
+
console.log(import_picocolors11.default.green(`
|
|
10899
|
+
✅ Tool signed successfully!`));
|
|
10900
|
+
console.log(import_picocolors11.default.cyan(`
|
|
10901
|
+
\uD83D\uDCCB Signature Details:`));
|
|
10902
|
+
console.log(`Tool: ${toolPath}`);
|
|
10903
|
+
console.log(`Signer: ${signerName}`);
|
|
10904
|
+
console.log(`Role: ${role}`);
|
|
10905
|
+
console.log(`Private key: ${expandedPrivateKeyPath}`);
|
|
10906
|
+
console.log(`Public key: ${publicKeyPath}`);
|
|
10907
|
+
if (options.verbose) {
|
|
10908
|
+
console.log(import_picocolors11.default.cyan(`
|
|
10909
|
+
\uD83D\uDCC4 Signed YAML preview:`));
|
|
10910
|
+
const lines = signedYaml.split(`
|
|
10911
|
+
`);
|
|
10912
|
+
const previewLines = lines.slice(0, 10).join(`
|
|
10913
|
+
`);
|
|
10914
|
+
console.log(import_picocolors11.default.dim(previewLines + (lines.length > 10 ? `
|
|
10915
|
+
...` : "")));
|
|
10916
|
+
}
|
|
10917
|
+
Se(import_picocolors11.default.green("Tool signature added to YAML file"));
|
|
10918
|
+
} catch (error) {
|
|
10919
|
+
console.error(import_picocolors11.default.red(`
|
|
10920
|
+
❌ Error signing tool: ${error.message}`));
|
|
10921
|
+
process.exit(1);
|
|
10922
|
+
}
|
|
10923
|
+
}
|
|
10924
|
+
|
|
9823
10925
|
// src/index.ts
|
|
9824
10926
|
var { values, positionals } = parseArgs({
|
|
9825
10927
|
args: process.argv,
|
|
@@ -9862,9 +10964,38 @@ var { values, positionals } = parseArgs({
|
|
|
9862
10964
|
type: "string",
|
|
9863
10965
|
short: "f"
|
|
9864
10966
|
},
|
|
10967
|
+
json: {
|
|
10968
|
+
type: "boolean"
|
|
10969
|
+
},
|
|
9865
10970
|
author: {
|
|
9866
10971
|
type: "string",
|
|
9867
10972
|
short: "a"
|
|
10973
|
+
},
|
|
10974
|
+
input: {
|
|
10975
|
+
type: "string",
|
|
10976
|
+
short: "i"
|
|
10977
|
+
},
|
|
10978
|
+
params: {
|
|
10979
|
+
type: "string"
|
|
10980
|
+
},
|
|
10981
|
+
dry: {
|
|
10982
|
+
type: "boolean"
|
|
10983
|
+
},
|
|
10984
|
+
verbose: {
|
|
10985
|
+
type: "boolean",
|
|
10986
|
+
short: "v"
|
|
10987
|
+
},
|
|
10988
|
+
policy: {
|
|
10989
|
+
type: "string"
|
|
10990
|
+
},
|
|
10991
|
+
"private-key": {
|
|
10992
|
+
type: "string"
|
|
10993
|
+
},
|
|
10994
|
+
role: {
|
|
10995
|
+
type: "string"
|
|
10996
|
+
},
|
|
10997
|
+
signer: {
|
|
10998
|
+
type: "string"
|
|
9868
10999
|
}
|
|
9869
11000
|
},
|
|
9870
11001
|
allowPositionals: true,
|
|
@@ -9902,7 +11033,7 @@ async function main() {
|
|
|
9902
11033
|
help: values.help,
|
|
9903
11034
|
limit: values.limit ? parseInt(values.limit) : undefined,
|
|
9904
11035
|
tags: values.tags ? (values.tags + "").split(",").map((t) => t.trim()) : undefined,
|
|
9905
|
-
format: values.format,
|
|
11036
|
+
format: values.json ? "json" : values.format,
|
|
9906
11037
|
author: values.author
|
|
9907
11038
|
});
|
|
9908
11039
|
break;
|
|
@@ -9925,17 +11056,39 @@ async function main() {
|
|
|
9925
11056
|
format: values.format
|
|
9926
11057
|
});
|
|
9927
11058
|
break;
|
|
11059
|
+
case "exec":
|
|
11060
|
+
await handleExecCommand(commandArgs, {
|
|
11061
|
+
help: values.help,
|
|
11062
|
+
input: values.input,
|
|
11063
|
+
params: values.params,
|
|
11064
|
+
timeout: values.timeout,
|
|
11065
|
+
dry: values.dry,
|
|
11066
|
+
verbose: values.verbose
|
|
11067
|
+
});
|
|
11068
|
+
break;
|
|
11069
|
+
case "sign":
|
|
11070
|
+
await handleSignCommand(commandArgs, {
|
|
11071
|
+
help: values.help,
|
|
11072
|
+
policy: values.policy,
|
|
11073
|
+
privateKey: values["private-key"],
|
|
11074
|
+
role: values.role,
|
|
11075
|
+
signer: values.signer,
|
|
11076
|
+
verbose: values.verbose
|
|
11077
|
+
});
|
|
11078
|
+
break;
|
|
9928
11079
|
case undefined:
|
|
9929
11080
|
if (values.help) {
|
|
9930
11081
|
showHelp();
|
|
9931
11082
|
} else {
|
|
9932
|
-
Ie(
|
|
11083
|
+
Ie(import_picocolors12.default.bgCyan(import_picocolors12.default.black(" Enact CLI ")));
|
|
9933
11084
|
const action = await ve({
|
|
9934
11085
|
message: "What would you like to do?",
|
|
9935
11086
|
options: [
|
|
9936
11087
|
{ value: "search", label: "\uD83D\uDD0D Search for tools" },
|
|
11088
|
+
{ value: "exec", label: "⚡ Execute a tool" },
|
|
9937
11089
|
{ value: "publish", label: "\uD83D\uDCE4 Publish a tool" },
|
|
9938
11090
|
{ value: "init", label: "\uD83D\uDCDD Create a new tool definition" },
|
|
11091
|
+
{ value: "sign", label: "✍️ Sign & verify tools" },
|
|
9939
11092
|
{ value: "auth", label: "\uD83D\uDD10 Manage authentication" },
|
|
9940
11093
|
{ value: "remote", label: "\uD83C\uDF10 Manage remote servers" },
|
|
9941
11094
|
{ value: "user", label: "\uD83D\uDC64 User operations" },
|
|
@@ -9955,6 +11108,14 @@ async function main() {
|
|
|
9955
11108
|
await handleSearchCommand([], {});
|
|
9956
11109
|
return;
|
|
9957
11110
|
}
|
|
11111
|
+
if (action === "exec") {
|
|
11112
|
+
await handleExecCommand([], {});
|
|
11113
|
+
return;
|
|
11114
|
+
}
|
|
11115
|
+
if (action === "sign") {
|
|
11116
|
+
await handleSignCommand([], {});
|
|
11117
|
+
return;
|
|
11118
|
+
}
|
|
9958
11119
|
if (action === "auth") {
|
|
9959
11120
|
const authAction = await ve({
|
|
9960
11121
|
message: "Authentication:",
|
|
@@ -10007,12 +11168,12 @@ async function main() {
|
|
|
10007
11168
|
}
|
|
10008
11169
|
break;
|
|
10009
11170
|
default:
|
|
10010
|
-
console.error(
|
|
11171
|
+
console.error(import_picocolors12.default.red(`Unknown command: ${command}`));
|
|
10011
11172
|
showHelp();
|
|
10012
11173
|
process.exit(1);
|
|
10013
11174
|
}
|
|
10014
11175
|
} catch (error) {
|
|
10015
|
-
console.error(
|
|
11176
|
+
console.error(import_picocolors12.default.red(`Error: ${error.message}`));
|
|
10016
11177
|
process.exit(1);
|
|
10017
11178
|
}
|
|
10018
11179
|
}
|