enact-cli 1.0.1 → 1.0.3

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 (2) hide show
  1. package/dist/index.js +866 -204
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7049,7 +7049,7 @@ var require_public_api = __commonJS((exports) => {
7049
7049
  });
7050
7050
 
7051
7051
  // src/index.ts
7052
- var import_picocolors8 = __toESM(require_picocolors(), 1);
7052
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
7053
7053
  import { parseArgs } from "util";
7054
7054
 
7055
7055
  // node_modules/@clack/prompts/dist/index.mjs
@@ -7285,6 +7285,9 @@ function BD(e, u) {
7285
7285
  }
7286
7286
  var AD = globalThis.process.platform.startsWith("win");
7287
7287
  var S = Symbol("clack:cancel");
7288
+ function pD(e) {
7289
+ return e === S;
7290
+ }
7288
7291
  function m(e, u) {
7289
7292
  const t = e;
7290
7293
  t.isTTY && t.setRawMode(u);
@@ -7758,21 +7761,32 @@ var import_picocolors3 = __toESM(require_picocolors(), 1);
7758
7761
  function showHelp() {
7759
7762
  console.log(`
7760
7763
  ${import_picocolors3.default.bold("Enact CLI")} ${import_picocolors3.default.dim("v0.1.0")}
7761
- ${import_picocolors3.default.dim("A simple CLI tool to publish documents")}
7764
+ ${import_picocolors3.default.dim("A CLI tool for managing and publishing Enact tools")}
7762
7765
 
7763
7766
  ${import_picocolors3.default.bold("Usage:")}
7764
7767
  ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("<command>")} [options]
7765
7768
 
7766
7769
  ${import_picocolors3.default.bold("Commands:")}
7767
- ${import_picocolors3.default.green("publish")} Publishes a document to a server
7768
-
7769
- ${import_picocolors3.default.bold("Options:")}
7770
- ${import_picocolors3.default.yellow("--help, -h")} Show this help message
7771
- ${import_picocolors3.default.yellow("--version, -v")} Show version information
7770
+ ${import_picocolors3.default.green("auth")} Manage authentication (login, logout, status, token)
7771
+ ${import_picocolors3.default.green("init")} Create a new tool definition
7772
+ ${import_picocolors3.default.green("publish")} Publish a tool to the registry
7773
+ ${import_picocolors3.default.green("search")} Search for tools in the registry
7774
+ ${import_picocolors3.default.green("remote")} Manage remote servers (add, list, remove)
7775
+ ${import_picocolors3.default.green("user")} User operations (get public key)
7776
+
7777
+ ${import_picocolors3.default.bold("Global Options:")}
7778
+ ${import_picocolors3.default.yellow("--help, -h")} Show help message
7779
+ ${import_picocolors3.default.yellow("--version, -v")} Show version information
7772
7780
 
7773
7781
  ${import_picocolors3.default.bold("Examples:")}
7774
- ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} document.md
7775
- ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} ${import_picocolors3.default.yellow("--url")} https://example.com/api document.md
7782
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.dim("# Interactive mode")}
7783
+ ${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")}
7784
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} my-tool.yaml ${import_picocolors3.default.dim("# Publish a tool")}
7785
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("auth")} login ${import_picocolors3.default.dim("# Login with OAuth")}
7786
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("init")} ${import_picocolors3.default.yellow("--minimal")} ${import_picocolors3.default.dim("# Create minimal tool template")}
7787
+
7788
+ ${import_picocolors3.default.bold("More Help:")}
7789
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("<command>")} ${import_picocolors3.default.yellow("--help")} ${import_picocolors3.default.dim("# Show command-specific help")}
7776
7790
  `);
7777
7791
  }
7778
7792
  function showVersion() {
@@ -7782,18 +7796,20 @@ function showPublishHelp() {
7782
7796
  console.log(`
7783
7797
  ${import_picocolors3.default.bold("Usage:")} ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} [options] [file]
7784
7798
 
7785
- ${import_picocolors3.default.bold("Publishes a document to a server")}
7799
+ ${import_picocolors3.default.bold("Publish a tool to the Enact registry")}
7786
7800
 
7787
7801
  ${import_picocolors3.default.bold("Arguments:")}
7788
- ${import_picocolors3.default.green("file")} The file to publish. If not provided, will prompt for a file.
7802
+ ${import_picocolors3.default.blue("file")} The tool definition file to publish (YAML format)
7789
7803
 
7790
7804
  ${import_picocolors3.default.bold("Options:")}
7791
- ${import_picocolors3.default.yellow("--help, -h")} Show this help message
7792
- ${import_picocolors3.default.yellow("--url")} Specify the server URL to publish to
7805
+ ${import_picocolors3.default.yellow("--help, -h")} Show this help message
7806
+ ${import_picocolors3.default.yellow("--url")} Specify the registry URL
7807
+ ${import_picocolors3.default.yellow("--token, -t")} Specify authentication token
7793
7808
 
7794
7809
  ${import_picocolors3.default.bold("Examples:")}
7795
- ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} document.md
7796
- ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} ${import_picocolors3.default.yellow("--url")} https://example.com/api document.md
7810
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} my-tool.yaml
7811
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} ${import_picocolors3.default.yellow("--url")} https://registry.example.com my-tool.yaml
7812
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} ${import_picocolors3.default.yellow("--token")} abc123 my-tool.yaml
7797
7813
  `);
7798
7814
  }
7799
7815
 
@@ -7862,11 +7878,297 @@ import { parse } from "url";
7862
7878
  import { randomBytes, createHash } from "crypto";
7863
7879
  import { exec } from "child_process";
7864
7880
  import { promisify } from "util";
7881
+
7882
+ // src/api/enact-api.ts
7883
+ class EnactApiClient {
7884
+ baseUrl;
7885
+ supabaseUrl;
7886
+ constructor(baseUrl = "https://enact.tools", supabaseUrl = "https://xjnhhxwxovjifdxdwzih.supabase.co") {
7887
+ this.baseUrl = baseUrl.replace(/\/$/, "");
7888
+ this.supabaseUrl = supabaseUrl.replace(/\/$/, "");
7889
+ }
7890
+ async makeRequest(endpoint, options = {}, token, tokenType = "jwt") {
7891
+ const url = endpoint.startsWith("http") ? endpoint : `${this.supabaseUrl}${endpoint}`;
7892
+ const headers = {
7893
+ "Content-Type": "application/json",
7894
+ ...options.headers
7895
+ };
7896
+ if (token) {
7897
+ if (tokenType === "jwt") {
7898
+ headers["Authorization"] = `Bearer ${token}`;
7899
+ } else {
7900
+ headers["X-API-Key"] = token;
7901
+ }
7902
+ }
7903
+ const response = await fetch(url, {
7904
+ ...options,
7905
+ headers
7906
+ });
7907
+ if (!response.ok) {
7908
+ const errorData = await response.json().catch(() => ({ error: "Unknown error" }));
7909
+ throw new EnactApiError(`${errorData.error || response.statusText}`, response.status, endpoint);
7910
+ }
7911
+ const responseData = await response.json();
7912
+ if (true) {
7913
+ console.log(`API Response for ${endpoint}:`, responseData);
7914
+ }
7915
+ return responseData;
7916
+ }
7917
+ async getTools(params) {
7918
+ const searchParams = new URLSearchParams;
7919
+ if (params?.limit)
7920
+ searchParams.set("limit", params.limit.toString());
7921
+ if (params?.offset)
7922
+ searchParams.set("offset", params.offset.toString());
7923
+ if (params?.tags)
7924
+ searchParams.set("tags", params.tags.join(","));
7925
+ if (params?.author)
7926
+ searchParams.set("author", params.author);
7927
+ const query = searchParams.toString();
7928
+ const endpoint = `/functions/v1/tools${query ? `?${query}` : ""}`;
7929
+ const response = await this.makeRequest(endpoint);
7930
+ if (Array.isArray(response)) {
7931
+ return response;
7932
+ } else if (response.data && Array.isArray(response.data)) {
7933
+ return response.data;
7934
+ } else if (response.tools && Array.isArray(response.tools)) {
7935
+ return response.tools;
7936
+ } else {
7937
+ console.warn("Unexpected response structure for getTools:", response);
7938
+ return [];
7939
+ }
7940
+ }
7941
+ async getTool(name) {
7942
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}`;
7943
+ return this.makeRequest(endpoint);
7944
+ }
7945
+ async getToolUsage(name) {
7946
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}/usage`;
7947
+ return this.makeRequest(endpoint);
7948
+ }
7949
+ async logToolUsage(name, usage) {
7950
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}/usage`;
7951
+ return this.makeRequest(endpoint, {
7952
+ method: "POST",
7953
+ body: JSON.stringify(usage)
7954
+ });
7955
+ }
7956
+ async searchTools(query) {
7957
+ const endpoint = "/functions/v1/tools-search";
7958
+ const response = await this.makeRequest(endpoint, {
7959
+ method: "POST",
7960
+ body: JSON.stringify(query)
7961
+ });
7962
+ if (Array.isArray(response)) {
7963
+ return response;
7964
+ } else if (response.data && Array.isArray(response.data)) {
7965
+ return response.data;
7966
+ } else if (response.results && Array.isArray(response.results)) {
7967
+ return response.results;
7968
+ } else if (response.tools && Array.isArray(response.tools)) {
7969
+ return response.tools;
7970
+ } else {
7971
+ console.warn("Unexpected response structure for searchTools:", response);
7972
+ return [];
7973
+ }
7974
+ }
7975
+ async publishTool(tool, token, tokenType = "cli") {
7976
+ const endpoint = "/functions/v1/tools";
7977
+ return this.makeRequest(endpoint, {
7978
+ method: "POST",
7979
+ body: JSON.stringify(tool)
7980
+ }, token, tokenType);
7981
+ }
7982
+ async updateTool(name, tool, token, tokenType = "cli") {
7983
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}`;
7984
+ return this.makeRequest(endpoint, {
7985
+ method: "PUT",
7986
+ body: JSON.stringify(tool)
7987
+ }, token, tokenType);
7988
+ }
7989
+ async deleteTool(name, token, tokenType = "cli") {
7990
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}`;
7991
+ return this.makeRequest(endpoint, {
7992
+ method: "DELETE"
7993
+ }, token, tokenType);
7994
+ }
7995
+ async createCLIToken(tokenData, jwtToken) {
7996
+ const endpoint = "/functions/v1/cli-token";
7997
+ return this.makeRequest(endpoint, {
7998
+ method: "POST",
7999
+ body: JSON.stringify(tokenData)
8000
+ }, jwtToken, "jwt");
8001
+ }
8002
+ async getCLITokens(jwtToken) {
8003
+ const endpoint = "/functions/v1/cli-token";
8004
+ return this.makeRequest(endpoint, {
8005
+ method: "GET"
8006
+ }, jwtToken, "jwt");
8007
+ }
8008
+ async deleteCLIToken(tokenId, jwtToken) {
8009
+ const endpoint = `/functions/v1/cli-token/${tokenId}`;
8010
+ return this.makeRequest(endpoint, {
8011
+ method: "DELETE"
8012
+ }, jwtToken, "jwt");
8013
+ }
8014
+ async exchangeOAuthCode(oauthData) {
8015
+ const endpoint = "/functions/v1/cli-oauth";
8016
+ return this.makeRequest(endpoint, {
8017
+ method: "POST",
8018
+ body: JSON.stringify(oauthData)
8019
+ });
8020
+ }
8021
+ async generateEmbeddings(data, token, tokenType = "cli") {
8022
+ const endpoint = "/functions/v1/generate-embeddings";
8023
+ return this.makeRequest(endpoint, {
8024
+ method: "POST",
8025
+ body: JSON.stringify(data)
8026
+ }, token, tokenType);
8027
+ }
8028
+ async toolExists(name) {
8029
+ try {
8030
+ await this.getTool(name);
8031
+ return true;
8032
+ } catch (error) {
8033
+ if (error instanceof Error && error.message.includes("404")) {
8034
+ return false;
8035
+ }
8036
+ throw error;
8037
+ }
8038
+ }
8039
+ async publishOrUpdateTool(tool, token, tokenType = "cli") {
8040
+ let exists;
8041
+ try {
8042
+ exists = await this.toolExists(tool.name);
8043
+ } catch (error) {
8044
+ exists = false;
8045
+ }
8046
+ if (exists) {
8047
+ const result = await this.updateTool(tool.name, tool, token, tokenType);
8048
+ return { isUpdate: true, result };
8049
+ } else {
8050
+ const result = await this.publishTool(tool, token, tokenType);
8051
+ return { isUpdate: false, result };
8052
+ }
8053
+ }
8054
+ async getToolsByTags(tags, limit = 20) {
8055
+ return this.searchTools({
8056
+ query: tags.join(" "),
8057
+ tags,
8058
+ limit
8059
+ });
8060
+ }
8061
+ async getToolsByAuthor(author, limit = 20) {
8062
+ return this.getTools({
8063
+ author,
8064
+ limit
8065
+ });
8066
+ }
8067
+ async getUserPublicKey(userId) {
8068
+ const url = `${this.supabaseUrl}/functions/v1/tools/user/public-key/${userId}`;
8069
+ const headers = {
8070
+ Accept: "application/json",
8071
+ "Content-Type": "application/json"
8072
+ };
8073
+ try {
8074
+ const response = await fetch(url, {
8075
+ method: "GET",
8076
+ headers
8077
+ });
8078
+ if (!response.ok) {
8079
+ const errorText = await response.text();
8080
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
8081
+ try {
8082
+ const errorJson = JSON.parse(errorText);
8083
+ if (errorJson.message) {
8084
+ errorMessage = errorJson.message;
8085
+ } else if (errorJson.error) {
8086
+ errorMessage = errorJson.error;
8087
+ }
8088
+ } catch {}
8089
+ throw new EnactApiError(errorMessage, response.status);
8090
+ }
8091
+ const data = await response.json();
8092
+ return data;
8093
+ } catch (error) {
8094
+ if (error instanceof EnactApiError) {
8095
+ throw error;
8096
+ }
8097
+ if (error instanceof Error) {
8098
+ if (error.message.includes("fetch")) {
8099
+ throw new EnactApiError("Network error: Could not connect to Enact API", 0);
8100
+ }
8101
+ throw new EnactApiError(error.message, 0);
8102
+ }
8103
+ throw new EnactApiError("Unknown error occurred", 0);
8104
+ }
8105
+ }
8106
+ generateOAuthUrl(options) {
8107
+ const params = new URLSearchParams({
8108
+ response_type: "code",
8109
+ client_id: options.clientId,
8110
+ redirect_uri: options.redirectUri,
8111
+ scope: options.scope,
8112
+ state: options.state,
8113
+ code_challenge: options.codeChallenge,
8114
+ code_challenge_method: options.codeChallengeMethod
8115
+ });
8116
+ return `${this.baseUrl}/auth/cli/oauth?${params.toString()}`;
8117
+ }
8118
+ validateTool(tool) {
8119
+ const errors2 = [];
8120
+ if (!tool.name || typeof tool.name !== "string") {
8121
+ errors2.push("Tool name is required and must be a string");
8122
+ } else if (!/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_\/-]+$/.test(tool.name)) {
8123
+ errors2.push("Tool name must follow hierarchical format: org/category/tool-name");
8124
+ }
8125
+ if (!tool.description || typeof tool.description !== "string") {
8126
+ errors2.push("Tool description is required and must be a string");
8127
+ }
8128
+ if (!tool.command || typeof tool.command !== "string") {
8129
+ errors2.push("Tool command is required and must be a string");
8130
+ }
8131
+ if (tool.timeout && typeof tool.timeout === "string") {
8132
+ if (!/^\d+[smh]$/.test(tool.timeout)) {
8133
+ errors2.push('Timeout must be in Go duration format (e.g., "30s", "5m", "1h")');
8134
+ }
8135
+ }
8136
+ if (tool.tags && !Array.isArray(tool.tags)) {
8137
+ errors2.push("Tags must be an array of strings");
8138
+ }
8139
+ if (tool.inputSchema && typeof tool.inputSchema !== "object") {
8140
+ errors2.push("inputSchema must be a valid JSON Schema object");
8141
+ }
8142
+ if (tool.outputSchema && typeof tool.outputSchema !== "object") {
8143
+ errors2.push("outputSchema must be a valid JSON Schema object");
8144
+ }
8145
+ return {
8146
+ valid: errors2.length === 0,
8147
+ errors: errors2
8148
+ };
8149
+ }
8150
+ }
8151
+ var enactApi = new EnactApiClient;
8152
+
8153
+ class EnactApiError extends Error {
8154
+ statusCode;
8155
+ endpoint;
8156
+ constructor(message, statusCode, endpoint) {
8157
+ super(message);
8158
+ this.statusCode = statusCode;
8159
+ this.endpoint = endpoint;
8160
+ this.name = "EnactApiError";
8161
+ }
8162
+ }
8163
+ function createEnactApiClient(baseUrl, supabaseUrl) {
8164
+ return new EnactApiClient(baseUrl, supabaseUrl);
8165
+ }
8166
+
8167
+ // src/commands/auth.ts
7865
8168
  var execAsync = promisify(exec);
7866
8169
  var CONFIG_DIR2 = join2(homedir2(), ".enact");
7867
8170
  var AUTH_FILE = join2(CONFIG_DIR2, "auth.json");
7868
8171
  var DEFAULT_SERVER = "https://enact.tools";
7869
- var SUPABASE_PROJECT_URL = "https://xjnhhxwxovjifdxdwzih.supabase.co";
7870
8172
  async function handleAuthCommand(args, options) {
7871
8173
  if (options.help || !args[0]) {
7872
8174
  console.log(`
@@ -7930,16 +8232,14 @@ async function handleLogin(serverUrl, callbackPort) {
7930
8232
  const codeChallenge = generateCodeChallenge(codeVerifier);
7931
8233
  const state = generateState();
7932
8234
  const redirectUri = `http://localhost:${callbackPort}/callback`;
7933
- const authParams = new URLSearchParams({
7934
- response_type: "code",
7935
- client_id: "enact-cli",
7936
- redirect_uri: redirectUri,
8235
+ const authUrl = enactApi.generateOAuthUrl({
8236
+ clientId: "enact-cli",
8237
+ redirectUri,
7937
8238
  scope: "publish read write",
7938
8239
  state,
7939
- code_challenge: codeChallenge,
7940
- code_challenge_method: "S256"
8240
+ codeChallenge,
8241
+ codeChallengeMethod: "S256"
7941
8242
  });
7942
- const authUrl = `${serverUrl}/auth/cli/oauth?${authParams.toString()}`;
7943
8243
  const s = Y2();
7944
8244
  s.start("Starting OAuth flow...");
7945
8245
  let server = null;
@@ -8102,49 +8402,20 @@ async function startCallbackServerWithFallback(port, serverUrl, codeVerifier, st
8102
8402
  });
8103
8403
  }
8104
8404
  async function exchangeCodeForToken(code, redirectUri, codeVerifier, serverUrl) {
8105
- const tokenEndpoint = `${SUPABASE_PROJECT_URL}/functions/v1/cli-oauth`;
8106
- console.log(`Exchanging code for token at: ${tokenEndpoint}`);
8405
+ console.log(`Exchanging code for token...`);
8107
8406
  console.log("Exchange params:", {
8108
8407
  code: code.substring(0, 8) + "...",
8109
8408
  redirectUri,
8110
8409
  codeVerifier: codeVerifier.substring(0, 8) + "..."
8111
8410
  });
8112
- const controller = new AbortController;
8113
- const timeoutId = setTimeout(() => controller.abort(), 30000);
8114
8411
  try {
8115
- const tokenResponse = await fetch(tokenEndpoint, {
8116
- method: "POST",
8117
- headers: {
8118
- "Content-Type": "application/json"
8119
- },
8120
- body: JSON.stringify({
8121
- grant_type: "authorization_code",
8122
- code,
8123
- redirect_uri: redirectUri,
8124
- client_id: "enact-cli",
8125
- code_verifier: codeVerifier
8126
- }),
8127
- signal: controller.signal
8412
+ const tokenData = await enactApi.exchangeOAuthCode({
8413
+ grant_type: "authorization_code",
8414
+ code,
8415
+ redirect_uri: redirectUri,
8416
+ client_id: "enact-cli",
8417
+ code_verifier: codeVerifier
8128
8418
  });
8129
- clearTimeout(timeoutId);
8130
- const responseText = await tokenResponse.text();
8131
- console.log("Token exchange response status:", tokenResponse.status);
8132
- if (!tokenResponse.ok) {
8133
- console.error("Token exchange failed with status:", tokenResponse.status);
8134
- console.error("Response body:", responseText);
8135
- throw new Error(`Token exchange failed (${tokenResponse.status}): ${responseText}`);
8136
- }
8137
- let tokenData;
8138
- try {
8139
- tokenData = JSON.parse(responseText);
8140
- } catch (parseError) {
8141
- console.error("Failed to parse token response as JSON:", responseText);
8142
- throw new Error(`Invalid JSON response from token endpoint`);
8143
- }
8144
- if (!tokenData.access_token) {
8145
- console.error("No access token in response:", tokenData);
8146
- throw new Error("No access token received from server");
8147
- }
8148
8419
  const expiresAt = new Date(Date.now() + tokenData.expires_in * 1000);
8149
8420
  await writeAuthConfig({
8150
8421
  token: tokenData.access_token,
@@ -8155,8 +8426,8 @@ async function exchangeCodeForToken(code, redirectUri, codeVerifier, serverUrl)
8155
8426
  });
8156
8427
  console.log("✓ Token stored successfully");
8157
8428
  } catch (error) {
8158
- if (error instanceof Error && error.name === "AbortError") {
8159
- throw new Error("Token exchange request timed out");
8429
+ if (error instanceof EnactApiError) {
8430
+ throw new Error(`Token exchange failed: ${error.message}`);
8160
8431
  }
8161
8432
  throw error;
8162
8433
  }
@@ -8295,15 +8566,13 @@ async function handlePublishCommand(args, options) {
8295
8566
  return;
8296
8567
  }
8297
8568
  Ie(import_picocolors5.default.bgBlue(import_picocolors5.default.white(" Publish Enact Tool ")));
8298
- let authHeaders;
8569
+ let token;
8299
8570
  try {
8300
8571
  if (options.token) {
8301
- authHeaders = {
8302
- "Content-Type": "application/json",
8303
- "X-API-Key": options.token
8304
- };
8572
+ token = options.token;
8305
8573
  } else {
8306
- authHeaders = await getAuthHeaders();
8574
+ const authHeaders = await getAuthHeaders();
8575
+ token = authHeaders["X-API-Key"];
8307
8576
  }
8308
8577
  } catch (error) {
8309
8578
  Se(import_picocolors5.default.red(`✗ Authentication required. Run "enact auth login" to authenticate.`));
@@ -8311,45 +8580,8 @@ async function handlePublishCommand(args, options) {
8311
8580
  }
8312
8581
  let filePath = args[0];
8313
8582
  if (!filePath) {
8314
- const history = await getHistory();
8315
- if (history.length > 0) {
8316
- const action = await ve({
8317
- message: "Select a tool manifest to publish:",
8318
- options: [
8319
- { value: "select", label: "Choose from recent files" },
8320
- { value: "new", label: "Specify a new file" }
8321
- ]
8322
- });
8323
- if (action === "select") {
8324
- const fileOptions = history.filter((file) => existsSync3(file) && isEnactFile(file)).map((file) => ({
8325
- value: file,
8326
- label: file
8327
- }));
8328
- if (fileOptions.length > 0) {
8329
- filePath = await ve({
8330
- message: "Select a tool manifest:",
8331
- options: fileOptions
8332
- });
8333
- } else {
8334
- Me("No recent Enact tool manifests found.", "History");
8335
- filePath = await he({
8336
- message: "Enter the path to the tool manifest (.yaml or .yml):",
8337
- validate: validateEnactFile
8338
- });
8339
- }
8340
- } else {
8341
- filePath = await he({
8342
- message: "Enter the path to the tool manifest (.yaml or .yml):",
8343
- validate: validateEnactFile
8344
- });
8345
- }
8346
- } else {
8347
- filePath = await he({
8348
- message: "Enter the path to the tool manifest (.yaml or .yml):",
8349
- validate: validateEnactFile
8350
- });
8351
- }
8352
- if (filePath === null) {
8583
+ filePath = await getFilePathInteractively();
8584
+ if (!filePath) {
8353
8585
  Se(import_picocolors5.default.yellow("Operation cancelled"));
8354
8586
  return;
8355
8587
  }
@@ -8364,9 +8596,11 @@ async function handlePublishCommand(args, options) {
8364
8596
  try {
8365
8597
  const content = await readFile3(filePath, "utf8");
8366
8598
  toolDefinition = $parse(content);
8367
- const validation = validateToolDefinition(toolDefinition);
8368
- if (validation) {
8369
- Se(import_picocolors5.default.red(`Invalid tool manifest: ${validation}`));
8599
+ const validation = enactApi.validateTool(toolDefinition);
8600
+ if (!validation.valid) {
8601
+ Se(import_picocolors5.default.red(`Invalid tool manifest:
8602
+ ${validation.errors.join(`
8603
+ `)}`));
8370
8604
  return;
8371
8605
  }
8372
8606
  } catch (error) {
@@ -8426,66 +8660,38 @@ Command: ${import_picocolors5.default.dim(toolDefinition.command)}`, "Tool Detai
8426
8660
  const spinner = Y2();
8427
8661
  spinner.start("Publishing tool to Enact registry");
8428
8662
  try {
8429
- let isUpdate = false;
8430
- try {
8431
- const checkResponse = await fetch(`${apiUrl}/${encodeURIComponent(toolDefinition.name)}`, {
8432
- method: "GET",
8433
- headers: {
8434
- "Content-Type": "application/json"
8435
- }
8436
- });
8437
- if (checkResponse.ok) {
8438
- isUpdate = true;
8439
- spinner.message("Tool exists, updating...");
8440
- }
8441
- } catch (checkError) {}
8442
- const toolData = {
8443
- ...toolDefinition,
8444
- inputSchema: toolDefinition.inputSchema,
8445
- outputSchema: toolDefinition.outputSchema,
8446
- env: toolDefinition.env,
8447
- enact: toolDefinition.enact || "1.0.0"
8448
- };
8449
- const method = isUpdate ? "PUT" : "POST";
8450
- const url = isUpdate ? `${apiUrl}/${encodeURIComponent(toolDefinition.name)}` : apiUrl;
8451
- spinner.message(`Publishing to URL: ${url} with method: ${method}`);
8452
- const publishResponse = await fetch(url, {
8453
- method,
8454
- headers: authHeaders,
8455
- body: JSON.stringify(toolData)
8456
- });
8457
- if (!publishResponse.ok) {
8458
- const errorData = await publishResponse.json().catch(() => ({ error: "Unknown error" }));
8459
- if (publishResponse.status === 409) {
8460
- throw new Error(`Tool '${toolDefinition.name}' already exists. Use a different name or update the existing tool.`);
8461
- } else if (publishResponse.status === 400) {
8462
- throw new Error(`Invalid tool definition: ${errorData.error || "Bad request"}`);
8463
- } else {
8464
- throw new Error(`Publish failed (${publishResponse.status}): ${errorData.error || publishResponse.statusText}`);
8465
- }
8466
- }
8467
- const result = await publishResponse.json();
8663
+ const apiClient = apiUrl ? createEnactApiClient(apiUrl) : enactApi;
8664
+ Me(`Using API URL: ${import_picocolors5.default.cyan(apiClient.baseUrl)}`, "API Client");
8665
+ const result = await apiClient.publishOrUpdateTool(toolDefinition, token, "cli");
8468
8666
  await addToHistory(filePath);
8469
- spinner.stop(`Tool ${isUpdate ? "updated" : "published"} successfully`);
8470
- let successMessage = `✓ ${import_picocolors5.default.bold(toolDefinition.name)} ${isUpdate ? "updated" : "published"} to Enact registry`;
8471
- if (result.tool?.id) {
8667
+ spinner.stop(`Tool ${result.isUpdate ? "updated" : "published"} successfully`);
8668
+ let successMessage = `✓ ${import_picocolors5.default.bold(toolDefinition.name)} ${result.isUpdate ? "updated" : "published"} to Enact registry`;
8669
+ if (result.result?.tool?.id) {
8472
8670
  successMessage += `
8473
- \uD83D\uDCC4 Tool ID: ${import_picocolors5.default.cyan(result.tool.id)}`;
8671
+ \uD83D\uDCC4 Tool ID: ${import_picocolors5.default.cyan(result.result.tool.id)}`;
8474
8672
  }
8475
- if (result.tool?.name) {
8673
+ if (result.result?.tool?.name) {
8476
8674
  successMessage += `
8477
- \uD83D\uDD27 Tool Name: ${import_picocolors5.default.blue(result.tool.name)}`;
8675
+ \uD83D\uDD27 Tool Name: ${import_picocolors5.default.blue(result.result.tool.name)}`;
8478
8676
  }
8479
8677
  successMessage += `
8480
8678
  \uD83D\uDD0D Discoverable via: "enact search ${toolDefinition.tags?.join(" ") || toolDefinition.name}"`;
8481
8679
  Se(import_picocolors5.default.green(successMessage));
8482
8680
  } catch (error) {
8483
8681
  spinner.stop("Failed to publish tool");
8484
- if (error instanceof Error) {
8485
- if (error.message.includes("401") || error.message.includes("403")) {
8682
+ if (error instanceof EnactApiError) {
8683
+ if (error.statusCode === 401 || error.statusCode === 403) {
8486
8684
  Me("Authentication failed. Your token may have expired.", "Error");
8487
8685
  Me('Run "enact auth login" to re-authenticate.', "Suggestion");
8488
- } else if (error.message.includes("ENOTFOUND") || error.message.includes("ECONNREFUSED")) {
8686
+ } else if (error.statusCode === 409) {
8687
+ Me(`Tool '${toolDefinition.name}' already exists and you don't have permission to update it.`, "Error");
8688
+ } else if (error.statusCode === 400) {
8689
+ Me(`Invalid tool definition: ${error.message}`, "Error");
8690
+ } else {
8691
+ Me(error.message, "Error");
8692
+ }
8693
+ } else if (error instanceof Error) {
8694
+ if (error.message.includes("ENOTFOUND") || error.message.includes("ECONNREFUSED")) {
8489
8695
  Me("Could not connect to the Enact registry. Check your internet connection and registry URL.", "Error");
8490
8696
  } else {
8491
8697
  Me(error.message, "Error");
@@ -8494,6 +8700,46 @@ Command: ${import_picocolors5.default.dim(toolDefinition.command)}`, "Tool Detai
8494
8700
  Se(import_picocolors5.default.red("Publish failed"));
8495
8701
  }
8496
8702
  }
8703
+ async function getFilePathInteractively() {
8704
+ const history = await getHistory();
8705
+ if (history.length > 0) {
8706
+ const action = await ve({
8707
+ message: "Select a tool manifest to publish:",
8708
+ options: [
8709
+ { value: "select", label: "Choose from recent files" },
8710
+ { value: "new", label: "Specify a new file" }
8711
+ ]
8712
+ });
8713
+ if (action === "select") {
8714
+ const fileOptions = history.filter((file) => existsSync3(file) && isEnactFile(file)).map((file) => ({
8715
+ value: file,
8716
+ label: file
8717
+ }));
8718
+ if (fileOptions.length > 0) {
8719
+ return await ve({
8720
+ message: "Select a tool manifest:",
8721
+ options: fileOptions
8722
+ });
8723
+ } else {
8724
+ Me("No recent Enact tool manifests found.", "History");
8725
+ return await he({
8726
+ message: "Enter the path to the tool manifest (.yaml or .yml):",
8727
+ validate: validateEnactFile
8728
+ });
8729
+ }
8730
+ } else {
8731
+ return await he({
8732
+ message: "Enter the path to the tool manifest (.yaml or .yml):",
8733
+ validate: validateEnactFile
8734
+ });
8735
+ }
8736
+ } else {
8737
+ return await he({
8738
+ message: "Enter the path to the tool manifest (.yaml or .yml):",
8739
+ validate: validateEnactFile
8740
+ });
8741
+ }
8742
+ }
8497
8743
  function isEnactFile(filePath) {
8498
8744
  const ext = extname(filePath).toLowerCase();
8499
8745
  return ext === ".yaml" || ext === ".yml";
@@ -8507,37 +8753,6 @@ function validateEnactFile(value) {
8507
8753
  return "File must be a YAML file (.yaml or .yml)";
8508
8754
  return;
8509
8755
  }
8510
- function validateToolDefinition(tool) {
8511
- if (!tool)
8512
- return "Tool definition is required";
8513
- if (!tool.name || typeof tool.name !== "string") {
8514
- return "Tool name is required and must be a string";
8515
- }
8516
- if (!tool.description || typeof tool.description !== "string") {
8517
- return "Tool description is required and must be a string";
8518
- }
8519
- if (!tool.command || typeof tool.command !== "string") {
8520
- return "Tool command is required and must be a string";
8521
- }
8522
- if (!/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_\/-]+$/.test(tool.name)) {
8523
- return "Tool name must follow hierarchical format: org/category/tool-name";
8524
- }
8525
- if (tool.timeout && typeof tool.timeout === "string") {
8526
- if (!/^\d+[smh]$/.test(tool.timeout)) {
8527
- return 'Timeout must be in Go duration format (e.g., "30s", "5m", "1h")';
8528
- }
8529
- }
8530
- if (tool.tags && !Array.isArray(tool.tags)) {
8531
- return "Tags must be an array of strings";
8532
- }
8533
- if (tool.inputSchema && typeof tool.inputSchema !== "object") {
8534
- return "inputSchema must be a valid JSON Schema object";
8535
- }
8536
- if (tool.outputSchema && typeof tool.outputSchema !== "object") {
8537
- return "outputSchema must be a valid JSON Schema object";
8538
- }
8539
- return;
8540
- }
8541
8756
 
8542
8757
  // src/commands/remote.ts
8543
8758
  var import_picocolors6 = __toESM(require_picocolors(), 1);
@@ -9195,6 +9410,401 @@ function yamlStringify(obj, indent = 0) {
9195
9410
  return result;
9196
9411
  }
9197
9412
 
9413
+ // src/commands/search.ts
9414
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
9415
+ async function handleSearchCommand(args, options) {
9416
+ if (options.help) {
9417
+ console.log(`
9418
+ Usage: enact search [query] [options]
9419
+
9420
+ Search for Enact tools in the registry.
9421
+
9422
+ Arguments:
9423
+ query Search query (keywords, tool names, descriptions)
9424
+
9425
+ Options:
9426
+ --help, -h Show this help message
9427
+ --limit <number> Maximum number of results (default: 20)
9428
+ --tags <tags> Filter by tags (comma-separated)
9429
+ --format <format> Output format: table, json, list (default: table)
9430
+ --author <author> Filter by author
9431
+
9432
+ Examples:
9433
+ enact search "text processing"
9434
+ enact search formatter --tags cli,text
9435
+ enact search --author myorg
9436
+ enact search prettier --limit 5 --format json
9437
+ `);
9438
+ return;
9439
+ }
9440
+ if (args.length === 0 && !options.author) {
9441
+ Ie(import_picocolors8.default.bgGreen(import_picocolors8.default.white(" Search Enact Tools ")));
9442
+ }
9443
+ let query = args.join(" ");
9444
+ if (!query && !options.author) {
9445
+ const queryResponse = await he({
9446
+ message: "What are you looking for?",
9447
+ placeholder: "Enter keywords, tool names, or descriptions...",
9448
+ validate: (value) => {
9449
+ if (!value.trim())
9450
+ return "Please enter a search query";
9451
+ return;
9452
+ }
9453
+ });
9454
+ if (queryResponse === null) {
9455
+ Se(import_picocolors8.default.yellow("Search cancelled"));
9456
+ return;
9457
+ }
9458
+ query = queryResponse;
9459
+ }
9460
+ let limit = options.limit;
9461
+ let tags = options.tags;
9462
+ let format = options.format || "table";
9463
+ let author = options.author;
9464
+ if (args.length === 0 && !options.author) {
9465
+ const addFilters = await ye({
9466
+ message: "Add additional filters?",
9467
+ initialValue: false
9468
+ });
9469
+ if (addFilters) {
9470
+ const tagsInput = await he({
9471
+ message: "Filter by tags (comma-separated, optional):",
9472
+ placeholder: "cli, text, formatter"
9473
+ });
9474
+ if (tagsInput) {
9475
+ tags = tagsInput.split(",").map((t) => t.trim()).filter((t) => t);
9476
+ }
9477
+ const authorInput = await he({
9478
+ message: "Filter by author (optional):",
9479
+ placeholder: "myorg, username"
9480
+ });
9481
+ if (authorInput) {
9482
+ author = authorInput;
9483
+ }
9484
+ limit = await he({
9485
+ message: "Maximum results:",
9486
+ placeholder: "20",
9487
+ initialValue: "20",
9488
+ validate: (value) => {
9489
+ const num = parseInt(value);
9490
+ if (isNaN(num) || num < 1 || num > 100) {
9491
+ return "Please enter a number between 1 and 100";
9492
+ }
9493
+ return;
9494
+ }
9495
+ }).then((val) => parseInt(val));
9496
+ format = await ve({
9497
+ message: "Output format:",
9498
+ options: [
9499
+ { value: "table", label: "Table (default)" },
9500
+ { value: "list", label: "Simple list" },
9501
+ { value: "json", label: "JSON output" }
9502
+ ]
9503
+ });
9504
+ }
9505
+ }
9506
+ const spinner = Y2();
9507
+ spinner.start("Searching tools...");
9508
+ try {
9509
+ let results;
9510
+ if (author && !query) {
9511
+ results = await enactApi.getToolsByAuthor(author, limit || 20);
9512
+ } else if (query) {
9513
+ results = await enactApi.searchTools({
9514
+ query,
9515
+ limit: limit || 20,
9516
+ tags,
9517
+ format
9518
+ });
9519
+ } else {
9520
+ results = await enactApi.getTools({
9521
+ limit: limit || 20,
9522
+ tags,
9523
+ author
9524
+ });
9525
+ }
9526
+ spinner.stop(`Found ${results.length} tool${results.length === 1 ? "" : "s"}`);
9527
+ if (results.length === 0) {
9528
+ Me("No tools found matching your criteria.", "No Results");
9529
+ Me(`Try:
9530
+ • Broader keywords
9531
+ • Removing filters
9532
+ • Different spelling`, "Suggestions");
9533
+ Se("");
9534
+ return;
9535
+ }
9536
+ if (format === "json") {
9537
+ console.log(JSON.stringify(results, null, 2));
9538
+ } else if (format === "list") {
9539
+ displayResultsList(results);
9540
+ } else {
9541
+ displayResultsTable(results);
9542
+ }
9543
+ if (args.length === 0 && !options.author && results.length > 1) {
9544
+ const viewDetail = await ye({
9545
+ message: "View details for a specific tool?",
9546
+ initialValue: false
9547
+ });
9548
+ if (viewDetail) {
9549
+ const selectedTool = await ve({
9550
+ message: "Select a tool to view details:",
9551
+ options: results.map((tool) => ({
9552
+ value: tool.name,
9553
+ label: `${tool.name} - ${tool.description.substring(0, 60)}${tool.description.length > 60 ? "..." : ""}`
9554
+ }))
9555
+ });
9556
+ if (selectedTool) {
9557
+ await showToolDetails(selectedTool);
9558
+ }
9559
+ }
9560
+ }
9561
+ if (args.length === 0 && !options.author) {
9562
+ Se(import_picocolors8.default.green("Search completed"));
9563
+ }
9564
+ } catch (error) {
9565
+ spinner.stop("Search failed");
9566
+ if (error instanceof EnactApiError) {
9567
+ Me(error.message, "Error");
9568
+ } else if (error instanceof Error) {
9569
+ if (error.message.includes("ENOTFOUND") || error.message.includes("ECONNREFUSED")) {
9570
+ Me("Could not connect to the Enact registry. Check your internet connection.", "Error");
9571
+ } else {
9572
+ Me(error.message, "Error");
9573
+ }
9574
+ }
9575
+ if (args.length === 0 && !options.author) {
9576
+ Se(import_picocolors8.default.red("Search failed"));
9577
+ } else {
9578
+ console.error(import_picocolors8.default.red(`Search failed: ${error.message}`));
9579
+ }
9580
+ }
9581
+ }
9582
+ function displayResultsTable(results) {
9583
+ console.log(`
9584
+ ` + import_picocolors8.default.bold("Search Results:"));
9585
+ console.log("═".repeat(100));
9586
+ const nameWidth = 25;
9587
+ const descWidth = 50;
9588
+ const tagsWidth = 20;
9589
+ console.log(import_picocolors8.default.bold(import_picocolors8.default.cyan("NAME".padEnd(nameWidth))) + " │ " + import_picocolors8.default.bold(import_picocolors8.default.cyan("DESCRIPTION".padEnd(descWidth))) + " │ " + import_picocolors8.default.bold(import_picocolors8.default.cyan("TAGS".padEnd(tagsWidth))));
9590
+ console.log("─".repeat(nameWidth) + "─┼─" + "─".repeat(descWidth) + "─┼─" + "─".repeat(tagsWidth));
9591
+ results.forEach((tool) => {
9592
+ const name = tool.name.length > nameWidth ? tool.name.substring(0, nameWidth - 3) + "..." : tool.name.padEnd(nameWidth);
9593
+ const desc = tool.description.length > descWidth ? tool.description.substring(0, descWidth - 3) + "..." : tool.description.padEnd(descWidth);
9594
+ const tags = (tool.tags || []).join(", ");
9595
+ const tagsDisplay = tags.length > tagsWidth ? tags.substring(0, tagsWidth - 3) + "..." : tags.padEnd(tagsWidth);
9596
+ console.log(import_picocolors8.default.green(name) + " │ " + import_picocolors8.default.dim(desc) + " │ " + import_picocolors8.default.yellow(tagsDisplay));
9597
+ });
9598
+ console.log("═".repeat(100));
9599
+ console.log(import_picocolors8.default.dim(`Total: ${results.length} tool${results.length === 1 ? "" : "s"}`));
9600
+ }
9601
+ function displayResultsList(results) {
9602
+ console.log(`
9603
+ ` + import_picocolors8.default.bold("Search Results:"));
9604
+ console.log("");
9605
+ results.forEach((tool, index) => {
9606
+ console.log(`${import_picocolors8.default.cyan(`${index + 1}.`)} ${import_picocolors8.default.bold(import_picocolors8.default.green(tool.name))}`);
9607
+ console.log(` ${import_picocolors8.default.dim(tool.description)}`);
9608
+ if (tool.tags && tool.tags.length > 0) {
9609
+ console.log(` ${import_picocolors8.default.yellow("Tags:")} ${tool.tags.join(", ")}`);
9610
+ }
9611
+ console.log("");
9612
+ });
9613
+ console.log(import_picocolors8.default.dim(`Total: ${results.length} tool${results.length === 1 ? "" : "s"}`));
9614
+ }
9615
+ async function showToolDetails(toolName) {
9616
+ const spinner = Y2();
9617
+ spinner.start(`Loading details for ${toolName}...`);
9618
+ try {
9619
+ const tool = await enactApi.getTool(toolName);
9620
+ const usage = await enactApi.getToolUsage(toolName);
9621
+ spinner.stop("Tool details loaded");
9622
+ console.log(`
9623
+ ` + import_picocolors8.default.bold(import_picocolors8.default.bgBlue(import_picocolors8.default.white(` ${tool.name} `))));
9624
+ console.log("");
9625
+ console.log(import_picocolors8.default.bold("Description:"));
9626
+ console.log(tool.description);
9627
+ console.log("");
9628
+ console.log(import_picocolors8.default.bold("Command:"));
9629
+ console.log(import_picocolors8.default.cyan(tool.command));
9630
+ console.log("");
9631
+ if (tool.tags && tool.tags.length > 0) {
9632
+ console.log(import_picocolors8.default.bold("Tags:"));
9633
+ console.log(tool.tags.map((tag) => import_picocolors8.default.yellow(tag)).join(", "));
9634
+ console.log("");
9635
+ }
9636
+ if (tool.timeout) {
9637
+ console.log(import_picocolors8.default.bold("Timeout:"));
9638
+ console.log(tool.timeout);
9639
+ console.log("");
9640
+ }
9641
+ if (tool.inputSchema) {
9642
+ console.log(import_picocolors8.default.bold("Input Schema:"));
9643
+ console.log(JSON.stringify(tool.inputSchema, null, 2));
9644
+ console.log("");
9645
+ }
9646
+ if (tool.examples && tool.examples.length > 0) {
9647
+ console.log(import_picocolors8.default.bold("Examples:"));
9648
+ tool.examples.forEach((example, index) => {
9649
+ console.log(import_picocolors8.default.cyan(`Example ${index + 1}:`));
9650
+ if (example.description) {
9651
+ console.log(` Description: ${example.description}`);
9652
+ }
9653
+ console.log(` Input: ${JSON.stringify(example.input)}`);
9654
+ if (example.output) {
9655
+ console.log(` Output: ${example.output}`);
9656
+ }
9657
+ console.log("");
9658
+ });
9659
+ }
9660
+ if (tool.env && Object.keys(tool.env).length > 0) {
9661
+ console.log(import_picocolors8.default.bold("Environment Variables:"));
9662
+ Object.entries(tool.env).forEach(([key, config]) => {
9663
+ console.log(` ${import_picocolors8.default.yellow(key)}: ${config.description}`);
9664
+ if (config.required) {
9665
+ console.log(` Required: ${import_picocolors8.default.red("Yes")}`);
9666
+ } else {
9667
+ console.log(` Required: ${import_picocolors8.default.green("No")}`);
9668
+ if (config.default) {
9669
+ console.log(` Default: ${config.default}`);
9670
+ }
9671
+ }
9672
+ });
9673
+ console.log("");
9674
+ }
9675
+ if (usage) {
9676
+ console.log(import_picocolors8.default.bold("Usage Statistics:"));
9677
+ console.log(` Views: ${usage.views || 0}`);
9678
+ console.log(` Downloads: ${usage.downloads || 0}`);
9679
+ console.log(` Executions: ${usage.executions || 0}`);
9680
+ console.log("");
9681
+ }
9682
+ } catch (error) {
9683
+ spinner.stop("Failed to load tool details");
9684
+ Me(`Failed to load details: ${error.message}`, "Error");
9685
+ }
9686
+ }
9687
+
9688
+ // src/commands/user.ts
9689
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
9690
+ async function handleUserCommand(args, options) {
9691
+ if (options.help) {
9692
+ showUserHelp();
9693
+ return;
9694
+ }
9695
+ const subcommand = args[0];
9696
+ switch (subcommand) {
9697
+ case "public-key":
9698
+ await handlePublicKeyCommand(args.slice(1), options);
9699
+ break;
9700
+ default:
9701
+ if (!subcommand) {
9702
+ console.error(import_picocolors9.default.red("Missing subcommand. Use --help for usage information."));
9703
+ } else {
9704
+ console.error(import_picocolors9.default.red(`Unknown subcommand: ${subcommand}`));
9705
+ }
9706
+ showUserHelp();
9707
+ process.exit(1);
9708
+ }
9709
+ }
9710
+ async function handlePublicKeyCommand(args, options) {
9711
+ Ie(import_picocolors9.default.bgBlue(import_picocolors9.default.white(" Get User Public Key ")));
9712
+ let token;
9713
+ try {
9714
+ if (options.token) {
9715
+ token = options.token;
9716
+ } else {
9717
+ const authHeaders = await getAuthHeaders();
9718
+ }
9719
+ } catch (error) {
9720
+ Se(import_picocolors9.default.red(`✗ Authentication required. Run "enact auth login" to authenticate.`));
9721
+ return;
9722
+ }
9723
+ let userId = args[0];
9724
+ if (!userId) {
9725
+ userId = await he({
9726
+ message: "Enter user ID:",
9727
+ placeholder: "user-123",
9728
+ validate: (value) => {
9729
+ if (!value)
9730
+ return "User ID is required";
9731
+ return;
9732
+ }
9733
+ });
9734
+ if (pD(userId)) {
9735
+ Se(import_picocolors9.default.yellow("Operation cancelled"));
9736
+ return;
9737
+ }
9738
+ }
9739
+ let apiUrl = options.server;
9740
+ if (!apiUrl) {
9741
+ const defaultUrl = await getDefaultUrl();
9742
+ console.log("defaultUrl", defaultUrl);
9743
+ if (defaultUrl) {} else {
9744
+ apiUrl = "https://api.enact.dev";
9745
+ }
9746
+ }
9747
+ const spinner = Y2();
9748
+ spinner.start("Fetching public key...");
9749
+ try {
9750
+ const apiClient = apiUrl ? createEnactApiClient(apiUrl) : enactApi;
9751
+ const data = await apiClient.getUserPublicKey(userId);
9752
+ spinner.stop("Public key retrieved successfully");
9753
+ if (options.format === "json") {
9754
+ console.log(JSON.stringify(data, null, 2));
9755
+ } else if (options.format === "raw" && data.publicKey) {
9756
+ console.log(data.publicKey);
9757
+ } else {
9758
+ Me(`User ID: ${import_picocolors9.default.cyan(userId)}
9759
+ Public Key: ${import_picocolors9.default.yellow(data.publicKey || "Not available")}
9760
+ Key Type: ${import_picocolors9.default.blue(data.keyType || "Unknown")}
9761
+ Created: ${import_picocolors9.default.gray(data.createdAt ? new Date(data.createdAt).toLocaleString() : "Unknown")}`, "Public Key Information");
9762
+ }
9763
+ Se(import_picocolors9.default.green("✓ Public key retrieved successfully"));
9764
+ } catch (error) {
9765
+ spinner.stop("Failed to fetch public key" + error);
9766
+ if (error instanceof EnactApiError) {
9767
+ if (error.statusCode === 401 || error.statusCode === 403) {
9768
+ Me("Authentication failed. Your token may have expired.", "Error");
9769
+ Me('Run "enact auth login" to re-authenticate.', "Suggestion");
9770
+ } else if (error.statusCode === 404) {
9771
+ Me(`User '${userId}' not found or no public key available.`, "Error");
9772
+ } else if (error.statusCode === 400) {
9773
+ Me(`Invalid user ID: ${error.message}`, "Error");
9774
+ } else {
9775
+ Me(error.message, "Error");
9776
+ }
9777
+ } else if (error instanceof Error) {
9778
+ if (error.message.includes("ENOTFOUND") || error.message.includes("ECONNREFUSED")) {
9779
+ Me("Could not connect to the Enact API. Check your internet connection and server URL.", "Error");
9780
+ } else {
9781
+ Me(error.message, "Error");
9782
+ }
9783
+ }
9784
+ Se(import_picocolors9.default.red("Failed to fetch public key"));
9785
+ }
9786
+ }
9787
+ function showUserHelp() {
9788
+ console.log(`
9789
+ ${import_picocolors9.default.bold("USAGE")}
9790
+ enact user <subcommand> [options]
9791
+
9792
+ ${import_picocolors9.default.bold("SUBCOMMANDS")}
9793
+ public-key [userId] Get a user's public key
9794
+
9795
+ ${import_picocolors9.default.bold("OPTIONS")}
9796
+ -h, --help Show help information
9797
+ -s, --server <url> API server URL (default: https://api.enact.dev)
9798
+ -t, --token <token> Authentication token
9799
+ -f, --format <format> Output format (json|raw|default)
9800
+
9801
+ ${import_picocolors9.default.bold("EXAMPLES")}
9802
+ enact user public-key user-123
9803
+ enact user public-key --format json
9804
+ enact user public-key user-456 --token your-token
9805
+ `);
9806
+ }
9807
+
9198
9808
  // src/index.ts
9199
9809
  var { values, positionals } = parseArgs({
9200
9810
  args: process.argv,
@@ -9225,6 +9835,21 @@ var { values, positionals } = parseArgs({
9225
9835
  minimal: {
9226
9836
  type: "boolean",
9227
9837
  short: "m"
9838
+ },
9839
+ limit: {
9840
+ type: "string",
9841
+ short: "l"
9842
+ },
9843
+ tags: {
9844
+ type: "string"
9845
+ },
9846
+ format: {
9847
+ type: "string",
9848
+ short: "f"
9849
+ },
9850
+ author: {
9851
+ type: "string",
9852
+ short: "a"
9228
9853
  }
9229
9854
  },
9230
9855
  allowPositionals: true,
@@ -9257,6 +9882,15 @@ async function main() {
9257
9882
  token: values.token
9258
9883
  });
9259
9884
  break;
9885
+ case "search":
9886
+ await handleSearchCommand(commandArgs, {
9887
+ help: values.help,
9888
+ limit: values.limit ? parseInt(values.limit) : undefined,
9889
+ tags: values.tags ? (values.tags + "").split(",").map((t) => t.trim()) : undefined,
9890
+ format: values.format,
9891
+ author: values.author
9892
+ });
9893
+ break;
9260
9894
  case "remote":
9261
9895
  await handleRemoteCommand(commandArgs, {
9262
9896
  help: values.help
@@ -9268,20 +9902,30 @@ async function main() {
9268
9902
  minimal: values.minimal
9269
9903
  });
9270
9904
  break;
9905
+ case "user":
9906
+ await handleUserCommand(commandArgs, {
9907
+ help: values.help,
9908
+ server: values.server,
9909
+ token: values.token,
9910
+ format: values.format
9911
+ });
9912
+ break;
9271
9913
  case undefined:
9272
9914
  if (values.help) {
9273
9915
  showHelp();
9274
9916
  } else {
9275
- Ie(import_picocolors8.default.bgCyan(import_picocolors8.default.black(" Enact CLI ")));
9917
+ Ie(import_picocolors10.default.bgCyan(import_picocolors10.default.black(" Enact CLI ")));
9276
9918
  const action = await ve({
9277
9919
  message: "What would you like to do?",
9278
9920
  options: [
9279
- { value: "auth", label: "Manage authentication" },
9280
- { value: "publish", label: "Publish a document" },
9281
- { value: "init", label: "Create a new tool definition" },
9282
- { value: "remote", label: "Manage remote servers" },
9283
- { value: "help", label: "Show help" },
9284
- { value: "exit", label: "Exit" }
9921
+ { value: "search", label: "\uD83D\uDD0D Search for tools" },
9922
+ { value: "publish", label: "\uD83D\uDCE4 Publish a tool" },
9923
+ { value: "init", label: "\uD83D\uDCDD Create a new tool definition" },
9924
+ { value: "auth", label: "\uD83D\uDD10 Manage authentication" },
9925
+ { value: "remote", label: "\uD83C\uDF10 Manage remote servers" },
9926
+ { value: "user", label: "\uD83D\uDC64 User operations" },
9927
+ { value: "help", label: "❓ Show help" },
9928
+ { value: "exit", label: "\uD83D\uDC4B Exit" }
9285
9929
  ]
9286
9930
  });
9287
9931
  if (action === null || action === "exit") {
@@ -9292,14 +9936,18 @@ async function main() {
9292
9936
  showHelp();
9293
9937
  return;
9294
9938
  }
9939
+ if (action === "search") {
9940
+ await handleSearchCommand([], {});
9941
+ return;
9942
+ }
9295
9943
  if (action === "auth") {
9296
9944
  const authAction = await ve({
9297
9945
  message: "Authentication:",
9298
9946
  options: [
9299
- { value: "login", label: "Login (OAuth)" },
9300
- { value: "status", label: "Check auth status" },
9301
- { value: "logout", label: "Logout" },
9302
- { value: "token", label: "Show token" }
9947
+ { value: "login", label: "\uD83D\uDD11 Login (OAuth)" },
9948
+ { value: "status", label: "\uD83D\uDCCA Check auth status" },
9949
+ { value: "logout", label: "\uD83D\uDEAA Logout" },
9950
+ { value: "token", label: "\uD83D\uDD10 Show token" }
9303
9951
  ]
9304
9952
  });
9305
9953
  if (authAction !== null) {
@@ -9309,17 +9957,19 @@ async function main() {
9309
9957
  }
9310
9958
  if (action === "publish") {
9311
9959
  await handlePublishCommand([], {});
9960
+ return;
9312
9961
  }
9313
9962
  if (action === "init") {
9314
9963
  await handleInitCommand([], {});
9964
+ return;
9315
9965
  }
9316
9966
  if (action === "remote") {
9317
9967
  const remoteAction = await ve({
9318
9968
  message: "Remote management:",
9319
9969
  options: [
9320
- { value: "add", label: "Add remote server" },
9321
- { value: "list", label: "List remote servers" },
9322
- { value: "remove", label: "Remove remote server" }
9970
+ { value: "add", label: "Add remote server" },
9971
+ { value: "list", label: "\uD83D\uDCCB List remote servers" },
9972
+ { value: "remove", label: "\uD83D\uDDD1️ Remove remote server" }
9323
9973
  ]
9324
9974
  });
9325
9975
  if (remoteAction !== null) {
@@ -9327,15 +9977,27 @@ async function main() {
9327
9977
  }
9328
9978
  return;
9329
9979
  }
9980
+ if (action === "user") {
9981
+ const userAction = await ve({
9982
+ message: "User operations:",
9983
+ options: [
9984
+ { value: "public-key", label: "\uD83D\uDD11 Get user public key" }
9985
+ ]
9986
+ });
9987
+ if (userAction !== null) {
9988
+ await handleUserCommand([userAction], {});
9989
+ }
9990
+ return;
9991
+ }
9330
9992
  }
9331
9993
  break;
9332
9994
  default:
9333
- console.error(import_picocolors8.default.red(`Unknown command: ${command}`));
9995
+ console.error(import_picocolors10.default.red(`Unknown command: ${command}`));
9334
9996
  showHelp();
9335
9997
  process.exit(1);
9336
9998
  }
9337
9999
  } catch (error) {
9338
- console.error(import_picocolors8.default.red(`Error: ${error.message}`));
10000
+ console.error(import_picocolors10.default.red(`Error: ${error.message}`));
9339
10001
  process.exit(1);
9340
10002
  }
9341
10003
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enact-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Official CLI for the Enact Protocol - package, secure, and discover AI tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",