enact-cli 1.0.1 → 1.0.4

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 +891 -215
  2. package/package.json +2 -2
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);
@@ -7755,55 +7758,82 @@ var $visitAsync = visit.visitAsync;
7755
7758
 
7756
7759
  // src/utils/help.ts
7757
7760
  var import_picocolors3 = __toESM(require_picocolors(), 1);
7761
+ import { readFileSync } from "fs";
7762
+ import { join } from "path";
7763
+ var __dirname = "/Users/keithgroves/projects/keithagroves/enact-project/enact-cli/src/utils";
7764
+ function getVersion() {
7765
+ try {
7766
+ const packageJsonPath = join(__dirname, "../../package.json");
7767
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
7768
+ return packageJson.version || "0.1.0";
7769
+ } catch (error) {
7770
+ return "0.1.0";
7771
+ }
7772
+ }
7758
7773
  function showHelp() {
7774
+ const version = getVersion();
7759
7775
  console.log(`
7760
- ${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")}
7776
+ ${import_picocolors3.default.bold("Enact CLI")} ${import_picocolors3.default.dim(`v${version}`)}
7777
+ ${import_picocolors3.default.dim("A CLI tool for managing and publishing Enact tools")}
7762
7778
 
7763
7779
  ${import_picocolors3.default.bold("Usage:")}
7764
7780
  ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("<command>")} [options]
7765
7781
 
7766
7782
  ${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
7783
+ ${import_picocolors3.default.green("auth")} Manage authentication (login, logout, status, token)
7784
+ ${import_picocolors3.default.green("init")} Create a new tool definition
7785
+ ${import_picocolors3.default.green("publish")} Publish a tool to the registry
7786
+ ${import_picocolors3.default.green("search")} Search for tools in the registry
7787
+ ${import_picocolors3.default.green("remote")} Manage remote servers (add, list, remove)
7788
+ ${import_picocolors3.default.green("user")} User operations (get public key)
7789
+
7790
+ ${import_picocolors3.default.bold("Global Options:")}
7791
+ ${import_picocolors3.default.yellow("--help, -h")} Show help message
7792
+ ${import_picocolors3.default.yellow("--version, -v")} Show version information
7772
7793
 
7773
7794
  ${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
7795
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.dim("# Interactive mode")}
7796
+ ${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")}
7797
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} my-tool.yaml ${import_picocolors3.default.dim("# Publish a tool")}
7798
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("auth")} login ${import_picocolors3.default.dim("# Login with OAuth")}
7799
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("init")} ${import_picocolors3.default.yellow("--minimal")} ${import_picocolors3.default.dim("# Create minimal tool template")}
7800
+
7801
+ ${import_picocolors3.default.bold("More Help:")}
7802
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("<command>")} ${import_picocolors3.default.yellow("--help")} ${import_picocolors3.default.dim("# Show command-specific help")}
7776
7803
  `);
7777
7804
  }
7778
7805
  function showVersion() {
7779
- console.log(`enact-cli v0.1.0`);
7806
+ const version = getVersion();
7807
+ console.log(`enact-cli v${version}`);
7780
7808
  }
7781
7809
  function showPublishHelp() {
7782
7810
  console.log(`
7783
7811
  ${import_picocolors3.default.bold("Usage:")} ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} [options] [file]
7784
7812
 
7785
- ${import_picocolors3.default.bold("Publishes a document to a server")}
7813
+ ${import_picocolors3.default.bold("Publish a tool to the Enact registry")}
7786
7814
 
7787
7815
  ${import_picocolors3.default.bold("Arguments:")}
7788
- ${import_picocolors3.default.green("file")} The file to publish. If not provided, will prompt for a file.
7816
+ ${import_picocolors3.default.blue("file")} The tool definition file to publish (YAML format)
7789
7817
 
7790
7818
  ${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
7819
+ ${import_picocolors3.default.yellow("--help, -h")} Show this help message
7820
+ ${import_picocolors3.default.yellow("--url")} Specify the registry URL
7821
+ ${import_picocolors3.default.yellow("--token, -t")} Specify authentication token
7793
7822
 
7794
7823
  ${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
7824
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} my-tool.yaml
7825
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} ${import_picocolors3.default.yellow("--url")} https://registry.example.com my-tool.yaml
7826
+ ${import_picocolors3.default.cyan("enact")} ${import_picocolors3.default.green("publish")} ${import_picocolors3.default.yellow("--token")} abc123 my-tool.yaml
7797
7827
  `);
7798
7828
  }
7799
7829
 
7800
7830
  // src/utils/config.ts
7801
7831
  import { homedir } from "os";
7802
- import { join } from "path";
7832
+ import { join as join2 } from "path";
7803
7833
  import { existsSync } from "fs";
7804
7834
  import { mkdir, readFile, writeFile } from "fs/promises";
7805
- var CONFIG_DIR = join(homedir(), ".enact");
7806
- var CONFIG_FILE = join(CONFIG_DIR, "config.json");
7835
+ var CONFIG_DIR = join2(homedir(), ".enact");
7836
+ var CONFIG_FILE = join2(CONFIG_DIR, "config.json");
7807
7837
  async function ensureConfig() {
7808
7838
  if (!existsSync(CONFIG_DIR)) {
7809
7839
  await mkdir(CONFIG_DIR, { recursive: true });
@@ -7855,18 +7885,304 @@ async function getDefaultUrl() {
7855
7885
  var import_picocolors4 = __toESM(require_picocolors(), 1);
7856
7886
  import { existsSync as existsSync2 } from "fs";
7857
7887
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
7858
- import { join as join2 } from "path";
7888
+ import { join as join3 } from "path";
7859
7889
  import { homedir as homedir2 } from "os";
7860
7890
  import { createServer } from "http";
7861
7891
  import { parse } from "url";
7862
7892
  import { randomBytes, createHash } from "crypto";
7863
7893
  import { exec } from "child_process";
7864
7894
  import { promisify } from "util";
7895
+
7896
+ // src/api/enact-api.ts
7897
+ class EnactApiClient {
7898
+ baseUrl;
7899
+ supabaseUrl;
7900
+ constructor(baseUrl = "https://enact.tools", supabaseUrl = "https://xjnhhxwxovjifdxdwzih.supabase.co") {
7901
+ this.baseUrl = baseUrl.replace(/\/$/, "");
7902
+ this.supabaseUrl = supabaseUrl.replace(/\/$/, "");
7903
+ }
7904
+ async makeRequest(endpoint, options = {}, token, tokenType = "jwt") {
7905
+ const url = endpoint.startsWith("http") ? endpoint : `${this.supabaseUrl}${endpoint}`;
7906
+ const headers = {
7907
+ "Content-Type": "application/json",
7908
+ ...options.headers
7909
+ };
7910
+ if (token) {
7911
+ if (tokenType === "jwt") {
7912
+ headers["Authorization"] = `Bearer ${token}`;
7913
+ } else {
7914
+ headers["X-API-Key"] = token;
7915
+ }
7916
+ }
7917
+ const response = await fetch(url, {
7918
+ ...options,
7919
+ headers
7920
+ });
7921
+ if (!response.ok) {
7922
+ const errorData = await response.json().catch(() => ({ error: "Unknown error" }));
7923
+ throw new EnactApiError(`${errorData.error || response.statusText}`, response.status, endpoint);
7924
+ }
7925
+ const responseData = await response.json();
7926
+ if (true) {
7927
+ console.log(`API Response for ${endpoint}:`, responseData);
7928
+ }
7929
+ return responseData;
7930
+ }
7931
+ async getTools(params) {
7932
+ const searchParams = new URLSearchParams;
7933
+ if (params?.limit)
7934
+ searchParams.set("limit", params.limit.toString());
7935
+ if (params?.offset)
7936
+ searchParams.set("offset", params.offset.toString());
7937
+ if (params?.tags)
7938
+ searchParams.set("tags", params.tags.join(","));
7939
+ if (params?.author)
7940
+ searchParams.set("author", params.author);
7941
+ const query = searchParams.toString();
7942
+ const endpoint = `/functions/v1/tools${query ? `?${query}` : ""}`;
7943
+ const response = await this.makeRequest(endpoint);
7944
+ if (Array.isArray(response)) {
7945
+ return response;
7946
+ } else if (response.data && Array.isArray(response.data)) {
7947
+ return response.data;
7948
+ } else if (response.tools && Array.isArray(response.tools)) {
7949
+ return response.tools;
7950
+ } else {
7951
+ console.warn("Unexpected response structure for getTools:", response);
7952
+ return [];
7953
+ }
7954
+ }
7955
+ async getTool(name) {
7956
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}`;
7957
+ return this.makeRequest(endpoint);
7958
+ }
7959
+ async getToolUsage(name) {
7960
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}/usage`;
7961
+ return this.makeRequest(endpoint);
7962
+ }
7963
+ async logToolUsage(name, usage) {
7964
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}/usage`;
7965
+ return this.makeRequest(endpoint, {
7966
+ method: "POST",
7967
+ body: JSON.stringify(usage)
7968
+ });
7969
+ }
7970
+ async searchTools(query) {
7971
+ const endpoint = "/functions/v1/tools-search";
7972
+ const response = await this.makeRequest(endpoint, {
7973
+ method: "POST",
7974
+ body: JSON.stringify(query)
7975
+ });
7976
+ if (Array.isArray(response)) {
7977
+ return response;
7978
+ } else if (response.data && Array.isArray(response.data)) {
7979
+ return response.data;
7980
+ } else if (response.results && Array.isArray(response.results)) {
7981
+ return response.results;
7982
+ } else if (response.tools && Array.isArray(response.tools)) {
7983
+ return response.tools;
7984
+ } else {
7985
+ console.warn("Unexpected response structure for searchTools:", response);
7986
+ return [];
7987
+ }
7988
+ }
7989
+ async publishTool(tool, token, tokenType = "cli") {
7990
+ const endpoint = "/functions/v1/tools";
7991
+ return this.makeRequest(endpoint, {
7992
+ method: "POST",
7993
+ body: JSON.stringify(tool)
7994
+ }, token, tokenType);
7995
+ }
7996
+ async updateTool(name, tool, token, tokenType = "cli") {
7997
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}`;
7998
+ return this.makeRequest(endpoint, {
7999
+ method: "PUT",
8000
+ body: JSON.stringify(tool)
8001
+ }, token, tokenType);
8002
+ }
8003
+ async deleteTool(name, token, tokenType = "cli") {
8004
+ const endpoint = `/functions/v1/tools/${encodeURIComponent(name)}`;
8005
+ return this.makeRequest(endpoint, {
8006
+ method: "DELETE"
8007
+ }, token, tokenType);
8008
+ }
8009
+ async createCLIToken(tokenData, jwtToken) {
8010
+ const endpoint = "/functions/v1/cli-token";
8011
+ return this.makeRequest(endpoint, {
8012
+ method: "POST",
8013
+ body: JSON.stringify(tokenData)
8014
+ }, jwtToken, "jwt");
8015
+ }
8016
+ async getCLITokens(jwtToken) {
8017
+ const endpoint = "/functions/v1/cli-token";
8018
+ return this.makeRequest(endpoint, {
8019
+ method: "GET"
8020
+ }, jwtToken, "jwt");
8021
+ }
8022
+ async deleteCLIToken(tokenId, jwtToken) {
8023
+ const endpoint = `/functions/v1/cli-token/${tokenId}`;
8024
+ return this.makeRequest(endpoint, {
8025
+ method: "DELETE"
8026
+ }, jwtToken, "jwt");
8027
+ }
8028
+ async exchangeOAuthCode(oauthData) {
8029
+ const endpoint = "/functions/v1/cli-oauth";
8030
+ return this.makeRequest(endpoint, {
8031
+ method: "POST",
8032
+ body: JSON.stringify(oauthData)
8033
+ });
8034
+ }
8035
+ async generateEmbeddings(data, token, tokenType = "cli") {
8036
+ const endpoint = "/functions/v1/generate-embeddings";
8037
+ return this.makeRequest(endpoint, {
8038
+ method: "POST",
8039
+ body: JSON.stringify(data)
8040
+ }, token, tokenType);
8041
+ }
8042
+ async toolExists(name) {
8043
+ try {
8044
+ await this.getTool(name);
8045
+ return true;
8046
+ } catch (error) {
8047
+ if (error instanceof Error && error.message.includes("404")) {
8048
+ return false;
8049
+ }
8050
+ throw error;
8051
+ }
8052
+ }
8053
+ async publishOrUpdateTool(tool, token, tokenType = "cli") {
8054
+ let exists;
8055
+ try {
8056
+ exists = await this.toolExists(tool.name);
8057
+ } catch (error) {
8058
+ exists = false;
8059
+ }
8060
+ if (exists) {
8061
+ const result = await this.updateTool(tool.name, tool, token, tokenType);
8062
+ return { isUpdate: true, result };
8063
+ } else {
8064
+ const result = await this.publishTool(tool, token, tokenType);
8065
+ return { isUpdate: false, result };
8066
+ }
8067
+ }
8068
+ async getToolsByTags(tags, limit = 20) {
8069
+ return this.searchTools({
8070
+ query: tags.join(" "),
8071
+ tags,
8072
+ limit
8073
+ });
8074
+ }
8075
+ async getToolsByAuthor(author, limit = 20) {
8076
+ return this.getTools({
8077
+ author,
8078
+ limit
8079
+ });
8080
+ }
8081
+ async getUserPublicKey(userId) {
8082
+ const url = `${this.supabaseUrl}/functions/v1/tools/user/public-key/${userId}`;
8083
+ const headers = {
8084
+ Accept: "application/json",
8085
+ "Content-Type": "application/json"
8086
+ };
8087
+ try {
8088
+ const response = await fetch(url, {
8089
+ method: "GET",
8090
+ headers
8091
+ });
8092
+ if (!response.ok) {
8093
+ const errorText = await response.text();
8094
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
8095
+ try {
8096
+ const errorJson = JSON.parse(errorText);
8097
+ if (errorJson.message) {
8098
+ errorMessage = errorJson.message;
8099
+ } else if (errorJson.error) {
8100
+ errorMessage = errorJson.error;
8101
+ }
8102
+ } catch {}
8103
+ throw new EnactApiError(errorMessage, response.status);
8104
+ }
8105
+ const data = await response.json();
8106
+ return data;
8107
+ } catch (error) {
8108
+ if (error instanceof EnactApiError) {
8109
+ throw error;
8110
+ }
8111
+ if (error instanceof Error) {
8112
+ if (error.message.includes("fetch")) {
8113
+ throw new EnactApiError("Network error: Could not connect to Enact API", 0);
8114
+ }
8115
+ throw new EnactApiError(error.message, 0);
8116
+ }
8117
+ throw new EnactApiError("Unknown error occurred", 0);
8118
+ }
8119
+ }
8120
+ generateOAuthUrl(options) {
8121
+ const params = new URLSearchParams({
8122
+ response_type: "code",
8123
+ client_id: options.clientId,
8124
+ redirect_uri: options.redirectUri,
8125
+ scope: options.scope,
8126
+ state: options.state,
8127
+ code_challenge: options.codeChallenge,
8128
+ code_challenge_method: options.codeChallengeMethod
8129
+ });
8130
+ return `${this.baseUrl}/auth/cli/oauth?${params.toString()}`;
8131
+ }
8132
+ validateTool(tool) {
8133
+ const errors2 = [];
8134
+ if (!tool.name || typeof tool.name !== "string") {
8135
+ errors2.push("Tool name is required and must be a string");
8136
+ } else if (!/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_\/-]+$/.test(tool.name)) {
8137
+ errors2.push("Tool name must follow hierarchical format: org/category/tool-name");
8138
+ }
8139
+ if (!tool.description || typeof tool.description !== "string") {
8140
+ errors2.push("Tool description is required and must be a string");
8141
+ }
8142
+ if (!tool.command || typeof tool.command !== "string") {
8143
+ errors2.push("Tool command is required and must be a string");
8144
+ }
8145
+ if (tool.timeout && typeof tool.timeout === "string") {
8146
+ if (!/^\d+[smh]$/.test(tool.timeout)) {
8147
+ errors2.push('Timeout must be in Go duration format (e.g., "30s", "5m", "1h")');
8148
+ }
8149
+ }
8150
+ if (tool.tags && !Array.isArray(tool.tags)) {
8151
+ errors2.push("Tags must be an array of strings");
8152
+ }
8153
+ if (tool.inputSchema && typeof tool.inputSchema !== "object") {
8154
+ errors2.push("inputSchema must be a valid JSON Schema object");
8155
+ }
8156
+ if (tool.outputSchema && typeof tool.outputSchema !== "object") {
8157
+ errors2.push("outputSchema must be a valid JSON Schema object");
8158
+ }
8159
+ return {
8160
+ valid: errors2.length === 0,
8161
+ errors: errors2
8162
+ };
8163
+ }
8164
+ }
8165
+ var enactApi = new EnactApiClient;
8166
+
8167
+ class EnactApiError extends Error {
8168
+ statusCode;
8169
+ endpoint;
8170
+ constructor(message, statusCode, endpoint) {
8171
+ super(message);
8172
+ this.statusCode = statusCode;
8173
+ this.endpoint = endpoint;
8174
+ this.name = "EnactApiError";
8175
+ }
8176
+ }
8177
+ function createEnactApiClient(baseUrl, supabaseUrl) {
8178
+ return new EnactApiClient(baseUrl, supabaseUrl);
8179
+ }
8180
+
8181
+ // src/commands/auth.ts
7865
8182
  var execAsync = promisify(exec);
7866
- var CONFIG_DIR2 = join2(homedir2(), ".enact");
7867
- var AUTH_FILE = join2(CONFIG_DIR2, "auth.json");
8183
+ var CONFIG_DIR2 = join3(homedir2(), ".enact");
8184
+ var AUTH_FILE = join3(CONFIG_DIR2, "auth.json");
7868
8185
  var DEFAULT_SERVER = "https://enact.tools";
7869
- var SUPABASE_PROJECT_URL = "https://xjnhhxwxovjifdxdwzih.supabase.co";
7870
8186
  async function handleAuthCommand(args, options) {
7871
8187
  if (options.help || !args[0]) {
7872
8188
  console.log(`
@@ -7930,16 +8246,14 @@ async function handleLogin(serverUrl, callbackPort) {
7930
8246
  const codeChallenge = generateCodeChallenge(codeVerifier);
7931
8247
  const state = generateState();
7932
8248
  const redirectUri = `http://localhost:${callbackPort}/callback`;
7933
- const authParams = new URLSearchParams({
7934
- response_type: "code",
7935
- client_id: "enact-cli",
7936
- redirect_uri: redirectUri,
8249
+ const authUrl = enactApi.generateOAuthUrl({
8250
+ clientId: "enact-cli",
8251
+ redirectUri,
7937
8252
  scope: "publish read write",
7938
8253
  state,
7939
- code_challenge: codeChallenge,
7940
- code_challenge_method: "S256"
8254
+ codeChallenge,
8255
+ codeChallengeMethod: "S256"
7941
8256
  });
7942
- const authUrl = `${serverUrl}/auth/cli/oauth?${authParams.toString()}`;
7943
8257
  const s = Y2();
7944
8258
  s.start("Starting OAuth flow...");
7945
8259
  let server = null;
@@ -8102,49 +8416,20 @@ async function startCallbackServerWithFallback(port, serverUrl, codeVerifier, st
8102
8416
  });
8103
8417
  }
8104
8418
  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}`);
8419
+ console.log(`Exchanging code for token...`);
8107
8420
  console.log("Exchange params:", {
8108
8421
  code: code.substring(0, 8) + "...",
8109
8422
  redirectUri,
8110
8423
  codeVerifier: codeVerifier.substring(0, 8) + "..."
8111
8424
  });
8112
- const controller = new AbortController;
8113
- const timeoutId = setTimeout(() => controller.abort(), 30000);
8114
8425
  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
8426
+ const tokenData = await enactApi.exchangeOAuthCode({
8427
+ grant_type: "authorization_code",
8428
+ code,
8429
+ redirect_uri: redirectUri,
8430
+ client_id: "enact-cli",
8431
+ code_verifier: codeVerifier
8128
8432
  });
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
8433
  const expiresAt = new Date(Date.now() + tokenData.expires_in * 1000);
8149
8434
  await writeAuthConfig({
8150
8435
  token: tokenData.access_token,
@@ -8155,8 +8440,8 @@ async function exchangeCodeForToken(code, redirectUri, codeVerifier, serverUrl)
8155
8440
  });
8156
8441
  console.log("✓ Token stored successfully");
8157
8442
  } catch (error) {
8158
- if (error instanceof Error && error.name === "AbortError") {
8159
- throw new Error("Token exchange request timed out");
8443
+ if (error instanceof EnactApiError) {
8444
+ throw new Error(`Token exchange failed: ${error.message}`);
8160
8445
  }
8161
8446
  throw error;
8162
8447
  }
@@ -8295,15 +8580,13 @@ async function handlePublishCommand(args, options) {
8295
8580
  return;
8296
8581
  }
8297
8582
  Ie(import_picocolors5.default.bgBlue(import_picocolors5.default.white(" Publish Enact Tool ")));
8298
- let authHeaders;
8583
+ let token;
8299
8584
  try {
8300
8585
  if (options.token) {
8301
- authHeaders = {
8302
- "Content-Type": "application/json",
8303
- "X-API-Key": options.token
8304
- };
8586
+ token = options.token;
8305
8587
  } else {
8306
- authHeaders = await getAuthHeaders();
8588
+ const authHeaders = await getAuthHeaders();
8589
+ token = authHeaders["X-API-Key"];
8307
8590
  }
8308
8591
  } catch (error) {
8309
8592
  Se(import_picocolors5.default.red(`✗ Authentication required. Run "enact auth login" to authenticate.`));
@@ -8311,45 +8594,8 @@ async function handlePublishCommand(args, options) {
8311
8594
  }
8312
8595
  let filePath = args[0];
8313
8596
  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) {
8597
+ filePath = await getFilePathInteractively();
8598
+ if (!filePath) {
8353
8599
  Se(import_picocolors5.default.yellow("Operation cancelled"));
8354
8600
  return;
8355
8601
  }
@@ -8364,9 +8610,11 @@ async function handlePublishCommand(args, options) {
8364
8610
  try {
8365
8611
  const content = await readFile3(filePath, "utf8");
8366
8612
  toolDefinition = $parse(content);
8367
- const validation = validateToolDefinition(toolDefinition);
8368
- if (validation) {
8369
- Se(import_picocolors5.default.red(`Invalid tool manifest: ${validation}`));
8613
+ const validation = enactApi.validateTool(toolDefinition);
8614
+ if (!validation.valid) {
8615
+ Se(import_picocolors5.default.red(`Invalid tool manifest:
8616
+ ${validation.errors.join(`
8617
+ `)}`));
8370
8618
  return;
8371
8619
  }
8372
8620
  } catch (error) {
@@ -8426,66 +8674,38 @@ Command: ${import_picocolors5.default.dim(toolDefinition.command)}`, "Tool Detai
8426
8674
  const spinner = Y2();
8427
8675
  spinner.start("Publishing tool to Enact registry");
8428
8676
  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();
8677
+ const apiClient = apiUrl ? createEnactApiClient(apiUrl) : enactApi;
8678
+ Me(`Using API URL: ${import_picocolors5.default.cyan(apiClient.baseUrl)}`, "API Client");
8679
+ const result = await apiClient.publishOrUpdateTool(toolDefinition, token, "cli");
8468
8680
  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) {
8681
+ spinner.stop(`Tool ${result.isUpdate ? "updated" : "published"} successfully`);
8682
+ let successMessage = `✓ ${import_picocolors5.default.bold(toolDefinition.name)} ${result.isUpdate ? "updated" : "published"} to Enact registry`;
8683
+ if (result.result?.tool?.id) {
8472
8684
  successMessage += `
8473
- \uD83D\uDCC4 Tool ID: ${import_picocolors5.default.cyan(result.tool.id)}`;
8685
+ \uD83D\uDCC4 Tool ID: ${import_picocolors5.default.cyan(result.result.tool.id)}`;
8474
8686
  }
8475
- if (result.tool?.name) {
8687
+ if (result.result?.tool?.name) {
8476
8688
  successMessage += `
8477
- \uD83D\uDD27 Tool Name: ${import_picocolors5.default.blue(result.tool.name)}`;
8689
+ \uD83D\uDD27 Tool Name: ${import_picocolors5.default.blue(result.result.tool.name)}`;
8478
8690
  }
8479
8691
  successMessage += `
8480
8692
  \uD83D\uDD0D Discoverable via: "enact search ${toolDefinition.tags?.join(" ") || toolDefinition.name}"`;
8481
8693
  Se(import_picocolors5.default.green(successMessage));
8482
8694
  } catch (error) {
8483
8695
  spinner.stop("Failed to publish tool");
8484
- if (error instanceof Error) {
8485
- if (error.message.includes("401") || error.message.includes("403")) {
8696
+ if (error instanceof EnactApiError) {
8697
+ if (error.statusCode === 401 || error.statusCode === 403) {
8486
8698
  Me("Authentication failed. Your token may have expired.", "Error");
8487
8699
  Me('Run "enact auth login" to re-authenticate.', "Suggestion");
8488
- } else if (error.message.includes("ENOTFOUND") || error.message.includes("ECONNREFUSED")) {
8700
+ } else if (error.statusCode === 409) {
8701
+ Me(`Tool '${toolDefinition.name}' already exists and you don't have permission to update it.`, "Error");
8702
+ } else if (error.statusCode === 400) {
8703
+ Me(`Invalid tool definition: ${error.message}`, "Error");
8704
+ } else {
8705
+ Me(error.message, "Error");
8706
+ }
8707
+ } else if (error instanceof Error) {
8708
+ if (error.message.includes("ENOTFOUND") || error.message.includes("ECONNREFUSED")) {
8489
8709
  Me("Could not connect to the Enact registry. Check your internet connection and registry URL.", "Error");
8490
8710
  } else {
8491
8711
  Me(error.message, "Error");
@@ -8494,6 +8714,46 @@ Command: ${import_picocolors5.default.dim(toolDefinition.command)}`, "Tool Detai
8494
8714
  Se(import_picocolors5.default.red("Publish failed"));
8495
8715
  }
8496
8716
  }
8717
+ async function getFilePathInteractively() {
8718
+ const history = await getHistory();
8719
+ if (history.length > 0) {
8720
+ const action = await ve({
8721
+ message: "Select a tool manifest to publish:",
8722
+ options: [
8723
+ { value: "select", label: "Choose from recent files" },
8724
+ { value: "new", label: "Specify a new file" }
8725
+ ]
8726
+ });
8727
+ if (action === "select") {
8728
+ const fileOptions = history.filter((file) => existsSync3(file) && isEnactFile(file)).map((file) => ({
8729
+ value: file,
8730
+ label: file
8731
+ }));
8732
+ if (fileOptions.length > 0) {
8733
+ return await ve({
8734
+ message: "Select a tool manifest:",
8735
+ options: fileOptions
8736
+ });
8737
+ } else {
8738
+ Me("No recent Enact tool manifests found.", "History");
8739
+ return await he({
8740
+ message: "Enter the path to the tool manifest (.yaml or .yml):",
8741
+ validate: validateEnactFile
8742
+ });
8743
+ }
8744
+ } else {
8745
+ return await he({
8746
+ message: "Enter the path to the tool manifest (.yaml or .yml):",
8747
+ validate: validateEnactFile
8748
+ });
8749
+ }
8750
+ } else {
8751
+ return await he({
8752
+ message: "Enter the path to the tool manifest (.yaml or .yml):",
8753
+ validate: validateEnactFile
8754
+ });
8755
+ }
8756
+ }
8497
8757
  function isEnactFile(filePath) {
8498
8758
  const ext = extname(filePath).toLowerCase();
8499
8759
  return ext === ".yaml" || ext === ".yml";
@@ -8507,46 +8767,15 @@ function validateEnactFile(value) {
8507
8767
  return "File must be a YAML file (.yaml or .yml)";
8508
8768
  return;
8509
8769
  }
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
8770
 
8542
8771
  // src/commands/remote.ts
8543
8772
  var import_picocolors6 = __toESM(require_picocolors(), 1);
8544
8773
  import { existsSync as existsSync4 } from "fs";
8545
8774
  import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
8546
- import { join as join3 } from "path";
8775
+ import { join as join4 } from "path";
8547
8776
  import { homedir as homedir3 } from "os";
8548
- var CONFIG_DIR3 = join3(homedir3(), ".enact");
8549
- var CONFIG_FILE2 = join3(CONFIG_DIR3, "config.json");
8777
+ var CONFIG_DIR3 = join4(homedir3(), ".enact");
8778
+ var CONFIG_FILE2 = join4(CONFIG_DIR3, "config.json");
8550
8779
  async function handleRemoteCommand(args, options) {
8551
8780
  if (options.help || !args[0]) {
8552
8781
  console.log(`
@@ -9195,6 +9424,401 @@ function yamlStringify(obj, indent = 0) {
9195
9424
  return result;
9196
9425
  }
9197
9426
 
9427
+ // src/commands/search.ts
9428
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
9429
+ async function handleSearchCommand(args, options) {
9430
+ if (options.help) {
9431
+ console.log(`
9432
+ Usage: enact search [query] [options]
9433
+
9434
+ Search for Enact tools in the registry.
9435
+
9436
+ Arguments:
9437
+ query Search query (keywords, tool names, descriptions)
9438
+
9439
+ Options:
9440
+ --help, -h Show this help message
9441
+ --limit <number> Maximum number of results (default: 20)
9442
+ --tags <tags> Filter by tags (comma-separated)
9443
+ --format <format> Output format: table, json, list (default: table)
9444
+ --author <author> Filter by author
9445
+
9446
+ Examples:
9447
+ enact search "text processing"
9448
+ enact search formatter --tags cli,text
9449
+ enact search --author myorg
9450
+ enact search prettier --limit 5 --format json
9451
+ `);
9452
+ return;
9453
+ }
9454
+ if (args.length === 0 && !options.author) {
9455
+ Ie(import_picocolors8.default.bgGreen(import_picocolors8.default.white(" Search Enact Tools ")));
9456
+ }
9457
+ let query = args.join(" ");
9458
+ if (!query && !options.author) {
9459
+ const queryResponse = await he({
9460
+ message: "What are you looking for?",
9461
+ placeholder: "Enter keywords, tool names, or descriptions...",
9462
+ validate: (value) => {
9463
+ if (!value.trim())
9464
+ return "Please enter a search query";
9465
+ return;
9466
+ }
9467
+ });
9468
+ if (queryResponse === null) {
9469
+ Se(import_picocolors8.default.yellow("Search cancelled"));
9470
+ return;
9471
+ }
9472
+ query = queryResponse;
9473
+ }
9474
+ let limit = options.limit;
9475
+ let tags = options.tags;
9476
+ let format = options.format || "table";
9477
+ let author = options.author;
9478
+ if (args.length === 0 && !options.author) {
9479
+ const addFilters = await ye({
9480
+ message: "Add additional filters?",
9481
+ initialValue: false
9482
+ });
9483
+ if (addFilters) {
9484
+ const tagsInput = await he({
9485
+ message: "Filter by tags (comma-separated, optional):",
9486
+ placeholder: "cli, text, formatter"
9487
+ });
9488
+ if (tagsInput) {
9489
+ tags = tagsInput.split(",").map((t) => t.trim()).filter((t) => t);
9490
+ }
9491
+ const authorInput = await he({
9492
+ message: "Filter by author (optional):",
9493
+ placeholder: "myorg, username"
9494
+ });
9495
+ if (authorInput) {
9496
+ author = authorInput;
9497
+ }
9498
+ limit = await he({
9499
+ message: "Maximum results:",
9500
+ placeholder: "20",
9501
+ initialValue: "20",
9502
+ validate: (value) => {
9503
+ const num = parseInt(value);
9504
+ if (isNaN(num) || num < 1 || num > 100) {
9505
+ return "Please enter a number between 1 and 100";
9506
+ }
9507
+ return;
9508
+ }
9509
+ }).then((val) => parseInt(val));
9510
+ format = await ve({
9511
+ message: "Output format:",
9512
+ options: [
9513
+ { value: "table", label: "Table (default)" },
9514
+ { value: "list", label: "Simple list" },
9515
+ { value: "json", label: "JSON output" }
9516
+ ]
9517
+ });
9518
+ }
9519
+ }
9520
+ const spinner = Y2();
9521
+ spinner.start("Searching tools...");
9522
+ try {
9523
+ let results;
9524
+ if (author && !query) {
9525
+ results = await enactApi.getToolsByAuthor(author, limit || 20);
9526
+ } else if (query) {
9527
+ results = await enactApi.searchTools({
9528
+ query,
9529
+ limit: limit || 20,
9530
+ tags,
9531
+ format
9532
+ });
9533
+ } else {
9534
+ results = await enactApi.getTools({
9535
+ limit: limit || 20,
9536
+ tags,
9537
+ author
9538
+ });
9539
+ }
9540
+ spinner.stop(`Found ${results.length} tool${results.length === 1 ? "" : "s"}`);
9541
+ if (results.length === 0) {
9542
+ Me("No tools found matching your criteria.", "No Results");
9543
+ Me(`Try:
9544
+ • Broader keywords
9545
+ • Removing filters
9546
+ • Different spelling`, "Suggestions");
9547
+ Se("");
9548
+ return;
9549
+ }
9550
+ if (format === "json") {
9551
+ console.log(JSON.stringify(results, null, 2));
9552
+ } else if (format === "list") {
9553
+ displayResultsList(results);
9554
+ } else {
9555
+ displayResultsTable(results);
9556
+ }
9557
+ if (args.length === 0 && !options.author && results.length > 1) {
9558
+ const viewDetail = await ye({
9559
+ message: "View details for a specific tool?",
9560
+ initialValue: false
9561
+ });
9562
+ if (viewDetail) {
9563
+ const selectedTool = await ve({
9564
+ message: "Select a tool to view details:",
9565
+ options: results.map((tool) => ({
9566
+ value: tool.name,
9567
+ label: `${tool.name} - ${tool.description.substring(0, 60)}${tool.description.length > 60 ? "..." : ""}`
9568
+ }))
9569
+ });
9570
+ if (selectedTool) {
9571
+ await showToolDetails(selectedTool);
9572
+ }
9573
+ }
9574
+ }
9575
+ if (args.length === 0 && !options.author) {
9576
+ Se(import_picocolors8.default.green("Search completed"));
9577
+ }
9578
+ } catch (error) {
9579
+ spinner.stop("Search failed");
9580
+ if (error instanceof EnactApiError) {
9581
+ Me(error.message, "Error");
9582
+ } else if (error instanceof Error) {
9583
+ if (error.message.includes("ENOTFOUND") || error.message.includes("ECONNREFUSED")) {
9584
+ Me("Could not connect to the Enact registry. Check your internet connection.", "Error");
9585
+ } else {
9586
+ Me(error.message, "Error");
9587
+ }
9588
+ }
9589
+ if (args.length === 0 && !options.author) {
9590
+ Se(import_picocolors8.default.red("Search failed"));
9591
+ } else {
9592
+ console.error(import_picocolors8.default.red(`Search failed: ${error.message}`));
9593
+ }
9594
+ }
9595
+ }
9596
+ function displayResultsTable(results) {
9597
+ console.log(`
9598
+ ` + import_picocolors8.default.bold("Search Results:"));
9599
+ console.log("═".repeat(100));
9600
+ const nameWidth = 25;
9601
+ const descWidth = 50;
9602
+ const tagsWidth = 20;
9603
+ 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))));
9604
+ console.log("─".repeat(nameWidth) + "─┼─" + "─".repeat(descWidth) + "─┼─" + "─".repeat(tagsWidth));
9605
+ results.forEach((tool) => {
9606
+ const name = tool.name.length > nameWidth ? tool.name.substring(0, nameWidth - 3) + "..." : tool.name.padEnd(nameWidth);
9607
+ const desc = tool.description.length > descWidth ? tool.description.substring(0, descWidth - 3) + "..." : tool.description.padEnd(descWidth);
9608
+ const tags = (tool.tags || []).join(", ");
9609
+ const tagsDisplay = tags.length > tagsWidth ? tags.substring(0, tagsWidth - 3) + "..." : tags.padEnd(tagsWidth);
9610
+ console.log(import_picocolors8.default.green(name) + " │ " + import_picocolors8.default.dim(desc) + " │ " + import_picocolors8.default.yellow(tagsDisplay));
9611
+ });
9612
+ console.log("═".repeat(100));
9613
+ console.log(import_picocolors8.default.dim(`Total: ${results.length} tool${results.length === 1 ? "" : "s"}`));
9614
+ }
9615
+ function displayResultsList(results) {
9616
+ console.log(`
9617
+ ` + import_picocolors8.default.bold("Search Results:"));
9618
+ console.log("");
9619
+ results.forEach((tool, index) => {
9620
+ console.log(`${import_picocolors8.default.cyan(`${index + 1}.`)} ${import_picocolors8.default.bold(import_picocolors8.default.green(tool.name))}`);
9621
+ console.log(` ${import_picocolors8.default.dim(tool.description)}`);
9622
+ if (tool.tags && tool.tags.length > 0) {
9623
+ console.log(` ${import_picocolors8.default.yellow("Tags:")} ${tool.tags.join(", ")}`);
9624
+ }
9625
+ console.log("");
9626
+ });
9627
+ console.log(import_picocolors8.default.dim(`Total: ${results.length} tool${results.length === 1 ? "" : "s"}`));
9628
+ }
9629
+ async function showToolDetails(toolName) {
9630
+ const spinner = Y2();
9631
+ spinner.start(`Loading details for ${toolName}...`);
9632
+ try {
9633
+ const tool = await enactApi.getTool(toolName);
9634
+ const usage = await enactApi.getToolUsage(toolName);
9635
+ spinner.stop("Tool details loaded");
9636
+ console.log(`
9637
+ ` + import_picocolors8.default.bold(import_picocolors8.default.bgBlue(import_picocolors8.default.white(` ${tool.name} `))));
9638
+ console.log("");
9639
+ console.log(import_picocolors8.default.bold("Description:"));
9640
+ console.log(tool.description);
9641
+ console.log("");
9642
+ console.log(import_picocolors8.default.bold("Command:"));
9643
+ console.log(import_picocolors8.default.cyan(tool.command));
9644
+ console.log("");
9645
+ if (tool.tags && tool.tags.length > 0) {
9646
+ console.log(import_picocolors8.default.bold("Tags:"));
9647
+ console.log(tool.tags.map((tag) => import_picocolors8.default.yellow(tag)).join(", "));
9648
+ console.log("");
9649
+ }
9650
+ if (tool.timeout) {
9651
+ console.log(import_picocolors8.default.bold("Timeout:"));
9652
+ console.log(tool.timeout);
9653
+ console.log("");
9654
+ }
9655
+ if (tool.inputSchema) {
9656
+ console.log(import_picocolors8.default.bold("Input Schema:"));
9657
+ console.log(JSON.stringify(tool.inputSchema, null, 2));
9658
+ console.log("");
9659
+ }
9660
+ if (tool.examples && tool.examples.length > 0) {
9661
+ console.log(import_picocolors8.default.bold("Examples:"));
9662
+ tool.examples.forEach((example, index) => {
9663
+ console.log(import_picocolors8.default.cyan(`Example ${index + 1}:`));
9664
+ if (example.description) {
9665
+ console.log(` Description: ${example.description}`);
9666
+ }
9667
+ console.log(` Input: ${JSON.stringify(example.input)}`);
9668
+ if (example.output) {
9669
+ console.log(` Output: ${example.output}`);
9670
+ }
9671
+ console.log("");
9672
+ });
9673
+ }
9674
+ if (tool.env && Object.keys(tool.env).length > 0) {
9675
+ console.log(import_picocolors8.default.bold("Environment Variables:"));
9676
+ Object.entries(tool.env).forEach(([key, config]) => {
9677
+ console.log(` ${import_picocolors8.default.yellow(key)}: ${config.description}`);
9678
+ if (config.required) {
9679
+ console.log(` Required: ${import_picocolors8.default.red("Yes")}`);
9680
+ } else {
9681
+ console.log(` Required: ${import_picocolors8.default.green("No")}`);
9682
+ if (config.default) {
9683
+ console.log(` Default: ${config.default}`);
9684
+ }
9685
+ }
9686
+ });
9687
+ console.log("");
9688
+ }
9689
+ if (usage) {
9690
+ console.log(import_picocolors8.default.bold("Usage Statistics:"));
9691
+ console.log(` Views: ${usage.views || 0}`);
9692
+ console.log(` Downloads: ${usage.downloads || 0}`);
9693
+ console.log(` Executions: ${usage.executions || 0}`);
9694
+ console.log("");
9695
+ }
9696
+ } catch (error) {
9697
+ spinner.stop("Failed to load tool details");
9698
+ Me(`Failed to load details: ${error.message}`, "Error");
9699
+ }
9700
+ }
9701
+
9702
+ // src/commands/user.ts
9703
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
9704
+ async function handleUserCommand(args, options) {
9705
+ if (options.help) {
9706
+ showUserHelp();
9707
+ return;
9708
+ }
9709
+ const subcommand = args[0];
9710
+ switch (subcommand) {
9711
+ case "public-key":
9712
+ await handlePublicKeyCommand(args.slice(1), options);
9713
+ break;
9714
+ default:
9715
+ if (!subcommand) {
9716
+ console.error(import_picocolors9.default.red("Missing subcommand. Use --help for usage information."));
9717
+ } else {
9718
+ console.error(import_picocolors9.default.red(`Unknown subcommand: ${subcommand}`));
9719
+ }
9720
+ showUserHelp();
9721
+ process.exit(1);
9722
+ }
9723
+ }
9724
+ async function handlePublicKeyCommand(args, options) {
9725
+ Ie(import_picocolors9.default.bgBlue(import_picocolors9.default.white(" Get User Public Key ")));
9726
+ let token;
9727
+ try {
9728
+ if (options.token) {
9729
+ token = options.token;
9730
+ } else {
9731
+ const authHeaders = await getAuthHeaders();
9732
+ }
9733
+ } catch (error) {
9734
+ Se(import_picocolors9.default.red(`✗ Authentication required. Run "enact auth login" to authenticate.`));
9735
+ return;
9736
+ }
9737
+ let userId = args[0];
9738
+ if (!userId) {
9739
+ userId = await he({
9740
+ message: "Enter user ID:",
9741
+ placeholder: "user-123",
9742
+ validate: (value) => {
9743
+ if (!value)
9744
+ return "User ID is required";
9745
+ return;
9746
+ }
9747
+ });
9748
+ if (pD(userId)) {
9749
+ Se(import_picocolors9.default.yellow("Operation cancelled"));
9750
+ return;
9751
+ }
9752
+ }
9753
+ let apiUrl = options.server;
9754
+ if (!apiUrl) {
9755
+ const defaultUrl = await getDefaultUrl();
9756
+ console.log("defaultUrl", defaultUrl);
9757
+ if (defaultUrl) {} else {
9758
+ apiUrl = "https://api.enact.dev";
9759
+ }
9760
+ }
9761
+ const spinner = Y2();
9762
+ spinner.start("Fetching public key...");
9763
+ try {
9764
+ const apiClient = apiUrl ? createEnactApiClient(apiUrl) : enactApi;
9765
+ const data = await apiClient.getUserPublicKey(userId);
9766
+ spinner.stop("Public key retrieved successfully");
9767
+ if (options.format === "json") {
9768
+ console.log(JSON.stringify(data, null, 2));
9769
+ } else if (options.format === "raw" && data.publicKey) {
9770
+ console.log(data.publicKey);
9771
+ } else {
9772
+ Me(`User ID: ${import_picocolors9.default.cyan(userId)}
9773
+ Public Key: ${import_picocolors9.default.yellow(data.publicKey || "Not available")}
9774
+ Key Type: ${import_picocolors9.default.blue(data.keyType || "Unknown")}
9775
+ Created: ${import_picocolors9.default.gray(data.createdAt ? new Date(data.createdAt).toLocaleString() : "Unknown")}`, "Public Key Information");
9776
+ }
9777
+ Se(import_picocolors9.default.green("✓ Public key retrieved successfully"));
9778
+ } catch (error) {
9779
+ spinner.stop("Failed to fetch public key" + error);
9780
+ if (error instanceof EnactApiError) {
9781
+ if (error.statusCode === 401 || error.statusCode === 403) {
9782
+ Me("Authentication failed. Your token may have expired.", "Error");
9783
+ Me('Run "enact auth login" to re-authenticate.', "Suggestion");
9784
+ } else if (error.statusCode === 404) {
9785
+ Me(`User '${userId}' not found or no public key available.`, "Error");
9786
+ } else if (error.statusCode === 400) {
9787
+ Me(`Invalid user ID: ${error.message}`, "Error");
9788
+ } else {
9789
+ Me(error.message, "Error");
9790
+ }
9791
+ } else if (error instanceof Error) {
9792
+ if (error.message.includes("ENOTFOUND") || error.message.includes("ECONNREFUSED")) {
9793
+ Me("Could not connect to the Enact API. Check your internet connection and server URL.", "Error");
9794
+ } else {
9795
+ Me(error.message, "Error");
9796
+ }
9797
+ }
9798
+ Se(import_picocolors9.default.red("Failed to fetch public key"));
9799
+ }
9800
+ }
9801
+ function showUserHelp() {
9802
+ console.log(`
9803
+ ${import_picocolors9.default.bold("USAGE")}
9804
+ enact user <subcommand> [options]
9805
+
9806
+ ${import_picocolors9.default.bold("SUBCOMMANDS")}
9807
+ public-key [userId] Get a user's public key
9808
+
9809
+ ${import_picocolors9.default.bold("OPTIONS")}
9810
+ -h, --help Show help information
9811
+ -s, --server <url> API server URL (default: https://api.enact.dev)
9812
+ -t, --token <token> Authentication token
9813
+ -f, --format <format> Output format (json|raw|default)
9814
+
9815
+ ${import_picocolors9.default.bold("EXAMPLES")}
9816
+ enact user public-key user-123
9817
+ enact user public-key --format json
9818
+ enact user public-key user-456 --token your-token
9819
+ `);
9820
+ }
9821
+
9198
9822
  // src/index.ts
9199
9823
  var { values, positionals } = parseArgs({
9200
9824
  args: process.argv,
@@ -9225,6 +9849,21 @@ var { values, positionals } = parseArgs({
9225
9849
  minimal: {
9226
9850
  type: "boolean",
9227
9851
  short: "m"
9852
+ },
9853
+ limit: {
9854
+ type: "string",
9855
+ short: "l"
9856
+ },
9857
+ tags: {
9858
+ type: "string"
9859
+ },
9860
+ format: {
9861
+ type: "string",
9862
+ short: "f"
9863
+ },
9864
+ author: {
9865
+ type: "string",
9866
+ short: "a"
9228
9867
  }
9229
9868
  },
9230
9869
  allowPositionals: true,
@@ -9257,6 +9896,15 @@ async function main() {
9257
9896
  token: values.token
9258
9897
  });
9259
9898
  break;
9899
+ case "search":
9900
+ await handleSearchCommand(commandArgs, {
9901
+ help: values.help,
9902
+ limit: values.limit ? parseInt(values.limit) : undefined,
9903
+ tags: values.tags ? (values.tags + "").split(",").map((t) => t.trim()) : undefined,
9904
+ format: values.format,
9905
+ author: values.author
9906
+ });
9907
+ break;
9260
9908
  case "remote":
9261
9909
  await handleRemoteCommand(commandArgs, {
9262
9910
  help: values.help
@@ -9268,20 +9916,30 @@ async function main() {
9268
9916
  minimal: values.minimal
9269
9917
  });
9270
9918
  break;
9919
+ case "user":
9920
+ await handleUserCommand(commandArgs, {
9921
+ help: values.help,
9922
+ server: values.server,
9923
+ token: values.token,
9924
+ format: values.format
9925
+ });
9926
+ break;
9271
9927
  case undefined:
9272
9928
  if (values.help) {
9273
9929
  showHelp();
9274
9930
  } else {
9275
- Ie(import_picocolors8.default.bgCyan(import_picocolors8.default.black(" Enact CLI ")));
9931
+ Ie(import_picocolors10.default.bgCyan(import_picocolors10.default.black(" Enact CLI ")));
9276
9932
  const action = await ve({
9277
9933
  message: "What would you like to do?",
9278
9934
  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" }
9935
+ { value: "search", label: "\uD83D\uDD0D Search for tools" },
9936
+ { value: "publish", label: "\uD83D\uDCE4 Publish a tool" },
9937
+ { value: "init", label: "\uD83D\uDCDD Create a new tool definition" },
9938
+ { value: "auth", label: "\uD83D\uDD10 Manage authentication" },
9939
+ { value: "remote", label: "\uD83C\uDF10 Manage remote servers" },
9940
+ { value: "user", label: "\uD83D\uDC64 User operations" },
9941
+ { value: "help", label: "❓ Show help" },
9942
+ { value: "exit", label: "\uD83D\uDC4B Exit" }
9285
9943
  ]
9286
9944
  });
9287
9945
  if (action === null || action === "exit") {
@@ -9292,14 +9950,18 @@ async function main() {
9292
9950
  showHelp();
9293
9951
  return;
9294
9952
  }
9953
+ if (action === "search") {
9954
+ await handleSearchCommand([], {});
9955
+ return;
9956
+ }
9295
9957
  if (action === "auth") {
9296
9958
  const authAction = await ve({
9297
9959
  message: "Authentication:",
9298
9960
  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" }
9961
+ { value: "login", label: "\uD83D\uDD11 Login (OAuth)" },
9962
+ { value: "status", label: "\uD83D\uDCCA Check auth status" },
9963
+ { value: "logout", label: "\uD83D\uDEAA Logout" },
9964
+ { value: "token", label: "\uD83D\uDD10 Show token" }
9303
9965
  ]
9304
9966
  });
9305
9967
  if (authAction !== null) {
@@ -9309,17 +9971,19 @@ async function main() {
9309
9971
  }
9310
9972
  if (action === "publish") {
9311
9973
  await handlePublishCommand([], {});
9974
+ return;
9312
9975
  }
9313
9976
  if (action === "init") {
9314
9977
  await handleInitCommand([], {});
9978
+ return;
9315
9979
  }
9316
9980
  if (action === "remote") {
9317
9981
  const remoteAction = await ve({
9318
9982
  message: "Remote management:",
9319
9983
  options: [
9320
- { value: "add", label: "Add remote server" },
9321
- { value: "list", label: "List remote servers" },
9322
- { value: "remove", label: "Remove remote server" }
9984
+ { value: "add", label: "Add remote server" },
9985
+ { value: "list", label: "\uD83D\uDCCB List remote servers" },
9986
+ { value: "remove", label: "\uD83D\uDDD1️ Remove remote server" }
9323
9987
  ]
9324
9988
  });
9325
9989
  if (remoteAction !== null) {
@@ -9327,15 +9991,27 @@ async function main() {
9327
9991
  }
9328
9992
  return;
9329
9993
  }
9994
+ if (action === "user") {
9995
+ const userAction = await ve({
9996
+ message: "User operations:",
9997
+ options: [
9998
+ { value: "public-key", label: "\uD83D\uDD11 Get user public key" }
9999
+ ]
10000
+ });
10001
+ if (userAction !== null) {
10002
+ await handleUserCommand([userAction], {});
10003
+ }
10004
+ return;
10005
+ }
9330
10006
  }
9331
10007
  break;
9332
10008
  default:
9333
- console.error(import_picocolors8.default.red(`Unknown command: ${command}`));
10009
+ console.error(import_picocolors10.default.red(`Unknown command: ${command}`));
9334
10010
  showHelp();
9335
10011
  process.exit(1);
9336
10012
  }
9337
10013
  } catch (error) {
9338
- console.error(import_picocolors8.default.red(`Error: ${error.message}`));
10014
+ console.error(import_picocolors10.default.red(`Error: ${error.message}`));
9339
10015
  process.exit(1);
9340
10016
  }
9341
10017
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enact-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
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",
@@ -61,4 +61,4 @@
61
61
  "picocolors": "^1.1.1",
62
62
  "yaml": "^2.8.0"
63
63
  }
64
- }
64
+ }