@enactprotocol/cli 2.2.4 → 2.3.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.
- package/README.md +4 -37
- package/dist/commands/cache/index.js +5 -5
- package/dist/commands/cache/index.js.map +1 -1
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +2 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/inspect/index.d.ts.map +1 -1
- package/dist/commands/inspect/index.js +3 -2
- package/dist/commands/inspect/index.js.map +1 -1
- package/dist/commands/install/index.d.ts +1 -1
- package/dist/commands/install/index.d.ts.map +1 -1
- package/dist/commands/install/index.js +69 -21
- package/dist/commands/install/index.js.map +1 -1
- package/dist/commands/learn/index.d.ts.map +1 -1
- package/dist/commands/learn/index.js +73 -14
- package/dist/commands/learn/index.js.map +1 -1
- package/dist/commands/list/index.d.ts +1 -1
- package/dist/commands/list/index.js +1 -1
- package/dist/commands/run/index.d.ts.map +1 -1
- package/dist/commands/run/index.js +166 -60
- package/dist/commands/run/index.js.map +1 -1
- package/dist/commands/serve/index.d.ts +9 -0
- package/dist/commands/serve/index.d.ts.map +1 -0
- package/dist/commands/serve/index.js +24 -0
- package/dist/commands/serve/index.js.map +1 -0
- package/dist/commands/validate/index.d.ts.map +1 -1
- package/dist/commands/validate/index.js +7 -37
- package/dist/commands/validate/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/utils/errors.js +2 -2
- package/package.json +6 -5
- package/src/commands/cache/index.ts +5 -5
- package/src/commands/env/README.md +1 -1
- package/src/commands/index.ts +3 -0
- package/src/commands/inspect/index.ts +3 -2
- package/src/commands/install/README.md +2 -2
- package/src/commands/install/index.ts +98 -21
- package/src/commands/learn/index.ts +89 -18
- package/src/commands/list/index.ts +1 -1
- package/src/commands/run/README.md +1 -1
- package/src/commands/run/index.ts +218 -67
- package/src/commands/serve/index.ts +26 -0
- package/src/commands/validate/index.ts +7 -42
- package/src/index.ts +5 -1
- package/src/utils/errors.ts +2 -2
- package/tests/commands/cache.test.ts +2 -2
- package/tests/commands/install-integration.test.ts +11 -12
- package/tests/commands/publish.test.ts +12 -2
- package/tests/commands/run.test.ts +3 -1
- package/tests/commands/serve.test.ts +82 -0
- package/tests/commands/sign.test.ts +1 -1
- package/tests/e2e.test.ts +56 -34
- package/tests/fixtures/calculator/skill.yaml +38 -0
- package/tests/fixtures/echo-tool/SKILL.md +3 -10
- package/tests/fixtures/env-tool/{enact.yaml → skill.yaml} +0 -6
- package/tests/fixtures/greeter/skill.yaml +22 -0
- package/tests/utils/ignore.test.ts +3 -1
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/tests/fixtures/calculator/enact.yaml +0 -34
- package/tests/fixtures/greeter/enact.yaml +0 -18
- /package/tests/fixtures/invalid-tool/{enact.yaml → skill.yaml} +0 -0
|
@@ -11,6 +11,38 @@
|
|
|
11
11
|
import { createApiClient, getAttestationList, getToolInfo, getToolVersion, verifyAllAttestations, } from "@enactprotocol/api";
|
|
12
12
|
import { getMinimumAttestations, getTrustPolicy, getTrustedAuditors, loadConfig, tryResolveTool, } from "@enactprotocol/shared";
|
|
13
13
|
import { TrustError, confirm, dim, error, formatError, header, info, json, newline, success, symbols, } from "../../utils";
|
|
14
|
+
/**
|
|
15
|
+
* Display available actions from an ActionsManifest
|
|
16
|
+
*/
|
|
17
|
+
function displayActions(actionsManifest) {
|
|
18
|
+
newline();
|
|
19
|
+
header("Available Actions");
|
|
20
|
+
newline();
|
|
21
|
+
// Iterate over action map (name is the key)
|
|
22
|
+
for (const [actionName, action] of Object.entries(actionsManifest.actions)) {
|
|
23
|
+
info(` ${actionName}`);
|
|
24
|
+
if (action.description) {
|
|
25
|
+
dim(` ${action.description}`);
|
|
26
|
+
}
|
|
27
|
+
// Show input parameters
|
|
28
|
+
if (action.inputSchema?.properties) {
|
|
29
|
+
const required = new Set(Array.isArray(action.inputSchema.required) ? action.inputSchema.required : []);
|
|
30
|
+
const props = action.inputSchema.properties;
|
|
31
|
+
const paramNames = Object.keys(props);
|
|
32
|
+
if (paramNames.length > 0) {
|
|
33
|
+
dim(" Parameters:");
|
|
34
|
+
for (const paramName of paramNames) {
|
|
35
|
+
const prop = props[paramName];
|
|
36
|
+
const isRequired = required.has(paramName);
|
|
37
|
+
const reqLabel = isRequired ? " (required)" : "";
|
|
38
|
+
dim(` - ${paramName}: ${prop?.type ?? "any"}${reqLabel}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
newline();
|
|
43
|
+
}
|
|
44
|
+
dim("Run an action with: enact run <skill>:<action> --args '{...}'");
|
|
45
|
+
}
|
|
14
46
|
/**
|
|
15
47
|
* Learn command handler
|
|
16
48
|
*/
|
|
@@ -29,6 +61,13 @@ async function learnHandler(toolName, options, ctx) {
|
|
|
29
61
|
version: resolution.manifest.version,
|
|
30
62
|
documentation: content,
|
|
31
63
|
source: "local",
|
|
64
|
+
actions: resolution.actionsManifest
|
|
65
|
+
? Object.entries(resolution.actionsManifest.actions).map(([name, a]) => ({
|
|
66
|
+
name,
|
|
67
|
+
description: a.description,
|
|
68
|
+
inputSchema: a.inputSchema,
|
|
69
|
+
}))
|
|
70
|
+
: null,
|
|
32
71
|
});
|
|
33
72
|
return;
|
|
34
73
|
}
|
|
@@ -36,6 +75,10 @@ async function learnHandler(toolName, options, ctx) {
|
|
|
36
75
|
dim("(installed locally)");
|
|
37
76
|
newline();
|
|
38
77
|
console.log(content);
|
|
78
|
+
// Display actions if available
|
|
79
|
+
if (resolution.actionsManifest) {
|
|
80
|
+
displayActions(resolution.actionsManifest);
|
|
81
|
+
}
|
|
39
82
|
return;
|
|
40
83
|
}
|
|
41
84
|
// Fallback for non-.md manifests
|
|
@@ -45,6 +88,13 @@ async function learnHandler(toolName, options, ctx) {
|
|
|
45
88
|
version: resolution.manifest.version,
|
|
46
89
|
documentation: resolution.manifest.doc ?? resolution.manifest.description ?? null,
|
|
47
90
|
source: "local",
|
|
91
|
+
actions: resolution.actionsManifest
|
|
92
|
+
? Object.entries(resolution.actionsManifest.actions).map(([name, a]) => ({
|
|
93
|
+
name,
|
|
94
|
+
description: a.description,
|
|
95
|
+
inputSchema: a.inputSchema,
|
|
96
|
+
}))
|
|
97
|
+
: null,
|
|
48
98
|
});
|
|
49
99
|
return;
|
|
50
100
|
}
|
|
@@ -52,6 +102,10 @@ async function learnHandler(toolName, options, ctx) {
|
|
|
52
102
|
dim("(installed locally)");
|
|
53
103
|
newline();
|
|
54
104
|
console.log(resolution.manifest.doc ?? resolution.manifest.description ?? "No documentation available.");
|
|
105
|
+
// Display actions if available
|
|
106
|
+
if (resolution.actionsManifest) {
|
|
107
|
+
displayActions(resolution.actionsManifest);
|
|
108
|
+
}
|
|
55
109
|
return;
|
|
56
110
|
}
|
|
57
111
|
// If --local flag is set, don't fetch from registry
|
|
@@ -96,23 +150,28 @@ async function learnHandler(toolName, options, ctx) {
|
|
|
96
150
|
const attestationsResponse = await getAttestationList(client, toolName, version);
|
|
97
151
|
const attestations = attestationsResponse.attestations;
|
|
98
152
|
if (attestations.length === 0) {
|
|
99
|
-
// No attestations found
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
throw new TrustError("Trust policy requires attestations. Cannot display documentation from unverified tools.");
|
|
153
|
+
// No attestations found — but if minimum_attestations is 0, that's fine
|
|
154
|
+
if (minimumAttestations === 0) {
|
|
155
|
+
// User explicitly configured zero required attestations — allow access
|
|
103
156
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
157
|
+
else {
|
|
158
|
+
info(`${symbols.warning} Tool ${toolName}@${version} has no attestations.`);
|
|
159
|
+
if (trustPolicy === "require_attestation") {
|
|
160
|
+
throw new TrustError("Trust policy requires attestations. Cannot display documentation from unverified tools.");
|
|
161
|
+
}
|
|
162
|
+
if (ctx.isInteractive && trustPolicy === "prompt") {
|
|
163
|
+
dim("Documentation from unverified tools may contain malicious content.");
|
|
164
|
+
const proceed = await confirm("View documentation from unverified tool?");
|
|
165
|
+
if (!proceed) {
|
|
166
|
+
info("Cancelled.");
|
|
167
|
+
process.exit(0);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (!ctx.isInteractive && trustPolicy === "prompt") {
|
|
171
|
+
throw new TrustError("Cannot display documentation from unverified tools in non-interactive mode.");
|
|
110
172
|
}
|
|
111
173
|
}
|
|
112
|
-
|
|
113
|
-
throw new TrustError("Cannot display documentation from unverified tools in non-interactive mode.");
|
|
114
|
-
}
|
|
115
|
-
// trustPolicy === "allow" - continue without prompting
|
|
174
|
+
// trustPolicy === "allow" or minimumAttestations === 0 - continue without prompting
|
|
116
175
|
}
|
|
117
176
|
else {
|
|
118
177
|
// Verify attestations locally (never trust registry's verification status)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/learn/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAEL,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/learn/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - Default: project tools (via .enact/tools.json)
|
|
6
6
|
* - --global/-g: global tools (via ~/.enact/tools.json)
|
|
7
7
|
*
|
|
8
|
-
* All tools are stored in ~/.
|
|
8
|
+
* All tools are stored in ~/.agent/skills/{tool}/
|
|
9
9
|
*/
|
|
10
10
|
import type { Command } from "commander";
|
|
11
11
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - Default: project tools (via .enact/tools.json)
|
|
6
6
|
* - --global/-g: global tools (via ~/.enact/tools.json)
|
|
7
7
|
*
|
|
8
|
-
* All tools are stored in ~/.
|
|
8
|
+
* All tools are stored in ~/.agent/skills/{tool}/
|
|
9
9
|
*/
|
|
10
10
|
import { getAliasesForTool, listInstalledTools, tryLoadManifestFromDir, } from "@enactprotocol/shared";
|
|
11
11
|
import { dim, error, formatError, header, info, json, newline, table, } from "../../utils";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/run/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/run/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsDH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAitCzC;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0C1D"}
|
|
@@ -13,9 +13,9 @@ import { tmpdir } from "node:os";
|
|
|
13
13
|
import { basename, dirname, join, resolve } from "node:path";
|
|
14
14
|
import * as clack from "@clack/prompts";
|
|
15
15
|
import { createApiClient, downloadBundle, getAttestationList, getToolInfo, getToolVersion, verifyAllAttestations, } from "@enactprotocol/api";
|
|
16
|
-
import { DaggerExecutionProvider } from "@enactprotocol/execution";
|
|
16
|
+
import { DaggerExecutionProvider, DockerExecutionProvider, ExecutionRouter, LocalExecutionProvider, } from "@enactprotocol/execution";
|
|
17
17
|
import { resolveSecrets, resolveToolEnv } from "@enactprotocol/secrets";
|
|
18
|
-
import { applyDefaults, getCacheDir, getMinimumAttestations, getTrustPolicy, getTrustedAuditors, loadConfig, prepareCommand, toolNameToPath, tryResolveTool, tryResolveToolDetailed, validateInputs, } from "@enactprotocol/shared";
|
|
18
|
+
import { applyDefaults, getCacheDir, getEffectiveInputSchema, getMinimumAttestations, getTrustPolicy, getTrustedAuditors, loadConfig, parseActionSpecifier, prepareActionCommand, prepareCommand, toolNameToPath, tryResolveTool, tryResolveToolDetailed, validateInputs, } from "@enactprotocol/shared";
|
|
19
19
|
import { EXIT_EXECUTION_ERROR, ManifestError, ToolNotFoundError, TrustError, ValidationError, colors, confirm, dim, error, formatError, handleError, info, json, keyValue, newline, success, symbols, withSpinner, } from "../../utils";
|
|
20
20
|
/**
|
|
21
21
|
* Parse input arguments from various formats
|
|
@@ -201,18 +201,18 @@ function atomicReplace(targetDir, sourceDir) {
|
|
|
201
201
|
/**
|
|
202
202
|
* Extract a bundle to the cache directory
|
|
203
203
|
*/
|
|
204
|
-
async function extractToCache(bundleData, toolName,
|
|
204
|
+
async function extractToCache(bundleData, toolName, _version) {
|
|
205
205
|
const cacheDir = getCacheDir();
|
|
206
206
|
const toolPath = toolNameToPath(toolName);
|
|
207
|
-
const
|
|
207
|
+
const destDir = join(cacheDir, toolPath);
|
|
208
208
|
// Create a temporary file for the bundle
|
|
209
209
|
const tempFile = join(cacheDir, `bundle-${Date.now()}.tar.gz`);
|
|
210
210
|
mkdirSync(dirname(tempFile), { recursive: true });
|
|
211
211
|
writeFileSync(tempFile, Buffer.from(bundleData));
|
|
212
|
-
// Create destination directory
|
|
213
|
-
mkdirSync(
|
|
212
|
+
// Create destination directory (flat, no version subdirectory — matches resolver)
|
|
213
|
+
mkdirSync(destDir, { recursive: true });
|
|
214
214
|
// Extract using tar command
|
|
215
|
-
const proc = Bun.spawn(["tar", "-xzf", tempFile, "-C",
|
|
215
|
+
const proc = Bun.spawn(["tar", "-xzf", tempFile, "-C", destDir], {
|
|
216
216
|
stdout: "pipe",
|
|
217
217
|
stderr: "pipe",
|
|
218
218
|
});
|
|
@@ -228,7 +228,7 @@ async function extractToCache(bundleData, toolName, version) {
|
|
|
228
228
|
const stderr = await new Response(proc.stderr).text();
|
|
229
229
|
throw new Error(`Failed to extract bundle: ${stderr}`);
|
|
230
230
|
}
|
|
231
|
-
return
|
|
231
|
+
return destDir;
|
|
232
232
|
}
|
|
233
233
|
/**
|
|
234
234
|
* Parse tool@version syntax
|
|
@@ -304,22 +304,27 @@ async function fetchAndCacheTool(toolSpec, options, ctx) {
|
|
|
304
304
|
const attestationsResponse = await getAttestationList(client, toolName, targetVersion);
|
|
305
305
|
const attestations = attestationsResponse.attestations;
|
|
306
306
|
if (attestations.length === 0) {
|
|
307
|
-
// No attestations found
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
throw new TrustError("Trust policy requires attestations. Execution blocked.");
|
|
307
|
+
// No attestations found — but if minimum_attestations is 0, that's fine
|
|
308
|
+
if (minimumAttestations === 0) {
|
|
309
|
+
// User explicitly configured zero required attestations — allow execution
|
|
311
310
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (
|
|
315
|
-
|
|
316
|
-
|
|
311
|
+
else {
|
|
312
|
+
info(`${symbols.warning} Tool ${toolName}@${targetVersion} has no attestations.`);
|
|
313
|
+
if (trustPolicy === "require_attestation") {
|
|
314
|
+
throw new TrustError("Trust policy requires attestations. Execution blocked.");
|
|
315
|
+
}
|
|
316
|
+
if (ctx.isInteractive && trustPolicy === "prompt") {
|
|
317
|
+
const proceed = await confirm("Run unverified tool?");
|
|
318
|
+
if (!proceed) {
|
|
319
|
+
info("Execution cancelled.");
|
|
320
|
+
process.exit(0);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else if (!ctx.isInteractive && trustPolicy === "prompt") {
|
|
324
|
+
throw new TrustError("Cannot run unverified tools in non-interactive mode.");
|
|
317
325
|
}
|
|
318
326
|
}
|
|
319
|
-
|
|
320
|
-
throw new TrustError("Cannot run unverified tools in non-interactive mode.");
|
|
321
|
-
}
|
|
322
|
-
// trustPolicy === "allow" - continue without prompting
|
|
327
|
+
// trustPolicy === "allow" or minimumAttestations === 0 - continue without prompting
|
|
323
328
|
}
|
|
324
329
|
else {
|
|
325
330
|
// Verify attestations locally (never trust registry's verification status)
|
|
@@ -460,23 +465,11 @@ function displayDryRun(manifest, inputs, command, env, inputPaths, outputPath, a
|
|
|
460
465
|
/**
|
|
461
466
|
* Display debug information about parameter resolution
|
|
462
467
|
*/
|
|
463
|
-
function displayDebugInfo(
|
|
468
|
+
function displayDebugInfo(rawInputs, inputsWithDefaults, finalInputs, env, command) {
|
|
464
469
|
newline();
|
|
465
470
|
info(colors.bold("Debug: Parameter Resolution"));
|
|
466
471
|
newline();
|
|
467
|
-
//
|
|
468
|
-
if (manifest.inputSchema?.properties) {
|
|
469
|
-
info("Schema Properties:");
|
|
470
|
-
const required = new Set(manifest.inputSchema.required || []);
|
|
471
|
-
for (const [name, prop] of Object.entries(manifest.inputSchema.properties)) {
|
|
472
|
-
const propSchema = prop;
|
|
473
|
-
const isRequired = required.has(name);
|
|
474
|
-
const hasDefault = propSchema.default !== undefined;
|
|
475
|
-
const status = isRequired ? colors.error("required") : colors.dim("optional");
|
|
476
|
-
dim(` ${name}: ${propSchema.type || "any"} [${status}]${hasDefault ? ` (default: ${JSON.stringify(propSchema.default)})` : ""}`);
|
|
477
|
-
}
|
|
478
|
-
newline();
|
|
479
|
-
}
|
|
472
|
+
// Schema information is available per-script via action.inputSchema
|
|
480
473
|
// Show raw inputs (what was provided)
|
|
481
474
|
info("Raw Inputs (provided by user):");
|
|
482
475
|
if (Object.keys(rawInputs).length === 0) {
|
|
@@ -572,36 +565,91 @@ function displayResult(result, options) {
|
|
|
572
565
|
}
|
|
573
566
|
}
|
|
574
567
|
}
|
|
568
|
+
/**
|
|
569
|
+
* Check if a specifier looks like a file path
|
|
570
|
+
*/
|
|
571
|
+
function isFilePathSpecifier(specifier) {
|
|
572
|
+
return (specifier.startsWith("/") ||
|
|
573
|
+
specifier.startsWith("./") ||
|
|
574
|
+
specifier.startsWith("../") ||
|
|
575
|
+
specifier === ".");
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* For file paths like /tmp/skill/action, try to parse the last segment as an action
|
|
579
|
+
* Returns { parentPath, actionName } or null if not applicable
|
|
580
|
+
*/
|
|
581
|
+
function tryParseFilePathAction(specifier) {
|
|
582
|
+
if (!isFilePathSpecifier(specifier)) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
const normalized = specifier.replace(/\\/g, "/");
|
|
586
|
+
const lastSlash = normalized.lastIndexOf("/");
|
|
587
|
+
// Need at least one slash and something after it
|
|
588
|
+
if (lastSlash <= 0 || lastSlash === normalized.length - 1) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
const parentPath = normalized.slice(0, lastSlash);
|
|
592
|
+
const actionName = normalized.slice(lastSlash + 1);
|
|
593
|
+
// Action name shouldn't look like a file extension
|
|
594
|
+
if (actionName.includes(".")) {
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
return { parentPath, actionName };
|
|
598
|
+
}
|
|
575
599
|
/**
|
|
576
600
|
* Run command handler
|
|
577
601
|
*/
|
|
578
602
|
async function runHandler(tool, options, ctx) {
|
|
579
603
|
let resolution = null;
|
|
580
604
|
let resolveResult = null;
|
|
605
|
+
// Parse the tool specifier to check for action (owner/skill/action format)
|
|
606
|
+
const { skillName, actionName: parsedActionName } = parseActionSpecifier(tool);
|
|
607
|
+
// Use --action flag if provided, otherwise use parsed action name
|
|
608
|
+
let actionName = options.action ?? parsedActionName;
|
|
609
|
+
let hasActionSpecifier = actionName !== undefined;
|
|
581
610
|
// Check if --remote flag is valid (requires namespace/name format)
|
|
582
|
-
const isRegistryFormat =
|
|
611
|
+
const isRegistryFormat = skillName.includes("/") && !skillName.startsWith("/") && !skillName.startsWith(".");
|
|
583
612
|
if (options.remote && !isRegistryFormat) {
|
|
584
613
|
throw new ValidationError(`--remote requires a registry tool name (e.g., user/tool), got: ${tool}`);
|
|
585
614
|
}
|
|
586
615
|
// Skip local resolution if --remote is set
|
|
587
616
|
if (!options.remote) {
|
|
588
617
|
// First, try to resolve locally (project → user → cache)
|
|
618
|
+
// Use the skill name (without action) for initial resolution
|
|
589
619
|
if (!options.verbose) {
|
|
590
|
-
resolveResult = tryResolveToolDetailed(
|
|
620
|
+
resolveResult = tryResolveToolDetailed(skillName, { startDir: ctx.cwd });
|
|
591
621
|
resolution = resolveResult.resolution;
|
|
592
622
|
}
|
|
593
623
|
else {
|
|
594
624
|
const spinner = clack.spinner();
|
|
595
|
-
spinner.start(`Resolving tool: ${
|
|
596
|
-
resolveResult = tryResolveToolDetailed(
|
|
625
|
+
spinner.start(`Resolving tool: ${skillName}${hasActionSpecifier ? ` (action: ${actionName})` : ""}`);
|
|
626
|
+
resolveResult = tryResolveToolDetailed(skillName, { startDir: ctx.cwd });
|
|
597
627
|
resolution = resolveResult.resolution;
|
|
598
628
|
if (resolution) {
|
|
599
|
-
spinner.stop(`${symbols.success} Resolved: ${
|
|
629
|
+
spinner.stop(`${symbols.success} Resolved: ${skillName}`);
|
|
600
630
|
}
|
|
601
631
|
else {
|
|
602
632
|
spinner.stop(`${symbols.info} Checking registry...`);
|
|
603
633
|
}
|
|
604
634
|
}
|
|
635
|
+
// If resolution failed and this is a file path, try treating the last segment as an action
|
|
636
|
+
// e.g., /tmp/skill/hello -> try /tmp/skill with action "hello"
|
|
637
|
+
if (!resolution && isFilePathSpecifier(tool) && !options.action) {
|
|
638
|
+
const parsed = tryParseFilePathAction(tool);
|
|
639
|
+
if (parsed) {
|
|
640
|
+
const parentResult = tryResolveToolDetailed(parsed.parentPath, { startDir: ctx.cwd });
|
|
641
|
+
if (parentResult.resolution?.actionsManifest) {
|
|
642
|
+
// Found a skill with actions at the parent path
|
|
643
|
+
resolution = parentResult.resolution;
|
|
644
|
+
resolveResult = parentResult;
|
|
645
|
+
actionName = parsed.actionName;
|
|
646
|
+
hasActionSpecifier = true;
|
|
647
|
+
if (options.verbose) {
|
|
648
|
+
info(`Detected action from path: ${parsed.actionName}`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
605
653
|
// If manifest was found but had errors, throw a descriptive error immediately
|
|
606
654
|
if (!resolution && resolveResult?.manifestFound && resolveResult?.error) {
|
|
607
655
|
const errorMessage = resolveResult.error.message;
|
|
@@ -614,25 +662,45 @@ async function runHandler(tool, options, ctx) {
|
|
|
614
662
|
// Check if this looks like a tool name (namespace/name format)
|
|
615
663
|
if (isRegistryFormat) {
|
|
616
664
|
resolution = !options.verbose
|
|
617
|
-
? await fetchAndCacheTool(
|
|
618
|
-
: await withSpinner(`Fetching ${
|
|
665
|
+
? await fetchAndCacheTool(skillName, options, ctx)
|
|
666
|
+
: await withSpinner(`Fetching ${skillName} from registry...`, async () => fetchAndCacheTool(skillName, options, ctx), `${symbols.success} Cached: ${skillName}`);
|
|
619
667
|
}
|
|
620
668
|
}
|
|
621
669
|
if (!resolution) {
|
|
622
670
|
if (options.local) {
|
|
623
|
-
throw new ToolNotFoundError(
|
|
671
|
+
throw new ToolNotFoundError(skillName, {
|
|
624
672
|
localOnly: true,
|
|
625
673
|
...(resolveResult?.searchedLocations && {
|
|
626
674
|
searchedLocations: resolveResult.searchedLocations,
|
|
627
675
|
}),
|
|
628
676
|
});
|
|
629
677
|
}
|
|
630
|
-
throw new ToolNotFoundError(
|
|
678
|
+
throw new ToolNotFoundError(skillName, {
|
|
631
679
|
...(resolveResult?.searchedLocations && {
|
|
632
680
|
searchedLocations: resolveResult.searchedLocations,
|
|
633
681
|
}),
|
|
634
682
|
});
|
|
635
683
|
}
|
|
684
|
+
// If a script was specified via colon syntax, resolve it from the manifest's scripts
|
|
685
|
+
let resolvedAction;
|
|
686
|
+
let actionsManifest;
|
|
687
|
+
// Use resolved skill identifier for error messages (manifest name or source directory)
|
|
688
|
+
const resolvedSkillId = resolution.manifest.name ?? resolution.sourceDir;
|
|
689
|
+
if (hasActionSpecifier) {
|
|
690
|
+
if (!resolution.actionsManifest) {
|
|
691
|
+
throw new ValidationError(`Skill "${resolvedSkillId}" does not define any scripts. Cannot execute script "${actionName}".`);
|
|
692
|
+
}
|
|
693
|
+
actionsManifest = resolution.actionsManifest;
|
|
694
|
+
// Map lookup - action name is the key
|
|
695
|
+
resolvedAction = actionsManifest.actions[actionName];
|
|
696
|
+
if (!resolvedAction) {
|
|
697
|
+
const availableActions = Object.keys(actionsManifest.actions).join(", ");
|
|
698
|
+
throw new ValidationError(`Action "${actionName}" not found in skill "${resolvedSkillId}". Available actions: ${availableActions}`);
|
|
699
|
+
}
|
|
700
|
+
if (options.verbose) {
|
|
701
|
+
info(`Using action: ${actionName}`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
636
704
|
const manifest = resolution.manifest;
|
|
637
705
|
// Parse --input flags to separate key=value params from path inputs
|
|
638
706
|
const { params: pathParams, inputPaths } = parseInputPaths(options.input);
|
|
@@ -640,12 +708,14 @@ async function runHandler(tool, options, ctx) {
|
|
|
640
708
|
const otherInputs = parseInputArgs(options.args, options.inputFile, undefined);
|
|
641
709
|
// Merge inputs: path params override other inputs
|
|
642
710
|
const inputs = { ...otherInputs, ...pathParams };
|
|
711
|
+
// Use action's inputSchema if executing an action, otherwise no schema
|
|
712
|
+
const effectiveInputSchema = resolvedAction ? getEffectiveInputSchema(resolvedAction) : undefined;
|
|
643
713
|
// Apply defaults from schema
|
|
644
|
-
const inputsWithDefaults =
|
|
645
|
-
? applyDefaults(inputs,
|
|
714
|
+
const inputsWithDefaults = effectiveInputSchema
|
|
715
|
+
? applyDefaults(inputs, effectiveInputSchema)
|
|
646
716
|
: inputs;
|
|
647
717
|
// Validate inputs against schema
|
|
648
|
-
const validation = validateInputs(inputsWithDefaults,
|
|
718
|
+
const validation = validateInputs(inputsWithDefaults, effectiveInputSchema);
|
|
649
719
|
if (!validation.valid) {
|
|
650
720
|
const errors = validation.errors.map((err) => `${err.path}: ${err.message}`).join(", ");
|
|
651
721
|
throw new ValidationError(`Input validation failed: ${errors}`);
|
|
@@ -682,8 +752,8 @@ async function runHandler(tool, options, ctx) {
|
|
|
682
752
|
}
|
|
683
753
|
}
|
|
684
754
|
}
|
|
685
|
-
// Check if this is an instruction-based tool (no command)
|
|
686
|
-
if (!manifest.command) {
|
|
755
|
+
// Check if this is an instruction-based tool (no command) - but actions always have commands
|
|
756
|
+
if (!manifest.command && !resolvedAction) {
|
|
687
757
|
// For instruction tools, just display the markdown body
|
|
688
758
|
let instructions;
|
|
689
759
|
// Try to get body from markdown file
|
|
@@ -722,11 +792,26 @@ async function runHandler(tool, options, ctx) {
|
|
|
722
792
|
}
|
|
723
793
|
return;
|
|
724
794
|
}
|
|
725
|
-
// Prepare command
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
795
|
+
// Prepare command
|
|
796
|
+
// For actions: use {{param}} template system (array form, no shell)
|
|
797
|
+
// For regular tools: use ${param} template system (shell interpolation)
|
|
798
|
+
let command;
|
|
799
|
+
if (resolvedAction) {
|
|
800
|
+
// Action execution: use prepareActionCommand for {{param}} templates
|
|
801
|
+
const actionCommand = resolvedAction.command;
|
|
802
|
+
if (typeof actionCommand === "string") {
|
|
803
|
+
// String form (no templates allowed by validation)
|
|
804
|
+
command = actionCommand.split(/\s+/).filter((s) => s.length > 0);
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
// Array form with {{param}} templates
|
|
808
|
+
command = prepareActionCommand(actionCommand, finalInputs, resolvedAction.inputSchema);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
else {
|
|
812
|
+
// Regular tool execution: use prepareCommand for ${param} templates
|
|
813
|
+
command = prepareCommand(manifest.command, finalInputs);
|
|
814
|
+
}
|
|
730
815
|
// Resolve environment variables (non-secrets)
|
|
731
816
|
const { resolved: envResolved } = resolveToolEnv(manifest.env ?? {}, ctx.cwd);
|
|
732
817
|
const envVars = {};
|
|
@@ -780,14 +865,14 @@ async function runHandler(tool, options, ctx) {
|
|
|
780
865
|
}
|
|
781
866
|
// Debug mode - show detailed parameter resolution info
|
|
782
867
|
if (options.debug) {
|
|
783
|
-
displayDebugInfo(
|
|
868
|
+
displayDebugInfo(inputs, inputsWithDefaults, finalInputs, envVars, command);
|
|
784
869
|
}
|
|
785
870
|
// Dry run mode
|
|
786
871
|
if (options.dryRun) {
|
|
787
872
|
displayDryRun(manifest, finalInputs, command, envVars, inputPaths, options.output, options.apply);
|
|
788
873
|
return;
|
|
789
874
|
}
|
|
790
|
-
// Execute the tool
|
|
875
|
+
// Execute the tool — select backend via execution router
|
|
791
876
|
const providerConfig = {};
|
|
792
877
|
if (options.timeout) {
|
|
793
878
|
providerConfig.defaultTimeout = parseTimeout(options.timeout);
|
|
@@ -795,7 +880,19 @@ async function runHandler(tool, options, ctx) {
|
|
|
795
880
|
if (options.verbose) {
|
|
796
881
|
providerConfig.verbose = true;
|
|
797
882
|
}
|
|
798
|
-
const
|
|
883
|
+
const config = loadConfig();
|
|
884
|
+
const router = new ExecutionRouter({
|
|
885
|
+
default: config.execution?.default,
|
|
886
|
+
fallback: config.execution?.fallback,
|
|
887
|
+
trusted_scopes: config.execution?.trusted_scopes,
|
|
888
|
+
});
|
|
889
|
+
router.registerProvider("local", new LocalExecutionProvider(providerConfig));
|
|
890
|
+
router.registerProvider("docker", new DockerExecutionProvider(providerConfig));
|
|
891
|
+
router.registerProvider("dagger", new DaggerExecutionProvider(providerConfig));
|
|
892
|
+
const provider = await router.selectProvider(manifest.name, {
|
|
893
|
+
forceLocal: options.local,
|
|
894
|
+
forceRemote: options.remote,
|
|
895
|
+
});
|
|
799
896
|
// For --apply, we export to a temp directory first, then atomically replace
|
|
800
897
|
let tempOutputDir;
|
|
801
898
|
if (options.apply && options.output) {
|
|
@@ -815,6 +912,13 @@ async function runHandler(tool, options, ctx) {
|
|
|
815
912
|
else if (options.output) {
|
|
816
913
|
execOptions.outputPath = resolve(options.output);
|
|
817
914
|
}
|
|
915
|
+
// Use executeAction for actions, execute for regular tools
|
|
916
|
+
if (resolvedAction && actionsManifest && actionName) {
|
|
917
|
+
return provider.executeAction(manifest, actionsManifest, actionName, resolvedAction, {
|
|
918
|
+
params: finalInputs,
|
|
919
|
+
envOverrides: envVars,
|
|
920
|
+
}, execOptions);
|
|
921
|
+
}
|
|
818
922
|
return provider.execute(manifest, {
|
|
819
923
|
params: finalInputs,
|
|
820
924
|
envOverrides: envVars,
|
|
@@ -822,7 +926,8 @@ async function runHandler(tool, options, ctx) {
|
|
|
822
926
|
};
|
|
823
927
|
// Build a descriptive message - container may need to be pulled
|
|
824
928
|
const containerImage = manifest.from ?? "node:18-alpine";
|
|
825
|
-
const
|
|
929
|
+
const toolDisplayName = resolvedAction && actionName ? `${manifest.name}:${actionName}` : manifest.name;
|
|
930
|
+
const spinnerMessage = `Running ${toolDisplayName} (${containerImage})...`;
|
|
826
931
|
const result = !options.verbose
|
|
827
932
|
? await executeTask()
|
|
828
933
|
: await withSpinner(spinnerMessage, executeTask, `${symbols.success} Execution complete`);
|
|
@@ -886,8 +991,8 @@ function parseTimeout(timeout) {
|
|
|
886
991
|
export function configureRunCommand(program) {
|
|
887
992
|
program
|
|
888
993
|
.command("run")
|
|
889
|
-
.description("Execute a tool with its manifest-defined command")
|
|
890
|
-
.argument("<tool>", "Tool to run (name, path, or '.' for current directory)")
|
|
994
|
+
.description("Execute a tool or action with its manifest-defined command")
|
|
995
|
+
.argument("<tool>", "Tool or action to run (name, owner/skill/action, path, or '.' for current directory)")
|
|
891
996
|
.option("-a, --args <json>", "Input arguments as JSON string (recommended)")
|
|
892
997
|
.option("-f, --input-file <path>", "Load input arguments from JSON file")
|
|
893
998
|
.option("-i, --input <value...>", "Input: key=value params, ./path for data, or name=./path for named inputs")
|
|
@@ -899,6 +1004,7 @@ export function configureRunCommand(program) {
|
|
|
899
1004
|
.option("-r, --remote", "Skip local resolution and fetch from registry")
|
|
900
1005
|
.option("--dry-run", "Show what would be executed without running")
|
|
901
1006
|
.option("--debug", "Show detailed parameter and environment variable resolution")
|
|
1007
|
+
.option("--action <name>", "Script to execute (alternative to colon syntax: tool:script)")
|
|
902
1008
|
.option("-v, --verbose", "Show progress spinners and detailed output")
|
|
903
1009
|
.option("--json", "Output result as JSON")
|
|
904
1010
|
.action(async (tool, options) => {
|