@piewf/cli 5.8.0 → 5.10.0
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/src/cli.d.ts +10 -0
- package/dist/src/cli.js +104 -10
- package/package.json +2 -2
package/dist/src/cli.d.ts
CHANGED
|
@@ -15,6 +15,15 @@ export interface CliOptions extends DoctorOptions {
|
|
|
15
15
|
}
|
|
16
16
|
export declare function formatWorkflowCliHelp(fn: WorkflowCatalogFunction, command?: string): string;
|
|
17
17
|
export declare function parseWorkflowCliArgs(schema: JsonSchema, rawArgs: readonly string[]): Record<string, JsonValue>;
|
|
18
|
+
type ScriptWorkflowCliArgs = {
|
|
19
|
+
help: true;
|
|
20
|
+
} | {
|
|
21
|
+
help: false;
|
|
22
|
+
scriptPath: string;
|
|
23
|
+
name: string;
|
|
24
|
+
args: JsonValue;
|
|
25
|
+
};
|
|
26
|
+
export declare function parseScriptWorkflowCliArgs(rawArgs: readonly string[]): ScriptWorkflowCliArgs;
|
|
18
27
|
export declare function parseDoctorArgs(rawArgs: readonly string[]): {
|
|
19
28
|
role?: string;
|
|
20
29
|
prompt?: string;
|
|
@@ -22,3 +31,4 @@ export declare function parseDoctorArgs(rawArgs: readonly string[]): {
|
|
|
22
31
|
};
|
|
23
32
|
export declare function parseDoctorCleanupArgs(rawArgs: readonly string[]): Required<Pick<DoctorCleanupOptions, "olderThanDays" | "yes">>;
|
|
24
33
|
export declare function runCli(args: readonly string[], options?: CliOptions, write?: (text: string) => void): Promise<number>;
|
|
34
|
+
export {};
|
package/dist/src/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { chmodSync, linkSync, mkdirSync, mkdtempSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
|
-
import { dirname, join } from "node:path";
|
|
5
|
+
import { basename, dirname, extname, join } from "node:path";
|
|
6
6
|
import { pathToFileURL } from "node:url";
|
|
7
7
|
import { ProjectTrustStore, SessionManager, SettingsManager, createAgentSessionFromServices, createAgentSessionServices, getAgentDir, hasTrustRequiringProjectResources } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { Value } from "typebox/value";
|
|
@@ -11,7 +11,8 @@ import { doctorCleanup, doctorCleanupExitCode, formatDoctorCleanupReport } from
|
|
|
11
11
|
import workflowExtension, { errorText, formatWorkflowProgress, isNodeError, jsonValue, loadAgentDefinitions, registeredWorkflowFunctions, truncateWorkflowProgress, workflowCatalog, workflowSettingsPath } from "pi-extensible-workflows";
|
|
12
12
|
import { portableEngineVersion, portablePiVersion, writePortableWorkflowBundle } from "./bundles.js";
|
|
13
13
|
import { runSessionInspector, transcriptFileLines } from "./session-inspector.js";
|
|
14
|
-
import { isPersistedRun } from "pi-extensible-workflows/persistence";
|
|
14
|
+
import { isPersistedRun, listPersistedSessionIds, listRunIds } from "pi-extensible-workflows/persistence";
|
|
15
|
+
import { shareTrajectoryRun } from "pi-extensible-workflows/trajectory";
|
|
15
16
|
function object(value) { return typeof value === "object" && value !== null && !Array.isArray(value); }
|
|
16
17
|
function has(value, key) { return Object.prototype.hasOwnProperty.call(value, key); }
|
|
17
18
|
function requiredArg(args, index) {
|
|
@@ -213,7 +214,53 @@ function launcherHelpLines() {
|
|
|
213
214
|
" --".padEnd(28) + "End launcher option parsing; pass later tokens to workflow input",
|
|
214
215
|
];
|
|
215
216
|
}
|
|
216
|
-
function workflowUsage() { return [`Usage: piewf run <workflow-name> [workflow arguments] | export <workflow-name> [--name <command>] [--output <path>] [--force]`, "", "Launcher options:", ...launcherHelpLines()].join("\n") + "\n"; }
|
|
217
|
+
function workflowUsage() { return [`Usage: piewf run <workflow-name> [workflow arguments] | run --script <path> [--name <workflow-name>] [--input <json>] | export <workflow-name> [--name <command>] [--output <path>] [--force]`, "", "Launcher options:", ...launcherHelpLines()].join("\n") + "\n"; }
|
|
218
|
+
function scriptWorkflowUsage() { return [`Usage: piewf run --script <path> [--name <workflow-name>] [--input <json>]`, "", "Options:", ...launcherHelpLines().slice(0, 2), " -h, --help".padEnd(28) + "Show this help"].join("\n") + "\n"; }
|
|
219
|
+
function scriptWorkflowName(scriptPath) {
|
|
220
|
+
const filename = basename(scriptPath);
|
|
221
|
+
const extension = extname(filename);
|
|
222
|
+
return extension ? filename.slice(0, -extension.length) : filename;
|
|
223
|
+
}
|
|
224
|
+
export function parseScriptWorkflowCliArgs(rawArgs) {
|
|
225
|
+
let scriptPath;
|
|
226
|
+
let name;
|
|
227
|
+
let input;
|
|
228
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
229
|
+
const token = requiredArg(rawArgs, index);
|
|
230
|
+
if (token === "--help" || token === "-h")
|
|
231
|
+
return { help: true };
|
|
232
|
+
const equals = token.indexOf("=");
|
|
233
|
+
const option = equals >= 0 ? token.slice(0, equals) : token;
|
|
234
|
+
if (option === "--script" || option === "--name" || option === "--input") {
|
|
235
|
+
const value = equals >= 0 ? token.slice(equals + 1) : rawArgs[++index];
|
|
236
|
+
if (!value?.trim())
|
|
237
|
+
throw new Error(`Missing value for ${option}`);
|
|
238
|
+
if (option === "--script") {
|
|
239
|
+
if (scriptPath !== undefined)
|
|
240
|
+
throw new Error("--script may only be provided once");
|
|
241
|
+
scriptPath = value;
|
|
242
|
+
}
|
|
243
|
+
else if (option === "--name") {
|
|
244
|
+
if (name !== undefined)
|
|
245
|
+
throw new Error("--name may only be provided once");
|
|
246
|
+
name = value;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
if (input !== undefined)
|
|
250
|
+
throw new Error("--input may only be provided once");
|
|
251
|
+
input = parseJsonInput(value);
|
|
252
|
+
}
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
throw new Error(`Unknown option: ${token}`);
|
|
256
|
+
}
|
|
257
|
+
if (scriptPath === undefined)
|
|
258
|
+
throw new Error("Missing required option: --script");
|
|
259
|
+
const workflowName = name === undefined ? scriptWorkflowName(scriptPath) : name.trim();
|
|
260
|
+
if (!workflowName)
|
|
261
|
+
throw new Error("Workflow name must be non-empty");
|
|
262
|
+
return { help: false, scriptPath, name: workflowName, args: input ?? null };
|
|
263
|
+
}
|
|
217
264
|
function exportUsage() { return [`Usage: piewf export <workflow-name> [--name <command>] [--output <path>] [--force] [--bundle]`, "", "Launcher options:", ...launcherHelpLines()].join("\n") + "\n"; }
|
|
218
265
|
function bundleUsage() { return [`Usage: piewf bundle <workflow-name> [--name <command>] [--output <directory>] [--force]`, "", "The bundle contains a launcher, manifest, workflow payload, and external-runtime setup instructions.", "Repeat --role, --alias, --tool, --command, or --environment to declare recipient requirements.", "Use --extension, --skill, --resource, and --dependency to copy selected payload resources."].join("\n") + "\n"; }
|
|
219
266
|
function parseInspectArgs(rawArgs) {
|
|
@@ -523,15 +570,20 @@ class CliProgress {
|
|
|
523
570
|
this.#run = undefined;
|
|
524
571
|
}
|
|
525
572
|
}
|
|
526
|
-
async function invokeWorkflow(
|
|
527
|
-
if (!Value.Check(fn.input, args))
|
|
528
|
-
throw new Error(`Invalid input for ${fn.name}`);
|
|
573
|
+
async function invokeWorkflow(launch, runtime, options, context) {
|
|
574
|
+
if (launch.fn && (!object(launch.args) || !Value.Check(launch.fn.input, launch.args)))
|
|
575
|
+
throw new Error(`Invalid input for ${launch.fn.name}`);
|
|
576
|
+
if (!launch.fn && launch.scriptPath === undefined)
|
|
577
|
+
throw new Error("Workflow launch has no source");
|
|
529
578
|
let announcedRunId;
|
|
530
579
|
const announceRunId = (runId) => { if (announcedRunId === runId)
|
|
531
580
|
return; announcedRunId = runId; options.stderr(`Run ID: ${runId}\n`); };
|
|
532
581
|
const progress = new CliProgress(options.stderr, options.isTTY ?? process.stderr.isTTY, announceRunId);
|
|
533
582
|
try {
|
|
534
|
-
const
|
|
583
|
+
const params = launch.scriptPath === undefined
|
|
584
|
+
? { name: launch.name, script: `return await ${launch.fn?.name ?? launch.name}(args);`, args: launch.args, foreground: true }
|
|
585
|
+
: { name: launch.name, scriptPath: launch.scriptPath, args: launch.args, foreground: true };
|
|
586
|
+
const result = await runtime.workflowTool.execute(randomUUID(), params, options.signal, (update) => { if (object(update) && object(update.details) && isPersistedRun(update.details.run))
|
|
535
587
|
progress.update(update.details.run); }, context);
|
|
536
588
|
if (!isHeadlessWorkflowResult(result))
|
|
537
589
|
throw new Error("Workflow returned an invalid result");
|
|
@@ -588,8 +640,22 @@ async function runWorkflowCli(rawArgs, options) {
|
|
|
588
640
|
options.write(workflowUsage());
|
|
589
641
|
return args.length ? 0 : 1;
|
|
590
642
|
}
|
|
643
|
+
const runtimeOptions = { ...options, ...(parsed.trustOverride !== undefined ? { trustOverride: parsed.trustOverride } : {}) };
|
|
644
|
+
const scriptMode = args[0] === "--script" || args[0]?.startsWith("--script=") || args[0]?.startsWith("--") && args.some((arg) => arg === "--script" || arg.startsWith("--script="));
|
|
645
|
+
if (scriptMode) {
|
|
646
|
+
const script = parseScriptWorkflowCliArgs(args);
|
|
647
|
+
if (script.help) {
|
|
648
|
+
options.write(scriptWorkflowUsage());
|
|
649
|
+
return 0;
|
|
650
|
+
}
|
|
651
|
+
return withWorkflowRuntime(runtimeOptions, async (runtime, context) => {
|
|
652
|
+
const result = await invokeWorkflow({ name: script.name, scriptPath: script.scriptPath, args: script.args }, runtime, options, context);
|
|
653
|
+
options.write(`${JSON.stringify(result.value)}\n`);
|
|
654
|
+
return 0;
|
|
655
|
+
});
|
|
656
|
+
}
|
|
591
657
|
const name = requiredArg(args, 0);
|
|
592
|
-
return withWorkflowRuntime(
|
|
658
|
+
return withWorkflowRuntime(runtimeOptions, async (runtime, context) => {
|
|
593
659
|
const help = args.slice(1).some((arg) => arg === "--help" || arg === "-h");
|
|
594
660
|
const fn = runtime.catalog.functions.find((candidate) => candidate.name === name);
|
|
595
661
|
if (!fn)
|
|
@@ -599,7 +665,7 @@ async function runWorkflowCli(rawArgs, options) {
|
|
|
599
665
|
return 0;
|
|
600
666
|
}
|
|
601
667
|
const input = parseWorkflowCliArgs(fn.input, args.slice(1));
|
|
602
|
-
const result = await invokeWorkflow(fn, input, runtime, options, context);
|
|
668
|
+
const result = await invokeWorkflow({ name: fn.name, fn, args: input }, runtime, options, context);
|
|
603
669
|
options.write(`${JSON.stringify(result.value)}\n`);
|
|
604
670
|
return 0;
|
|
605
671
|
});
|
|
@@ -801,6 +867,34 @@ export async function runCli(args, options = {}, write = (text) => { process.std
|
|
|
801
867
|
return 1;
|
|
802
868
|
}
|
|
803
869
|
}
|
|
870
|
+
if (args[0] === "share") {
|
|
871
|
+
if (args.length !== 2 || args[1] === "--help" || args[1] === "-h") {
|
|
872
|
+
write("Usage: piewf share <run-id>\n\nExports the run as a static Trajectory report and uploads it as a secret GitHub gist via the gh CLI.\nSecret gists are unlisted, not private: anyone with the link can read the full report.\n");
|
|
873
|
+
return args.some((arg) => arg === "--help" || arg === "-h") ? 0 : 1;
|
|
874
|
+
}
|
|
875
|
+
try {
|
|
876
|
+
const runId = requiredArg(args, 1);
|
|
877
|
+
const cwd = options.cwd ?? process.cwd();
|
|
878
|
+
let sessionId;
|
|
879
|
+
for (const candidate of await listPersistedSessionIds(cwd)) {
|
|
880
|
+
if ((await listRunIds(cwd, candidate, homedir(), false)).includes(runId)) {
|
|
881
|
+
sessionId = candidate;
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
if (!sessionId) {
|
|
886
|
+
stderr(`Error: workflow run ${runId} was not found under ${cwd}\n`);
|
|
887
|
+
return 1;
|
|
888
|
+
}
|
|
889
|
+
const result = await shareTrajectoryRun({ cwd, sessionId, runId });
|
|
890
|
+
write(`Share URL: ${result.shareUrl}\nGist: ${result.gistUrl}\nSecret gist: anyone with the link can read the full report.\n`);
|
|
891
|
+
return 0;
|
|
892
|
+
}
|
|
893
|
+
catch (error) {
|
|
894
|
+
stderr(`Error: ${errorText(error)}\n`);
|
|
895
|
+
return 1;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
804
898
|
if (args[0] === "bundle" || args[0] === "run" || args[0] === "export") {
|
|
805
899
|
try {
|
|
806
900
|
const workflowOptions = { write, stderr, ...(options.cwd !== undefined ? { cwd: options.cwd } : {}), ...(options.agentDir !== undefined ? { agentDir: options.agentDir } : {}), ...(options.signal ? { signal: options.signal } : {}), ...(options.trustOverride !== undefined ? { trustOverride: options.trustOverride } : {}), ...(options.isTTY !== undefined ? { isTTY: options.isTTY } : {}), ...(options.skillPaths?.length ? { skillPaths: [...options.skillPaths] } : {}) };
|
|
@@ -813,7 +907,7 @@ export async function runCli(args, options = {}, write = (text) => { process.std
|
|
|
813
907
|
return 1;
|
|
814
908
|
}
|
|
815
909
|
}
|
|
816
|
-
write("Usage: piewf doctor [role] [--role <role>] [--prompt <text>] [--json] | inspect [session-id] [--json|--summary] [--failed] | transcript <session-file> | bundle <workflow-name> [--name <command>] [--output <path>] [--force] | run <workflow-name> [workflow arguments] | export <workflow-name> [--name <command>] [--output <path>] [--force] [--bundle]\n");
|
|
910
|
+
write("Usage: piewf doctor [role] [--role <role>] [--prompt <text>] [--json] | inspect [session-id] [--json|--summary] [--failed] | transcript <session-file> | share <run-id> | bundle <workflow-name> [--name <command>] [--output <path>] [--force] | run <workflow-name> [workflow arguments] | run --script <path> [--name <workflow-name>] [--input <json>] | export <workflow-name> [--name <command>] [--output <path>] [--force] [--bundle]\n");
|
|
817
911
|
return 1;
|
|
818
912
|
}
|
|
819
913
|
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piewf/cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.10.0",
|
|
4
4
|
"description": "CLI for pi-extensible-workflows",
|
|
5
5
|
"homepage": "https://vekexasia.github.io/pi-extensible-workflows/",
|
|
6
6
|
"repository": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"license": "MIT",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"pi-extensible-workflows": "5.
|
|
49
|
+
"pi-extensible-workflows": "5.10.0"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|