@enactprotocol/cli 2.1.21 → 2.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { ensureGlobalSetup } from "@enactprotocol/shared";
9
9
  import { Command } from "commander";
10
10
  import { configureAuthCommand, configureCacheCommand, configureConfigCommand, configureEnvCommand, configureExecCommand, configureInfoCommand, configureInitCommand, configureInspectCommand, configureInstallCommand, configureLearnCommand, configureListCommand, configurePublishCommand, configureReportCommand, configureRunCommand, configureSearchCommand, configureSetupCommand, configureSignCommand, configureTrustCommand, configureUnyankCommand, configureVisibilityCommand, configureYankCommand, } from "./commands";
11
11
  import { error, formatError } from "./utils";
12
- export const version = "2.1.21";
12
+ export const version = "2.1.23";
13
13
  // Main CLI entry point
14
14
  async function main() {
15
15
  // Ensure global setup is complete on first run
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enactprotocol/cli",
3
- "version": "2.1.21",
3
+ "version": "2.1.23",
4
4
  "description": "Command-line interface for Enact - the npm for AI tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,10 +34,10 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@clack/prompts": "^0.11.0",
37
- "@enactprotocol/api": "2.1.21",
38
- "@enactprotocol/execution": "2.1.21",
39
- "@enactprotocol/secrets": "2.1.21",
40
- "@enactprotocol/shared": "2.1.21",
37
+ "@enactprotocol/api": "2.1.23",
38
+ "@enactprotocol/execution": "2.1.23",
39
+ "@enactprotocol/secrets": "2.1.23",
40
+ "@enactprotocol/shared": "2.1.23",
41
41
  "commander": "^12.1.0",
42
42
  "picocolors": "^1.1.1"
43
43
  },
@@ -151,45 +151,63 @@ export async function getValidToken(): Promise<string | null> {
151
151
  return null;
152
152
  }
153
153
 
154
- // Check if token is expired
154
+ // Check if token is expired using stored expiry or JWT exp claim
155
155
  const expiryStr = await getSecret(AUTH_NAMESPACE, TOKEN_EXPIRY_KEY);
156
+ let isExpiredOrExpiring = false;
157
+
156
158
  if (expiryStr) {
157
159
  const expiry = new Date(expiryStr);
158
- if (expiry.getTime() - Date.now() < 60000) {
159
- // Less than 1 minute left, try to refresh
160
- const refreshToken = await getStoredRefreshToken();
161
- if (refreshToken) {
162
- const authMethod = await getSecret(AUTH_NAMESPACE, AUTH_METHOD_KEY);
163
-
164
- if (authMethod === "supabase") {
165
- // Use Supabase refresh
166
- const result = await refreshSupabaseToken(refreshToken);
167
- if (result) {
168
- await storeTokens(
169
- result.access_token,
170
- result.refresh_token,
171
- result.expires_in,
172
- "supabase"
173
- );
174
- return result.access_token;
175
- }
176
- } else {
177
- // Use legacy API refresh
178
- try {
179
- const client = createApiClient();
180
- const result = await refreshAccessToken(client, refreshToken);
181
- await storeTokens(result.access_token, refreshToken, result.expires_in, "legacy");
182
- return result.access_token;
183
- } catch {
184
- // Refresh failed
185
- }
160
+ isExpiredOrExpiring = expiry.getTime() - Date.now() < 60000; // Less than 1 minute left
161
+ } else {
162
+ // No stored expiry - check JWT exp claim directly
163
+ try {
164
+ const [, payloadBase64] = accessToken.split(".");
165
+ if (payloadBase64) {
166
+ const payload = JSON.parse(Buffer.from(payloadBase64, "base64").toString());
167
+ if (payload.exp) {
168
+ isExpiredOrExpiring = payload.exp * 1000 - Date.now() < 60000;
186
169
  }
170
+ }
171
+ } catch {
172
+ // Can't parse JWT, assume it might be expired
173
+ isExpiredOrExpiring = true;
174
+ }
175
+ }
187
176
 
188
- // Refresh failed, need to re-authenticate
189
- await clearStoredTokens();
190
- return null;
177
+ if (isExpiredOrExpiring) {
178
+ // Try to refresh
179
+ const refreshToken = await getStoredRefreshToken();
180
+ if (refreshToken) {
181
+ const authMethod = await getSecret(AUTH_NAMESPACE, AUTH_METHOD_KEY);
182
+
183
+ if (authMethod === "supabase") {
184
+ // Use Supabase refresh
185
+ const result = await refreshSupabaseToken(refreshToken);
186
+ if (result) {
187
+ await storeTokens(
188
+ result.access_token,
189
+ result.refresh_token,
190
+ result.expires_in,
191
+ "supabase"
192
+ );
193
+ return result.access_token;
194
+ }
195
+ } else {
196
+ // Use legacy API refresh
197
+ try {
198
+ const client = createApiClient();
199
+ const result = await refreshAccessToken(client, refreshToken);
200
+ await storeTokens(result.access_token, refreshToken, result.expires_in, "legacy");
201
+ return result.access_token;
202
+ } catch {
203
+ // Refresh failed
204
+ }
191
205
  }
192
206
  }
207
+
208
+ // Refresh failed or no refresh token, need to re-authenticate
209
+ await clearStoredTokens();
210
+ return null;
193
211
  }
194
212
 
195
213
  return accessToken;
@@ -218,11 +218,16 @@ async function infoHandler(
218
218
  config.registry?.url ??
219
219
  "https://siikwkfgsmouioodghho.supabase.co/functions/v1";
220
220
 
221
- // Get auth token - try stored JWT first (for private tools), then fall back to config
221
+ // Get auth token - try stored JWT first (for private tools), then fall back to config/env/anon
222
222
  const { getValidToken } = await import("../auth/index.js");
223
223
  let authToken: string | undefined = (await getValidToken()) ?? undefined;
224
224
  if (!authToken) {
225
- authToken = config.registry?.authToken;
225
+ authToken = config.registry?.authToken ?? process.env.ENACT_AUTH_TOKEN;
226
+ }
227
+ // Fall back to anon key for unauthenticated public access
228
+ if (!authToken && registryUrl.includes("siikwkfgsmouioodghho.supabase.co")) {
229
+ authToken =
230
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNpaWt3a2Znc21vdWlvb2RnaGhvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQ2MTkzMzksImV4cCI6MjA4MDE5NTMzOX0.kxnx6-IPFhmGx6rzNx36vbyhFMFZKP_jFqaDbKnJ_E0";
226
231
  }
227
232
 
228
233
  const client = createApiClient({
@@ -49,4 +49,17 @@ Run with: \`enact run ./tools/<name> --args '{"name": "World"}'\`
49
49
  enact env set API_KEY --secret --namespace author/tool # Set secret
50
50
  enact env list # List env vars
51
51
  \`\`\`
52
+
53
+ ## Creating Your Own Tools
54
+ To scaffold a new publishable tool with SKILL.md and AGENTS.md templates:
55
+ \`\`\`bash
56
+ enact init --tool # Create tool in current directory
57
+ enact init --tool --name username/my-tool # Create with specific name
58
+ \`\`\`
59
+
60
+ ## Getting Help
61
+ \`\`\`bash
62
+ enact help # Show all commands
63
+ enact <command> --help # Help for specific command
64
+ \`\`\`
52
65
  `;
@@ -130,11 +130,16 @@ async function inspectHandler(
130
130
  return;
131
131
  }
132
132
 
133
- // Get auth token - try stored JWT first (for private tools), then fall back to config
133
+ // Get auth token - try stored JWT first (for private tools), then fall back to config/env/anon
134
134
  const { getValidToken } = await import("../auth/index.js");
135
135
  let authToken: string | undefined = (await getValidToken()) ?? undefined;
136
136
  if (!authToken) {
137
- authToken = config.registry?.authToken;
137
+ authToken = config.registry?.authToken ?? process.env.ENACT_AUTH_TOKEN;
138
+ }
139
+ // Fall back to anon key for unauthenticated public access
140
+ if (!authToken && registryUrl.includes("siikwkfgsmouioodghho.supabase.co")) {
141
+ authToken =
142
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNpaWt3a2Znc21vdWlvb2RnaGhvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQ2MTkzMzksImV4cCI6MjA4MDE5NTMzOX0.kxnx6-IPFhmGx6rzNx36vbyhFMFZKP_jFqaDbKnJ_E0";
138
143
  }
139
144
 
140
145
  const client = createApiClient({
@@ -161,11 +161,16 @@ async function installFromRegistry(
161
161
  config.registry?.url ??
162
162
  "https://siikwkfgsmouioodghho.supabase.co/functions/v1";
163
163
 
164
- // Get auth token - try stored JWT first (for private tools), then fall back to config
164
+ // Get auth token - try stored JWT first (for private tools), then fall back to config/env/anon
165
165
  const { getValidToken } = await import("../auth/index.js");
166
166
  let authToken: string | undefined = (await getValidToken()) ?? undefined;
167
167
  if (!authToken) {
168
- authToken = config.registry?.authToken;
168
+ authToken = config.registry?.authToken ?? process.env.ENACT_AUTH_TOKEN;
169
+ }
170
+ // Fall back to anon key for unauthenticated public access
171
+ if (!authToken && registryUrl.includes("siikwkfgsmouioodghho.supabase.co")) {
172
+ authToken =
173
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNpaWt3a2Znc21vdWlvb2RnaGhvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQ2MTkzMzksImV4cCI6MjA4MDE5NTMzOX0.kxnx6-IPFhmGx6rzNx36vbyhFMFZKP_jFqaDbKnJ_E0";
169
174
  }
170
175
 
171
176
  const client = createApiClient({
@@ -597,8 +597,15 @@ async function runHandler(tool: string, options: RunOptions, ctx: CommandContext
597
597
  return;
598
598
  }
599
599
 
600
- // Prepare command
601
- const command = prepareCommand(manifest.command, finalInputs);
600
+ // Prepare command - only substitute ${...} patterns that match inputSchema properties
601
+ const knownParameters = manifest.inputSchema?.properties
602
+ ? new Set(Object.keys(manifest.inputSchema.properties))
603
+ : undefined;
604
+ const command = prepareCommand(
605
+ manifest.command,
606
+ finalInputs,
607
+ knownParameters ? { knownParameters } : {}
608
+ );
602
609
 
603
610
  // Resolve environment variables (non-secrets)
604
611
  const { resolved: envResolved } = resolveToolEnv(manifest.env ?? {}, ctx.cwd);
package/src/index.ts CHANGED
@@ -34,7 +34,7 @@ import {
34
34
  } from "./commands";
35
35
  import { error, formatError } from "./utils";
36
36
 
37
- export const version = "2.1.21";
37
+ export const version = "2.1.23";
38
38
 
39
39
  // Export types for external use
40
40
  export type { GlobalOptions, CommandContext } from "./types";