chore: add ion package, codesight wiki, work plans, ascli config
New @boocode/ion package (v0.0.1) for inference optimization network. .codesight/ wiki artifacts for codebase documentation. .omo/ work plans for openspec cleanup and enhanced file panel.
This commit is contained in:
55
packages/ion/src/cli/commands/abandon.ts
Normal file
55
packages/ion/src/cli/commands/abandon.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* `workflow abandon` — Cancel a non-terminal workflow run.
|
||||
*
|
||||
* Marks the run as cancelled. Only works on runs that are not
|
||||
* already in a terminal state (completed, failed, cancelled).
|
||||
*
|
||||
* @example
|
||||
* workflow abandon abc123
|
||||
* workflow abandon abc123 --json
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printJson } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface AbandonResult {
|
||||
runId: string;
|
||||
abandoned: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
async function abandonWorkflowRun(_runId: string): Promise<AbandonResult> {
|
||||
throw new Error('not implemented yet: abandonWorkflowRun');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function abandonCommand(
|
||||
args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
if (args.length === 0) {
|
||||
throw new Error('Missing required argument: <run-id>\n\nUsage: workflow abandon <run-id> [--json]');
|
||||
}
|
||||
|
||||
const runId = args[0]!;
|
||||
|
||||
const result = await abandonWorkflowRun(runId);
|
||||
|
||||
if (options.json) {
|
||||
printJson(result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.abandoned) {
|
||||
console.log(`⊘ Run ${result.runId} abandoned (cancelled).`);
|
||||
} else {
|
||||
console.log(`Failed to abandon run ${result.runId}: ${result.message}`);
|
||||
}
|
||||
}
|
||||
60
packages/ion/src/cli/commands/approve.ts
Normal file
60
packages/ion/src/cli/commands/approve.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* `workflow approve` — Approve a paused workflow run.
|
||||
*
|
||||
* @example
|
||||
* workflow approve abc123
|
||||
* workflow approve abc123 "Looks good" --json
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printJson } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface ApproveResult {
|
||||
runId: string;
|
||||
approved: boolean;
|
||||
comment?: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
async function approveWorkflowRun(
|
||||
_runId: string,
|
||||
_comment?: string,
|
||||
): Promise<ApproveResult> {
|
||||
throw new Error('not implemented yet: approveWorkflowRun');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function approveCommand(
|
||||
args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
if (args.length === 0) {
|
||||
throw new Error('Missing required argument: <run-id>\n\nUsage: workflow approve <run-id> [comment] [--json]');
|
||||
}
|
||||
|
||||
const runId = args[0]!;
|
||||
const comment = args.length > 1 ? args.slice(1).join(' ') : undefined;
|
||||
|
||||
const result = await approveWorkflowRun(runId, comment);
|
||||
|
||||
if (options.json) {
|
||||
printJson(result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.approved) {
|
||||
console.log(`✓ Run ${result.runId} approved.`);
|
||||
if (result.comment) {
|
||||
console.log(` Comment: ${result.comment}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`✗ Failed to approve run ${result.runId}: ${result.message}`);
|
||||
}
|
||||
}
|
||||
74
packages/ion/src/cli/commands/cleanup.ts
Normal file
74
packages/ion/src/cli/commands/cleanup.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* `workflow cleanup` — Remove old workflow run artifacts.
|
||||
*
|
||||
* Default retention: 7 days. Removes run data older than the specified
|
||||
* number of days.
|
||||
*
|
||||
* @example
|
||||
* workflow cleanup
|
||||
* workflow cleanup 30 --json
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printJson } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface CleanupResult {
|
||||
removedRuns: number;
|
||||
removedEvents: number;
|
||||
freedBytes: number;
|
||||
retentionDays: number;
|
||||
}
|
||||
|
||||
async function cleanupWorkflowRuns(
|
||||
_days: number,
|
||||
_cwd?: string,
|
||||
): Promise<CleanupResult> {
|
||||
throw new Error('not implemented yet: cleanupWorkflowRuns');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function cleanupCommand(
|
||||
args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
// First positional arg is the number of days (default 7).
|
||||
const days = args.length > 0 ? parseInt(args[0]!, 10) : 7;
|
||||
|
||||
if (isNaN(days) || days < 1) {
|
||||
throw new Error(`Invalid retention days: ${args[0]}. Must be a positive integer.`);
|
||||
}
|
||||
|
||||
const result = await cleanupWorkflowRuns(days, options.cwd);
|
||||
|
||||
if (options.json) {
|
||||
printJson(result);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Cleanup complete (retention: ${result.retentionDays} days).`);
|
||||
console.log(` Runs removed: ${result.removedRuns}`);
|
||||
console.log(` Events removed: ${result.removedEvents}`);
|
||||
console.log(` Space freed: ${formatBytes(result.freedBytes)}`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function formatBytes(bytes: number): string {
|
||||
if (bytes === 0) return '0 B';
|
||||
const units = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.min(
|
||||
Math.floor(Math.log(bytes) / Math.log(1024)),
|
||||
units.length - 1,
|
||||
);
|
||||
const value = bytes / Math.pow(1024, i);
|
||||
return `${value.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
|
||||
}
|
||||
62
packages/ion/src/cli/commands/convert.ts
Normal file
62
packages/ion/src/cli/commands/convert.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* `workflow convert` — Convert a .sop.md file to a YAML workflow definition.
|
||||
*
|
||||
* Reads the SOP markdown file, parses its structure, and outputs
|
||||
* a corresponding YAML workflow definition.
|
||||
*
|
||||
* @example
|
||||
* workflow convert deploy.sop.md
|
||||
* workflow convert deploy.sop.md --output workflows/deploy.yaml
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printJson } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface ConvertResult {
|
||||
inputFile: string;
|
||||
outputFile: string;
|
||||
workflowName: string;
|
||||
nodeCount: number;
|
||||
}
|
||||
|
||||
async function convertSopToYaml(
|
||||
_inputPath: string,
|
||||
_outputPath?: string,
|
||||
): Promise<ConvertResult> {
|
||||
throw new Error('not implemented yet: convertSopToYaml');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function convertCommand(
|
||||
args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
if (args.length === 0) {
|
||||
throw new Error('Missing required argument: <file.sop.md>\n\nUsage: workflow convert <file.sop.md> [--output <path>]');
|
||||
}
|
||||
|
||||
const inputPath = args[0]!;
|
||||
|
||||
if (!inputPath.endsWith('.sop.md')) {
|
||||
throw new Error(`Input file must end with .sop.md, got: ${inputPath}`);
|
||||
}
|
||||
|
||||
const result = await convertSopToYaml(inputPath, options.output);
|
||||
|
||||
if (options.json) {
|
||||
printJson(result);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Converted: ${result.inputFile}`);
|
||||
console.log(` Output: ${result.outputFile}`);
|
||||
console.log(` Workflow: ${result.workflowName}`);
|
||||
console.log(` Nodes: ${result.nodeCount}`);
|
||||
}
|
||||
59
packages/ion/src/cli/commands/list.ts
Normal file
59
packages/ion/src/cli/commands/list.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* `workflow list` — List all available workflows.
|
||||
*
|
||||
* Discovers workflows from both bundled and project sources and displays
|
||||
* them in a formatted table (or JSON with --json).
|
||||
*
|
||||
* @example
|
||||
* workflow list
|
||||
* workflow list --json
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printTable, printJson } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface WorkflowEntry {
|
||||
name: string;
|
||||
description: string;
|
||||
source: 'bundled' | 'project';
|
||||
}
|
||||
|
||||
async function discoverWorkflows(_cwd?: string): Promise<WorkflowEntry[]> {
|
||||
throw new Error('not implemented yet: discoverWorkflows');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function listCommand(
|
||||
_args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
const workflows = await discoverWorkflows(options.cwd);
|
||||
|
||||
if (options.json) {
|
||||
printJson(workflows);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Available workflows:');
|
||||
console.log('');
|
||||
|
||||
printTable(
|
||||
workflows.map((w) => ({
|
||||
name: w.name,
|
||||
description: w.description,
|
||||
source: w.source,
|
||||
})),
|
||||
[
|
||||
{ header: 'Name', field: 'name', minWidth: 20 },
|
||||
{ header: 'Description', field: 'description', minWidth: 30 },
|
||||
{ header: 'Source', field: 'source', minWidth: 10 },
|
||||
],
|
||||
);
|
||||
}
|
||||
62
packages/ion/src/cli/commands/reject.ts
Normal file
62
packages/ion/src/cli/commands/reject.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* `workflow reject` — Reject a paused workflow run.
|
||||
*
|
||||
* Sets $REJECTION_REASON with the provided reason string.
|
||||
*
|
||||
* @example
|
||||
* workflow reject abc123
|
||||
* workflow reject abc123 "Not compliant" --json
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printJson } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface RejectResult {
|
||||
runId: string;
|
||||
rejected: boolean;
|
||||
reason?: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
async function rejectWorkflowRun(
|
||||
_runId: string,
|
||||
_reason?: string,
|
||||
): Promise<RejectResult> {
|
||||
throw new Error('not implemented yet: rejectWorkflowRun');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function rejectCommand(
|
||||
args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
if (args.length === 0) {
|
||||
throw new Error('Missing required argument: <run-id>\n\nUsage: workflow reject <run-id> [reason] [--json]');
|
||||
}
|
||||
|
||||
const runId = args[0]!;
|
||||
const reason = args.length > 1 ? args.slice(1).join(' ') : undefined;
|
||||
|
||||
const result = await rejectWorkflowRun(runId, reason);
|
||||
|
||||
if (options.json) {
|
||||
printJson(result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.rejected) {
|
||||
console.log(`✗ Run ${result.runId} rejected.`);
|
||||
if (result.reason) {
|
||||
console.log(` Reason: ${result.reason}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`Failed to reject run ${result.runId}: ${result.message}`);
|
||||
}
|
||||
}
|
||||
55
packages/ion/src/cli/commands/resume.ts
Normal file
55
packages/ion/src/cli/commands/resume.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* `workflow resume` — Resume a failed workflow run.
|
||||
*
|
||||
* Skips completed nodes and re-executes from the failure point.
|
||||
*
|
||||
* @example
|
||||
* workflow resume abc123
|
||||
* workflow resume abc123 --json
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printJson } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface ResumeResult {
|
||||
runId: string;
|
||||
resumed: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
async function resumeWorkflowRun(_runId: string): Promise<ResumeResult> {
|
||||
throw new Error('not implemented yet: resumeWorkflowRun');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function resumeCommand(
|
||||
args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
if (args.length === 0) {
|
||||
throw new Error('Missing required argument: <run-id>\n\nUsage: workflow resume <run-id> [--json]');
|
||||
}
|
||||
|
||||
const runId = args[0]!;
|
||||
|
||||
const result = await resumeWorkflowRun(runId);
|
||||
|
||||
if (options.json) {
|
||||
printJson(result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.resumed) {
|
||||
console.log(`↻ Run ${result.runId} resumed.`);
|
||||
console.log(` ${result.message}`);
|
||||
} else {
|
||||
console.log(`Failed to resume run ${result.runId}: ${result.message}`);
|
||||
}
|
||||
}
|
||||
94
packages/ion/src/cli/commands/run.ts
Normal file
94
packages/ion/src/cli/commands/run.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* `workflow run` — Execute a workflow by name.
|
||||
*
|
||||
* Resolves the workflow, passes message args, and shows real-time progress.
|
||||
* With --detach, runs in background and returns the run ID immediately.
|
||||
*
|
||||
* @example
|
||||
* workflow run deploy
|
||||
* workflow run deploy --cwd /tmp/project --json
|
||||
* workflow run deploy --detach
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printJson } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface WorkflowRunResult {
|
||||
id: string;
|
||||
workflowName: string;
|
||||
status: string;
|
||||
output?: Record<string, unknown>;
|
||||
error?: string;
|
||||
startedAt: string;
|
||||
completedAt?: string;
|
||||
}
|
||||
|
||||
async function resolveWorkflow(
|
||||
_name: string,
|
||||
_cwd?: string,
|
||||
): Promise<unknown> {
|
||||
throw new Error('not implemented yet: resolveWorkflow');
|
||||
}
|
||||
|
||||
async function executeWorkflow(
|
||||
_workflow: unknown,
|
||||
_messageArgs: string[],
|
||||
_options: { cwd?: string; detach?: boolean },
|
||||
): Promise<WorkflowRunResult> {
|
||||
throw new Error('not implemented yet: executeWorkflow');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function runCommand(
|
||||
args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
if (args.length === 0) {
|
||||
throw new Error('Missing required argument: <name>\n\nUsage: workflow run <name> [args...] [--cwd <path>] [--detach] [--json]');
|
||||
}
|
||||
|
||||
const workflowName = args[0]!;
|
||||
const messageArgs = args.slice(1);
|
||||
const detach = options.json ? false : false; // --detach is a flag, not in CliOptions yet
|
||||
|
||||
// Parse --detach from raw args (it's a boolean flag).
|
||||
// This is handled by the arg parser in the main entry point,
|
||||
// but since CliOptions doesn't have detach, we check process.argv.
|
||||
const isDetach = process.argv.includes('--detach');
|
||||
|
||||
const workflow = await resolveWorkflow(workflowName, options.cwd);
|
||||
const result = await executeWorkflow(workflow, messageArgs, {
|
||||
cwd: options.cwd,
|
||||
detach: isDetach,
|
||||
});
|
||||
|
||||
if (options.json) {
|
||||
printJson(result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDetach) {
|
||||
console.log(`Workflow started in background.`);
|
||||
console.log(`Run ID: ${result.id}`);
|
||||
console.log(`Workflow: ${result.workflowName}`);
|
||||
console.log(`Status: ${result.status}`);
|
||||
} else {
|
||||
console.log(`Workflow run completed.`);
|
||||
console.log(` Run ID: ${result.id}`);
|
||||
console.log(` Workflow: ${result.workflowName}`);
|
||||
console.log(` Status: ${result.status}`);
|
||||
if (result.output) {
|
||||
console.log(` Output: ${JSON.stringify(result.output)}`);
|
||||
}
|
||||
if (result.error) {
|
||||
console.log(` Error: ${result.error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
91
packages/ion/src/cli/commands/runs.ts
Normal file
91
packages/ion/src/cli/commands/runs.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* `workflow runs` — List recent workflow runs with filters.
|
||||
*
|
||||
* @example
|
||||
* workflow runs
|
||||
* workflow runs --status failed --limit 10 --json
|
||||
* workflow runs --all
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printTable, printJson, formatTimestamp, formatDuration } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface RunRecord {
|
||||
id: string;
|
||||
workflowName: string;
|
||||
status: string;
|
||||
startedAt: string;
|
||||
duration?: number; // ms, absent if still running
|
||||
currentNode?: string;
|
||||
}
|
||||
|
||||
async function listWorkflowRuns(_filters: {
|
||||
status?: string;
|
||||
limit?: number;
|
||||
all?: boolean;
|
||||
cwd?: string;
|
||||
}): Promise<RunRecord[]> {
|
||||
throw new Error('not implemented yet: listWorkflowRuns');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function runsCommand(
|
||||
args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
// Parse --status, --limit, --all from args/options.
|
||||
// These are already extracted by parseArgs into options.
|
||||
const status = typeof (options as Record<string, unknown>).status === 'string'
|
||||
? (options as Record<string, unknown>).status as string
|
||||
: undefined;
|
||||
const limit = typeof (options as Record<string, unknown>).limit === 'string'
|
||||
? parseInt((options as Record<string, unknown>).limit as string, 10)
|
||||
: 50;
|
||||
const all = (options as Record<string, unknown>).all === true;
|
||||
|
||||
const runs = await listWorkflowRuns({
|
||||
status,
|
||||
limit,
|
||||
all,
|
||||
cwd: options.cwd,
|
||||
});
|
||||
|
||||
if (options.json) {
|
||||
printJson(runs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (runs.length === 0) {
|
||||
console.log('No workflow runs found.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Showing ${runs.length} run(s):`);
|
||||
console.log('');
|
||||
|
||||
printTable(
|
||||
runs.map((r) => ({
|
||||
id: r.id,
|
||||
workflow: r.workflowName,
|
||||
status: r.status,
|
||||
started: formatTimestamp(new Date(r.startedAt)),
|
||||
duration: r.duration != null ? formatDuration(r.duration) : '-',
|
||||
currentNode: r.currentNode ?? '-',
|
||||
})),
|
||||
[
|
||||
{ header: 'ID', field: 'id', minWidth: 26 },
|
||||
{ header: 'Workflow', field: 'workflow', minWidth: 20 },
|
||||
{ header: 'Status', field: 'status', minWidth: 10 },
|
||||
{ header: 'Started', field: 'started', minWidth: 19 },
|
||||
{ header: 'Duration', field: 'duration', minWidth: 10 },
|
||||
{ header: 'Node', field: 'currentNode', minWidth: 15 },
|
||||
],
|
||||
);
|
||||
}
|
||||
67
packages/ion/src/cli/commands/status.ts
Normal file
67
packages/ion/src/cli/commands/status.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* `workflow status` — Show active (running + paused) workflow runs.
|
||||
*
|
||||
* @example
|
||||
* workflow status
|
||||
* workflow status --json
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printTable, printJson, formatDuration } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface ActiveRun {
|
||||
id: string;
|
||||
workflowName: string;
|
||||
status: string;
|
||||
duration: number; // ms
|
||||
currentNode?: string;
|
||||
}
|
||||
|
||||
async function getActiveRuns(_cwd?: string): Promise<ActiveRun[]> {
|
||||
throw new Error('not implemented yet: getActiveRuns');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function statusCommand(
|
||||
_args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
const runs = await getActiveRuns(options.cwd);
|
||||
|
||||
if (options.json) {
|
||||
printJson(runs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (runs.length === 0) {
|
||||
console.log('No active workflow runs.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Active workflow runs:');
|
||||
console.log('');
|
||||
|
||||
printTable(
|
||||
runs.map((r) => ({
|
||||
id: r.id,
|
||||
workflow: r.workflowName,
|
||||
status: r.status,
|
||||
duration: formatDuration(r.duration),
|
||||
currentNode: r.currentNode ?? '-',
|
||||
})),
|
||||
[
|
||||
{ header: 'ID', field: 'id', minWidth: 26 },
|
||||
{ header: 'Workflow', field: 'workflow', minWidth: 20 },
|
||||
{ header: 'Status', field: 'status', minWidth: 10 },
|
||||
{ header: 'Duration', field: 'duration', minWidth: 10 },
|
||||
{ header: 'Current Node', field: 'currentNode', minWidth: 15 },
|
||||
],
|
||||
);
|
||||
}
|
||||
66
packages/ion/src/cli/commands/validate.ts
Normal file
66
packages/ion/src/cli/commands/validate.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* `workflow validate` — Validate a workflow definition without executing.
|
||||
*
|
||||
* Loads the workflow, runs schema validation, and reports any errors.
|
||||
*
|
||||
* @example
|
||||
* workflow validate deploy
|
||||
* workflow validate deploy --json
|
||||
*/
|
||||
|
||||
import type { CliOptions } from '../utils.js';
|
||||
import { printJson } from '../utils.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stub: engine integration (not implemented yet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface ValidationError {
|
||||
path: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ValidateResult {
|
||||
valid: boolean;
|
||||
errors: ValidationError[];
|
||||
workflowName: string;
|
||||
}
|
||||
|
||||
async function validateWorkflow(
|
||||
_name: string,
|
||||
_cwd?: string,
|
||||
): Promise<ValidateResult> {
|
||||
throw new Error('not implemented yet: validateWorkflow');
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function validateCommand(
|
||||
args: string[],
|
||||
options: CliOptions,
|
||||
): Promise<void> {
|
||||
if (args.length === 0) {
|
||||
throw new Error('Missing required argument: <name>\n\nUsage: workflow validate <name> [--json]');
|
||||
}
|
||||
|
||||
const workflowName = args[0]!;
|
||||
|
||||
const result = await validateWorkflow(workflowName, options.cwd);
|
||||
|
||||
if (options.json) {
|
||||
printJson(result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.valid) {
|
||||
console.log(`✓ Workflow "${result.workflowName}" is valid.`);
|
||||
} else {
|
||||
console.log(`✗ Workflow "${result.workflowName}" has ${result.errors.length} error(s):`);
|
||||
console.log('');
|
||||
for (const err of result.errors) {
|
||||
console.log(` ${err.path}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user