vigthoria-cli 1.6.55 → 1.6.57
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/commands/fork.d.ts +16 -0
- package/dist/commands/fork.js +148 -0
- package/dist/commands/history.d.ts +16 -0
- package/dist/commands/history.js +92 -0
- package/dist/commands/legion.d.ts +29 -0
- package/dist/commands/legion.js +223 -0
- package/dist/commands/replay.d.ts +17 -0
- package/dist/commands/replay.js +140 -0
- package/dist/index.js +53 -0
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Config } from '../utils/config.js';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
interface ForkOptions {
|
|
4
|
+
eventIndex?: number;
|
|
5
|
+
project?: string;
|
|
6
|
+
json?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class ForkCommand {
|
|
9
|
+
private config;
|
|
10
|
+
private logger;
|
|
11
|
+
constructor(config: Config, logger: Logger);
|
|
12
|
+
private getHeaders;
|
|
13
|
+
private getBaseUrl;
|
|
14
|
+
run(runId: string, message: string, options: ForkOptions): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ForkCommand = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* fork.ts — Fork from an existing V3 agent run and stream the result.
|
|
9
|
+
*/
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
12
|
+
class ForkCommand {
|
|
13
|
+
config;
|
|
14
|
+
logger;
|
|
15
|
+
constructor(config, logger) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
}
|
|
19
|
+
getHeaders() {
|
|
20
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
21
|
+
const token = process.env.VIGTHORIA_TOKEN ||
|
|
22
|
+
process.env.VIGTHORIA_AUTH_TOKEN ||
|
|
23
|
+
this.config.get('authToken');
|
|
24
|
+
if (token) {
|
|
25
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
26
|
+
headers['Cookie'] = `vigthoria-auth-token=${token}`;
|
|
27
|
+
}
|
|
28
|
+
const serviceKey = process.env.VIGTHORIA_V3_SERVICE_KEY;
|
|
29
|
+
if (serviceKey)
|
|
30
|
+
headers['X-Service-Key'] = serviceKey;
|
|
31
|
+
return headers;
|
|
32
|
+
}
|
|
33
|
+
getBaseUrl() {
|
|
34
|
+
return (process.env.VIGTHORIA_V3_AGENT_URL ||
|
|
35
|
+
process.env.V3_AGENT_URL ||
|
|
36
|
+
'http://127.0.0.1:8030');
|
|
37
|
+
}
|
|
38
|
+
async run(runId, message, options) {
|
|
39
|
+
const project = options.project || process.cwd();
|
|
40
|
+
const eventIndex = options.eventIndex || 0;
|
|
41
|
+
const spinner = (0, logger_js_1.createSpinner)(`Forking run ${runId}...`).start();
|
|
42
|
+
try {
|
|
43
|
+
const baseUrl = this.getBaseUrl();
|
|
44
|
+
const body = {
|
|
45
|
+
workspace_root: project,
|
|
46
|
+
from_event_index: eventIndex,
|
|
47
|
+
new_request: message || '',
|
|
48
|
+
stream: true,
|
|
49
|
+
context: '',
|
|
50
|
+
};
|
|
51
|
+
const resp = await fetch(`${baseUrl}/api/runs/${encodeURIComponent(runId)}/fork`, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: this.getHeaders(),
|
|
54
|
+
body: JSON.stringify(body),
|
|
55
|
+
});
|
|
56
|
+
if (!resp.ok) {
|
|
57
|
+
spinner.stop();
|
|
58
|
+
if (resp.status === 404) {
|
|
59
|
+
this.logger.error(`Run ${runId} not found or has no event log.`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
this.logger.error(`Fork failed: ${resp.status} ${resp.statusText}`);
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
spinner.stop();
|
|
67
|
+
console.log(chalk_1.default.bold(`\n${logger_js_1.CH.success} Forked from ${chalk_1.default.cyan(runId)} at event ${eventIndex}\n`));
|
|
68
|
+
if (!resp.body) {
|
|
69
|
+
this.logger.error('No response body');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Stream SSE events
|
|
73
|
+
const reader = resp.body.getReader();
|
|
74
|
+
const decoder = new TextDecoder();
|
|
75
|
+
let buffer = '';
|
|
76
|
+
let toolCallNum = 0;
|
|
77
|
+
while (true) {
|
|
78
|
+
const { done, value } = await reader.read();
|
|
79
|
+
if (done)
|
|
80
|
+
break;
|
|
81
|
+
buffer += decoder.decode(value, { stream: true });
|
|
82
|
+
const lines = buffer.split('\n');
|
|
83
|
+
buffer = lines.pop() || '';
|
|
84
|
+
for (const line of lines) {
|
|
85
|
+
if (!line.startsWith('data: '))
|
|
86
|
+
continue;
|
|
87
|
+
const payload = line.slice(6).trim();
|
|
88
|
+
if (payload === '[DONE]') {
|
|
89
|
+
console.log(chalk_1.default.bold(`\n${logger_js_1.CH.success} Fork run complete\n`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const evt = JSON.parse(payload);
|
|
94
|
+
const type = evt.type || 'unknown';
|
|
95
|
+
if (options.json) {
|
|
96
|
+
console.log(JSON.stringify(evt));
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
switch (type) {
|
|
100
|
+
case 'context':
|
|
101
|
+
console.log(chalk_1.default.blue(` context: ${evt.context_id || '?'}`) +
|
|
102
|
+
(evt.forked_from ? chalk_1.default.dim(` (forked from ${evt.forked_from})`) : ''));
|
|
103
|
+
break;
|
|
104
|
+
case 'start':
|
|
105
|
+
console.log(chalk_1.default.green(` ▶ START`) + ` task=${evt.task_id || '?'}` +
|
|
106
|
+
(evt.forked_from ? chalk_1.default.dim(` forked_from=${evt.forked_from}`) : ''));
|
|
107
|
+
break;
|
|
108
|
+
case 'plan':
|
|
109
|
+
console.log(chalk_1.default.magenta(` 📋 PLAN`) + ` ${evt.tasks?.length || 0} tasks`);
|
|
110
|
+
break;
|
|
111
|
+
case 'tool_call':
|
|
112
|
+
toolCallNum++;
|
|
113
|
+
console.log(chalk_1.default.yellow(` 🔧 #${toolCallNum}`) + ` ${evt.name || '?'}(${JSON.stringify(evt.arguments || {}).substring(0, 60)})`);
|
|
114
|
+
break;
|
|
115
|
+
case 'tool_result': {
|
|
116
|
+
const icon = evt.success !== false ? chalk_1.default.green('✓') : chalk_1.default.red('✗');
|
|
117
|
+
console.log(` ${icon} ${evt.name || '?'}: ${chalk_1.default.dim((evt.output || '').substring(0, 100))}`);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
case 'message':
|
|
121
|
+
console.log(chalk_1.default.cyan(` 💬 `) + (evt.content || '').substring(0, 150));
|
|
122
|
+
break;
|
|
123
|
+
case 'complete': {
|
|
124
|
+
const seal = evt.seal_score ? `[${evt.seal_score.tier} ${evt.seal_score.overall}]` : '';
|
|
125
|
+
console.log(chalk_1.default.green(` ✅ COMPLETE `) + chalk_1.default.yellow(seal) +
|
|
126
|
+
` ${evt.iterations || '?'} iterations, ${evt.tool_calls || '?'} tool calls`);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
case 'error':
|
|
130
|
+
console.log(chalk_1.default.red(` ❌ ERROR: `) + (evt.message || '').substring(0, 200));
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
console.log(chalk_1.default.dim(` ${type}: ${JSON.stringify(evt).substring(0, 80)}`));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// skip unparseable lines
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
spinner.stop();
|
|
144
|
+
this.logger.error(`Fork error: ${err.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.ForkCommand = ForkCommand;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Config } from '../utils/config.js';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
interface HistoryOptions {
|
|
4
|
+
limit?: number;
|
|
5
|
+
json?: boolean;
|
|
6
|
+
project?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class HistoryCommand {
|
|
9
|
+
private config;
|
|
10
|
+
private logger;
|
|
11
|
+
constructor(config: Config, logger: Logger);
|
|
12
|
+
private getHeaders;
|
|
13
|
+
private getBaseUrl;
|
|
14
|
+
run(options: HistoryOptions): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HistoryCommand = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* history.ts — List recent V3 agent runs with summaries.
|
|
9
|
+
*/
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
12
|
+
class HistoryCommand {
|
|
13
|
+
config;
|
|
14
|
+
logger;
|
|
15
|
+
constructor(config, logger) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
}
|
|
19
|
+
getHeaders() {
|
|
20
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
21
|
+
const token = process.env.VIGTHORIA_TOKEN ||
|
|
22
|
+
process.env.VIGTHORIA_AUTH_TOKEN ||
|
|
23
|
+
this.config.get('authToken');
|
|
24
|
+
if (token) {
|
|
25
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
26
|
+
headers['Cookie'] = `vigthoria-auth-token=${token}`;
|
|
27
|
+
}
|
|
28
|
+
const serviceKey = process.env.VIGTHORIA_V3_SERVICE_KEY;
|
|
29
|
+
if (serviceKey)
|
|
30
|
+
headers['X-Service-Key'] = serviceKey;
|
|
31
|
+
return headers;
|
|
32
|
+
}
|
|
33
|
+
getBaseUrl() {
|
|
34
|
+
return (process.env.VIGTHORIA_V3_AGENT_URL ||
|
|
35
|
+
process.env.V3_AGENT_URL ||
|
|
36
|
+
'http://127.0.0.1:8030');
|
|
37
|
+
}
|
|
38
|
+
async run(options) {
|
|
39
|
+
const limit = options.limit || 20;
|
|
40
|
+
const project = options.project || process.cwd();
|
|
41
|
+
const spinner = (0, logger_js_1.createSpinner)('Loading run history...').start();
|
|
42
|
+
try {
|
|
43
|
+
const baseUrl = this.getBaseUrl();
|
|
44
|
+
const params = new URLSearchParams({
|
|
45
|
+
limit: String(limit),
|
|
46
|
+
workspace_root: project,
|
|
47
|
+
});
|
|
48
|
+
const resp = await fetch(`${baseUrl}/api/runs?${params}`, {
|
|
49
|
+
headers: this.getHeaders(),
|
|
50
|
+
});
|
|
51
|
+
if (!resp.ok) {
|
|
52
|
+
spinner.stop();
|
|
53
|
+
this.logger.error(`Failed to fetch runs: ${resp.status} ${resp.statusText}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const data = (await resp.json());
|
|
57
|
+
spinner.stop();
|
|
58
|
+
if (options.json) {
|
|
59
|
+
console.log(JSON.stringify(data, null, 2));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (data.runs.length === 0) {
|
|
63
|
+
this.logger.info(chalk_1.default.dim('No runs found.'));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.log(chalk_1.default.bold(`\n${logger_js_1.CH.success} Recent Runs (${data.count})\n`));
|
|
67
|
+
for (const run of data.runs) {
|
|
68
|
+
const tier = run.seal_score?.tier || 'unknown';
|
|
69
|
+
const tierColor = tier === 'gold' ? chalk_1.default.yellow :
|
|
70
|
+
tier === 'silver' ? chalk_1.default.white :
|
|
71
|
+
tier === 'bronze' ? chalk_1.default.hex('#cd7f32') :
|
|
72
|
+
tier === 'failed' ? chalk_1.default.red :
|
|
73
|
+
chalk_1.default.dim;
|
|
74
|
+
const score = run.seal_score?.overall != null ? `${run.seal_score.overall}` : '—';
|
|
75
|
+
const status = run.failed > 0
|
|
76
|
+
? chalk_1.default.red(`${run.completed}/${run.task_count} ✗`)
|
|
77
|
+
: chalk_1.default.green(`${run.completed}/${run.task_count} ✓`);
|
|
78
|
+
console.log(` ${chalk_1.default.cyan(run.run_id.substring(0, 16))} ` +
|
|
79
|
+
`${chalk_1.default.dim(run.archived_at || '?')} ` +
|
|
80
|
+
`${status} ` +
|
|
81
|
+
`${tierColor(`[${tier} ${score}]`)} ` +
|
|
82
|
+
`${chalk_1.default.white(run.request?.substring(0, 60) || '(no request)')}`);
|
|
83
|
+
}
|
|
84
|
+
console.log();
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
spinner.stop();
|
|
88
|
+
this.logger.error(`History error: ${err.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.HistoryCommand = HistoryCommand;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vigthoria CLI - Legion Parallel Orchestration
|
|
3
|
+
*
|
|
4
|
+
* Submit work to Hyper Loop's Legion engine for parallel execution.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* vigthoria legion "Refactor auth and update tests"
|
|
8
|
+
* vigthoria legion --workers List available Legion workers
|
|
9
|
+
* vigthoria legion --status Show Legion infrastructure status
|
|
10
|
+
*/
|
|
11
|
+
import { Config } from '../utils/config.js';
|
|
12
|
+
import { Logger } from '../utils/logger.js';
|
|
13
|
+
interface LegionOptions {
|
|
14
|
+
workers?: boolean;
|
|
15
|
+
status?: boolean;
|
|
16
|
+
worker?: string;
|
|
17
|
+
project?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class LegionCommand {
|
|
20
|
+
private config;
|
|
21
|
+
private logger;
|
|
22
|
+
constructor(config: Config, logger: Logger);
|
|
23
|
+
private getHeaders;
|
|
24
|
+
run(request: string | undefined, options: LegionOptions): Promise<void>;
|
|
25
|
+
private planAndExecute;
|
|
26
|
+
private showWorkers;
|
|
27
|
+
private showStatus;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Vigthoria CLI - Legion Parallel Orchestration
|
|
4
|
+
*
|
|
5
|
+
* Submit work to Hyper Loop's Legion engine for parallel execution.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* vigthoria legion "Refactor auth and update tests"
|
|
9
|
+
* vigthoria legion --workers List available Legion workers
|
|
10
|
+
* vigthoria legion --status Show Legion infrastructure status
|
|
11
|
+
*/
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.LegionCommand = void 0;
|
|
17
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
18
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
19
|
+
const HYPERLOOP_URLS = [
|
|
20
|
+
'http://localhost:8020/api/hyperloop',
|
|
21
|
+
'http://10.0.0.2:8020/api/hyperloop',
|
|
22
|
+
];
|
|
23
|
+
class LegionCommand {
|
|
24
|
+
config;
|
|
25
|
+
logger;
|
|
26
|
+
constructor(config, logger) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
}
|
|
30
|
+
getHeaders() {
|
|
31
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
32
|
+
const token = this.config.get('authToken');
|
|
33
|
+
if (token) {
|
|
34
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
35
|
+
headers['Cookie'] = `vigthoria-auth-token=${token}`;
|
|
36
|
+
}
|
|
37
|
+
return headers;
|
|
38
|
+
}
|
|
39
|
+
async run(request, options) {
|
|
40
|
+
if (options.workers) {
|
|
41
|
+
await this.showWorkers();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (options.status) {
|
|
45
|
+
await this.showStatus();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!request) {
|
|
49
|
+
console.log(chalk_1.default.yellow('Usage: vigthoria legion "<task description>"'));
|
|
50
|
+
console.log(chalk_1.default.gray(' --workers List available Legion workers'));
|
|
51
|
+
console.log(chalk_1.default.gray(' --status Show Legion infrastructure status'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
await this.planAndExecute(request, options);
|
|
55
|
+
}
|
|
56
|
+
async planAndExecute(request, options) {
|
|
57
|
+
const spinner = (0, logger_js_1.createSpinner)('Submitting to Legion parallel orchestrator...').start();
|
|
58
|
+
const body = {
|
|
59
|
+
request,
|
|
60
|
+
context: {
|
|
61
|
+
workspace: options.project || process.cwd(),
|
|
62
|
+
},
|
|
63
|
+
constraints: {},
|
|
64
|
+
};
|
|
65
|
+
for (const baseUrl of HYPERLOOP_URLS) {
|
|
66
|
+
try {
|
|
67
|
+
const response = await fetch(`${baseUrl}/legion/plan/execute`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: this.getHeaders(),
|
|
70
|
+
body: JSON.stringify(body),
|
|
71
|
+
signal: AbortSignal.timeout(300000),
|
|
72
|
+
});
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const errorText = await response.text().catch(() => '');
|
|
75
|
+
throw new Error(`Legion API ${response.status}: ${errorText.slice(0, 200)}`);
|
|
76
|
+
}
|
|
77
|
+
const result = await response.json();
|
|
78
|
+
spinner.stop();
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(chalk_1.default.bold.white(` ${logger_js_1.CH.hLine.repeat(3)} Legion Execution Report ${logger_js_1.CH.hLine.repeat(34)}`));
|
|
81
|
+
console.log();
|
|
82
|
+
if (result.status === 'success' || result.ok) {
|
|
83
|
+
console.log(chalk_1.default.green(` ${logger_js_1.CH.success} Legion completed successfully`));
|
|
84
|
+
}
|
|
85
|
+
else if (result.status === 'partial') {
|
|
86
|
+
console.log(chalk_1.default.yellow(` ${logger_js_1.CH.warn} Legion completed partially`));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log(chalk_1.default.red(` ${logger_js_1.CH.error} Legion execution failed`));
|
|
90
|
+
}
|
|
91
|
+
// Show execution graph results
|
|
92
|
+
if (result.graph_results || result.results) {
|
|
93
|
+
const results = result.graph_results || result.results || [];
|
|
94
|
+
const steps = Array.isArray(results) ? results : (typeof results === 'object' ? Object.values(results) : []);
|
|
95
|
+
if (steps.length > 0) {
|
|
96
|
+
console.log();
|
|
97
|
+
console.log(chalk_1.default.white(' Steps:'));
|
|
98
|
+
for (const step of steps) {
|
|
99
|
+
const stepObj = step;
|
|
100
|
+
const icon = stepObj.status === 'completed' || stepObj.success
|
|
101
|
+
? chalk_1.default.green(logger_js_1.CH.success)
|
|
102
|
+
: chalk_1.default.red(logger_js_1.CH.error);
|
|
103
|
+
const name = stepObj.step_id || stepObj.worker || stepObj.name || 'step';
|
|
104
|
+
const summary = stepObj.summary || stepObj.result || '';
|
|
105
|
+
console.log(` ${icon} ${chalk_1.default.white(String(name))}${summary ? chalk_1.default.gray(` — ${String(summary).slice(0, 100)}`) : ''}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Show timing and artifacts
|
|
110
|
+
if (result.elapsed_seconds || result.elapsed) {
|
|
111
|
+
console.log();
|
|
112
|
+
console.log(chalk_1.default.gray(` Time: ${(result.elapsed_seconds || result.elapsed || 0).toFixed(1)}s`));
|
|
113
|
+
}
|
|
114
|
+
if (result.content) {
|
|
115
|
+
console.log();
|
|
116
|
+
console.log(chalk_1.default.white(String(result.content).slice(0, 500)));
|
|
117
|
+
}
|
|
118
|
+
console.log();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (error?.name === 'AbortError') {
|
|
123
|
+
spinner.stop();
|
|
124
|
+
this.logger.error('Legion request timed out');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// Try next URL
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
spinner.stop();
|
|
132
|
+
this.logger.error('Could not reach Hyper Loop Legion API. Is vigthoria-hyper-loop running?');
|
|
133
|
+
}
|
|
134
|
+
async showWorkers() {
|
|
135
|
+
const spinner = (0, logger_js_1.createSpinner)('Fetching Legion worker catalog...').start();
|
|
136
|
+
for (const baseUrl of HYPERLOOP_URLS) {
|
|
137
|
+
try {
|
|
138
|
+
const response = await fetch(`${baseUrl}/legion/workers`, {
|
|
139
|
+
signal: AbortSignal.timeout(10000),
|
|
140
|
+
headers: this.getHeaders(),
|
|
141
|
+
});
|
|
142
|
+
if (!response.ok)
|
|
143
|
+
continue;
|
|
144
|
+
const data = await response.json();
|
|
145
|
+
spinner.stop();
|
|
146
|
+
console.log();
|
|
147
|
+
console.log(chalk_1.default.bold.white(` ${logger_js_1.CH.hLine.repeat(3)} Legion Worker Catalog ${logger_js_1.CH.hLine.repeat(37)}`));
|
|
148
|
+
console.log();
|
|
149
|
+
const workers = data.workers || data.catalog || data;
|
|
150
|
+
if (Array.isArray(workers)) {
|
|
151
|
+
for (const w of workers) {
|
|
152
|
+
const wObj = w;
|
|
153
|
+
const statusColor = wObj.status === 'active' ? chalk_1.default.green : chalk_1.default.yellow;
|
|
154
|
+
console.log(` ${statusColor(logger_js_1.CH.bullet)} ${chalk_1.default.white(wObj.name || wObj.worker || 'unknown')} ${chalk_1.default.gray(wObj.description || '')}`);
|
|
155
|
+
if (wObj.status) {
|
|
156
|
+
console.log(chalk_1.default.gray(` Status: ${wObj.status}`));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (typeof workers === 'object') {
|
|
161
|
+
for (const [name, info] of Object.entries(workers)) {
|
|
162
|
+
const infoObj = info;
|
|
163
|
+
const statusColor = infoObj.maturity === 'active' ? chalk_1.default.green : chalk_1.default.yellow;
|
|
164
|
+
console.log(` ${statusColor(logger_js_1.CH.bullet)} ${chalk_1.default.white(name)} ${chalk_1.default.gray(infoObj.description || '')}`);
|
|
165
|
+
if (infoObj.maturity)
|
|
166
|
+
console.log(chalk_1.default.gray(` Maturity: ${infoObj.maturity}`));
|
|
167
|
+
if (infoObj.domains)
|
|
168
|
+
console.log(chalk_1.default.gray(` Domains: ${Array.isArray(infoObj.domains) ? infoObj.domains.join(', ') : infoObj.domains}`));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
console.log();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
spinner.stop();
|
|
179
|
+
this.logger.error('Could not reach Hyper Loop. Is vigthoria-hyper-loop running?');
|
|
180
|
+
}
|
|
181
|
+
async showStatus() {
|
|
182
|
+
const spinner = (0, logger_js_1.createSpinner)('Checking Legion infrastructure...').start();
|
|
183
|
+
for (const baseUrl of HYPERLOOP_URLS) {
|
|
184
|
+
try {
|
|
185
|
+
const response = await fetch(`${baseUrl}/status`, {
|
|
186
|
+
signal: AbortSignal.timeout(10000),
|
|
187
|
+
headers: this.getHeaders(),
|
|
188
|
+
});
|
|
189
|
+
if (!response.ok)
|
|
190
|
+
continue;
|
|
191
|
+
const data = await response.json();
|
|
192
|
+
spinner.stop();
|
|
193
|
+
console.log();
|
|
194
|
+
console.log(chalk_1.default.bold.white(` ${logger_js_1.CH.hLine.repeat(3)} Legion Infrastructure ${logger_js_1.CH.hLine.repeat(37)}`));
|
|
195
|
+
console.log();
|
|
196
|
+
console.log(chalk_1.default.gray(` Hyper Loop: `) + chalk_1.default.green('online'));
|
|
197
|
+
if (data.workers || data.active_workers) {
|
|
198
|
+
const count = data.workers || data.active_workers;
|
|
199
|
+
console.log(chalk_1.default.gray(` Active workers: `) + chalk_1.default.white(String(typeof count === 'number' ? count : Object.keys(count).length)));
|
|
200
|
+
}
|
|
201
|
+
if (data.remotes || data.infrastructure) {
|
|
202
|
+
const remotes = data.remotes || data.infrastructure;
|
|
203
|
+
if (typeof remotes === 'object') {
|
|
204
|
+
for (const [name, status] of Object.entries(remotes)) {
|
|
205
|
+
const icon = status?.reachable ? chalk_1.default.green(logger_js_1.CH.success) : chalk_1.default.red(logger_js_1.CH.error);
|
|
206
|
+
console.log(` ${icon} ${chalk_1.default.white(name)}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
console.log();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
spinner.stop();
|
|
218
|
+
console.log();
|
|
219
|
+
console.log(chalk_1.default.gray(' Hyper Loop: ') + chalk_1.default.red('offline'));
|
|
220
|
+
console.log();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
exports.LegionCommand = LegionCommand;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Config } from '../utils/config.js';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
interface ReplayOptions {
|
|
4
|
+
speed?: number;
|
|
5
|
+
json?: boolean;
|
|
6
|
+
project?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class ReplayCommand {
|
|
9
|
+
private config;
|
|
10
|
+
private logger;
|
|
11
|
+
constructor(config: Config, logger: Logger);
|
|
12
|
+
private getHeaders;
|
|
13
|
+
private getBaseUrl;
|
|
14
|
+
private sleep;
|
|
15
|
+
run(runId: string, options: ReplayOptions): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ReplayCommand = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* replay.ts — Replay events from a V3 agent run step-by-step.
|
|
9
|
+
*/
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
12
|
+
class ReplayCommand {
|
|
13
|
+
config;
|
|
14
|
+
logger;
|
|
15
|
+
constructor(config, logger) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
}
|
|
19
|
+
getHeaders() {
|
|
20
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
21
|
+
const token = process.env.VIGTHORIA_TOKEN ||
|
|
22
|
+
process.env.VIGTHORIA_AUTH_TOKEN ||
|
|
23
|
+
this.config.get('authToken');
|
|
24
|
+
if (token) {
|
|
25
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
26
|
+
headers['Cookie'] = `vigthoria-auth-token=${token}`;
|
|
27
|
+
}
|
|
28
|
+
const serviceKey = process.env.VIGTHORIA_V3_SERVICE_KEY;
|
|
29
|
+
if (serviceKey)
|
|
30
|
+
headers['X-Service-Key'] = serviceKey;
|
|
31
|
+
return headers;
|
|
32
|
+
}
|
|
33
|
+
getBaseUrl() {
|
|
34
|
+
return (process.env.VIGTHORIA_V3_AGENT_URL ||
|
|
35
|
+
process.env.V3_AGENT_URL ||
|
|
36
|
+
'http://127.0.0.1:8030');
|
|
37
|
+
}
|
|
38
|
+
sleep(ms) {
|
|
39
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
40
|
+
}
|
|
41
|
+
async run(runId, options) {
|
|
42
|
+
const speed = options.speed || 200;
|
|
43
|
+
const project = options.project || process.cwd();
|
|
44
|
+
const spinner = (0, logger_js_1.createSpinner)(`Loading events for run ${runId}...`).start();
|
|
45
|
+
try {
|
|
46
|
+
const baseUrl = this.getBaseUrl();
|
|
47
|
+
const params = new URLSearchParams({ workspace_root: project });
|
|
48
|
+
const resp = await fetch(`${baseUrl}/api/runs/${encodeURIComponent(runId)}/events?${params}`, {
|
|
49
|
+
headers: this.getHeaders(),
|
|
50
|
+
});
|
|
51
|
+
if (!resp.ok) {
|
|
52
|
+
spinner.stop();
|
|
53
|
+
if (resp.status === 404) {
|
|
54
|
+
this.logger.error(`No event log found for run ${runId}. Events are only recorded for runs made after the replay system was deployed.`);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this.logger.error(`Failed to fetch events: ${resp.status} ${resp.statusText}`);
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const data = (await resp.json());
|
|
62
|
+
spinner.stop();
|
|
63
|
+
if (options.json) {
|
|
64
|
+
console.log(JSON.stringify(data, null, 2));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.log(chalk_1.default.bold(`\n${logger_js_1.CH.success} Replaying run ${chalk_1.default.cyan(runId)} (${data.count} events)\n`));
|
|
68
|
+
let toolCallNum = 0;
|
|
69
|
+
for (let i = 0; i < data.events.length; i++) {
|
|
70
|
+
const evt = data.events[i];
|
|
71
|
+
const type = evt.type || 'unknown';
|
|
72
|
+
const idx = chalk_1.default.dim(`[${String(i + 1).padStart(3)}/${data.count}]`);
|
|
73
|
+
switch (type) {
|
|
74
|
+
case 'context':
|
|
75
|
+
console.log(`${idx} ${chalk_1.default.blue('context')} workspace=${evt.workspace_root || '?'}`);
|
|
76
|
+
break;
|
|
77
|
+
case 'start':
|
|
78
|
+
console.log(`${idx} ${chalk_1.default.green('▶ START')} task=${evt.task_id || '?'} ${evt.continuation ? chalk_1.default.dim(`(continuation #${evt.continuation})`) : ''}`);
|
|
79
|
+
break;
|
|
80
|
+
case 'thinking':
|
|
81
|
+
console.log(`${idx} ${chalk_1.default.dim('💭 thinking')} ${(evt.content || '').substring(0, 100)}`);
|
|
82
|
+
break;
|
|
83
|
+
case 'plan':
|
|
84
|
+
const taskCount = evt.tasks?.length || 0;
|
|
85
|
+
console.log(`${idx} ${chalk_1.default.magenta('📋 PLAN')} ${taskCount} tasks`);
|
|
86
|
+
if (evt.tasks) {
|
|
87
|
+
for (const t of evt.tasks.slice(0, 5)) {
|
|
88
|
+
console.log(` ${chalk_1.default.dim('├')} ${t.id || '?'}: ${(t.title || '').substring(0, 60)}`);
|
|
89
|
+
}
|
|
90
|
+
if (taskCount > 5)
|
|
91
|
+
console.log(` ${chalk_1.default.dim('└')} ...and ${taskCount - 5} more`);
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
case 'tool_call':
|
|
95
|
+
toolCallNum++;
|
|
96
|
+
console.log(`${idx} ${chalk_1.default.yellow(`🔧 tool_call #${toolCallNum}`)} ${chalk_1.default.bold(evt.name || '?')}(${JSON.stringify(evt.arguments || {}).substring(0, 80)})`);
|
|
97
|
+
break;
|
|
98
|
+
case 'tool_result': {
|
|
99
|
+
const success = evt.success !== false;
|
|
100
|
+
const icon = success ? chalk_1.default.green('✓') : chalk_1.default.red('✗');
|
|
101
|
+
const output = (evt.output || '').substring(0, 120);
|
|
102
|
+
console.log(`${idx} ${icon} ${evt.name || '?'}: ${chalk_1.default.dim(output)}`);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case 'message':
|
|
106
|
+
console.log(`${idx} ${chalk_1.default.cyan('💬 message')} ${(evt.content || '').substring(0, 120)}`);
|
|
107
|
+
break;
|
|
108
|
+
case 'complete': {
|
|
109
|
+
const summary = (evt.summary || '').substring(0, 200);
|
|
110
|
+
const seal = evt.seal_score ? ` [${evt.seal_score.tier} ${evt.seal_score.overall}]` : '';
|
|
111
|
+
console.log(`${idx} ${chalk_1.default.green(`✅ COMPLETE`)}${chalk_1.default.yellow(seal)} ${evt.iterations || '?'} iterations, ${evt.tool_calls || '?'} tool calls`);
|
|
112
|
+
if (summary)
|
|
113
|
+
console.log(` ${chalk_1.default.dim(summary)}`);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case 'error':
|
|
117
|
+
console.log(`${idx} ${chalk_1.default.red('❌ ERROR')} ${(evt.message || '').substring(0, 200)}`);
|
|
118
|
+
if (evt.checkpointed)
|
|
119
|
+
console.log(` ${chalk_1.default.yellow('Checkpointed — can be continued or forked')}`);
|
|
120
|
+
break;
|
|
121
|
+
case 'file_mutation':
|
|
122
|
+
console.log(`${idx} ${chalk_1.default.blue('📁 file_mutation')} ${evt.kind || '?'} ${evt.path || '?'}`);
|
|
123
|
+
break;
|
|
124
|
+
default:
|
|
125
|
+
console.log(`${idx} ${chalk_1.default.dim(type)} ${JSON.stringify(evt).substring(0, 100)}`);
|
|
126
|
+
}
|
|
127
|
+
// Delay between events for replay effect
|
|
128
|
+
if (i < data.events.length - 1) {
|
|
129
|
+
await this.sleep(speed);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
console.log(chalk_1.default.bold(`\n${logger_js_1.CH.success} Replay complete (${data.count} events)\n`));
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
spinner.stop();
|
|
136
|
+
this.logger.error(`Replay error: ${err.message}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.ReplayCommand = ReplayCommand;
|
package/dist/index.js
CHANGED
|
@@ -67,6 +67,10 @@ const deploy_js_1 = require("./commands/deploy.js");
|
|
|
67
67
|
const bridge_js_1 = require("./commands/bridge.js");
|
|
68
68
|
const workflow_js_1 = require("./commands/workflow.js");
|
|
69
69
|
const preview_js_1 = require("./commands/preview.js");
|
|
70
|
+
const legion_js_1 = require("./commands/legion.js");
|
|
71
|
+
const history_js_1 = require("./commands/history.js");
|
|
72
|
+
const replay_js_1 = require("./commands/replay.js");
|
|
73
|
+
const fork_js_1 = require("./commands/fork.js");
|
|
70
74
|
const config_js_2 = require("./utils/config.js");
|
|
71
75
|
const logger_js_1 = require("./utils/logger.js");
|
|
72
76
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -685,6 +689,55 @@ async function main() {
|
|
|
685
689
|
screenshot: options.screenshot,
|
|
686
690
|
});
|
|
687
691
|
});
|
|
692
|
+
// ==================== LEGION COMMAND ====================
|
|
693
|
+
program
|
|
694
|
+
.command('legion [request]')
|
|
695
|
+
.description('Run parallel tasks via Hyper Loop Legion orchestrator')
|
|
696
|
+
.option('--workers', 'List available Legion workers')
|
|
697
|
+
.option('--status', 'Show Legion infrastructure status')
|
|
698
|
+
.option('-w, --worker <name>', 'Execute a specific worker')
|
|
699
|
+
.option('-p, --project <path>', 'Project directory', process.cwd())
|
|
700
|
+
.action(async (request, options) => {
|
|
701
|
+
const legion = new legion_js_1.LegionCommand(config, logger);
|
|
702
|
+
await legion.run(request, {
|
|
703
|
+
workers: options.workers,
|
|
704
|
+
status: options.status,
|
|
705
|
+
worker: options.worker,
|
|
706
|
+
project: options.project,
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
// ==================== REPLAY & FORK COMMANDS ====================
|
|
710
|
+
program
|
|
711
|
+
.command('history')
|
|
712
|
+
.alias('runs')
|
|
713
|
+
.description('List recent V3 agent runs')
|
|
714
|
+
.option('-n, --limit <count>', 'Number of runs to show', '20')
|
|
715
|
+
.option('-p, --project <path>', 'Project directory', process.cwd())
|
|
716
|
+
.option('--json', 'Machine-readable JSON output', false)
|
|
717
|
+
.action(async (options) => {
|
|
718
|
+
const history = new history_js_1.HistoryCommand(config, logger);
|
|
719
|
+
await history.run({ limit: parseInt(options.limit, 10) || 20, json: options.json, project: options.project });
|
|
720
|
+
});
|
|
721
|
+
program
|
|
722
|
+
.command('replay <runId>')
|
|
723
|
+
.description('Replay events from a V3 agent run step-by-step')
|
|
724
|
+
.option('-s, --speed <ms>', 'Delay between events in ms', '200')
|
|
725
|
+
.option('-p, --project <path>', 'Project directory', process.cwd())
|
|
726
|
+
.option('--json', 'Machine-readable JSON output', false)
|
|
727
|
+
.action(async (runId, options) => {
|
|
728
|
+
const replay = new replay_js_1.ReplayCommand(config, logger);
|
|
729
|
+
await replay.run(runId, { speed: parseInt(options.speed, 10) || 200, json: options.json, project: options.project });
|
|
730
|
+
});
|
|
731
|
+
program
|
|
732
|
+
.command('fork <runId> [message]')
|
|
733
|
+
.description('Fork from an existing V3 agent run with new instructions')
|
|
734
|
+
.option('-e, --event-index <index>', 'Fork from specific event index', '0')
|
|
735
|
+
.option('-p, --project <path>', 'Project directory', process.cwd())
|
|
736
|
+
.option('--json', 'Machine-readable JSON output', false)
|
|
737
|
+
.action(async (runId, message, options) => {
|
|
738
|
+
const fork = new fork_js_1.ForkCommand(config, logger);
|
|
739
|
+
await fork.run(runId, message || '', { eventIndex: parseInt(options.eventIndex, 10) || 0, project: options.project, json: options.json });
|
|
740
|
+
});
|
|
688
741
|
// ==================== AUTH COMMANDS ====================
|
|
689
742
|
// Auth commands
|
|
690
743
|
program
|