vigthoria-cli 1.9.2 → 1.9.5
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 +15 -5
- package/dist/commands/auth.d.ts +28 -38
- package/dist/commands/auth.js +389 -320
- package/dist/commands/chat.d.ts +3 -0
- package/dist/commands/chat.js +66 -15
- package/dist/commands/index.js +1 -1
- package/dist/commands/legion.d.ts +22 -19
- package/dist/commands/legion.js +550 -132
- package/dist/commands/preview.js +32 -7
- package/dist/commands/repo.js +19 -13
- package/dist/commands/update.d.ts +9 -0
- package/dist/commands/update.js +235 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +114 -33
- package/dist/utils/api.d.ts +25 -70
- package/dist/utils/api.js +784 -695
- package/dist/utils/tools.d.ts +11 -0
- package/dist/utils/tools.js +222 -0
- package/package.json +7 -1
package/dist/commands/preview.js
CHANGED
|
@@ -254,14 +254,36 @@ class PreviewCommand {
|
|
|
254
254
|
* Open URL in default browser
|
|
255
255
|
*/
|
|
256
256
|
openBrowser(url) {
|
|
257
|
-
const {
|
|
257
|
+
const { execFile } = require('child_process');
|
|
258
258
|
const platform = process.platform;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
259
|
+
// Validate URL scheme before passing to OS — prevents shell injection via crafted URLs
|
|
260
|
+
try {
|
|
261
|
+
const parsed = new URL(url);
|
|
262
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:')
|
|
263
|
+
return;
|
|
264
|
+
const safeUrl = parsed.href;
|
|
265
|
+
if (platform === 'darwin') {
|
|
266
|
+
execFile('open', [safeUrl], (err) => {
|
|
267
|
+
if (err)
|
|
268
|
+
this.logger.debug(`Could not auto-open browser: ${err.message}`);
|
|
269
|
+
});
|
|
263
270
|
}
|
|
264
|
-
|
|
271
|
+
else if (platform === 'win32') {
|
|
272
|
+
execFile('cmd', ['/c', 'start', '', safeUrl], (err) => {
|
|
273
|
+
if (err)
|
|
274
|
+
this.logger.debug(`Could not auto-open browser: ${err.message}`);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
execFile('xdg-open', [safeUrl], (err) => {
|
|
279
|
+
if (err)
|
|
280
|
+
this.logger.debug(`Could not auto-open browser: ${err.message}`);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
this.logger.debug(`Skipping browser open — invalid URL: ${url}`);
|
|
286
|
+
}
|
|
265
287
|
}
|
|
266
288
|
/**
|
|
267
289
|
* Show consolidated diff of recent agent changes using git
|
|
@@ -322,7 +344,10 @@ class PreviewCommand {
|
|
|
322
344
|
let oldContent = '';
|
|
323
345
|
if (modified.includes(relPath)) {
|
|
324
346
|
try {
|
|
325
|
-
|
|
347
|
+
// Validate relPath is a safe relative path before interpolating — prevents injection
|
|
348
|
+
if (/^[a-zA-Z0-9_\-./]+$/.test(relPath) && !relPath.includes('..')) {
|
|
349
|
+
oldContent = execSync(`git show HEAD:${relPath}`, { cwd: projectPath, encoding: 'utf-8' });
|
|
350
|
+
}
|
|
326
351
|
}
|
|
327
352
|
catch {
|
|
328
353
|
// File is new or not tracked
|
package/dist/commands/repo.js
CHANGED
|
@@ -460,17 +460,17 @@ class RepoCommand {
|
|
|
460
460
|
const os = require('os');
|
|
461
461
|
const platform = os.platform();
|
|
462
462
|
if (platform === 'win32') {
|
|
463
|
-
// Use PowerShell's Expand-Archive
|
|
464
|
-
const {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
});
|
|
463
|
+
// Use PowerShell's Expand-Archive — args as array to prevent injection
|
|
464
|
+
const { execFileSync } = await import('child_process');
|
|
465
|
+
execFileSync('powershell', [
|
|
466
|
+
'-NoProfile', '-NonInteractive', '-Command',
|
|
467
|
+
`Expand-Archive -Path '${tempArchive.replace(/'/g, "''")}' -DestinationPath '${outputPath.replace(/'/g, "''")}' -Force`
|
|
468
|
+
], { stdio: 'ignore', windowsHide: true });
|
|
469
469
|
}
|
|
470
470
|
else {
|
|
471
|
-
// Use unzip on Unix-like systems
|
|
472
|
-
const {
|
|
473
|
-
|
|
471
|
+
// Use unzip on Unix-like systems — args as array to prevent injection
|
|
472
|
+
const { execFileSync } = await import('child_process');
|
|
473
|
+
execFileSync('unzip', ['-o', tempArchive, '-d', outputPath], { stdio: 'ignore' });
|
|
474
474
|
}
|
|
475
475
|
fs.unlinkSync(tempArchive);
|
|
476
476
|
}
|
|
@@ -750,14 +750,20 @@ class RepoCommand {
|
|
|
750
750
|
console.log(chalk_1.default.bold(` ${data.url}\n`));
|
|
751
751
|
if (options.browser) {
|
|
752
752
|
try {
|
|
753
|
-
const {
|
|
753
|
+
const { execFileSync } = await import('child_process');
|
|
754
754
|
const platform = process.platform;
|
|
755
|
+
// Validate URL scheme before passing to OS — prevents shell injection
|
|
756
|
+
const parsedUrl = new URL(data.url);
|
|
757
|
+
if (parsedUrl.protocol !== 'https:' && parsedUrl.protocol !== 'http:') {
|
|
758
|
+
throw new Error('Unsafe URL scheme for browser open');
|
|
759
|
+
}
|
|
760
|
+
const safeUrl = parsedUrl.href;
|
|
755
761
|
if (platform === 'win32')
|
|
756
|
-
|
|
762
|
+
execFileSync('cmd', ['/c', 'start', '', safeUrl], { stdio: 'ignore', windowsHide: true });
|
|
757
763
|
else if (platform === 'darwin')
|
|
758
|
-
|
|
764
|
+
execFileSync('open', [safeUrl], { stdio: 'ignore' });
|
|
759
765
|
else
|
|
760
|
-
|
|
766
|
+
execFileSync('xdg-open', [safeUrl], { stdio: 'ignore' });
|
|
761
767
|
}
|
|
762
768
|
catch { /* browser open is optional */ }
|
|
763
769
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
|
|
2
|
+
interface UpdateOptions {
|
|
3
|
+
check?: boolean;
|
|
4
|
+
packageManager?: PackageManager;
|
|
5
|
+
global?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function updateCommand(options?: UpdateOptions): Promise<void>;
|
|
8
|
+
export declare function update(): Promise<void>;
|
|
9
|
+
export default updateCommand;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.updateCommand = updateCommand;
|
|
37
|
+
exports.update = update;
|
|
38
|
+
const node_child_process_1 = require("node:child_process");
|
|
39
|
+
const node_fs_1 = require("node:fs");
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
const node_util_1 = require("node:util");
|
|
42
|
+
const tools_js_1 = require("../utils/tools.js");
|
|
43
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
44
|
+
function markSuccessExit() {
|
|
45
|
+
process.exitCode = 0;
|
|
46
|
+
}
|
|
47
|
+
function markErrorExit() {
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
}
|
|
50
|
+
function findPackageJson(startDir) {
|
|
51
|
+
let currentDir = startDir;
|
|
52
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
53
|
+
const candidate = path.join(currentDir, 'package.json');
|
|
54
|
+
if ((0, node_fs_1.existsSync)(candidate)) {
|
|
55
|
+
return candidate;
|
|
56
|
+
}
|
|
57
|
+
const parentDir = path.join(currentDir, '..');
|
|
58
|
+
if (parentDir === currentDir) {
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
currentDir = parentDir;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
function readOwnPackageJson() {
|
|
66
|
+
const currentFile = __filename;
|
|
67
|
+
const packagePath = findPackageJson(path.join(currentFile, '..'));
|
|
68
|
+
if (!packagePath) {
|
|
69
|
+
throw new Error('Unable to locate package.json for the Vigthoria CLI.');
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse((0, node_fs_1.readFileSync)(packagePath, 'utf8'));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
76
|
+
throw new Error(`Unable to read package metadata: ${message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function compareVersions(a, b) {
|
|
80
|
+
const parse = (version) => version.replace(/^v/, '').split(/[.-]/).map((part) => {
|
|
81
|
+
const value = Number.parseInt(part, 10);
|
|
82
|
+
return Number.isNaN(value) ? 0 : value;
|
|
83
|
+
});
|
|
84
|
+
const left = parse(a);
|
|
85
|
+
const right = parse(b);
|
|
86
|
+
const length = Math.max(left.length, right.length);
|
|
87
|
+
for (let index = 0; index < length; index += 1) {
|
|
88
|
+
const leftValue = left[index] ?? 0;
|
|
89
|
+
const rightValue = right[index] ?? 0;
|
|
90
|
+
if (leftValue > rightValue)
|
|
91
|
+
return 1;
|
|
92
|
+
if (leftValue < rightValue)
|
|
93
|
+
return -1;
|
|
94
|
+
}
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
function quoteCmdArg(value) {
|
|
98
|
+
if (!/[\s"&()<>^|]/.test(value)) {
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
return `"${value.replace(/(\\*)"/g, '$1$1\\"')}"`;
|
|
102
|
+
}
|
|
103
|
+
function toPlatformCommand(command, args) {
|
|
104
|
+
if (process.platform !== 'win32') {
|
|
105
|
+
return { command, args };
|
|
106
|
+
}
|
|
107
|
+
const cmdPath = process.env.ComSpec || path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe');
|
|
108
|
+
return {
|
|
109
|
+
command: cmdPath,
|
|
110
|
+
args: ['/d', '/s', '/c', [command, ...args].map(quoteCmdArg).join(' ')],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
async function execPackageManager(command, args, timeout) {
|
|
114
|
+
const platformCommand = toPlatformCommand(command, args);
|
|
115
|
+
const { stdout } = await execFileAsync(platformCommand.command, platformCommand.args, {
|
|
116
|
+
timeout,
|
|
117
|
+
maxBuffer: 1024 * 1024,
|
|
118
|
+
windowsHide: true,
|
|
119
|
+
});
|
|
120
|
+
return stdout;
|
|
121
|
+
}
|
|
122
|
+
async function getLatestVersion(packageName) {
|
|
123
|
+
try {
|
|
124
|
+
const stdout = await execPackageManager('npm', ['view', packageName, 'version'], 30_000);
|
|
125
|
+
const latest = stdout.trim();
|
|
126
|
+
if (!latest) {
|
|
127
|
+
throw new Error('npm returned an empty version response');
|
|
128
|
+
}
|
|
129
|
+
return latest;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
133
|
+
throw new Error(`Unable to check the latest published version: ${message}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function getVersionInfo() {
|
|
137
|
+
const packageJson = readOwnPackageJson();
|
|
138
|
+
const packageName = packageJson.name;
|
|
139
|
+
const current = packageJson.version;
|
|
140
|
+
if (!packageName || !current) {
|
|
141
|
+
throw new Error('The CLI package metadata must include both name and version.');
|
|
142
|
+
}
|
|
143
|
+
const latest = await getLatestVersion(packageName);
|
|
144
|
+
return {
|
|
145
|
+
current,
|
|
146
|
+
latest,
|
|
147
|
+
packageName,
|
|
148
|
+
updateAvailable: compareVersions(current, latest) < 0,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function resolveInstallCommand(packageManager, packageName, installGlobal) {
|
|
152
|
+
const target = `${packageName}@latest`;
|
|
153
|
+
switch (packageManager) {
|
|
154
|
+
case 'pnpm':
|
|
155
|
+
return { command: 'pnpm', args: installGlobal ? ['add', '--global', target] : ['add', '-D', target] };
|
|
156
|
+
case 'yarn':
|
|
157
|
+
return { command: 'yarn', args: installGlobal ? ['global', 'add', target] : ['add', '--dev', target] };
|
|
158
|
+
case 'bun':
|
|
159
|
+
return { command: 'bun', args: installGlobal ? ['add', '--global', target] : ['add', '--dev', target] };
|
|
160
|
+
case 'npm':
|
|
161
|
+
default:
|
|
162
|
+
return { command: 'npm', args: installGlobal ? ['install', '--global', target] : ['install', '--save-dev', target] };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function formatCommand(command) {
|
|
166
|
+
return [command.command, ...command.args].map(quoteCmdArg).join(' ');
|
|
167
|
+
}
|
|
168
|
+
async function runInstall(packageManager, packageName, installGlobal) {
|
|
169
|
+
const installCommand = resolveInstallCommand(packageManager, packageName, installGlobal);
|
|
170
|
+
await execPackageManager(installCommand.command, installCommand.args, 120_000);
|
|
171
|
+
}
|
|
172
|
+
async function updateCommand(options = {}) {
|
|
173
|
+
try {
|
|
174
|
+
const info = await getVersionInfo();
|
|
175
|
+
console.log(`Vigthoria CLI current version: ${info.current}`);
|
|
176
|
+
console.log(`Vigthoria CLI latest version: ${info.latest}`);
|
|
177
|
+
if (!info.updateAvailable) {
|
|
178
|
+
console.log('You are already running the latest Vigthoria CLI.');
|
|
179
|
+
markSuccessExit();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
console.log(`Update available for ${info.packageName}: ${info.current} → ${info.latest}`);
|
|
183
|
+
if (options.check) {
|
|
184
|
+
markSuccessExit();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const packageManager = options.packageManager ?? 'npm';
|
|
188
|
+
const installGlobal = options.global ?? true;
|
|
189
|
+
console.log(`Installing ${info.packageName}@latest with ${packageManager}...`);
|
|
190
|
+
if (process.platform === 'win32') {
|
|
191
|
+
const installerResult = await (0, tools_js_1.installUpdateWindows)();
|
|
192
|
+
if (!installerResult.success && installerResult.error === 'ENOENT') {
|
|
193
|
+
const fallbackCommand = resolveInstallCommand(packageManager, info.packageName, installGlobal);
|
|
194
|
+
console.warn('Windows update installer was not found (ENOENT).');
|
|
195
|
+
console.warn('The bundled Windows installer may be missing from this installation.');
|
|
196
|
+
console.warn(`Manual installation command: ${formatCommand(fallbackCommand)}`);
|
|
197
|
+
console.warn('If automatic fallback fails, run the command above manually or download the latest Windows installer from the Vigthoria release page.');
|
|
198
|
+
await execPackageManager(fallbackCommand.command, fallbackCommand.args, 120_000);
|
|
199
|
+
console.log('Package manager installation finished successfully.');
|
|
200
|
+
}
|
|
201
|
+
else if (!installerResult.success) {
|
|
202
|
+
throw new Error(installerResult.error || 'Windows installer failed to start.');
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.log('Windows installer started successfully. Complete the installer prompts to finish updating.');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
await runInstall(packageManager, info.packageName, installGlobal);
|
|
210
|
+
console.log('Package manager installation finished successfully.');
|
|
211
|
+
}
|
|
212
|
+
console.log('Vigthoria CLI update complete.');
|
|
213
|
+
markSuccessExit();
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
217
|
+
console.error(`Update failed: ${message}`);
|
|
218
|
+
markErrorExit();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async function update() {
|
|
222
|
+
try {
|
|
223
|
+
await updateCommand();
|
|
224
|
+
if (process.exitCode === 1) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
markSuccessExit();
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
231
|
+
console.error(`Update failed: ${message}`);
|
|
232
|
+
markErrorExit();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
exports.default = updateCommand;
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* vigthoria workflow - Manage repeatable VigFlow workflows
|
|
16
16
|
* vigthoria operator - Start BMAD operator mode
|
|
17
17
|
*/
|
|
18
|
+
export declare function validateReleaseMetadata(): boolean;
|
|
18
19
|
export declare function setupErrorHandlers(): void;
|
|
19
|
-
export declare function main(): Promise<void>;
|
|
20
|
+
export declare function main(args: string[]): Promise<void>;
|
|
20
21
|
export declare const __cliErrorHandlingReady = true;
|
package/dist/index.js
CHANGED
|
@@ -54,6 +54,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
54
54
|
};
|
|
55
55
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
56
|
exports.__cliErrorHandlingReady = void 0;
|
|
57
|
+
exports.validateReleaseMetadata = validateReleaseMetadata;
|
|
57
58
|
exports.setupErrorHandlers = setupErrorHandlers;
|
|
58
59
|
exports.main = main;
|
|
59
60
|
const commander_1 = require("commander");
|
|
@@ -96,17 +97,19 @@ function getInvokedBinaryName() {
|
|
|
96
97
|
return path.basename(executable, path.extname(executable)).toLowerCase();
|
|
97
98
|
}
|
|
98
99
|
// Get version from package.json dynamically
|
|
100
|
+
function getPackageMetadataPaths() {
|
|
101
|
+
return [
|
|
102
|
+
path.join(__dirname, '..', 'package.json'),
|
|
103
|
+
path.join(__dirname, '..', '..', 'package.json'),
|
|
104
|
+
path.join(process.cwd(), 'package.json'),
|
|
105
|
+
path.join(process.cwd(), 'node_modules', 'vigthoria-cli', 'package.json'),
|
|
106
|
+
// Also check global npm paths on Windows
|
|
107
|
+
path.join(process.env.APPDATA || '', 'npm', 'node_modules', 'vigthoria-cli', 'package.json'),
|
|
108
|
+
];
|
|
109
|
+
}
|
|
99
110
|
function getVersion() {
|
|
100
111
|
try {
|
|
101
|
-
|
|
102
|
-
const possiblePaths = [
|
|
103
|
-
path.join(__dirname, '..', 'package.json'),
|
|
104
|
-
path.join(__dirname, '..', '..', 'package.json'),
|
|
105
|
-
path.join(process.cwd(), 'node_modules', 'vigthoria-cli', 'package.json'),
|
|
106
|
-
// Also check global npm paths on Windows
|
|
107
|
-
path.join(process.env.APPDATA || '', 'npm', 'node_modules', 'vigthoria-cli', 'package.json'),
|
|
108
|
-
];
|
|
109
|
-
for (const p of possiblePaths) {
|
|
112
|
+
for (const p of getPackageMetadataPaths()) {
|
|
110
113
|
if (fs.existsSync(p)) {
|
|
111
114
|
const pkg = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
112
115
|
if (pkg.name === 'vigthoria-cli') {
|
|
@@ -116,9 +119,59 @@ function getVersion() {
|
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
121
|
catch (e) {
|
|
119
|
-
|
|
122
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
123
|
+
if (process.env.VIGTHORIA_DEBUG_VERSION === '1') {
|
|
124
|
+
console.error(chalk_1.default.gray(`Unable to read package version: ${message}`));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return '1.9.3';
|
|
128
|
+
}
|
|
129
|
+
function validateReleaseMetadata() {
|
|
130
|
+
try {
|
|
131
|
+
const packagePath = getPackageMetadataPaths().find((candidate) => fs.existsSync(candidate));
|
|
132
|
+
if (!packagePath) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
136
|
+
const packageDir = path.dirname(packagePath);
|
|
137
|
+
const readmePath = path.join(packageDir, 'README.md');
|
|
138
|
+
if (!fs.existsSync(readmePath)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
const readme = fs.readFileSync(readmePath, 'utf8');
|
|
142
|
+
const bins = pkg.bin && typeof pkg.bin === 'object' ? pkg.bin : {};
|
|
143
|
+
const requiredReadmePhrases = [
|
|
144
|
+
'npm install -g vigthoria-cli',
|
|
145
|
+
'curl -fsSL https://cli.vigthoria.io/install.sh | bash',
|
|
146
|
+
'irm https://cli.vigthoria.io/install.ps1 | iex',
|
|
147
|
+
'vigthoria login',
|
|
148
|
+
'vigthoria chat',
|
|
149
|
+
'vig c',
|
|
150
|
+
'vigthoria edit',
|
|
151
|
+
'vigthoria update',
|
|
152
|
+
'vigthoria doctor',
|
|
153
|
+
'version 1.9.3',
|
|
154
|
+
'vigthoria-cli-1.9.3.tgz',
|
|
155
|
+
];
|
|
156
|
+
return (pkg.name === 'vigthoria-cli' &&
|
|
157
|
+
pkg.version === '1.9.3' &&
|
|
158
|
+
pkg.description === 'Vigthoria Coder CLI - AI-powered terminal coding assistant' &&
|
|
159
|
+
pkg.main === 'dist/index.js' &&
|
|
160
|
+
bins.vigthoria === 'dist/index.js' &&
|
|
161
|
+
bins.vig === 'dist/index.js' &&
|
|
162
|
+
bins['vigthoria-chat'] === 'dist/index.js' &&
|
|
163
|
+
requiredReadmePhrases.every((phrase) => readme.includes(phrase)) &&
|
|
164
|
+
/Version\s+1\.9\.3/i.test(readme) &&
|
|
165
|
+
!readme.includes('vigthoria-cli-1.9.2.tgz') &&
|
|
166
|
+
!/version\s+1\.9\.2/i.test(readme));
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
170
|
+
if (process.env.VIGTHORIA_DEBUG_VERSION === '1') {
|
|
171
|
+
console.error(chalk_1.default.gray(`Release metadata validation failed: ${message}`));
|
|
172
|
+
}
|
|
173
|
+
return false;
|
|
120
174
|
}
|
|
121
|
-
return '1.9.2';
|
|
122
175
|
}
|
|
123
176
|
const VERSION = getVersion();
|
|
124
177
|
const VIGTHORIA_DEFAULT_MANIFEST_URL = process.env.VIGTHORIA_UPDATE_MANIFEST_URL || "https://coder.vigthoria.io/releases/manifest.json";
|
|
@@ -177,6 +230,13 @@ async function installGlobalPackageWithNpm(packageSpec) {
|
|
|
177
230
|
const { execFileSync, execSync } = await import('child_process');
|
|
178
231
|
const attempts = [];
|
|
179
232
|
if (process.platform === 'win32') {
|
|
233
|
+
// First-choice: cmd.exe /c npm — resolves npm.cmd shims on any Windows Node setup
|
|
234
|
+
const comspec = process.env.COMSPEC || 'cmd.exe';
|
|
235
|
+
attempts.push({
|
|
236
|
+
label: 'cmd.exe /c npm',
|
|
237
|
+
command: comspec,
|
|
238
|
+
args: ['/c', 'npm', 'install', '-g', packageSpec],
|
|
239
|
+
});
|
|
180
240
|
const npmExecPath = process.env.npm_execpath;
|
|
181
241
|
if (npmExecPath && fs.existsSync(npmExecPath)) {
|
|
182
242
|
attempts.push({
|
|
@@ -403,14 +463,16 @@ function setupErrorHandlers() {
|
|
|
403
463
|
}
|
|
404
464
|
});
|
|
405
465
|
}
|
|
406
|
-
async function main() {
|
|
466
|
+
async function main(args) {
|
|
407
467
|
const program = new commander_1.Command();
|
|
408
468
|
const config = new config_js_2.Config();
|
|
409
469
|
const logger = new logger_js_1.Logger();
|
|
410
470
|
const invokedBinaryName = getInvokedBinaryName();
|
|
411
|
-
const
|
|
412
|
-
const
|
|
413
|
-
const
|
|
471
|
+
const argv = [...args];
|
|
472
|
+
const firstArg = argv[2];
|
|
473
|
+
const jsonOutputRequested = argv.includes('--json');
|
|
474
|
+
const directPromptRequested = argv.includes('--prompt') || argv.includes('-P');
|
|
475
|
+
const isLegionCortexRequest = invokedBinaryName === 'vigthoria' && argv[2] === 'legion' && argv.includes('--cortex');
|
|
414
476
|
if (invokedBinaryName === 'vigthoria-chat') {
|
|
415
477
|
const knownCommands = new Set([
|
|
416
478
|
'chat', 'chat-resume', 'agent', 'edit', 'generate', 'explain', 'fix', 'review', 'cancel',
|
|
@@ -418,17 +480,18 @@ async function main() {
|
|
|
418
480
|
'--help', '-h', '--version', '-V', 'help', 'version',
|
|
419
481
|
]);
|
|
420
482
|
if (!firstArg || firstArg.startsWith('-') || !knownCommands.has(firstArg)) {
|
|
421
|
-
|
|
483
|
+
argv.splice(2, 0, 'chat');
|
|
422
484
|
}
|
|
423
485
|
}
|
|
424
|
-
const requestedCommand = resolveRequestedCommand(
|
|
425
|
-
// Skip gateway JWT auth when running on-server with a service key (e.g.,
|
|
486
|
+
const requestedCommand = resolveRequestedCommand(argv);
|
|
487
|
+
// Skip gateway JWT auth when running on-server with a service key (e.g., Cortex on-box).
|
|
426
488
|
// The service key is checked by Hyper Loop directly — no user session needed.
|
|
427
489
|
const hasServiceKey = !!(process.env.HYPERLOOP_SERVICE_KEY ||
|
|
428
490
|
process.env.V3_SERVICE_KEY);
|
|
429
|
-
if (!hasServiceKey && requestedCommand && isAuthProtectedCommand(requestedCommand)) {
|
|
491
|
+
if (!hasServiceKey && !isLegionCortexRequest && requestedCommand && isAuthProtectedCommand(requestedCommand)) {
|
|
430
492
|
const authOk = await enforceGatewayAuthSession(config, logger, jsonOutputRequested);
|
|
431
493
|
if (!authOk) {
|
|
494
|
+
process.exitCode = 1;
|
|
432
495
|
return;
|
|
433
496
|
}
|
|
434
497
|
}
|
|
@@ -988,13 +1051,18 @@ Examples:
|
|
|
988
1051
|
.description('Run parallel tasks via Hyper Loop Legion orchestrator')
|
|
989
1052
|
.option('--workers', 'List available Legion workers')
|
|
990
1053
|
.option('--status', 'Show Legion infrastructure status')
|
|
991
|
-
.option('--
|
|
992
|
-
.option('--approve', 'Auto-approve
|
|
1054
|
+
.option('--cortex', 'Vigthoria Cortex: maximum intelligence execution')
|
|
1055
|
+
.option('--approve', 'Auto-approve Cortex execution prompt')
|
|
993
1056
|
.option('--no-approve', 'Require interactive approval before execution')
|
|
994
|
-
.option('--auto-charge', 'Attempt direct VigCoin top-up when
|
|
995
|
-
.option('--plan-only', 'Run
|
|
996
|
-
.option('--
|
|
997
|
-
.option('--
|
|
1057
|
+
.option('--auto-charge', 'Attempt direct VigCoin top-up when Cortex balance is low')
|
|
1058
|
+
.option('--plan-only', 'Run Cortex estimator only; do not execute Legion job')
|
|
1059
|
+
.option('--force-budget', 'Allow execution when estimated budget exceeds the hard safe-stop ceiling')
|
|
1060
|
+
.option('--ignore-preflight', 'Bypass mandatory local preflight checks (no warranty)')
|
|
1061
|
+
.option('--speed', 'Enable speed mode (allows optional role skips when convergence is detected)')
|
|
1062
|
+
.option('--repro-cmd <command>', 'Local reproduction/preflight command to validate before cloud spend')
|
|
1063
|
+
.option('--expect-repro-fail', 'Require repro command to fail (non-zero) before proceeding')
|
|
1064
|
+
.option('--models <csv>', 'Comma-separated model IDs to constrain Cortex selection')
|
|
1065
|
+
.option('-t, --timeout <seconds>', 'Override Legion execution timeout in seconds (defaults to server policy)')
|
|
998
1066
|
.option('-w, --worker <name>', 'Execute a specific worker')
|
|
999
1067
|
.option('-p, --project <path>', 'Project directory', process.cwd())
|
|
1000
1068
|
.action(async (request, options) => {
|
|
@@ -1003,11 +1071,16 @@ Examples:
|
|
|
1003
1071
|
await legion.run(request, {
|
|
1004
1072
|
workers: options.workers,
|
|
1005
1073
|
status: options.status,
|
|
1006
|
-
|
|
1074
|
+
cortex: Boolean(options.cortex),
|
|
1007
1075
|
approve: options.approve,
|
|
1008
1076
|
noApprove: options.approve === false,
|
|
1009
1077
|
autoCharge: options.autoCharge,
|
|
1010
1078
|
planOnly: options.planOnly,
|
|
1079
|
+
forceBudget: options.forceBudget,
|
|
1080
|
+
ignorePreflight: options.ignorePreflight,
|
|
1081
|
+
speed: options.speed,
|
|
1082
|
+
reproCmd: options.reproCmd,
|
|
1083
|
+
expectReproFail: options.expectReproFail,
|
|
1011
1084
|
models: options.models,
|
|
1012
1085
|
timeoutSec: Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? parsedTimeout : undefined,
|
|
1013
1086
|
worker: options.worker,
|
|
@@ -1060,8 +1133,11 @@ Examples:
|
|
|
1060
1133
|
// Auth commands
|
|
1061
1134
|
program
|
|
1062
1135
|
.command('login')
|
|
1063
|
-
.description('Login to Vigthoria Coder')
|
|
1064
|
-
.option('-t, --token <token>', 'API token')
|
|
1136
|
+
.description('Login to Vigthoria Coder (prompts for credentials when run without flags)')
|
|
1137
|
+
.option('-t, --token <token>', 'API token for token-based authentication')
|
|
1138
|
+
.option('-e, --email <email>', 'Account email for credential-based login')
|
|
1139
|
+
.option('-p, --password <password>', 'Account password for credential-based login')
|
|
1140
|
+
.option('--device', 'Use OAuth device flow (requires server support)')
|
|
1065
1141
|
.action(async (options) => {
|
|
1066
1142
|
await (0, auth_js_1.handleLogin)(options);
|
|
1067
1143
|
});
|
|
@@ -1083,13 +1159,14 @@ Examples:
|
|
|
1083
1159
|
.action(() => {
|
|
1084
1160
|
const checks = [
|
|
1085
1161
|
['Node.js', process.version],
|
|
1086
|
-
['Platform',
|
|
1162
|
+
['Platform', `${process.platform} ${process.arch}`],
|
|
1087
1163
|
['Working directory', process.cwd()],
|
|
1088
1164
|
];
|
|
1089
1165
|
console.log('Vigthoria CLI diagnostics');
|
|
1090
1166
|
for (const [label, value] of checks) {
|
|
1091
1167
|
console.log(`- ${label}: ${value}`);
|
|
1092
1168
|
}
|
|
1169
|
+
process.exitCode = 0;
|
|
1093
1170
|
});
|
|
1094
1171
|
// Config command
|
|
1095
1172
|
program
|
|
@@ -1264,7 +1341,7 @@ Examples:
|
|
|
1264
1341
|
await legion.run(undefined, {
|
|
1265
1342
|
status: true,
|
|
1266
1343
|
workers: false,
|
|
1267
|
-
|
|
1344
|
+
cortex: false,
|
|
1268
1345
|
approve: false,
|
|
1269
1346
|
noApprove: true,
|
|
1270
1347
|
planOnly: false,
|
|
@@ -1317,12 +1394,16 @@ Examples:
|
|
|
1317
1394
|
}
|
|
1318
1395
|
try {
|
|
1319
1396
|
// Default to chat if no command
|
|
1320
|
-
if (
|
|
1397
|
+
if (args.length === 2) {
|
|
1321
1398
|
const chat = new chat_js_1.ChatCommand(config, logger);
|
|
1322
1399
|
await chat.run({ model: 'code', project: process.cwd() });
|
|
1400
|
+
process.exitCode = 0;
|
|
1323
1401
|
return;
|
|
1324
1402
|
}
|
|
1325
|
-
await program.parseAsync(
|
|
1403
|
+
await program.parseAsync(args);
|
|
1404
|
+
if (process.exitCode === undefined || process.exitCode === 0) {
|
|
1405
|
+
process.exitCode = 0;
|
|
1406
|
+
}
|
|
1326
1407
|
}
|
|
1327
1408
|
catch (error) {
|
|
1328
1409
|
reportCliError(error, jsonOutputRequested);
|
|
@@ -1336,7 +1417,7 @@ process.on('unhandledRejection', (reason) => {
|
|
|
1336
1417
|
});
|
|
1337
1418
|
async function bootstrapCli() {
|
|
1338
1419
|
try {
|
|
1339
|
-
await main();
|
|
1420
|
+
await main(process.argv);
|
|
1340
1421
|
}
|
|
1341
1422
|
catch (err) {
|
|
1342
1423
|
handleFatalCliError(err, process.argv.includes('--json'));
|