vigthoria-cli 1.8.12 → 1.8.14
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/cancel.d.ts +18 -0
- package/dist/commands/cancel.js +144 -0
- package/dist/index.js +12 -1
- package/dist/utils/tools.d.ts +1 -0
- package/dist/utils/tools.js +83 -0
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Config } from '../utils/config.js';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
interface CancelOptions {
|
|
4
|
+
all?: boolean;
|
|
5
|
+
list?: boolean;
|
|
6
|
+
json?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class CancelCommand {
|
|
9
|
+
private config;
|
|
10
|
+
private logger;
|
|
11
|
+
constructor(config: Config, logger: Logger);
|
|
12
|
+
private getHeaders;
|
|
13
|
+
private getBaseUrl;
|
|
14
|
+
private listActive;
|
|
15
|
+
private cancelOne;
|
|
16
|
+
run(contextId: string | undefined, options: CancelOptions): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
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.CancelCommand = void 0;
|
|
7
|
+
const api_js_1 = require("../utils/api.js");
|
|
8
|
+
/**
|
|
9
|
+
* cancel.ts — Cancel an in-flight V3 agent run, or cancel all active runs.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* vig cancel <context_id> Cancel a single run
|
|
13
|
+
* vig cancel --all Cancel every currently-active run
|
|
14
|
+
* vig cancel --list Just list active runs (no cancellation)
|
|
15
|
+
*/
|
|
16
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
17
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
18
|
+
class CancelCommand {
|
|
19
|
+
config;
|
|
20
|
+
logger;
|
|
21
|
+
constructor(config, logger) {
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.logger = logger;
|
|
24
|
+
}
|
|
25
|
+
getHeaders() {
|
|
26
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
27
|
+
const token = process.env.VIGTHORIA_TOKEN ||
|
|
28
|
+
process.env.VIGTHORIA_AUTH_TOKEN ||
|
|
29
|
+
this.config.get('authToken');
|
|
30
|
+
if (token) {
|
|
31
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
32
|
+
headers['Cookie'] = `vigthoria-auth-token=${token}`;
|
|
33
|
+
}
|
|
34
|
+
const serviceKey = process.env.VIGTHORIA_V3_SERVICE_KEY;
|
|
35
|
+
if (serviceKey)
|
|
36
|
+
headers['X-Service-Key'] = serviceKey;
|
|
37
|
+
return headers;
|
|
38
|
+
}
|
|
39
|
+
getBaseUrl() {
|
|
40
|
+
const configuredApiUrl = String(this.config.get('apiUrl') || 'https://coder.vigthoria.io').replace(/\/$/, '');
|
|
41
|
+
const allowLocal = (0, api_js_1.isServerRuntime)() && process.env.VIGTHORIA_ALLOW_LOCAL_V3_AGENT === '1';
|
|
42
|
+
return (process.env.VIGTHORIA_V3_AGENT_URL ||
|
|
43
|
+
process.env.V3_AGENT_URL ||
|
|
44
|
+
(allowLocal ? 'http://127.0.0.1:8030' : null) ||
|
|
45
|
+
configuredApiUrl);
|
|
46
|
+
}
|
|
47
|
+
async listActive(baseUrl) {
|
|
48
|
+
const resp = await fetch(`${baseUrl}/api/runs/active`, { headers: this.getHeaders() });
|
|
49
|
+
if (!resp.ok) {
|
|
50
|
+
if (resp.status === 404 || resp.status === 502 || resp.status === 503) {
|
|
51
|
+
this.logger.error(`V3 cancel API is not available on ${baseUrl}. Backend may not expose /api/runs/active. ` +
|
|
52
|
+
`(Requires V3 agent server.py with the cancel endpoint patched in.)`);
|
|
53
|
+
}
|
|
54
|
+
else if (resp.status === 401 || resp.status === 403) {
|
|
55
|
+
this.logger.error(`Authentication failed (${resp.status}). Run \`vig login\` first.`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
this.logger.error(`Failed to list active runs: ${resp.status} ${resp.statusText}`);
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return (await resp.json());
|
|
63
|
+
}
|
|
64
|
+
async cancelOne(baseUrl, contextId) {
|
|
65
|
+
const resp = await fetch(`${baseUrl}/api/runs/${encodeURIComponent(contextId)}/cancel`, { method: 'POST', headers: this.getHeaders() });
|
|
66
|
+
if (!resp.ok) {
|
|
67
|
+
if (resp.status === 404) {
|
|
68
|
+
this.logger.warn(`No active run for context_id=${contextId}`);
|
|
69
|
+
}
|
|
70
|
+
else if (resp.status === 401 || resp.status === 403) {
|
|
71
|
+
this.logger.error(`Authentication failed (${resp.status}). Run \`vig login\` first.`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.logger.error(`Cancel failed for ${contextId}: ${resp.status} ${resp.statusText}`);
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return (await resp.json());
|
|
79
|
+
}
|
|
80
|
+
async run(contextId, options) {
|
|
81
|
+
const baseUrl = this.getBaseUrl();
|
|
82
|
+
if (options.list) {
|
|
83
|
+
const spinner = (0, logger_js_1.createSpinner)('Loading active runs...').start();
|
|
84
|
+
const active = await this.listActive(baseUrl);
|
|
85
|
+
spinner.stop();
|
|
86
|
+
if (!active)
|
|
87
|
+
return;
|
|
88
|
+
if (options.json) {
|
|
89
|
+
console.log(JSON.stringify(active, null, 2));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (active.active_count === 0) {
|
|
93
|
+
this.logger.info(chalk_1.default.dim('No active runs.'));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
console.log(chalk_1.default.bold(`\n${logger_js_1.CH.success} Active Runs (${active.active_count}/${active.max_concurrent_runs})\n`));
|
|
97
|
+
for (const cid of active.active_context_ids) {
|
|
98
|
+
console.log(` ${chalk_1.default.cyan(cid)}`);
|
|
99
|
+
}
|
|
100
|
+
console.log();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (options.all) {
|
|
104
|
+
const spinner = (0, logger_js_1.createSpinner)('Loading active runs...').start();
|
|
105
|
+
const active = await this.listActive(baseUrl);
|
|
106
|
+
spinner.stop();
|
|
107
|
+
if (!active)
|
|
108
|
+
return;
|
|
109
|
+
if (active.active_count === 0) {
|
|
110
|
+
this.logger.info(chalk_1.default.dim('No active runs to cancel.'));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const results = [];
|
|
114
|
+
for (const cid of active.active_context_ids) {
|
|
115
|
+
const sp = (0, logger_js_1.createSpinner)(`Cancelling ${cid}...`).start();
|
|
116
|
+
const r = await this.cancelOne(baseUrl, cid);
|
|
117
|
+
sp.stop();
|
|
118
|
+
if (r) {
|
|
119
|
+
results.push(r);
|
|
120
|
+
this.logger.success(`Cancelled ${chalk_1.default.cyan(cid)} (asyncio_cancelled=${r.asyncio_cancelled})`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (options.json)
|
|
124
|
+
console.log(JSON.stringify({ cancelled: results }, null, 2));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!contextId) {
|
|
128
|
+
this.logger.error('Usage: vig cancel <context_id> | --all | --list');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const spinner = (0, logger_js_1.createSpinner)(`Cancelling ${contextId}...`).start();
|
|
132
|
+
const r = await this.cancelOne(baseUrl, contextId);
|
|
133
|
+
spinner.stop();
|
|
134
|
+
if (!r)
|
|
135
|
+
return;
|
|
136
|
+
if (options.json) {
|
|
137
|
+
console.log(JSON.stringify(r, null, 2));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this.logger.success(`Cancelled ${chalk_1.default.cyan(contextId)} ` +
|
|
141
|
+
`(asyncio_cancelled=${r.asyncio_cancelled}, active_runs_after=${r.active_runs_after})`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.CancelCommand = CancelCommand;
|
package/dist/index.js
CHANGED
|
@@ -71,6 +71,7 @@ const legion_js_1 = require("./commands/legion.js");
|
|
|
71
71
|
const history_js_1 = require("./commands/history.js");
|
|
72
72
|
const replay_js_1 = require("./commands/replay.js");
|
|
73
73
|
const fork_js_1 = require("./commands/fork.js");
|
|
74
|
+
const cancel_js_1 = require("./commands/cancel.js");
|
|
74
75
|
const config_js_2 = require("./utils/config.js");
|
|
75
76
|
const logger_js_1 = require("./utils/logger.js");
|
|
76
77
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -163,7 +164,7 @@ async function main() {
|
|
|
163
164
|
const directPromptRequested = process.argv.includes('--prompt') || process.argv.includes('-P');
|
|
164
165
|
if (invokedBinaryName === 'vigthoria-chat') {
|
|
165
166
|
const knownCommands = new Set([
|
|
166
|
-
'chat', 'chat-resume', 'agent', 'edit', 'generate', 'explain', 'fix', 'review',
|
|
167
|
+
'chat', 'chat-resume', 'agent', 'edit', 'generate', 'explain', 'fix', 'review', 'cancel',
|
|
167
168
|
'hub', 'repo', 'deploy', 'operator', 'workflow', 'flow', 'login', 'logout', 'status', 'config', 'update',
|
|
168
169
|
'--help', '-h', '--version', '-V', 'help', 'version',
|
|
169
170
|
]);
|
|
@@ -770,6 +771,16 @@ Examples:
|
|
|
770
771
|
const fork = new fork_js_1.ForkCommand(config, logger);
|
|
771
772
|
await fork.run(runId, message || '', { eventIndex: parseInt(options.eventIndex, 10) || 0, project: options.project, json: options.json });
|
|
772
773
|
});
|
|
774
|
+
program
|
|
775
|
+
.command('cancel [contextId]')
|
|
776
|
+
.description('Cancel an in-flight V3 agent run (by context_id), or use --all to cancel every active run')
|
|
777
|
+
.option('-a, --all', 'Cancel all currently-active runs', false)
|
|
778
|
+
.option('-l, --list', 'List active runs without cancelling', false)
|
|
779
|
+
.option('--json', 'Machine-readable JSON output', false)
|
|
780
|
+
.action(async (contextId, options) => {
|
|
781
|
+
const cancel = new cancel_js_1.CancelCommand(config, logger);
|
|
782
|
+
await cancel.run(contextId, { all: options.all, list: options.list, json: options.json });
|
|
783
|
+
});
|
|
773
784
|
// ==================== AUTH COMMANDS ====================
|
|
774
785
|
// Auth commands
|
|
775
786
|
program
|
package/dist/utils/tools.d.ts
CHANGED
package/dist/utils/tools.js
CHANGED
|
@@ -399,6 +399,19 @@ class AgenticTools {
|
|
|
399
399
|
riskLevel: 'low',
|
|
400
400
|
category: 'read',
|
|
401
401
|
},
|
|
402
|
+
{
|
|
403
|
+
name: 'browser',
|
|
404
|
+
description: 'Drive a real headless Chromium via the Vigthoria DevTools Bridge. Use to capture a page (screenshot+HTML+metrics), collect console/page errors, or run a quick smoke test on a URL. Cross-platform.',
|
|
405
|
+
parameters: [
|
|
406
|
+
{ name: 'mode', description: "One of: 'capture' (screenshot + html + metrics), 'errors' (console + page errors), 'test' (quick smoke test)", required: true },
|
|
407
|
+
{ name: 'url', description: 'Target URL (must start with http:// or https://)', required: true },
|
|
408
|
+
{ name: 'include_screenshot', description: "For mode=capture: 'true' to include base64 screenshot (default true), 'false' to skip", required: false },
|
|
409
|
+
],
|
|
410
|
+
requiresPermission: true,
|
|
411
|
+
dangerous: false,
|
|
412
|
+
riskLevel: 'low',
|
|
413
|
+
category: 'read',
|
|
414
|
+
},
|
|
402
415
|
{
|
|
403
416
|
name: 'ssh_exec',
|
|
404
417
|
description: 'Execute a command on a remote server via SSH (useful for Unix commands from Windows)',
|
|
@@ -602,6 +615,8 @@ class AgenticTools {
|
|
|
602
615
|
return this.repo(call.args);
|
|
603
616
|
case 'fetch_url':
|
|
604
617
|
return this.fetchUrl(call.args);
|
|
618
|
+
case 'browser':
|
|
619
|
+
return this.browserTool(call.args);
|
|
605
620
|
case 'ssh_exec':
|
|
606
621
|
return this.sshExec(call.args);
|
|
607
622
|
case 'task':
|
|
@@ -1857,6 +1872,74 @@ class AgenticTools {
|
|
|
1857
1872
|
return this.createErrorResult(ToolErrorType.NETWORK_ERROR, error.message, 'Check your internet connection and ensure the URL is correct.');
|
|
1858
1873
|
}
|
|
1859
1874
|
}
|
|
1875
|
+
async browserTool(args) {
|
|
1876
|
+
const mode = (args.mode || '').toLowerCase();
|
|
1877
|
+
const url = args.url;
|
|
1878
|
+
if (!['capture', 'errors', 'test'].includes(mode)) {
|
|
1879
|
+
return this.createErrorResult(ToolErrorType.INVALID_ARGS, `Invalid mode: ${args.mode}`, "mode must be one of: 'capture', 'errors', 'test'");
|
|
1880
|
+
}
|
|
1881
|
+
if (!url || (!url.startsWith('http://') && !url.startsWith('https://'))) {
|
|
1882
|
+
return this.createErrorResult(ToolErrorType.INVALID_ARGS, 'URL must start with http:// or https://', 'Provide a valid URL, e.g., https://example.com');
|
|
1883
|
+
}
|
|
1884
|
+
const host = process.env.VIGTHORIA_DEVTOOLS_BRIDGE_HOST || '127.0.0.1';
|
|
1885
|
+
const port = process.env.VIGTHORIA_DEVTOOLS_BRIDGE_PORT || '4016';
|
|
1886
|
+
const base = `http://${host}:${port}`;
|
|
1887
|
+
const toolName = mode === 'capture' ? 'capture_state' :
|
|
1888
|
+
mode === 'errors' ? 'get_page_errors' :
|
|
1889
|
+
'run_test';
|
|
1890
|
+
const params = { url };
|
|
1891
|
+
if (mode === 'capture' && (args.include_screenshot || '').toLowerCase() === 'false') {
|
|
1892
|
+
params.includeScreenshot = false;
|
|
1893
|
+
}
|
|
1894
|
+
if (mode === 'test') {
|
|
1895
|
+
params.testType = 'quick';
|
|
1896
|
+
}
|
|
1897
|
+
const controller = new AbortController();
|
|
1898
|
+
const timeoutMs = mode === 'test' ? 60000 : 45000;
|
|
1899
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
1900
|
+
try {
|
|
1901
|
+
const res = await fetch(`${base}/api/mcp/tools`, {
|
|
1902
|
+
method: 'POST',
|
|
1903
|
+
headers: { 'Content-Type': 'application/json', 'User-Agent': 'Vigthoria-CLI-Browser/1.0' },
|
|
1904
|
+
body: JSON.stringify({ tool: toolName, params }),
|
|
1905
|
+
signal: controller.signal,
|
|
1906
|
+
});
|
|
1907
|
+
clearTimeout(timeout);
|
|
1908
|
+
if (!res.ok) {
|
|
1909
|
+
return this.createErrorResult(ToolErrorType.NETWORK_ERROR, `DevTools Bridge HTTP ${res.status}`, 'Ensure the bridge is running (vigthoria bridge status). On a developer workstation it must be installed and started locally.');
|
|
1910
|
+
}
|
|
1911
|
+
const data = await res.json();
|
|
1912
|
+
if (!data.success) {
|
|
1913
|
+
return this.createErrorResult(ToolErrorType.NETWORK_ERROR, data.error || 'DevTools Bridge returned unsuccessful response', 'Check the bridge logs.');
|
|
1914
|
+
}
|
|
1915
|
+
let payload;
|
|
1916
|
+
if (mode === 'capture' && data.result && typeof data.result === 'object') {
|
|
1917
|
+
const r = data.result;
|
|
1918
|
+
const trimmed = {
|
|
1919
|
+
url: r.url,
|
|
1920
|
+
timestamp: r.timestamp,
|
|
1921
|
+
metrics: r.metrics,
|
|
1922
|
+
performance: r.performance,
|
|
1923
|
+
html_excerpt: typeof r.html === 'string' ? r.html.slice(0, 4000) : undefined,
|
|
1924
|
+
screenshot_data_url_length: typeof r.screenshot === 'string' ? r.screenshot.length : 0,
|
|
1925
|
+
};
|
|
1926
|
+
payload = JSON.stringify(trimmed, null, 2);
|
|
1927
|
+
}
|
|
1928
|
+
else {
|
|
1929
|
+
payload = JSON.stringify(data.result, null, 2).slice(0, 8000);
|
|
1930
|
+
}
|
|
1931
|
+
return {
|
|
1932
|
+
success: true,
|
|
1933
|
+
output: payload,
|
|
1934
|
+
metadata: { tool: 'browser', mode, url },
|
|
1935
|
+
};
|
|
1936
|
+
}
|
|
1937
|
+
catch (e) {
|
|
1938
|
+
clearTimeout(timeout);
|
|
1939
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1940
|
+
return this.createErrorResult(ToolErrorType.NETWORK_ERROR, `Browser tool failed: ${msg}`, "Verify the DevTools Bridge is reachable on host:port (defaults 127.0.0.1:4016).");
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1860
1943
|
/**
|
|
1861
1944
|
* Execute command via SSH on remote server
|
|
1862
1945
|
* Useful for running Unix commands from Windows
|