vigthoria-cli 1.13.20 → 1.13.22
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/completions/_vigthoria +49 -0
- package/completions/vigthoria.bash +6 -0
- package/completions/vigthoria.fish +41 -0
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +63 -128
- package/dist/commands/background.d.ts +0 -2
- package/dist/commands/background.js +26 -34
- package/dist/commands/bridge.js +45 -41
- package/dist/commands/cancel.d.ts +1 -0
- package/dist/commands/cancel.js +22 -16
- package/dist/commands/chat.d.ts +3 -42
- package/dist/commands/chat.js +378 -1123
- package/dist/commands/coding-registration.d.ts +4 -0
- package/dist/commands/coding-registration.js +24 -0
- package/dist/commands/config.js +23 -29
- package/dist/commands/conversation-registration.d.ts +4 -0
- package/dist/commands/conversation-registration.js +122 -0
- package/dist/commands/deploy.d.ts +8 -2
- package/dist/commands/deploy.js +92 -90
- package/dist/commands/device.d.ts +1 -2
- package/dist/commands/device.js +37 -21
- package/dist/commands/doctor-registration.d.ts +4 -0
- package/dist/commands/doctor-registration.js +200 -0
- package/dist/commands/edit.js +22 -33
- package/dist/commands/explain.js +7 -10
- package/dist/commands/fork.d.ts +1 -2
- package/dist/commands/fork.js +72 -22
- package/dist/commands/game.d.ts +1 -1
- package/dist/commands/game.js +17 -6
- package/dist/commands/generate.js +7 -9
- package/dist/commands/history.js +11 -7
- package/dist/commands/hub.d.ts +2 -2
- package/dist/commands/hub.js +34 -35
- package/dist/commands/legion.d.ts +0 -1
- package/dist/commands/legion.js +62 -79
- package/dist/commands/music.d.ts +1 -2
- package/dist/commands/music.js +62 -27
- package/dist/commands/platform-registration.d.ts +4 -0
- package/dist/commands/platform-registration.js +45 -0
- package/dist/commands/preview.d.ts +0 -1
- package/dist/commands/preview.js +82 -35
- package/dist/commands/product-run-registration.d.ts +4 -0
- package/dist/commands/product-run-registration.js +490 -0
- package/dist/commands/replay.d.ts +1 -2
- package/dist/commands/replay.js +15 -11
- package/dist/commands/repo.d.ts +14 -7
- package/dist/commands/repo.js +245 -196
- package/dist/commands/review.js +9 -12
- package/dist/commands/security.d.ts +1 -2
- package/dist/commands/security.js +12 -6
- package/dist/commands/update-registration.d.ts +3 -0
- package/dist/commands/update-registration.js +338 -0
- package/dist/commands/v4-registration.d.ts +4 -0
- package/dist/commands/v4-registration.js +24 -0
- package/dist/commands/v4.d.ts +1 -1
- package/dist/commands/v4.js +46 -8
- package/dist/commands/wallet.d.ts +1 -3
- package/dist/commands/wallet.js +18 -45
- package/dist/commands/workflow.d.ts +2 -0
- package/dist/commands/workflow.js +52 -39
- package/dist/index.js +122 -1589
- package/dist/utils/agent-stream-state.d.ts +27 -0
- package/dist/utils/agent-stream-state.js +93 -0
- package/dist/utils/agentRoute.d.ts +2 -1
- package/dist/utils/agentRoute.js +22 -9
- package/dist/utils/agentRunOutcome.d.ts +16 -0
- package/dist/utils/agentRunOutcome.js +37 -2
- package/dist/utils/api-client-factory.d.ts +8 -0
- package/dist/utils/api-client-factory.js +8 -0
- package/dist/utils/api.d.ts +44 -266
- package/dist/utils/api.js +647 -3180
- package/dist/utils/auth-session.d.ts +41 -0
- package/dist/utils/auth-session.js +224 -0
- package/dist/utils/brain-hub-client.d.ts +0 -4
- package/dist/utils/brain-hub-client.js +6 -3
- package/dist/utils/bridge-client.js +2 -0
- package/dist/utils/capability-health-service.d.ts +48 -0
- package/dist/utils/capability-health-service.js +64 -0
- package/dist/utils/chat-prompt-policy.d.ts +18 -0
- package/dist/utils/chat-prompt-policy.js +61 -0
- package/dist/utils/chat-tool-call-parser.d.ts +11 -0
- package/dist/utils/chat-tool-call-parser.js +71 -0
- package/dist/utils/cli-state.d.ts +22 -4
- package/dist/utils/cli-state.js +105 -47
- package/dist/utils/code-operations-service.d.ts +104 -0
- package/dist/utils/code-operations-service.js +837 -0
- package/dist/utils/codebase-indexer.d.ts +1 -0
- package/dist/utils/codebase-indexer.js +28 -8
- package/dist/utils/command-contract.d.ts +61 -0
- package/dist/utils/command-contract.js +149 -0
- package/dist/utils/command-policy.d.ts +10 -0
- package/dist/utils/command-policy.js +54 -0
- package/dist/utils/config.d.ts +13 -5
- package/dist/utils/config.js +220 -94
- package/dist/utils/context-ranker.js +11 -2
- package/dist/utils/context-state.d.ts +39 -0
- package/dist/utils/context-state.js +52 -0
- package/dist/utils/deckEvents.d.ts +0 -5
- package/dist/utils/deckEvents.js +3 -2
- package/dist/utils/desktop-bridge-client.js +2 -0
- package/dist/utils/direct-output-policy.d.ts +3 -0
- package/dist/utils/direct-output-policy.js +76 -0
- package/dist/utils/durable-json.d.ts +57 -0
- package/dist/utils/durable-json.js +319 -0
- package/dist/utils/fastAgentRouter.d.ts +5 -6
- package/dist/utils/fastAgentRouter.js +17 -25
- package/dist/utils/files.js +21 -13
- package/dist/utils/frontend-preview-service.d.ts +76 -0
- package/dist/utils/frontend-preview-service.js +482 -0
- package/dist/utils/goaEvents.d.ts +0 -4
- package/dist/utils/goaEvents.js +5 -2
- package/dist/utils/logger.d.ts +2 -1
- package/dist/utils/logger.js +27 -20
- package/dist/utils/mcp-context-client.d.ts +24 -0
- package/dist/utils/mcp-context-client.js +58 -0
- package/dist/utils/model-governance.d.ts +18 -0
- package/dist/utils/model-governance.js +75 -0
- package/dist/utils/model-transport-service.d.ts +44 -0
- package/dist/utils/model-transport-service.js +111 -0
- package/dist/utils/mutation-journal.d.ts +42 -0
- package/dist/utils/mutation-journal.js +387 -0
- package/dist/utils/network-policy.d.ts +24 -0
- package/dist/utils/network-policy.js +202 -0
- package/dist/utils/operator-client.d.ts +45 -0
- package/dist/utils/operator-client.js +159 -0
- package/dist/utils/post-write-validator.js +7 -9
- package/dist/utils/preview-screenshot-adapter.d.ts +13 -0
- package/dist/utils/preview-screenshot-adapter.js +36 -0
- package/dist/utils/process-policy.d.ts +23 -0
- package/dist/utils/process-policy.js +284 -0
- package/dist/utils/project-memory.d.ts +13 -3
- package/dist/utils/project-memory.js +70 -17
- package/dist/utils/prompt-workspace-resolver.d.ts +14 -0
- package/dist/utils/prompt-workspace-resolver.js +64 -0
- package/dist/utils/release-install.d.ts +11 -0
- package/dist/utils/release-install.js +155 -0
- package/dist/utils/release-policy.d.ts +64 -0
- package/dist/utils/release-policy.js +293 -0
- package/dist/utils/requestIntent.js +22 -3
- package/dist/utils/runtime-capability.d.ts +8 -0
- package/dist/utils/runtime-capability.js +16 -0
- package/dist/utils/secret-policy.d.ts +20 -0
- package/dist/utils/secret-policy.js +160 -0
- package/dist/utils/session.d.ts +17 -3
- package/dist/utils/session.js +354 -110
- package/dist/utils/tool-approval-service.d.ts +17 -0
- package/dist/utils/tool-approval-service.js +78 -0
- package/dist/utils/tool-capability-providers.d.ts +57 -0
- package/dist/utils/tool-capability-providers.js +57 -0
- package/dist/utils/tools.d.ts +2 -23
- package/dist/utils/tools.js +216 -508
- package/dist/utils/update-policy.d.ts +18 -0
- package/dist/utils/update-policy.js +137 -0
- package/dist/utils/v3-agent-client.d.ts +40 -0
- package/dist/utils/v3-agent-client.js +165 -0
- package/dist/utils/v3-workspace-service.d.ts +28 -0
- package/dist/utils/v3-workspace-service.js +279 -0
- package/dist/utils/vigflow-client.d.ts +95 -0
- package/dist/utils/vigflow-client.js +105 -0
- package/dist/utils/workspace-boundary.d.ts +45 -0
- package/dist/utils/workspace-boundary.js +377 -0
- package/dist/utils/workspace-brain-service.d.ts +2 -0
- package/dist/utils/workspace-brain-service.js +11 -1
- package/dist/utils/workspace-cache.js +37 -15
- package/dist/utils/workspace-stream.js +35 -10
- package/install.ps1 +85 -142
- package/install.sh +75 -80
- package/package.json +43 -9
- package/release-policy.json +43 -0
- package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +1 -1
- package/scripts/release/generate-completions.mjs +24 -0
- package/scripts/release/install-release.mjs +10 -0
- package/scripts/release/publish-cli-release.mjs +116 -0
- package/scripts/release/publish-cli-release.sh +2 -70
- package/scripts/release/resolve-release-manifest.mjs +26 -0
- package/scripts/release/test-balanced-model-live.sh +42 -0
- package/scripts/release/validate-live-service-gates.sh +90 -0
- package/scripts/release/validate-no-go-gates.sh +135 -174
- package/scripts/release/verify-runtime-consistency.mjs +4 -1
- package/dist/commands/index.d.ts +0 -12
- package/dist/commands/index.js +0 -194
- package/dist/commands/update.d.ts +0 -9
- package/dist/commands/update.js +0 -301
- package/dist/utils/templateInstantPath.d.ts +0 -18
- package/dist/utils/templateInstantPath.js +0 -228
package/dist/index.js
CHANGED
|
@@ -16,53 +16,45 @@
|
|
|
16
16
|
* vigthoria operator - Start BMAD operator mode
|
|
17
17
|
*/
|
|
18
18
|
import { Command } from 'commander';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
19
|
+
import { registerConversationCommands } from './commands/conversation-registration.js';
|
|
20
|
+
import { registerCodingCommands } from './commands/coding-registration.js';
|
|
21
|
+
import { registerDoctorCommand } from './commands/doctor-registration.js';
|
|
22
|
+
import { registerProductAndRunCommands } from './commands/product-run-registration.js';
|
|
23
|
+
import { checkForUpdatesQuietly, registerUpdateCommand } from './commands/update-registration.js';
|
|
23
24
|
import { handleLogin, handleLogout, registerAuthCommands, statusAction } from './commands/auth.js';
|
|
24
25
|
import { ConfigCommand } from './commands/config.js';
|
|
25
|
-
import { ReviewCommand } from './commands/review.js';
|
|
26
|
-
import { HubCommand } from './commands/hub.js';
|
|
27
|
-
import { MusicCommand } from './commands/music.js';
|
|
28
|
-
import { RepoCommand } from './commands/repo.js';
|
|
29
|
-
import { DeployCommand } from './commands/deploy.js';
|
|
30
26
|
import { BridgeCommand } from './commands/bridge.js';
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import { PreviewCommand } from './commands/preview.js';
|
|
34
|
-
import { GameCommand } from './commands/game.js';
|
|
27
|
+
import { registerPlatformCommands } from './commands/platform-registration.js';
|
|
28
|
+
import { registerWorkflowCommands } from './commands/workflow.js';
|
|
35
29
|
import { LegionCommand } from './commands/legion.js';
|
|
36
|
-
import { HistoryCommand } from './commands/history.js';
|
|
37
|
-
import { ReplayCommand } from './commands/replay.js';
|
|
38
|
-
import { ForkCommand } from './commands/fork.js';
|
|
39
|
-
import { CancelCommand } from './commands/cancel.js';
|
|
40
|
-
import { BackgroundCommand } from './commands/background.js';
|
|
41
|
-
import { SecurityCommand } from './commands/security.js';
|
|
42
30
|
import { WalletCommand } from './commands/wallet.js';
|
|
43
|
-
import {
|
|
44
|
-
import { runV4Menu } from './commands/v4-menu.js';
|
|
31
|
+
import { registerV4Commands } from './commands/v4-registration.js';
|
|
45
32
|
import { Config } from './utils/config.js';
|
|
46
33
|
import { Logger, CH } from './utils/logger.js';
|
|
47
34
|
import chalk from 'chalk';
|
|
48
35
|
import * as fs from 'fs';
|
|
49
36
|
import * as path from 'path';
|
|
50
|
-
import * as os from 'os';
|
|
51
37
|
import { fileURLToPath } from 'url';
|
|
52
|
-
import { createRequire } from 'module';
|
|
53
|
-
import { createHash } from 'crypto';
|
|
54
38
|
import axios from 'axios';
|
|
55
39
|
import { renderDynamicHelp, selectInteractiveCommand } from './utils/command-menu.js';
|
|
56
|
-
import { isIOSEnvironment, resolveClientOsSlug } from './utils/clientManifest.js';
|
|
57
40
|
// ESM shim — TypeScript emits ESM under "type": "module", so __dirname
|
|
58
41
|
// is not available natively. Restore the CommonJS-style helper so the
|
|
59
42
|
// pre-existing package-discovery logic continues to work unchanged.
|
|
60
43
|
const __filename = fileURLToPath(import.meta.url);
|
|
61
44
|
const __dirname = path.dirname(__filename);
|
|
62
|
-
import {
|
|
63
|
-
import {
|
|
45
|
+
import { sanitizeUserFacingErrorText } from './utils/api.js';
|
|
46
|
+
import { createAPIClient } from './utils/api-client-factory.js';
|
|
47
|
+
import { createGatewayPreflightCacheKey, isOfflineMode, isUpdateCheckSuppressed, readGatewayPreflightCache, writeGatewayPreflightCache, } from './utils/cli-state.js';
|
|
64
48
|
import { applyLocalTestfarmDefaults, isLocalTestfarmMode } from './utils/localTestMode.js';
|
|
49
|
+
import { installAxiosNetworkPolicy, installGlobalFetchPolicy } from './utils/network-policy.js';
|
|
50
|
+
import { installConsoleRedaction } from './utils/secret-policy.js';
|
|
51
|
+
import { commandNameFromArgv, failureEnvelope, normalizeCommandError, CliCommandError, } from './utils/command-contract.js';
|
|
52
|
+
import { commandAuthRequirement, commanderCommandPath } from './utils/command-policy.js';
|
|
65
53
|
applyLocalTestfarmDefaults();
|
|
54
|
+
if (process.env.VIGTHORIA_CAPTURE_RUNTIME_MODEL !== '1')
|
|
55
|
+
installConsoleRedaction();
|
|
56
|
+
installGlobalFetchPolicy();
|
|
57
|
+
installAxiosNetworkPolicy(axios, 'auto');
|
|
66
58
|
// --- Terminal column-width safety clamp -----------------------------------
|
|
67
59
|
// Node's `readline` module (used for the interactive chat `> ` prompt and a
|
|
68
60
|
// few other prompts) redraws the current input line on every keystroke using
|
|
@@ -101,13 +93,6 @@ if (process.stdout.isTTY) {
|
|
|
101
93
|
if (process.stderr.isTTY) {
|
|
102
94
|
process.stderr.on('resize', () => clampStreamSize(process.stderr));
|
|
103
95
|
}
|
|
104
|
-
function isApiError(error) {
|
|
105
|
-
return Boolean(error &&
|
|
106
|
-
typeof error === 'object' &&
|
|
107
|
-
typeof error.status === 'number' &&
|
|
108
|
-
typeof error.message === 'string' &&
|
|
109
|
-
typeof error.code === 'string');
|
|
110
|
-
}
|
|
111
96
|
function getInvokedBinaryName() {
|
|
112
97
|
const executable = process.argv[1] || 'vigthoria';
|
|
113
98
|
return path.basename(executable, path.extname(executable)).toLowerCase();
|
|
@@ -217,309 +202,67 @@ export function validateReleaseMetadata() {
|
|
|
217
202
|
return false;
|
|
218
203
|
}
|
|
219
204
|
}
|
|
220
|
-
function compareVersions(v1, v2) {
|
|
221
|
-
const parts1 = v1.split('.').map(Number);
|
|
222
|
-
const parts2 = v2.split('.').map(Number);
|
|
223
|
-
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
224
|
-
const p1 = parts1[i] || 0;
|
|
225
|
-
const p2 = parts2[i] || 0;
|
|
226
|
-
if (p1 < p2)
|
|
227
|
-
return -1;
|
|
228
|
-
if (p1 > p2)
|
|
229
|
-
return 1;
|
|
230
|
-
}
|
|
231
|
-
return 0;
|
|
232
|
-
}
|
|
233
|
-
function maxVersion(...versions) {
|
|
234
|
-
let best = null;
|
|
235
|
-
for (const version of versions) {
|
|
236
|
-
if (!version)
|
|
237
|
-
continue;
|
|
238
|
-
if (!best || compareVersions(version, best) > 0)
|
|
239
|
-
best = version;
|
|
240
|
-
}
|
|
241
|
-
return best;
|
|
242
|
-
}
|
|
243
|
-
async function fetchNpmLatestVersion() {
|
|
244
|
-
const { execSync } = await import('child_process');
|
|
245
|
-
try {
|
|
246
|
-
// Hard 5s timeout: a slow/blocked path to the public npm registry must
|
|
247
|
-
// never hang the caller (e.g. the Workbench's blocking `vigthoria update`
|
|
248
|
-
// invocation) indefinitely.
|
|
249
|
-
const latest = execSync('npm view vigthoria-cli version', {
|
|
250
|
-
encoding: 'utf8',
|
|
251
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
252
|
-
windowsHide: true,
|
|
253
|
-
timeout: 5000,
|
|
254
|
-
}).trim();
|
|
255
|
-
return latest || null;
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
205
|
const VERSION = getVersion();
|
|
262
|
-
const VIGTHORIA_DEFAULT_MANIFEST_URL = process.env.VIGTHORIA_UPDATE_MANIFEST_URL || "https://coder.vigthoria.io/releases/manifest.json";
|
|
263
|
-
function resolveManifestEntry(manifest, channel) {
|
|
264
|
-
const normalized = String(channel || 'stable').trim() || 'stable';
|
|
265
|
-
if (manifest.channels && manifest.channels[normalized]) {
|
|
266
|
-
return manifest.channels[normalized];
|
|
267
|
-
}
|
|
268
|
-
const direct = manifest[normalized];
|
|
269
|
-
if (direct && typeof direct === 'object') {
|
|
270
|
-
return direct;
|
|
271
|
-
}
|
|
272
|
-
if (normalized === 'stable') {
|
|
273
|
-
const rootVersion = typeof manifest.version === 'string' ? manifest.version : '';
|
|
274
|
-
const rootUrl = typeof manifest.url === 'string' ? manifest.url : '';
|
|
275
|
-
const rootSha = typeof manifest.sha256 === 'string' ? manifest.sha256 : undefined;
|
|
276
|
-
if (rootVersion && rootUrl) {
|
|
277
|
-
return { version: rootVersion, url: rootUrl, sha256: rootSha };
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
async function downloadFile(url, targetPath) {
|
|
283
|
-
const response = await axios.get(url, {
|
|
284
|
-
responseType: 'arraybuffer',
|
|
285
|
-
timeout: 20000,
|
|
286
|
-
maxRedirects: 5,
|
|
287
|
-
validateStatus: (status) => status >= 200 && status < 300,
|
|
288
|
-
});
|
|
289
|
-
fs.writeFileSync(targetPath, Buffer.from(response.data));
|
|
290
|
-
}
|
|
291
|
-
const TRUSTED_RELEASE_HOSTS = new Set([
|
|
292
|
-
'extension.vigthoria.io',
|
|
293
|
-
'landing.vigthoria.io',
|
|
294
|
-
'coder.vigthoria.io',
|
|
295
|
-
'market.vigthoria.io',
|
|
296
|
-
]);
|
|
297
|
-
function validateReleaseUrl(rawUrl, allowLocalhost = false) {
|
|
298
|
-
const parsed = new URL(rawUrl);
|
|
299
|
-
const local = parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1' || parsed.hostname === '::1';
|
|
300
|
-
if (parsed.protocol !== 'https:' && !(allowLocalhost && local && parsed.protocol === 'http:')) {
|
|
301
|
-
throw new Error('Release URLs must use HTTPS (HTTP is allowed only for an explicit localhost manifest).');
|
|
302
|
-
}
|
|
303
|
-
if (!TRUSTED_RELEASE_HOSTS.has(parsed.hostname) && !(allowLocalhost && local)) {
|
|
304
|
-
throw new Error(`Untrusted release host: ${parsed.hostname}`);
|
|
305
|
-
}
|
|
306
|
-
return parsed;
|
|
307
|
-
}
|
|
308
|
-
function sha256File(filePath) {
|
|
309
|
-
const hash = createHash('sha256');
|
|
310
|
-
const data = fs.readFileSync(filePath);
|
|
311
|
-
hash.update(data);
|
|
312
|
-
return hash.digest('hex');
|
|
313
|
-
}
|
|
314
|
-
async function installGlobalPackageWithNpm(packageSpec) {
|
|
315
|
-
const { execFileSync, execSync } = await import('child_process');
|
|
316
|
-
const attempts = [];
|
|
317
|
-
if (process.platform === 'win32') {
|
|
318
|
-
// First-choice: cmd.exe /c npm — resolves npm.cmd shims on any Windows Node setup
|
|
319
|
-
const comspec = process.env.COMSPEC || 'cmd.exe';
|
|
320
|
-
attempts.push({
|
|
321
|
-
label: 'cmd.exe /c npm',
|
|
322
|
-
command: comspec,
|
|
323
|
-
args: ['/c', 'npm', 'install', '-g', packageSpec],
|
|
324
|
-
});
|
|
325
|
-
const npmExecPath = process.env.npm_execpath;
|
|
326
|
-
if (npmExecPath && fs.existsSync(npmExecPath)) {
|
|
327
|
-
attempts.push({
|
|
328
|
-
label: 'node+npm_execpath',
|
|
329
|
-
command: process.execPath,
|
|
330
|
-
args: [npmExecPath, 'install', '-g', packageSpec],
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
try {
|
|
334
|
-
const resolvedNpmCmd = execSync('where npm.cmd', {
|
|
335
|
-
encoding: 'utf8',
|
|
336
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
337
|
-
windowsHide: true,
|
|
338
|
-
}).split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
339
|
-
if (resolvedNpmCmd) {
|
|
340
|
-
attempts.push({
|
|
341
|
-
label: 'where npm.cmd',
|
|
342
|
-
command: resolvedNpmCmd,
|
|
343
|
-
args: ['install', '-g', packageSpec],
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
catch {
|
|
348
|
-
// Fall through to generic attempts below.
|
|
349
|
-
}
|
|
350
|
-
attempts.push({
|
|
351
|
-
label: 'npm.cmd',
|
|
352
|
-
command: 'npm.cmd',
|
|
353
|
-
args: ['install', '-g', packageSpec],
|
|
354
|
-
});
|
|
355
|
-
// Last-resort Windows shell fallback for PATH/cmd shim edge-cases.
|
|
356
|
-
attempts.push({
|
|
357
|
-
label: 'npm via shell',
|
|
358
|
-
command: 'npm',
|
|
359
|
-
args: ['install', '-g', packageSpec],
|
|
360
|
-
shell: true,
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
let resolvedNpm = '';
|
|
365
|
-
try {
|
|
366
|
-
resolvedNpm = execSync('which npm', {
|
|
367
|
-
encoding: 'utf8',
|
|
368
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
369
|
-
}).trim();
|
|
370
|
-
}
|
|
371
|
-
catch {
|
|
372
|
-
// Keep resolvedNpm empty and use plain `npm` below.
|
|
373
|
-
}
|
|
374
|
-
attempts.push({
|
|
375
|
-
label: resolvedNpm ? 'which npm' : 'npm',
|
|
376
|
-
command: resolvedNpm || 'npm',
|
|
377
|
-
args: ['install', '-g', packageSpec],
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
let lastError = null;
|
|
381
|
-
for (const attempt of attempts) {
|
|
382
|
-
try {
|
|
383
|
-
console.log(chalk.gray(`[update] installer executable: ${attempt.command}${attempt.shell ? ' (shell)' : ''}`));
|
|
384
|
-
execFileSync(attempt.command, attempt.args, {
|
|
385
|
-
stdio: 'inherit',
|
|
386
|
-
windowsHide: true,
|
|
387
|
-
shell: attempt.shell === true,
|
|
388
|
-
// Hard cap so a stalled/hung npm install (registry outage, network
|
|
389
|
-
// issue, etc.) can never block the caller indefinitely - especially
|
|
390
|
-
// important since the Workbench invokes this synchronously.
|
|
391
|
-
timeout: 180000,
|
|
392
|
-
});
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
catch (error) {
|
|
396
|
-
lastError = error;
|
|
397
|
-
const code = error?.code || error?.status || 'unknown';
|
|
398
|
-
console.error(chalk.yellow(`[update] install attempt failed via ${attempt.label}: ${code}`));
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
const finalCode = lastError?.code || lastError?.status || 'unknown';
|
|
402
|
-
throw new Error(`Unable to launch npm installer after ${attempts.length} attempt(s). Last error: ${finalCode}`);
|
|
403
|
-
}
|
|
404
|
-
function renderUpdateBanner(latestVersion) {
|
|
405
|
-
if (compareVersions(latestVersion, '1.4.0') >= 0 && compareVersions(VERSION, '1.4.0') < 0) {
|
|
406
|
-
console.log(chalk.red.bold(`\n${CH.warnEmoji} SECURITY UPDATE AVAILABLE`));
|
|
407
|
-
console.log(chalk.red(` Version ${VERSION} has security vulnerabilities.`));
|
|
408
|
-
console.log(chalk.yellow(` Please update to ${latestVersion} immediately:`));
|
|
409
|
-
console.log(chalk.white.bold(' npm install -g vigthoria-cli@latest\n'));
|
|
410
|
-
}
|
|
411
|
-
else {
|
|
412
|
-
console.log(chalk.yellow(`\n${CH.warnEmoji} Update available: ${VERSION} -> ${latestVersion}`));
|
|
413
|
-
console.log(chalk.gray(' Run `vigthoria update` to install\n'));
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
// Check for updates quietly on startup. Cached (24 h) so we never block
|
|
417
|
-
// startup on `npm view`. Honours VIGTHORIA_OFFLINE / VIGTHORIA_NO_UPDATE_CHECK.
|
|
418
|
-
async function checkForUpdatesQuietly() {
|
|
419
|
-
if (isUpdateCheckSuppressed()) {
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
const cached = readCachedLatestVersion();
|
|
423
|
-
if (cached) {
|
|
424
|
-
if (compareVersions(cached, VERSION) > 0) {
|
|
425
|
-
renderUpdateBanner(cached);
|
|
426
|
-
}
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
// Cache miss: probe npm registry in the background, with a hard 5 s
|
|
430
|
-
// timeout. Result is cached for 24 h so subsequent runs are instant.
|
|
431
|
-
try {
|
|
432
|
-
const { execSync } = await import('child_process');
|
|
433
|
-
const npmVersion = execSync('npm view vigthoria-cli version', {
|
|
434
|
-
encoding: 'utf8',
|
|
435
|
-
timeout: 5000,
|
|
436
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
437
|
-
windowsHide: true,
|
|
438
|
-
}).trim();
|
|
439
|
-
if (!npmVersion) {
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
writeCachedLatestVersion(npmVersion);
|
|
443
|
-
if (compareVersions(npmVersion, VERSION) > 0) {
|
|
444
|
-
renderUpdateBanner(npmVersion);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
catch {
|
|
448
|
-
// Network or npm failure should never block CLI; do not cache failure.
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
206
|
const VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE = 'Vigthoria Gate way user authentification failed. Please log out and login again.';
|
|
452
|
-
function
|
|
453
|
-
for (let i = 2; i < argv.length; i++) {
|
|
454
|
-
const token = String(argv[i] || '').trim();
|
|
455
|
-
if (!token || token.startsWith('-'))
|
|
456
|
-
continue;
|
|
457
|
-
return token;
|
|
458
|
-
}
|
|
459
|
-
return '';
|
|
460
|
-
}
|
|
461
|
-
function isAuthProtectedCommand(command) {
|
|
462
|
-
const protectedCommands = new Set([
|
|
463
|
-
'chat', 'c', 'chat-resume',
|
|
464
|
-
'agent', 'a', 'operator', 'op',
|
|
465
|
-
'edit', 'e', 'generate', 'g', 'explain', 'x', 'fix', 'f', 'review', 'r',
|
|
466
|
-
'workflow', 'flow', 'hub', 'marketplace', 'deploy', 'host', 'preview', 'device',
|
|
467
|
-
'legion', 'history', 'runs', 'replay', 'fork', 'cancel', 'background', 'bg',
|
|
468
|
-
'repo', 'repository',
|
|
469
|
-
]);
|
|
470
|
-
return protectedCommands.has(command);
|
|
471
|
-
}
|
|
472
|
-
async function enforceGatewayAuthSession(config, logger, jsonOutputRequested) {
|
|
207
|
+
async function enforceGatewayAuthSession(config, logger) {
|
|
473
208
|
if (isLocalTestfarmMode()) {
|
|
474
|
-
return
|
|
209
|
+
return;
|
|
475
210
|
}
|
|
476
211
|
const explicitEnvToken = Boolean(process.env.VIGTHORIA_TOKEN || process.env.VIGTHORIA_AUTH_TOKEN);
|
|
477
212
|
if (!config.isAuthenticated() && !explicitEnvToken) {
|
|
478
|
-
return
|
|
213
|
+
return;
|
|
479
214
|
}
|
|
480
215
|
// Offline mode disables every outbound preflight call.
|
|
481
216
|
if (isOfflineMode()) {
|
|
482
|
-
return
|
|
217
|
+
return;
|
|
483
218
|
}
|
|
219
|
+
const credential = String(process.env.VIGTHORIA_TOKEN
|
|
220
|
+
|| process.env.VIGTHORIA_AUTH_TOKEN
|
|
221
|
+
|| config.get('authToken')
|
|
222
|
+
|| '');
|
|
223
|
+
const preflightCacheKey = createGatewayPreflightCacheKey({
|
|
224
|
+
endpoint: String(config.get('apiUrl') || 'https://coder.vigthoria.io'),
|
|
225
|
+
credential,
|
|
226
|
+
capability: 'gateway-auth-session',
|
|
227
|
+
relevantConfiguration: {
|
|
228
|
+
modelsApiUrl: config.get('modelsApiUrl'),
|
|
229
|
+
selfHostedModelsApiUrl: config.get('selfHostedModelsApiUrl'),
|
|
230
|
+
},
|
|
231
|
+
});
|
|
484
232
|
// Short-lived cache so back-to-back CLI invocations don't re-probe. Never use
|
|
485
233
|
// a cached success when an explicit env token overrides the stored session.
|
|
486
234
|
if (!explicitEnvToken) {
|
|
487
|
-
const cached = readGatewayPreflightCache();
|
|
235
|
+
const cached = readGatewayPreflightCache(preflightCacheKey);
|
|
488
236
|
if (cached === true) {
|
|
489
|
-
return
|
|
237
|
+
return;
|
|
490
238
|
}
|
|
491
239
|
}
|
|
492
|
-
const api =
|
|
240
|
+
const api = createAPIClient(config, logger);
|
|
493
241
|
try {
|
|
494
242
|
const tokenCheck = await api.validateToken();
|
|
495
243
|
if (!tokenCheck.valid) {
|
|
496
|
-
writeGatewayPreflightCache(false);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
501
|
-
else {
|
|
502
|
-
console.log(chalk.red(VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE));
|
|
503
|
-
}
|
|
504
|
-
return false;
|
|
244
|
+
writeGatewayPreflightCache(preflightCacheKey, false);
|
|
245
|
+
throw new CliCommandError(VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE, {
|
|
246
|
+
code: 'AUTH_SESSION_INVALID',
|
|
247
|
+
category: 'authentication',
|
|
248
|
+
});
|
|
505
249
|
}
|
|
506
|
-
writeGatewayPreflightCache(true);
|
|
507
|
-
return
|
|
250
|
+
writeGatewayPreflightCache(preflightCacheKey, true);
|
|
251
|
+
return;
|
|
508
252
|
}
|
|
509
|
-
catch {
|
|
253
|
+
catch (error) {
|
|
254
|
+
if (error instanceof CliCommandError)
|
|
255
|
+
throw error;
|
|
510
256
|
if (explicitEnvToken) {
|
|
511
|
-
writeGatewayPreflightCache(false);
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
console.log(chalk.red(VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE));
|
|
518
|
-
}
|
|
519
|
-
return false;
|
|
257
|
+
writeGatewayPreflightCache(preflightCacheKey, false);
|
|
258
|
+
throw new CliCommandError(VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE, {
|
|
259
|
+
code: 'AUTH_SESSION_INVALID',
|
|
260
|
+
category: 'authentication',
|
|
261
|
+
cause: error,
|
|
262
|
+
});
|
|
520
263
|
}
|
|
521
264
|
// Network or transient backend outages should not be misclassified as auth failure.
|
|
522
|
-
return
|
|
265
|
+
return;
|
|
523
266
|
}
|
|
524
267
|
finally {
|
|
525
268
|
api.destroy();
|
|
@@ -534,46 +277,14 @@ function scrubMessageForUser(message) {
|
|
|
534
277
|
}
|
|
535
278
|
}
|
|
536
279
|
function normalizeCliError(error) {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const responseMessage = typeof extended.response?.data?.error === 'string'
|
|
540
|
-
? extended.response.data.error
|
|
541
|
-
: typeof extended.response?.data?.message === 'string'
|
|
542
|
-
? extended.response.data.message
|
|
543
|
-
: undefined;
|
|
544
|
-
const rawMessage = responseMessage || extended.message || 'An unexpected CLI error occurred.';
|
|
545
|
-
// Purely local, client-side errors (e.g. "this config path doesn't exist
|
|
546
|
-
// on YOUR machine") can legitimately mention local paths that happen to
|
|
547
|
-
// look like internal server paths (e.g. `/var/www/...`-style directories
|
|
548
|
-
// the user copied/typed themselves). Scrubbing those makes an otherwise
|
|
549
|
-
// actionable message unreadable, so commands that construct their own
|
|
550
|
-
// local filesystem errors can opt out via `skipPathScrub`.
|
|
551
|
-
return {
|
|
552
|
-
message: extended.skipPathScrub ? rawMessage : scrubMessageForUser(rawMessage),
|
|
553
|
-
code: extended.code,
|
|
554
|
-
status: extended.status || extended.statusCode || extended.response?.status,
|
|
555
|
-
details: extended.details || extended.response?.data,
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
if (error && typeof error === 'object') {
|
|
559
|
-
const value = error;
|
|
560
|
-
const rawMessage = typeof value.message === 'string' && value.message.trim() ? value.message : 'An unexpected CLI error occurred.';
|
|
561
|
-
return {
|
|
562
|
-
message: scrubMessageForUser(rawMessage),
|
|
563
|
-
code: typeof value.code === 'string' || typeof value.code === 'number' ? value.code : undefined,
|
|
564
|
-
status: typeof value.status === 'number' ? value.status : undefined,
|
|
565
|
-
details: value.details,
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
const rawMessage = typeof error === 'string' && error.trim() ? error : 'An unexpected CLI error occurred.';
|
|
569
|
-
return {
|
|
570
|
-
message: scrubMessageForUser(rawMessage),
|
|
571
|
-
};
|
|
280
|
+
const skipPathScrub = Boolean(error && typeof error === 'object' && error.skipPathScrub === true);
|
|
281
|
+
return normalizeCommandError(error, skipPathScrub ? String : scrubMessageForUser);
|
|
572
282
|
}
|
|
283
|
+
let activeCommandPath = '';
|
|
573
284
|
function formatCliError(error, jsonOutputRequested = false) {
|
|
574
285
|
const normalized = normalizeCliError(error);
|
|
575
286
|
if (jsonOutputRequested) {
|
|
576
|
-
return JSON.stringify(
|
|
287
|
+
return JSON.stringify(failureEnvelope(activeCommandPath || commandNameFromArgv(process.argv), normalized), null, 2);
|
|
577
288
|
}
|
|
578
289
|
const parts = [chalk.red('Error:'), normalized.message];
|
|
579
290
|
if (normalized.code)
|
|
@@ -583,6 +294,7 @@ function formatCliError(error, jsonOutputRequested = false) {
|
|
|
583
294
|
return parts.join(' ');
|
|
584
295
|
}
|
|
585
296
|
function reportCliError(error, jsonOutputRequested = false) {
|
|
297
|
+
const normalized = normalizeCliError(error);
|
|
586
298
|
const output = formatCliError(error, jsonOutputRequested);
|
|
587
299
|
if (jsonOutputRequested) {
|
|
588
300
|
console.log(output);
|
|
@@ -590,10 +302,10 @@ function reportCliError(error, jsonOutputRequested = false) {
|
|
|
590
302
|
else {
|
|
591
303
|
console.error(output);
|
|
592
304
|
}
|
|
305
|
+
return normalized.exitCode;
|
|
593
306
|
}
|
|
594
307
|
function handleFatalCliError(error, jsonOutputRequested = false) {
|
|
595
|
-
reportCliError(error, jsonOutputRequested);
|
|
596
|
-
process.exitCode = 1;
|
|
308
|
+
process.exitCode = reportCliError(error, jsonOutputRequested);
|
|
597
309
|
}
|
|
598
310
|
let errorHandlersInstalled = false;
|
|
599
311
|
export function setupErrorHandlers() {
|
|
@@ -604,7 +316,7 @@ export function setupErrorHandlers() {
|
|
|
604
316
|
process.on('unhandledRejection', (reason) => {
|
|
605
317
|
const rejection = reason instanceof Error ? reason : new Error(String(reason || 'Unhandled promise rejection'));
|
|
606
318
|
handleFatalCliError(rejection, process.argv.includes('--json'));
|
|
607
|
-
process.exit(1);
|
|
319
|
+
process.exit(process.exitCode || 1);
|
|
608
320
|
});
|
|
609
321
|
process.on('uncaughtException', (error) => {
|
|
610
322
|
handleFatalCliError(error, process.argv.includes('--json'));
|
|
@@ -616,17 +328,31 @@ export function setupErrorHandlers() {
|
|
|
616
328
|
});
|
|
617
329
|
}
|
|
618
330
|
export async function main(args) {
|
|
331
|
+
// Metadata inspection must remain side-effect free. In particular, package
|
|
332
|
+
// managers and release probes routinely invoke --version with a read-only or
|
|
333
|
+
// nonexistent HOME, so do not construct Config (which owns durable state)
|
|
334
|
+
// before satisfying this top-level contract.
|
|
335
|
+
if (process.env.VIGTHORIA_CAPTURE_RUNTIME_MODEL !== '1' && (args[2] === '--version' || args[2] === '-V')) {
|
|
336
|
+
console.log(VERSION);
|
|
337
|
+
process.exitCode = 0;
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const jsonOutputRequested = args.includes('--json');
|
|
619
341
|
const program = new Command();
|
|
342
|
+
program.exitOverride();
|
|
343
|
+
program.configureOutput({
|
|
344
|
+
// Commander writes parse failures before throwing. Suppress that legacy
|
|
345
|
+
// side channel and let the single renderer below own stderr/stdout.
|
|
346
|
+
writeErr: () => undefined,
|
|
347
|
+
});
|
|
620
348
|
const config = new Config();
|
|
621
349
|
const logger = new Logger();
|
|
622
350
|
const invokedBinaryName = getInvokedBinaryName();
|
|
623
351
|
const argv = [...args];
|
|
624
352
|
const firstArg = argv[2];
|
|
625
|
-
const jsonOutputRequested = argv.includes('--json');
|
|
626
353
|
const directPromptRequested = argv.includes('--prompt') || argv.includes('-P');
|
|
627
354
|
const helpOrVersionRequested = argv.some((token) => token === '--help' || token === '-h' || token === '--version' || token === '-V' ||
|
|
628
355
|
token === 'help' || token === 'version');
|
|
629
|
-
const isLegionCortexRequest = invokedBinaryName === 'vigthoria' && argv[2] === 'legion' && argv.includes('--cortex');
|
|
630
356
|
if (invokedBinaryName === 'vigthoria-chat') {
|
|
631
357
|
const knownCommands = new Set([
|
|
632
358
|
'chat', 'chat-resume', 'agent', 'edit', 'generate', 'explain', 'fix', 'review', 'cancel', 'background', 'bg',
|
|
@@ -637,18 +363,6 @@ export async function main(args) {
|
|
|
637
363
|
argv.splice(2, 0, 'chat');
|
|
638
364
|
}
|
|
639
365
|
}
|
|
640
|
-
const requestedCommand = resolveRequestedCommand(argv);
|
|
641
|
-
// Skip gateway JWT auth when running on-server with a service key (e.g., Cortex on-box).
|
|
642
|
-
// The service key is checked by Hyper Loop directly — no user session needed.
|
|
643
|
-
const hasServiceKey = !!(process.env.HYPERLOOP_SERVICE_KEY ||
|
|
644
|
-
process.env.V3_SERVICE_KEY);
|
|
645
|
-
if (!hasServiceKey && !isLegionCortexRequest && requestedCommand && isAuthProtectedCommand(requestedCommand)) {
|
|
646
|
-
const authOk = await enforceGatewayAuthSession(config, logger, jsonOutputRequested);
|
|
647
|
-
if (!authOk) {
|
|
648
|
-
process.exitCode = 1;
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
366
|
if (process.env.VIGTHORIA_NO_BANNER !== '1' &&
|
|
653
367
|
!jsonOutputRequested &&
|
|
654
368
|
!directPromptRequested &&
|
|
@@ -691,879 +405,18 @@ export async function main(args) {
|
|
|
691
405
|
!jsonOutputRequested &&
|
|
692
406
|
!directPromptRequested &&
|
|
693
407
|
!helpOrVersionRequested) {
|
|
694
|
-
void checkForUpdatesQuietly();
|
|
408
|
+
void checkForUpdatesQuietly(VERSION);
|
|
695
409
|
}
|
|
696
410
|
program
|
|
697
411
|
.name(invokedBinaryName === 'vigthoria-chat' ? 'vigthoria-chat' : 'vigthoria')
|
|
698
412
|
.description('AI-powered terminal coding assistant for Vigthoria Coder subscribers')
|
|
699
413
|
.version(VERSION);
|
|
700
|
-
|
|
701
|
-
program
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
.option('-p, --project <path>', 'Set project context path')
|
|
707
|
-
.option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
|
|
708
|
-
.option('-a, --agent', 'Enable agentic mode (default: true for best quality)', true)
|
|
709
|
-
.option('--no-agent', 'Disable agentic mode (simple chat only)')
|
|
710
|
-
.option('-r, --resume', 'Resume last session for this project', false)
|
|
711
|
-
.option('--prompt <text>', 'Run a single prompt directly and exit')
|
|
712
|
-
.option('-w, --workflow <selector>', 'Route prompts through a named or explicit VigFlow workflow target')
|
|
713
|
-
.option('--json', 'Emit machine-readable JSON output for direct prompt runs', false)
|
|
714
|
-
.option('--auto-approve', 'Auto-approve agent actions (dangerous!)', false)
|
|
715
|
-
.option('--bridge <url>', 'Connect to Vigthoria Commando Bridge for remote admin observability')
|
|
716
|
-
.action(async (options) => {
|
|
717
|
-
const chat = new ChatCommand(config, logger, program);
|
|
718
|
-
await chat.run({
|
|
719
|
-
model: options.model,
|
|
720
|
-
project: options.project,
|
|
721
|
-
projectProvided: typeof options.project === 'string' && options.project.trim().length > 0,
|
|
722
|
-
newProject: options.newProject,
|
|
723
|
-
agent: options.agent,
|
|
724
|
-
workflow: options.workflow,
|
|
725
|
-
json: options.json,
|
|
726
|
-
autoApprove: options.autoApprove,
|
|
727
|
-
resume: options.resume,
|
|
728
|
-
prompt: options.prompt,
|
|
729
|
-
bridge: options.bridge,
|
|
730
|
-
});
|
|
731
|
-
});
|
|
732
|
-
program
|
|
733
|
-
.command('chat-resume')
|
|
734
|
-
.description('Resume the latest chat session for the current or specified project')
|
|
735
|
-
.option('-m, --model <model>', 'Select AI model (agent, code, code-35b, code-9b, balanced, balanced-4b, cloud, ultra)')
|
|
736
|
-
.option('-p, --project <path>', 'Set project context path')
|
|
737
|
-
.option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
|
|
738
|
-
.option('-a, --agent', 'Enable agentic mode (default: true for best quality)', true)
|
|
739
|
-
.option('--no-agent', 'Disable agentic mode (simple chat only)')
|
|
740
|
-
.option('--prompt <text>', 'Run a single prompt directly and exit after resuming context')
|
|
741
|
-
.option('-w, --workflow <selector>', 'Route prompts through a named or explicit VigFlow workflow target')
|
|
742
|
-
.option('--json', 'Emit machine-readable JSON output for direct prompt runs', false)
|
|
743
|
-
.option('--auto-approve', 'Auto-approve agent actions (dangerous!)', false)
|
|
744
|
-
.option('--bridge <url>', 'Connect to Vigthoria Commando Bridge for remote admin observability')
|
|
745
|
-
.action(async (options) => {
|
|
746
|
-
const chat = new ChatCommand(config, logger, program);
|
|
747
|
-
await chat.run({
|
|
748
|
-
model: options.model,
|
|
749
|
-
project: options.project,
|
|
750
|
-
projectProvided: typeof options.project === 'string' && options.project.trim().length > 0,
|
|
751
|
-
newProject: options.newProject,
|
|
752
|
-
agent: options.agent,
|
|
753
|
-
workflow: options.workflow,
|
|
754
|
-
json: options.json,
|
|
755
|
-
autoApprove: options.autoApprove,
|
|
756
|
-
resume: true,
|
|
757
|
-
prompt: options.prompt,
|
|
758
|
-
bridge: options.bridge,
|
|
759
|
-
});
|
|
760
|
-
});
|
|
761
|
-
// Agent command - Agentic mode (Vigthoria Autonomous)
|
|
762
|
-
// Uses Vigthoria v3 Code 35B or Vigthoria Cloud for complex tasks
|
|
763
|
-
program
|
|
764
|
-
.command('agent')
|
|
765
|
-
.alias('a')
|
|
766
|
-
.description('Start agentic mode - AI can read/write files, run commands')
|
|
767
|
-
.option('-m, --model <model>', 'Select AI model (agent, code, cloud, ultra)')
|
|
768
|
-
.option('-p, --project <path>', 'Set project context path')
|
|
769
|
-
.option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
|
|
770
|
-
.option('--prompt <text>', 'Run a single agent prompt directly and exit')
|
|
771
|
-
.option('-w, --workflow <selector>', 'Run the prompt through a named or explicit VigFlow workflow target')
|
|
772
|
-
.option('--json', 'Emit machine-readable JSON output for direct prompt runs', false)
|
|
773
|
-
.option('--retry', 'Retry failed tasks from the last agent run in this workspace session', false)
|
|
774
|
-
.option('--continue', 'Continue unfinished tasks from the last agent run in this workspace session', false)
|
|
775
|
-
.option('--auto-approve', 'Auto-approve all actions (dangerous!)', false)
|
|
776
|
-
.option('--bridge <url>', 'Connect to Vigthoria Commando Bridge for remote admin observability')
|
|
777
|
-
.action(async (options) => {
|
|
778
|
-
const chat = new ChatCommand(config, logger, program);
|
|
779
|
-
await chat.run({
|
|
780
|
-
model: options.model,
|
|
781
|
-
project: options.project,
|
|
782
|
-
projectProvided: typeof options.project === 'string' && options.project.trim().length > 0,
|
|
783
|
-
newProject: options.newProject,
|
|
784
|
-
agent: true,
|
|
785
|
-
operator: false,
|
|
786
|
-
workflow: options.workflow,
|
|
787
|
-
json: options.json,
|
|
788
|
-
autoApprove: options.autoApprove,
|
|
789
|
-
prompt: options.prompt,
|
|
790
|
-
bridge: options.bridge,
|
|
791
|
-
retry: options.retry,
|
|
792
|
-
continue: options.continue,
|
|
793
|
-
});
|
|
794
|
-
});
|
|
795
|
-
program
|
|
796
|
-
.command('operator')
|
|
797
|
-
.alias('op')
|
|
798
|
-
.description('Start BMAD operator mode for infrastructure and system workflows')
|
|
799
|
-
.option('-m, --model <model>', 'Select operator model (code, agent, cloud)')
|
|
800
|
-
.option('-p, --project <path>', 'Set project context path')
|
|
801
|
-
.option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
|
|
802
|
-
.option('--prompt <text>', 'Run a single operator prompt directly and exit')
|
|
803
|
-
.option('-w, --workflow <selector>', 'Run the prompt through a named or explicit VigFlow workflow target')
|
|
804
|
-
.option('--save-plan', 'Save the completed BMAD plan into VigFlow for rerun and audit', false)
|
|
805
|
-
.option('--json', 'Emit machine-readable JSON output for direct prompt runs', false)
|
|
806
|
-
.option('--bridge <url>', 'Connect to Vigthoria Commando Bridge for remote admin observability')
|
|
807
|
-
.action(async (options) => {
|
|
808
|
-
const chat = new ChatCommand(config, logger, program);
|
|
809
|
-
await chat.run({
|
|
810
|
-
model: options.model,
|
|
811
|
-
project: options.project,
|
|
812
|
-
projectProvided: typeof options.project === 'string' && options.project.trim().length > 0,
|
|
813
|
-
newProject: options.newProject,
|
|
814
|
-
agent: false,
|
|
815
|
-
operator: true,
|
|
816
|
-
workflow: options.workflow,
|
|
817
|
-
savePlan: options.savePlan,
|
|
818
|
-
json: options.json,
|
|
819
|
-
prompt: options.prompt,
|
|
820
|
-
bridge: options.bridge,
|
|
821
|
-
});
|
|
822
|
-
});
|
|
823
|
-
new V4Command(config, logger).register(program);
|
|
824
|
-
program
|
|
825
|
-
.command('v4-menu')
|
|
826
|
-
.description('Internal: interactive V3/V4 agent + provider picker used by the V4 Operating Agent launcher')
|
|
827
|
-
.option('--default-agent <agent>', 'v3 | v4', 'v3')
|
|
828
|
-
.option('--default-provider <provider>', 'cloud | v3_local | byok', 'cloud')
|
|
829
|
-
.option('--byok-api-key <key>', 'Existing BYOK api key', '')
|
|
830
|
-
.option('--byok-base-url <url>', 'Existing BYOK base url', '')
|
|
831
|
-
.option('--byok-model <name>', 'Existing BYOK model', '')
|
|
832
|
-
.requiredOption('--out-file <path>', 'Write JSON selection result to this file')
|
|
833
|
-
.action(async (options) => {
|
|
834
|
-
await runV4Menu({
|
|
835
|
-
defaultAgent: options.defaultAgent,
|
|
836
|
-
defaultProvider: options.defaultProvider,
|
|
837
|
-
byokApiKey: options.byokApiKey,
|
|
838
|
-
byokBaseUrl: options.byokBaseUrl,
|
|
839
|
-
byokModel: options.byokModel,
|
|
840
|
-
outFile: options.outFile,
|
|
841
|
-
});
|
|
842
|
-
});
|
|
843
|
-
program
|
|
844
|
-
.command('bridge')
|
|
845
|
-
.description('Inspect local DevTools Bridge availability for browser debugging tasks')
|
|
846
|
-
.command('status')
|
|
847
|
-
.description('Show live DevTools Bridge status')
|
|
848
|
-
.action(async () => {
|
|
849
|
-
const bridge = new BridgeCommand(config, logger);
|
|
850
|
-
await bridge.status();
|
|
851
|
-
});
|
|
852
|
-
const deviceCommand = program
|
|
853
|
-
.command('device')
|
|
854
|
-
.alias('adb')
|
|
855
|
-
.description('Android Developer Bridge commands via local ADB');
|
|
856
|
-
deviceCommand
|
|
857
|
-
.command('status')
|
|
858
|
-
.description('Show ADB availability and connected Android devices')
|
|
859
|
-
.option('--json', 'Emit JSON output', false)
|
|
860
|
-
.action(async (options) => {
|
|
861
|
-
const device = new DeviceCommand(config, logger);
|
|
862
|
-
await device.status(options);
|
|
863
|
-
});
|
|
864
|
-
deviceCommand
|
|
865
|
-
.command('list')
|
|
866
|
-
.description('List connected Android devices')
|
|
867
|
-
.option('--json', 'Emit JSON output', false)
|
|
868
|
-
.action(async (options) => {
|
|
869
|
-
const device = new DeviceCommand(config, logger);
|
|
870
|
-
await device.list(options);
|
|
871
|
-
});
|
|
872
|
-
deviceCommand
|
|
873
|
-
.command('screenshot')
|
|
874
|
-
.description('Capture a screenshot from an Android device')
|
|
875
|
-
.option('-d, --device <id>', 'ADB device id')
|
|
876
|
-
.option('-o, --output <path>', 'Output PNG path')
|
|
877
|
-
.action(async (options) => {
|
|
878
|
-
const device = new DeviceCommand(config, logger);
|
|
879
|
-
await device.screenshot(options);
|
|
880
|
-
});
|
|
881
|
-
deviceCommand
|
|
882
|
-
.command('install <apk>')
|
|
883
|
-
.description('Install or update an APK on an Android device')
|
|
884
|
-
.option('-d, --device <id>', 'ADB device id')
|
|
885
|
-
.option('--json', 'Emit JSON output', false)
|
|
886
|
-
.action(async (apk, options) => {
|
|
887
|
-
const device = new DeviceCommand(config, logger);
|
|
888
|
-
await device.install(apk, options);
|
|
889
|
-
});
|
|
890
|
-
deviceCommand
|
|
891
|
-
.command('launch <package>')
|
|
892
|
-
.description('Launch an Android app by package name')
|
|
893
|
-
.option('-d, --device <id>', 'ADB device id')
|
|
894
|
-
.option('-a, --activity <activity>', 'Activity class, for example .MainActivity')
|
|
895
|
-
.option('--json', 'Emit JSON output', false)
|
|
896
|
-
.action(async (packageName, options) => {
|
|
897
|
-
const device = new DeviceCommand(config, logger);
|
|
898
|
-
await device.launch(packageName, options);
|
|
899
|
-
});
|
|
900
|
-
deviceCommand
|
|
901
|
-
.command('logs')
|
|
902
|
-
.description('Read Android logcat output')
|
|
903
|
-
.option('-d, --device <id>', 'ADB device id')
|
|
904
|
-
.option('-n, --lines <count>', 'Number of log lines for snapshot mode', '250')
|
|
905
|
-
.option('-f, --follow', 'Follow logcat until interrupted', false)
|
|
906
|
-
.option('--json', 'Emit JSON output for snapshot mode', false)
|
|
907
|
-
.action(async (options) => {
|
|
908
|
-
const device = new DeviceCommand(config, logger);
|
|
909
|
-
await device.logs(options);
|
|
910
|
-
});
|
|
911
|
-
deviceCommand
|
|
912
|
-
.command('tcpip')
|
|
913
|
-
.description('Enable wireless ADB tcpip mode on the selected USB-connected device')
|
|
914
|
-
.option('-d, --device <id>', 'ADB device id')
|
|
915
|
-
.option('-p, --port <port>', 'Wireless ADB port', '5555')
|
|
916
|
-
.option('--json', 'Emit JSON output', false)
|
|
917
|
-
.action(async (options) => {
|
|
918
|
-
const device = new DeviceCommand(config, logger);
|
|
919
|
-
await device.tcpip(options);
|
|
920
|
-
});
|
|
921
|
-
deviceCommand
|
|
922
|
-
.command('connect <address>')
|
|
923
|
-
.description('Connect to a wireless ADB device, for example 192.168.1.30:5555')
|
|
924
|
-
.option('--json', 'Emit JSON output', false)
|
|
925
|
-
.action(async (address, options) => {
|
|
926
|
-
const device = new DeviceCommand(config, logger);
|
|
927
|
-
await device.connect(address, options);
|
|
928
|
-
});
|
|
929
|
-
deviceCommand
|
|
930
|
-
.command('disconnect [address]')
|
|
931
|
-
.description('Disconnect a wireless ADB device')
|
|
932
|
-
.option('--json', 'Emit JSON output', false)
|
|
933
|
-
.action(async (address, options) => {
|
|
934
|
-
const device = new DeviceCommand(config, logger);
|
|
935
|
-
await device.disconnect(address, options);
|
|
936
|
-
});
|
|
937
|
-
deviceCommand.action(async () => {
|
|
938
|
-
const device = new DeviceCommand(config, logger);
|
|
939
|
-
await device.status({});
|
|
940
|
-
});
|
|
941
|
-
// Security command group - Security DevOps scans and fix plans
|
|
942
|
-
const securityCommand = program
|
|
943
|
-
.command('security')
|
|
944
|
-
.alias('vsec')
|
|
945
|
-
.description('Run security scans, scores, and fix plans via Vigthoria MCP security tools');
|
|
946
|
-
securityCommand
|
|
947
|
-
.command('scan')
|
|
948
|
-
.description('Scan project for security issues')
|
|
949
|
-
.option('-d, --dir <path>', 'Directory to scan (default: current directory)')
|
|
950
|
-
.option('--json', 'Emit JSON output', false)
|
|
951
|
-
.action(async (options) => {
|
|
952
|
-
const security = new SecurityCommand(config, logger);
|
|
953
|
-
await security.scan({ dir: options.dir, json: options.json });
|
|
954
|
-
});
|
|
955
|
-
securityCommand
|
|
956
|
-
.command('score')
|
|
957
|
-
.description('Calculate project security score')
|
|
958
|
-
.option('-d, --dir <path>', 'Directory to score (default: current directory)')
|
|
959
|
-
.option('--json', 'Emit JSON output', false)
|
|
960
|
-
.action(async (options) => {
|
|
961
|
-
const security = new SecurityCommand(config, logger);
|
|
962
|
-
await security.score({ dir: options.dir, json: options.json });
|
|
963
|
-
});
|
|
964
|
-
securityCommand
|
|
965
|
-
.command('fix')
|
|
966
|
-
.description('Generate security fix plan')
|
|
967
|
-
.option('-d, --dir <path>', 'Directory to inspect (default: current directory)')
|
|
968
|
-
.option('-i, --issue-id <id>', 'Single issue id to target')
|
|
969
|
-
.option('--apply', 'Request auto-apply mode (safe mode still requires explicit patching)', false)
|
|
970
|
-
.option('--json', 'Emit JSON output', false)
|
|
971
|
-
.action(async (options) => {
|
|
972
|
-
const security = new SecurityCommand(config, logger);
|
|
973
|
-
await security.fix({ dir: options.dir, issueId: options.issueId, apply: options.apply, json: options.json });
|
|
974
|
-
});
|
|
975
|
-
// Edit command - Edit files with AI
|
|
976
|
-
program
|
|
977
|
-
.command('edit <file>')
|
|
978
|
-
.alias('e')
|
|
979
|
-
.description('Edit a file with AI assistance')
|
|
980
|
-
.option('-i, --instruction <text>', 'Editing instruction')
|
|
981
|
-
.option('-m, --model <model>', 'Select AI model', 'code')
|
|
982
|
-
.option('--apply', 'Automatically apply changes without confirmation', false)
|
|
983
|
-
.action(async (file, options) => {
|
|
984
|
-
const edit = new EditCommand(config, logger);
|
|
985
|
-
await edit.run(file, options);
|
|
986
|
-
});
|
|
987
|
-
// Generate command - Generate code
|
|
988
|
-
program
|
|
989
|
-
.command('generate <description>')
|
|
990
|
-
.alias('g')
|
|
991
|
-
.description('Generate code from description')
|
|
992
|
-
.option('-l, --language <lang>', 'Target language (auto-detected from description, or specify: javascript, typescript, python, html, etc.)')
|
|
993
|
-
.option('-o, --output <file>', 'Output file path')
|
|
994
|
-
.option('-m, --model <model>', 'Select AI model', 'code')
|
|
995
|
-
.option('-p, --pro', 'Senior Developer Mode: plan, generate, quality check (recommended)', false)
|
|
996
|
-
.action(async (description, options) => {
|
|
997
|
-
const generate = new GenerateCommand(config, logger);
|
|
998
|
-
await generate.run(description, options);
|
|
999
|
-
});
|
|
1000
|
-
// Explain command - Explain code
|
|
1001
|
-
program
|
|
1002
|
-
.command('explain <file>')
|
|
1003
|
-
.alias('x')
|
|
1004
|
-
.description('Explain code in a file')
|
|
1005
|
-
.option('-l, --lines <range>', 'Line range (e.g., 1-50)')
|
|
1006
|
-
.option('-d, --detail <level>', 'Detail level (brief, normal, detailed)', 'normal')
|
|
1007
|
-
.action(async (file, options) => {
|
|
1008
|
-
const explain = new ExplainCommand(config, logger);
|
|
1009
|
-
await explain.run(file, options);
|
|
1010
|
-
});
|
|
1011
|
-
// Fix command - Fix code issues
|
|
1012
|
-
program
|
|
1013
|
-
.command('fix [file]')
|
|
1014
|
-
.alias('f')
|
|
1015
|
-
.description('Fix issues in a file')
|
|
1016
|
-
.option('-t, --type <type>', 'Fix type (bugs, style, security, performance)', 'bugs')
|
|
1017
|
-
.option('--apply', 'Automatically apply fixes', false)
|
|
1018
|
-
.action(async (file, options) => {
|
|
1019
|
-
if (!file) {
|
|
1020
|
-
logger.error('Usage: vigthoria fix <file> [--type bugs|style|security|performance] [--apply]');
|
|
1021
|
-
process.exitCode = 1;
|
|
1022
|
-
return;
|
|
1023
|
-
}
|
|
1024
|
-
const edit = new EditCommand(config, logger);
|
|
1025
|
-
await edit.fix(file, options);
|
|
1026
|
-
});
|
|
1027
|
-
// Review command - Code review
|
|
1028
|
-
program
|
|
1029
|
-
.command('review <file>')
|
|
1030
|
-
.alias('r')
|
|
1031
|
-
.description('Review code quality')
|
|
1032
|
-
.option('-f, --format <format>', 'Output format (text, json, markdown)', 'text')
|
|
1033
|
-
.action(async (file, options) => {
|
|
1034
|
-
const review = new ReviewCommand(config, logger);
|
|
1035
|
-
await review.run(file, options);
|
|
1036
|
-
});
|
|
1037
|
-
const workflowCommand = program
|
|
1038
|
-
.command('workflow')
|
|
1039
|
-
.alias('flow')
|
|
1040
|
-
.description('List, instantiate, and run repeatable VigFlow workflows');
|
|
1041
|
-
workflowCommand
|
|
1042
|
-
.command('templates')
|
|
1043
|
-
.alias('catalog')
|
|
1044
|
-
.description('List available VigFlow templates')
|
|
1045
|
-
.option('-c, --category <category>', 'Filter templates by category')
|
|
1046
|
-
.option('-s, --search <query>', 'Search template names, descriptions, and tags')
|
|
1047
|
-
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1048
|
-
.action(async (options) => {
|
|
1049
|
-
const workflow = new WorkflowCommand(config, logger);
|
|
1050
|
-
await workflow.templates(options);
|
|
1051
|
-
});
|
|
1052
|
-
workflowCommand
|
|
1053
|
-
.command('list')
|
|
1054
|
-
.alias('ls')
|
|
1055
|
-
.description('List workflows created for the current account')
|
|
1056
|
-
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1057
|
-
.action(async (options) => {
|
|
1058
|
-
const workflow = new WorkflowCommand(config, logger);
|
|
1059
|
-
await workflow.list(options);
|
|
1060
|
-
});
|
|
1061
|
-
workflowCommand
|
|
1062
|
-
.command('use-template <templateId>')
|
|
1063
|
-
.description('Create a workflow from a built-in or saved template')
|
|
1064
|
-
.option('-n, --name <name>', 'Override the workflow name')
|
|
1065
|
-
.option('-v, --variables <json>', 'Template variables as a JSON object')
|
|
1066
|
-
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1067
|
-
.action(async (templateId, options) => {
|
|
1068
|
-
const workflow = new WorkflowCommand(config, logger);
|
|
1069
|
-
await workflow.useTemplate(templateId, options);
|
|
1070
|
-
});
|
|
1071
|
-
workflowCommand
|
|
1072
|
-
.command('run <workflowId>')
|
|
1073
|
-
.description('Run a workflow immediately')
|
|
1074
|
-
.option('-d, --data <json>', 'Execution input data as a JSON object')
|
|
1075
|
-
.option('--no-brain', 'Do not attach local Project Brain context to this workflow run')
|
|
1076
|
-
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1077
|
-
.action(async (workflowId, options) => {
|
|
1078
|
-
const workflow = new WorkflowCommand(config, logger);
|
|
1079
|
-
await workflow.run(workflowId, options);
|
|
1080
|
-
});
|
|
1081
|
-
workflowCommand
|
|
1082
|
-
.command('status <executionId>')
|
|
1083
|
-
.description('Get the current or final status of a workflow execution')
|
|
1084
|
-
.option('--no-brain', 'Do not remember this workflow status in local Project Brain')
|
|
1085
|
-
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1086
|
-
.action(async (executionId, options) => {
|
|
1087
|
-
const workflow = new WorkflowCommand(config, logger);
|
|
1088
|
-
await workflow.status(executionId, options);
|
|
1089
|
-
});
|
|
1090
|
-
workflowCommand.action(async () => {
|
|
1091
|
-
const workflow = new WorkflowCommand(config, logger);
|
|
1092
|
-
await workflow.templates({});
|
|
1093
|
-
});
|
|
1094
|
-
// ==================== HUB / MARKETPLACE COMMANDS ====================
|
|
1095
|
-
// Hub command - Discover and activate API modules
|
|
1096
|
-
const hubCommand = program
|
|
1097
|
-
.command('hub')
|
|
1098
|
-
.alias('marketplace')
|
|
1099
|
-
.description('Discover, search, and activate Vigthoria API modules');
|
|
1100
|
-
hubCommand
|
|
1101
|
-
.command('discover')
|
|
1102
|
-
.alias('d')
|
|
1103
|
-
.description('Interactive module discovery - find the right APIs for your project')
|
|
1104
|
-
.action(async () => {
|
|
1105
|
-
const hub = new HubCommand(config, logger);
|
|
1106
|
-
await hub.discover();
|
|
1107
|
-
});
|
|
1108
|
-
hubCommand
|
|
1109
|
-
.command('list')
|
|
1110
|
-
.alias('ls')
|
|
1111
|
-
.description('List all available API modules')
|
|
1112
|
-
.option('-c, --category <category>', 'Filter by category (payments, communication, ai, creative, media)')
|
|
1113
|
-
.action(async (options) => {
|
|
1114
|
-
const hub = new HubCommand(config, logger);
|
|
1115
|
-
await hub.list(options);
|
|
1116
|
-
});
|
|
1117
|
-
hubCommand
|
|
1118
|
-
.command('search <query>')
|
|
1119
|
-
.alias('find')
|
|
1120
|
-
.description('Semantic search for modules (e.g., "generate background music for my app")')
|
|
1121
|
-
.action(async (query) => {
|
|
1122
|
-
const hub = new HubCommand(config, logger);
|
|
1123
|
-
await hub.search(query);
|
|
1124
|
-
});
|
|
1125
|
-
hubCommand
|
|
1126
|
-
.command('activate <module>')
|
|
1127
|
-
.alias('enable')
|
|
1128
|
-
.description('Activate a module for your API key (enables pay-as-you-go)')
|
|
1129
|
-
.action(async (module) => {
|
|
1130
|
-
const hub = new HubCommand(config, logger);
|
|
1131
|
-
await hub.activate(module);
|
|
1132
|
-
});
|
|
1133
|
-
hubCommand
|
|
1134
|
-
.command('active')
|
|
1135
|
-
.description('Show your currently active modules')
|
|
1136
|
-
.action(async () => {
|
|
1137
|
-
const hub = new HubCommand(config, logger);
|
|
1138
|
-
await hub.active();
|
|
1139
|
-
});
|
|
1140
|
-
hubCommand
|
|
1141
|
-
.command('info <module>')
|
|
1142
|
-
.alias('details')
|
|
1143
|
-
.description('Get detailed information about a module')
|
|
1144
|
-
.action(async (module) => {
|
|
1145
|
-
const hub = new HubCommand(config, logger);
|
|
1146
|
-
await hub.info(module);
|
|
1147
|
-
});
|
|
1148
|
-
// Default hub action shows discover
|
|
1149
|
-
hubCommand.action(async () => {
|
|
1150
|
-
const hub = new HubCommand(config, logger);
|
|
1151
|
-
await hub.discover();
|
|
1152
|
-
});
|
|
1153
|
-
const musicCommand = program
|
|
1154
|
-
.command('music')
|
|
1155
|
-
.alias('audio')
|
|
1156
|
-
.description('Generate songs with Vigthoria Music AI / ACE-Step');
|
|
1157
|
-
musicCommand
|
|
1158
|
-
.command('generate [prompt]')
|
|
1159
|
-
.alias('g')
|
|
1160
|
-
.description('Queue full-song generation and return a task id')
|
|
1161
|
-
.option('--lyrics <text>', 'Provide fixed lyrics instead of auto-writing lyrics')
|
|
1162
|
-
.option('--duration <seconds>', 'Song duration in seconds', '120')
|
|
1163
|
-
.option('--genre <genre>', 'Music genre', 'pop')
|
|
1164
|
-
.option('--language <code>', 'Vocal language', 'en')
|
|
1165
|
-
.option('--voice-type <type>', 'Voice type (female, male, mixed)', 'female')
|
|
1166
|
-
.option('--style-tags <tags>', 'Additional style tags')
|
|
1167
|
-
.option('--voice <path>', 'Upload a local voice reference for cloning')
|
|
1168
|
-
.option('--wait', 'Poll until the task reaches a terminal state', false)
|
|
1169
|
-
.option('--poll-interval <seconds>', 'Polling interval when --wait is used', '5')
|
|
1170
|
-
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1171
|
-
.action(async (prompt, options) => {
|
|
1172
|
-
const music = new MusicCommand(config, logger);
|
|
1173
|
-
await music.generate(prompt, options);
|
|
1174
|
-
});
|
|
1175
|
-
musicCommand
|
|
1176
|
-
.command('status <taskId>')
|
|
1177
|
-
.description('Check a queued music generation task')
|
|
1178
|
-
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1179
|
-
.action(async (taskId, options) => {
|
|
1180
|
-
const music = new MusicCommand(config, logger);
|
|
1181
|
-
await music.status(taskId, options);
|
|
1182
|
-
});
|
|
1183
|
-
// ==================== REPO COMMANDS ====================
|
|
1184
|
-
// Repo command - Push/Pull projects to/from Vigthoria Community Repository
|
|
1185
|
-
const repoCommand = program
|
|
1186
|
-
.command('repo')
|
|
1187
|
-
.alias('repository')
|
|
1188
|
-
.description('Push and pull projects to/from your Vigthoria Community Repository');
|
|
1189
|
-
repoCommand
|
|
1190
|
-
.command('push [path]')
|
|
1191
|
-
.alias('upload')
|
|
1192
|
-
.description('Push current or specified project to Vigthoria Community Repository')
|
|
1193
|
-
.option('-n, --name <projectName>', 'Project name override for the remote repo')
|
|
1194
|
-
.option('-v, --visibility <type>', 'Set visibility (private, restricted, public)', 'private')
|
|
1195
|
-
.option('-d, --description <text>', 'Project description')
|
|
1196
|
-
.option('-f, --force', 'Overwrite existing project', false)
|
|
1197
|
-
.option('-y, --yes', 'Run non-interactively with provided/default values', false)
|
|
1198
|
-
.option('--retry <reviewId>', 'Link this corrected push to a repository security review')
|
|
1199
|
-
.option('--open-in <engine>', 'After push, open project in engine: shop, visual, game')
|
|
1200
|
-
.option('--browser', 'Open result URL in default browser', false)
|
|
1201
|
-
.action(async (pathArg, options) => {
|
|
1202
|
-
const repo = new RepoCommand(config, logger);
|
|
1203
|
-
await repo.push({
|
|
1204
|
-
path: pathArg,
|
|
1205
|
-
name: options.name,
|
|
1206
|
-
visibility: options.visibility,
|
|
1207
|
-
description: options.description,
|
|
1208
|
-
force: options.force,
|
|
1209
|
-
yes: options.yes,
|
|
1210
|
-
retry: options.retry
|
|
1211
|
-
});
|
|
1212
|
-
if (options.openIn) {
|
|
1213
|
-
const engine = options.openIn;
|
|
1214
|
-
if (!['shop', 'visual', 'game'].includes(engine)) {
|
|
1215
|
-
logger.warn(`Unknown engine "${engine}". Use: shop, visual, game`);
|
|
1216
|
-
}
|
|
1217
|
-
else {
|
|
1218
|
-
await repo.openIn(engine, options.name || undefined, { browser: options.browser });
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
});
|
|
1222
|
-
repoCommand
|
|
1223
|
-
.command('review [reviewId]')
|
|
1224
|
-
.description('Show repository security review status, findings, and retry guidance')
|
|
1225
|
-
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1226
|
-
.action(async (reviewId, options) => {
|
|
1227
|
-
const repo = new RepoCommand(config, logger);
|
|
1228
|
-
await repo.review(reviewId, { json: options.json });
|
|
1229
|
-
});
|
|
1230
|
-
repoCommand
|
|
1231
|
-
.command('pull <name>')
|
|
1232
|
-
.alias('download')
|
|
1233
|
-
.description('Pull a project from your Vigthoria Community Repository')
|
|
1234
|
-
.option('-o, --output <path>', 'Output directory path')
|
|
1235
|
-
.option('-f, --force', 'Overwrite existing directory', false)
|
|
1236
|
-
.action(async (name, options) => {
|
|
1237
|
-
const repo = new RepoCommand(config, logger);
|
|
1238
|
-
await repo.pull(name, {
|
|
1239
|
-
output: options.output,
|
|
1240
|
-
force: options.force
|
|
1241
|
-
});
|
|
1242
|
-
});
|
|
1243
|
-
repoCommand
|
|
1244
|
-
.command('list')
|
|
1245
|
-
.alias('ls')
|
|
1246
|
-
.description('List all your projects in Vigthoria Community Repository')
|
|
1247
|
-
.option('-v, --visibility <type>', 'Filter by visibility (private, restricted, public)')
|
|
1248
|
-
.action(async (options) => {
|
|
1249
|
-
const repo = new RepoCommand(config, logger);
|
|
1250
|
-
await repo.list({ visibility: options.visibility });
|
|
1251
|
-
});
|
|
1252
|
-
repoCommand
|
|
1253
|
-
.command('status')
|
|
1254
|
-
.description('Show sync status of current project')
|
|
1255
|
-
.action(async () => {
|
|
1256
|
-
const repo = new RepoCommand(config, logger);
|
|
1257
|
-
await repo.status();
|
|
1258
|
-
});
|
|
1259
|
-
repoCommand
|
|
1260
|
-
.command('share <name>')
|
|
1261
|
-
.description('Generate a shareable link for a project')
|
|
1262
|
-
.option('-e, --expires <duration>', 'Link expiration (e.g., 7d, 24h, 30m)', '7d')
|
|
1263
|
-
.action(async (name, options) => {
|
|
1264
|
-
const repo = new RepoCommand(config, logger);
|
|
1265
|
-
await repo.share(name, { expires: options.expires });
|
|
1266
|
-
});
|
|
1267
|
-
repoCommand
|
|
1268
|
-
.command('delete <name>')
|
|
1269
|
-
.alias('rm')
|
|
1270
|
-
.description('Remove a project from your Vigthoria Community Repository')
|
|
1271
|
-
.action(async (name) => {
|
|
1272
|
-
const repo = new RepoCommand(config, logger);
|
|
1273
|
-
await repo.delete(name);
|
|
1274
|
-
});
|
|
1275
|
-
repoCommand
|
|
1276
|
-
.command('clone <url>')
|
|
1277
|
-
.description('Clone a public project from Vigthoria Community')
|
|
1278
|
-
.option('-o, --output <path>', 'Output directory path')
|
|
1279
|
-
.option('-f, --force', 'Overwrite existing directory', false)
|
|
1280
|
-
.action(async (url, options) => {
|
|
1281
|
-
const repo = new RepoCommand(config, logger);
|
|
1282
|
-
await repo.clone(url, {
|
|
1283
|
-
output: options.output,
|
|
1284
|
-
force: options.force
|
|
1285
|
-
});
|
|
1286
|
-
});
|
|
1287
|
-
repoCommand
|
|
1288
|
-
.command('open-in <engine> [projectName]')
|
|
1289
|
-
.description('Open a Vigthoria Community Repository project in the Shop Engine, Visual Editor, or Gaming Engine')
|
|
1290
|
-
.option('--browser', 'Open result URL in default browser', false)
|
|
1291
|
-
.option('--shop-id <id>', 'Target shop ID (for shop engine)', 'default')
|
|
1292
|
-
.addHelpText('after', `
|
|
1293
|
-
Examples:
|
|
1294
|
-
vigthoria repo open-in shop my-store
|
|
1295
|
-
vigthoria repo open-in visual my-website
|
|
1296
|
-
vigthoria repo open-in game my-game --browser`)
|
|
1297
|
-
.action(async (engine, projectName, options) => {
|
|
1298
|
-
if (!['shop', 'visual', 'game'].includes(engine)) {
|
|
1299
|
-
logger.error(`Unknown engine "${engine}". Valid options: shop, visual, game`);
|
|
1300
|
-
process.exit(1);
|
|
1301
|
-
}
|
|
1302
|
-
const repo = new RepoCommand(config, logger);
|
|
1303
|
-
await repo.openIn(engine, projectName, {
|
|
1304
|
-
browser: options.browser,
|
|
1305
|
-
shopId: options.shopId
|
|
1306
|
-
});
|
|
1307
|
-
});
|
|
1308
|
-
// Default repo action shows help with available subcommands
|
|
1309
|
-
repoCommand.action(() => {
|
|
1310
|
-
repoCommand.outputHelp();
|
|
1311
|
-
});
|
|
1312
|
-
// ==================== BACKGROUND AGENT JOBS ====================
|
|
1313
|
-
const backgroundCommand = program
|
|
1314
|
-
.command('background')
|
|
1315
|
-
.alias('bg')
|
|
1316
|
-
.description('Run and manage detached Vigthoria Agent jobs');
|
|
1317
|
-
backgroundCommand
|
|
1318
|
-
.command('start <prompt...>')
|
|
1319
|
-
.description('Start an Agent job in the background and return immediately')
|
|
1320
|
-
.option('--workspace <path>', 'Local workspace path', process.cwd())
|
|
1321
|
-
.option('--model <model>', 'Execution lane: agent, cloud, cloud-reason, or ultra', 'agent')
|
|
1322
|
-
.option('--json', 'Emit JSON output', false)
|
|
1323
|
-
.action(async (prompt, options) => {
|
|
1324
|
-
const bg = new BackgroundCommand(config, logger);
|
|
1325
|
-
await bg.start(prompt, options);
|
|
1326
|
-
});
|
|
1327
|
-
backgroundCommand
|
|
1328
|
-
.command('list')
|
|
1329
|
-
.alias('ls')
|
|
1330
|
-
.description('List recent background jobs')
|
|
1331
|
-
.option('--limit <n>', 'Maximum jobs to show', '20')
|
|
1332
|
-
.option('--json', 'Emit JSON output', false)
|
|
1333
|
-
.action(async (options) => {
|
|
1334
|
-
const bg = new BackgroundCommand(config, logger);
|
|
1335
|
-
await bg.list({ ...options, limit: Number(options.limit) || 20 });
|
|
1336
|
-
});
|
|
1337
|
-
backgroundCommand
|
|
1338
|
-
.command('status <jobId>')
|
|
1339
|
-
.description('Show background job status')
|
|
1340
|
-
.option('--json', 'Emit JSON output', false)
|
|
1341
|
-
.action(async (jobId, options) => {
|
|
1342
|
-
const bg = new BackgroundCommand(config, logger);
|
|
1343
|
-
await bg.status(jobId, options);
|
|
1344
|
-
});
|
|
1345
|
-
backgroundCommand
|
|
1346
|
-
.command('apply <jobId>')
|
|
1347
|
-
.description('Apply completed background job file changes to the local workspace')
|
|
1348
|
-
.option('--workspace <path>', 'Local workspace path', process.cwd())
|
|
1349
|
-
.option('--json', 'Emit JSON output', false)
|
|
1350
|
-
.action(async (jobId, options) => {
|
|
1351
|
-
const bg = new BackgroundCommand(config, logger);
|
|
1352
|
-
await bg.apply(jobId, options);
|
|
1353
|
-
});
|
|
1354
|
-
backgroundCommand
|
|
1355
|
-
.command('cancel <jobId>')
|
|
1356
|
-
.description('Cancel a running background job')
|
|
1357
|
-
.option('--json', 'Emit JSON output', false)
|
|
1358
|
-
.action(async (jobId, options) => {
|
|
1359
|
-
const bg = new BackgroundCommand(config, logger);
|
|
1360
|
-
await bg.cancel(jobId, options);
|
|
1361
|
-
});
|
|
1362
|
-
backgroundCommand
|
|
1363
|
-
.command('approve <jobId> <approvalId>')
|
|
1364
|
-
.description('Resolve a pending two-stage command gate for a background job')
|
|
1365
|
-
.option('--deny', 'Deny the command instead of approving it', false)
|
|
1366
|
-
.option('--json', 'Emit JSON output', false)
|
|
1367
|
-
.action(async (jobId, approvalId, options) => {
|
|
1368
|
-
const bg = new BackgroundCommand(config, logger);
|
|
1369
|
-
await bg.approve(jobId, approvalId, options);
|
|
1370
|
-
});
|
|
1371
|
-
backgroundCommand.action(() => {
|
|
1372
|
-
backgroundCommand.outputHelp();
|
|
1373
|
-
});
|
|
1374
|
-
// ==================== DEPLOY COMMANDS ====================
|
|
1375
|
-
// Deploy command - Host projects on Vigthoria
|
|
1376
|
-
const deployCommand = program
|
|
1377
|
-
.command('deploy')
|
|
1378
|
-
.alias('host')
|
|
1379
|
-
.description('Deploy and host your project on Vigthoria infrastructure');
|
|
1380
|
-
deployCommand
|
|
1381
|
-
.command('preview')
|
|
1382
|
-
.description('Deploy to free preview URL')
|
|
1383
|
-
.option('-p, --project <path>', 'Project directory path', process.cwd())
|
|
1384
|
-
.action(async (options) => {
|
|
1385
|
-
const deploy = new DeployCommand(config, logger);
|
|
1386
|
-
await deploy.deployToPreview(options.project);
|
|
1387
|
-
});
|
|
1388
|
-
deployCommand
|
|
1389
|
-
.command('subdomain <name>')
|
|
1390
|
-
.description('Deploy to yourname.vigthoria.io')
|
|
1391
|
-
.option('-p, --project <path>', 'Project directory path', process.cwd())
|
|
1392
|
-
.action(async (name, options) => {
|
|
1393
|
-
const deploy = new DeployCommand(config, logger);
|
|
1394
|
-
await deploy.deployToSubdomain(name, options.project);
|
|
1395
|
-
});
|
|
1396
|
-
deployCommand
|
|
1397
|
-
.command('custom <domain>')
|
|
1398
|
-
.description('Deploy to your custom domain')
|
|
1399
|
-
.option('-p, --project <path>', 'Project directory path', process.cwd())
|
|
1400
|
-
.action(async (domain, options) => {
|
|
1401
|
-
const deploy = new DeployCommand(config, logger);
|
|
1402
|
-
await deploy.deployToCustomDomain(domain, options.project);
|
|
1403
|
-
});
|
|
1404
|
-
deployCommand
|
|
1405
|
-
.command('list')
|
|
1406
|
-
.alias('ls')
|
|
1407
|
-
.description('List all your deployments')
|
|
1408
|
-
.action(async () => {
|
|
1409
|
-
const deploy = new DeployCommand(config, logger);
|
|
1410
|
-
await deploy.list();
|
|
1411
|
-
});
|
|
1412
|
-
deployCommand
|
|
1413
|
-
.command('plans')
|
|
1414
|
-
.description('Show hosting plans and pricing')
|
|
1415
|
-
.action(async () => {
|
|
1416
|
-
const deploy = new DeployCommand(config, logger);
|
|
1417
|
-
await deploy.showPlans();
|
|
1418
|
-
});
|
|
1419
|
-
deployCommand
|
|
1420
|
-
.command('status [domain]')
|
|
1421
|
-
.description('Check deployment status')
|
|
1422
|
-
.action(async (domain) => {
|
|
1423
|
-
const deploy = new DeployCommand(config, logger);
|
|
1424
|
-
await deploy.status(domain);
|
|
1425
|
-
});
|
|
1426
|
-
deployCommand
|
|
1427
|
-
.command('verify <domain>')
|
|
1428
|
-
.description('Verify DNS configuration for custom domain')
|
|
1429
|
-
.action(async (domain) => {
|
|
1430
|
-
const deploy = new DeployCommand(config, logger);
|
|
1431
|
-
await deploy.verify(domain);
|
|
1432
|
-
});
|
|
1433
|
-
deployCommand
|
|
1434
|
-
.command('remove <domain>')
|
|
1435
|
-
.alias('rm')
|
|
1436
|
-
.description('Remove a deployment')
|
|
1437
|
-
.action(async (domain) => {
|
|
1438
|
-
const deploy = new DeployCommand(config, logger);
|
|
1439
|
-
await deploy.remove(domain);
|
|
1440
|
-
});
|
|
1441
|
-
// Default deploy action shows interactive wizard
|
|
1442
|
-
deployCommand
|
|
1443
|
-
.option('-s, --subdomain <name>', 'Deploy to subdomain')
|
|
1444
|
-
.option('-d, --domain <domain>', 'Deploy to custom domain')
|
|
1445
|
-
.option('-p, --project <path>', 'Project directory path', process.cwd())
|
|
1446
|
-
.action(async (options) => {
|
|
1447
|
-
const deploy = new DeployCommand(config, logger);
|
|
1448
|
-
await deploy.deploy({
|
|
1449
|
-
subdomain: options.subdomain,
|
|
1450
|
-
domain: options.domain,
|
|
1451
|
-
project: options.project
|
|
1452
|
-
});
|
|
1453
|
-
});
|
|
1454
|
-
// ==================== PREVIEW COMMAND ====================
|
|
1455
|
-
program
|
|
1456
|
-
.command('preview')
|
|
1457
|
-
.description('Preview project locally with visual diffs and proof validation')
|
|
1458
|
-
.option('-p, --project <path>', 'Project directory path', process.cwd())
|
|
1459
|
-
.option('-e, --entry <file>', 'Entry HTML file (auto-detected if omitted)')
|
|
1460
|
-
.option('--port <number>', 'Local server port', parseInt)
|
|
1461
|
-
.option('--no-open', 'Do not auto-open browser')
|
|
1462
|
-
.option('--diff', 'Show consolidated diff of recent agent changes')
|
|
1463
|
-
.option('--proof', 'Run Template Service preview gate and persist proof bundle')
|
|
1464
|
-
.option('--screenshot', 'Capture screenshot via Puppeteer')
|
|
1465
|
-
.action(async (options) => {
|
|
1466
|
-
const preview = new PreviewCommand(config, logger);
|
|
1467
|
-
await preview.run({
|
|
1468
|
-
project: options.project,
|
|
1469
|
-
entry: options.entry,
|
|
1470
|
-
port: options.port,
|
|
1471
|
-
open: options.open,
|
|
1472
|
-
diff: options.diff,
|
|
1473
|
-
proof: options.proof,
|
|
1474
|
-
screenshot: options.screenshot,
|
|
1475
|
-
});
|
|
1476
|
-
});
|
|
1477
|
-
// ==================== GAME COMMAND ====================
|
|
1478
|
-
const gameCommand = program.command('game').description('Create, run, and validate Babylon/VGE game projects');
|
|
1479
|
-
gameCommand.command('init [name]').description('Create a production-ready client-rendered Babylon/VGE game').option('--engine <engine>', 'Game engine', 'babylon').option('--no-install', 'Create files without installing dependencies').option('--package-manager <manager>', 'npm, pnpm, yarn, or bun').action(async (name, options) => { await new GameCommand(logger).init(name, options); });
|
|
1480
|
-
gameCommand.command('run').description('Run the local game development server').option('-p, --project <path>', 'Game project directory', process.cwd()).option('--port <number>', 'Development server port', parseInt).option('--no-open', 'Do not open a browser').option('--package-manager <manager>', 'npm, pnpm, yarn, or bun').action(async (options) => { await new GameCommand(logger).run(options); });
|
|
1481
|
-
gameCommand.command('validate').description('Build and validate a Babylon/VGE game project').option('-p, --project <path>', 'Game project directory', process.cwd()).option('--json', 'Print machine-readable validation results').option('--package-manager <manager>', 'npm, pnpm, yarn, or bun').action(async (options) => { await new GameCommand(logger).validate(options); });
|
|
1482
|
-
gameCommand.action(() => gameCommand.outputHelp());
|
|
1483
|
-
// ==================== LEGION COMMAND ====================
|
|
1484
|
-
program
|
|
1485
|
-
.command('legion [request]')
|
|
1486
|
-
.description('Run parallel tasks via Hyper Loop Legion orchestrator')
|
|
1487
|
-
.option('--workers', 'List available Legion workers')
|
|
1488
|
-
.option('--status', 'Show Legion infrastructure status')
|
|
1489
|
-
.option('--cortex', 'Vigthoria Cortex: maximum intelligence execution')
|
|
1490
|
-
.option('--approve', 'Auto-approve Cortex execution prompt')
|
|
1491
|
-
.option('--no-approve', 'Require interactive approval before execution')
|
|
1492
|
-
.option('--auto-charge', 'Compatibility flag; low balances still require a verified User Hub top-up')
|
|
1493
|
-
.option('--plan-only', 'Run Cortex estimator only; do not execute Legion job')
|
|
1494
|
-
.option('--force-budget', 'Allow execution when estimated budget exceeds the hard safe-stop ceiling')
|
|
1495
|
-
.option('--ignore-preflight', 'Bypass mandatory local preflight checks (no warranty)')
|
|
1496
|
-
.option('--speed', 'Enable speed mode (allows optional role skips when convergence is detected)')
|
|
1497
|
-
.option('--repro-cmd <command>', 'Local reproduction/preflight command to validate before cloud spend')
|
|
1498
|
-
.option('--expect-repro-fail', 'Require repro command to fail (non-zero) before proceeding')
|
|
1499
|
-
.option('--models <csv>', 'Comma-separated model IDs to constrain Cortex selection')
|
|
1500
|
-
.option('-t, --timeout <seconds>', 'Override Legion execution timeout in seconds (defaults to server policy)')
|
|
1501
|
-
.option('-w, --worker <name>', 'Execute a specific worker')
|
|
1502
|
-
.option('-p, --project <path>', 'Project directory', process.cwd())
|
|
1503
|
-
.action(async (request, options) => {
|
|
1504
|
-
const legion = new LegionCommand(config, logger);
|
|
1505
|
-
const parsedTimeout = Number.parseInt(String(options.timeout || ''), 10);
|
|
1506
|
-
await legion.run(request, {
|
|
1507
|
-
workers: options.workers,
|
|
1508
|
-
status: options.status,
|
|
1509
|
-
cortex: Boolean(options.cortex),
|
|
1510
|
-
approve: options.approve,
|
|
1511
|
-
noApprove: options.approve === false,
|
|
1512
|
-
autoCharge: options.autoCharge,
|
|
1513
|
-
planOnly: options.planOnly,
|
|
1514
|
-
forceBudget: options.forceBudget,
|
|
1515
|
-
ignorePreflight: options.ignorePreflight,
|
|
1516
|
-
speed: options.speed,
|
|
1517
|
-
reproCmd: options.reproCmd,
|
|
1518
|
-
expectReproFail: options.expectReproFail,
|
|
1519
|
-
models: options.models,
|
|
1520
|
-
timeoutSec: Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? parsedTimeout : undefined,
|
|
1521
|
-
worker: options.worker,
|
|
1522
|
-
project: options.project,
|
|
1523
|
-
});
|
|
1524
|
-
});
|
|
1525
|
-
// ==================== REPLAY & FORK COMMANDS ====================
|
|
1526
|
-
program
|
|
1527
|
-
.command('history')
|
|
1528
|
-
.alias('runs')
|
|
1529
|
-
.description('List recent V3 agent runs')
|
|
1530
|
-
.option('-n, --limit <count>', 'Number of runs to show', '20')
|
|
1531
|
-
.option('-p, --project <path>', 'Project directory', process.cwd())
|
|
1532
|
-
.option('--json', 'Machine-readable JSON output', false)
|
|
1533
|
-
.action(async (options) => {
|
|
1534
|
-
const history = new HistoryCommand(config, logger);
|
|
1535
|
-
await history.run({ limit: parseInt(options.limit, 10) || 20, json: options.json, project: options.project });
|
|
1536
|
-
});
|
|
1537
|
-
program
|
|
1538
|
-
.command('replay <runId>')
|
|
1539
|
-
.description('Replay events from a V3 agent run step-by-step')
|
|
1540
|
-
.option('-s, --speed <ms>', 'Delay between events in ms', '200')
|
|
1541
|
-
.option('-p, --project <path>', 'Project directory', process.cwd())
|
|
1542
|
-
.option('--json', 'Machine-readable JSON output', false)
|
|
1543
|
-
.action(async (runId, options) => {
|
|
1544
|
-
const replay = new ReplayCommand(config, logger);
|
|
1545
|
-
await replay.run(runId, { speed: parseInt(options.speed, 10) || 200, json: options.json, project: options.project });
|
|
1546
|
-
});
|
|
1547
|
-
program
|
|
1548
|
-
.command('fork <runId> [message]')
|
|
1549
|
-
.description('Fork from an existing V3 agent run with new instructions')
|
|
1550
|
-
.option('-e, --event-index <index>', 'Fork from specific event index', '0')
|
|
1551
|
-
.option('-p, --project <path>', 'Project directory', process.cwd())
|
|
1552
|
-
.option('--json', 'Machine-readable JSON output', false)
|
|
1553
|
-
.action(async (runId, message, options) => {
|
|
1554
|
-
const fork = new ForkCommand(config, logger);
|
|
1555
|
-
await fork.run(runId, message || '', { eventIndex: parseInt(options.eventIndex, 10) || 0, project: options.project, json: options.json });
|
|
1556
|
-
});
|
|
1557
|
-
program
|
|
1558
|
-
.command('cancel [contextId]')
|
|
1559
|
-
.description('Cancel an in-flight V3 agent run (by context_id), or use --all to cancel every active run')
|
|
1560
|
-
.option('-a, --all', 'Cancel all currently-active runs', false)
|
|
1561
|
-
.option('-l, --list', 'List active runs without cancelling', false)
|
|
1562
|
-
.option('--json', 'Machine-readable JSON output', false)
|
|
1563
|
-
.action(async (contextId, options) => {
|
|
1564
|
-
const cancel = new CancelCommand(config, logger);
|
|
1565
|
-
await cancel.run(contextId, { all: options.all, list: options.list, json: options.json });
|
|
1566
|
-
});
|
|
414
|
+
registerConversationCommands(program, config, logger);
|
|
415
|
+
registerV4Commands(program, config, logger);
|
|
416
|
+
registerPlatformCommands(program, config, logger);
|
|
417
|
+
registerCodingCommands(program, config, logger);
|
|
418
|
+
registerWorkflowCommands(program, config, logger);
|
|
419
|
+
registerProductAndRunCommands(program, config, logger);
|
|
1567
420
|
// ==================== AUTH COMMANDS ====================
|
|
1568
421
|
// Auth commands
|
|
1569
422
|
program
|
|
@@ -1590,182 +443,7 @@ Examples:
|
|
|
1590
443
|
});
|
|
1591
444
|
registerAuthCommands(program);
|
|
1592
445
|
new WalletCommand().register(program);
|
|
1593
|
-
program
|
|
1594
|
-
.command('doctor')
|
|
1595
|
-
.description('Run local Vigthoria CLI diagnostics (no network calls unless --check-api)')
|
|
1596
|
-
.option('--check-api', 'Also probe Vigthoria API reachability', false)
|
|
1597
|
-
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1598
|
-
.action(async (options) => {
|
|
1599
|
-
const apiUrl = String(config.get('apiUrl') || 'https://coder.vigthoria.io').replace(/\/$/, '');
|
|
1600
|
-
const modelsApiUrl = String(config.get('modelsApiUrl') || 'https://api.vigthoria.io').replace(/\/$/, '');
|
|
1601
|
-
const offline = isOfflineMode();
|
|
1602
|
-
const updateSuppressed = isUpdateCheckSuppressed();
|
|
1603
|
-
const subscription = config.get('subscription') || { plan: null, status: null, expiresAt: null };
|
|
1604
|
-
const iosSandbox = isIOSEnvironment();
|
|
1605
|
-
const platformSupport = iosSandbox
|
|
1606
|
-
? 'experimental-mobile-terminal-sandbox'
|
|
1607
|
-
: process.platform === 'darwin'
|
|
1608
|
-
? 'supported-macos-desktop'
|
|
1609
|
-
: process.platform === 'win32' || process.platform === 'linux'
|
|
1610
|
-
? 'supported-desktop'
|
|
1611
|
-
: 'unsupported';
|
|
1612
|
-
const nodeMatch = process.versions.node.match(/^(\d+)\.(\d+)/);
|
|
1613
|
-
const nodeCompatible = Boolean(nodeMatch && (Number(nodeMatch[1]) > 20
|
|
1614
|
-
|| (Number(nodeMatch[1]) === 20 && Number(nodeMatch[2]) >= 19)));
|
|
1615
|
-
const runtimeRequire = createRequire(import.meta.url);
|
|
1616
|
-
const requiredRuntimePackages = ['axios', 'commander', 'inquirer', 'ws'];
|
|
1617
|
-
const missingRuntimePackages = requiredRuntimePackages.filter((name) => {
|
|
1618
|
-
try {
|
|
1619
|
-
runtimeRequire.resolve(name);
|
|
1620
|
-
return false;
|
|
1621
|
-
}
|
|
1622
|
-
catch {
|
|
1623
|
-
return true;
|
|
1624
|
-
}
|
|
1625
|
-
});
|
|
1626
|
-
const sessionsDir = path.join(os.homedir(), '.vigthoria', 'sessions');
|
|
1627
|
-
let sessionStorageWritable = false;
|
|
1628
|
-
let latestCheckpoint = null;
|
|
1629
|
-
try {
|
|
1630
|
-
fs.mkdirSync(sessionsDir, { recursive: true, mode: 0o700 });
|
|
1631
|
-
const probePath = path.join(sessionsDir, `.doctor-${process.pid}-${Date.now()}.tmp`);
|
|
1632
|
-
fs.writeFileSync(probePath, '{}', { encoding: 'utf8', mode: 0o600 });
|
|
1633
|
-
fs.unlinkSync(probePath);
|
|
1634
|
-
sessionStorageWritable = true;
|
|
1635
|
-
const latestSessionFile = fs.readdirSync(sessionsDir)
|
|
1636
|
-
.filter((name) => name.endsWith('.json'))
|
|
1637
|
-
.map((name) => ({ name, mtime: fs.statSync(path.join(sessionsDir, name)).mtimeMs }))
|
|
1638
|
-
.sort((left, right) => right.mtime - left.mtime)[0]?.name;
|
|
1639
|
-
if (latestSessionFile) {
|
|
1640
|
-
const persisted = JSON.parse(fs.readFileSync(path.join(sessionsDir, latestSessionFile), 'utf8'));
|
|
1641
|
-
const checkpoint = persisted?.activeAgentRun;
|
|
1642
|
-
if (checkpoint && typeof checkpoint === 'object') {
|
|
1643
|
-
latestCheckpoint = {
|
|
1644
|
-
executionId: checkpoint.executionId || null,
|
|
1645
|
-
status: checkpoint.status || null,
|
|
1646
|
-
updatedAt: checkpoint.updatedAt || null,
|
|
1647
|
-
resumable: ['submitted', 'running', 'failed'].includes(String(checkpoint.status || '')),
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
catch {
|
|
1653
|
-
sessionStorageWritable = false;
|
|
1654
|
-
}
|
|
1655
|
-
const envToken = String(process.env.VIGTHORIA_AUTH_TOKEN || process.env.VIGTHORIA_TOKEN || '').trim();
|
|
1656
|
-
const configuredToken = envToken || String(config.get('authToken') || '').trim();
|
|
1657
|
-
const tokenSegments = configuredToken ? configuredToken.split('.').length : 0;
|
|
1658
|
-
const report = {
|
|
1659
|
-
cliVersion: VERSION,
|
|
1660
|
-
nodeVersion: process.version,
|
|
1661
|
-
nodeCompatible,
|
|
1662
|
-
runtimeDependencies: missingRuntimePackages.length === 0 ? 'complete' : `missing: ${missingRuntimePackages.join(', ')}`,
|
|
1663
|
-
platform: `${process.platform} ${process.arch}`,
|
|
1664
|
-
clientOs: resolveClientOsSlug(),
|
|
1665
|
-
platformSupport,
|
|
1666
|
-
platformLimitations: iosSandbox
|
|
1667
|
-
? 'Not a native iOS app. Requires an iSH-like Linux terminal with Node.js >=20.19, npm and filesystem access; Workbench and device tooling are unavailable.'
|
|
1668
|
-
: null,
|
|
1669
|
-
cwd: process.cwd(),
|
|
1670
|
-
homeDir: os.homedir(),
|
|
1671
|
-
configPath: config.getConfigPath(),
|
|
1672
|
-
stateFile: getCliStateFile(),
|
|
1673
|
-
apiUrl,
|
|
1674
|
-
modelsApiUrl,
|
|
1675
|
-
loggedIn: config.isAuthenticated(),
|
|
1676
|
-
authToken: configuredToken
|
|
1677
|
-
? { present: true, source: envToken ? 'environment' : 'secure-config', structure: tokenSegments === 3 ? 'jwt' : 'opaque' }
|
|
1678
|
-
: { present: false, source: null, structure: null },
|
|
1679
|
-
sessionPersistence: {
|
|
1680
|
-
directory: sessionsDir,
|
|
1681
|
-
writable: sessionStorageWritable,
|
|
1682
|
-
latestCheckpoint,
|
|
1683
|
-
},
|
|
1684
|
-
subscriptionPlan: subscription.plan || null,
|
|
1685
|
-
subscriptionStatus: subscription.status || null,
|
|
1686
|
-
offlineMode: offline,
|
|
1687
|
-
updateCheckSuppressed: updateSuppressed,
|
|
1688
|
-
envOverrides: {
|
|
1689
|
-
VIGTHORIA_API_URL: process.env.VIGTHORIA_API_URL || null,
|
|
1690
|
-
VIGTHORIA_V3_AGENT_URL: process.env.VIGTHORIA_V3_AGENT_URL || null,
|
|
1691
|
-
VIGTHORIA_OFFLINE: process.env.VIGTHORIA_OFFLINE || null,
|
|
1692
|
-
VIGTHORIA_TOKEN: process.env.VIGTHORIA_TOKEN ? '<set>' : null,
|
|
1693
|
-
HYPERLOOP_SERVICE_KEY: process.env.HYPERLOOP_SERVICE_KEY ? '<set>' : null,
|
|
1694
|
-
V3_SERVICE_KEY: process.env.V3_SERVICE_KEY ? '<set>' : null,
|
|
1695
|
-
},
|
|
1696
|
-
};
|
|
1697
|
-
if (options.checkApi && !offline) {
|
|
1698
|
-
const api = new APIClient(config, logger);
|
|
1699
|
-
if (configuredToken) {
|
|
1700
|
-
try {
|
|
1701
|
-
const tokenCheck = await api.validateToken({ allowNetworkFailOpen: false, enforceTokenShape: true });
|
|
1702
|
-
report.authValidation = tokenCheck.valid ? 'authenticated' : `failed (${tokenCheck.error || 'invalid token'})`;
|
|
1703
|
-
}
|
|
1704
|
-
catch (error) {
|
|
1705
|
-
report.authValidation = `unreachable (${sanitizeUserFacingErrorText(error.message)})`;
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
else {
|
|
1709
|
-
report.authValidation = 'not checked (login required)';
|
|
1710
|
-
}
|
|
1711
|
-
try {
|
|
1712
|
-
const probe = await axios.get(`${apiUrl}/api/health`, { timeout: 12000, validateStatus: () => true });
|
|
1713
|
-
report.apiHealth = probe.status >= 200 && probe.status < 400 ? 'online' : `status ${probe.status}`;
|
|
1714
|
-
}
|
|
1715
|
-
catch (error) {
|
|
1716
|
-
report.apiHealth = `unreachable (${error.message})`;
|
|
1717
|
-
}
|
|
1718
|
-
try {
|
|
1719
|
-
const probe = await axios.get(`${modelsApiUrl}/health`, { timeout: 12000, validateStatus: () => true });
|
|
1720
|
-
const body = typeof probe.data === 'string' ? probe.data : JSON.stringify(probe.data || {});
|
|
1721
|
-
report.modelsApiHealth = probe.status >= 200 && probe.status < 400
|
|
1722
|
-
? 'online'
|
|
1723
|
-
: probe.status === 503 && /pre-release validation|closed/i.test(body)
|
|
1724
|
-
? 'pre-release-closed (Agent mode uses the Coder gateway)'
|
|
1725
|
-
: `status ${probe.status}`;
|
|
1726
|
-
}
|
|
1727
|
-
catch (error) {
|
|
1728
|
-
report.modelsApiHealth = `unreachable (${error.message})`;
|
|
1729
|
-
}
|
|
1730
|
-
if (configuredToken) {
|
|
1731
|
-
try {
|
|
1732
|
-
const capabilities = await api.getCapabilityTruthStatus({ workspacePath: process.cwd() });
|
|
1733
|
-
report.agentInfrastructure = {
|
|
1734
|
-
overall: capabilities.overallOk ? 'online' : 'degraded',
|
|
1735
|
-
v3Agent: capabilities.v3Agent.ok ? 'online' : capabilities.v3Agent.error || 'offline',
|
|
1736
|
-
hyperLoop: capabilities.hyperLoop.ok ? 'online' : capabilities.hyperLoop.error || 'offline',
|
|
1737
|
-
repoMemory: capabilities.repoMemory.ok ? 'online' : capabilities.repoMemory.error || 'offline',
|
|
1738
|
-
};
|
|
1739
|
-
}
|
|
1740
|
-
catch (error) {
|
|
1741
|
-
report.agentInfrastructure = `unreachable (${sanitizeUserFacingErrorText(error.message)})`;
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
if (options.json) {
|
|
1746
|
-
console.log(JSON.stringify(report, null, 2));
|
|
1747
|
-
}
|
|
1748
|
-
else {
|
|
1749
|
-
console.log(chalk.bold('Vigthoria CLI diagnostics'));
|
|
1750
|
-
for (const [key, value] of Object.entries(report)) {
|
|
1751
|
-
if (value && typeof value === 'object') {
|
|
1752
|
-
console.log(chalk.gray(`- ${key}:`));
|
|
1753
|
-
for (const [subKey, subVal] of Object.entries(value)) {
|
|
1754
|
-
console.log(chalk.gray(` - ${subKey}: ${subVal ?? '(none)'}`));
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
else {
|
|
1758
|
-
console.log(chalk.gray(`- ${key}: ${value ?? '(none)'}`));
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
if (!options.checkApi) {
|
|
1762
|
-
console.log(chalk.gray('\nTip: pass --check-api to verify API reachability.'));
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
const requiredLocalChecksPassed = nodeCompatible && missingRuntimePackages.length === 0 && sessionStorageWritable;
|
|
1766
|
-
const coderReachable = !options.checkApi || offline || report.apiHealth === 'online';
|
|
1767
|
-
process.exitCode = requiredLocalChecksPassed && coderReachable ? 0 : 1;
|
|
1768
|
-
});
|
|
446
|
+
registerDoctorCommand(program, config, logger, VERSION);
|
|
1769
447
|
// Config command
|
|
1770
448
|
program
|
|
1771
449
|
.command('config')
|
|
@@ -1778,178 +456,7 @@ Examples:
|
|
|
1778
456
|
const configCmd = new ConfigCommand(config, logger);
|
|
1779
457
|
await configCmd.run(options);
|
|
1780
458
|
});
|
|
1781
|
-
|
|
1782
|
-
program
|
|
1783
|
-
.command('update')
|
|
1784
|
-
.alias('upgrade')
|
|
1785
|
-
.description('Check for updates and upgrade Vigthoria CLI. Default manifest: https://extension.vigthoria.io/downloads/manifest.json')
|
|
1786
|
-
.option('-c, --check', 'Only check for updates, don\'t install')
|
|
1787
|
-
.option('-f, --from <target>', 'Install update from npm spec, local .tgz path, or URL')
|
|
1788
|
-
.option('-m, --manifest <url>', 'Update manifest URL for server-driven releases')
|
|
1789
|
-
.option('--channel <name>', 'Release channel to use from manifest (default: stable)', 'stable')
|
|
1790
|
-
.option('--allow-downgrade', 'Allow installing an older version from custom update source')
|
|
1791
|
-
.action(async (options) => {
|
|
1792
|
-
const updateTarget = typeof options.from === 'string' ? options.from.trim() : '';
|
|
1793
|
-
const manifestUrl = typeof options.manifest === 'string' ? options.manifest.trim() : VIGTHORIA_DEFAULT_MANIFEST_URL.trim();
|
|
1794
|
-
const channel = typeof options.channel === 'string' ? options.channel.trim() : 'stable';
|
|
1795
|
-
const allowDowngrade = !!options.allowDowngrade;
|
|
1796
|
-
const gitPackageSpec = 'git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git';
|
|
1797
|
-
if (updateTarget) {
|
|
1798
|
-
if (!isSafeNpmPackageSpec(updateTarget)) {
|
|
1799
|
-
console.error(chalk.red('Refusing to run installer with unsafe package spec.'));
|
|
1800
|
-
console.error(chalk.gray('Allowed characters: A-Z a-z 0-9 . _ - / @ : + \\ ='));
|
|
1801
|
-
process.exitCode = 1;
|
|
1802
|
-
return;
|
|
1803
|
-
}
|
|
1804
|
-
try {
|
|
1805
|
-
if (options.check) {
|
|
1806
|
-
console.log(chalk.cyan(`Update source configured: ${updateTarget}`));
|
|
1807
|
-
console.log(chalk.gray('Run `vigthoria update --from <target>` to install from this source'));
|
|
1808
|
-
return;
|
|
1809
|
-
}
|
|
1810
|
-
console.log(chalk.cyan(`Installing update from ${updateTarget}...`));
|
|
1811
|
-
await installGlobalPackageWithNpm(updateTarget);
|
|
1812
|
-
console.log(chalk.green('Update installed successfully'));
|
|
1813
|
-
console.log(chalk.gray('Please restart the CLI to use the new version'));
|
|
1814
|
-
return;
|
|
1815
|
-
}
|
|
1816
|
-
catch (error) {
|
|
1817
|
-
console.error(chalk.red('Failed to install update from target:'), error.message);
|
|
1818
|
-
console.log(chalk.gray(`Try manually: npm install -g ${updateTarget}`));
|
|
1819
|
-
process.exitCode = 1;
|
|
1820
|
-
return;
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
if (isOfflineMode()) {
|
|
1824
|
-
console.log(chalk.yellow('Offline mode (VIGTHORIA_OFFLINE=1): skipping update check.'));
|
|
1825
|
-
return;
|
|
1826
|
-
}
|
|
1827
|
-
const currentVersion = VERSION;
|
|
1828
|
-
let manifestEntry = null;
|
|
1829
|
-
if (manifestUrl) {
|
|
1830
|
-
try {
|
|
1831
|
-
const explicitLocalManifest = Boolean(options.manifest)
|
|
1832
|
-
&& ['localhost', '127.0.0.1', '::1'].includes(new URL(manifestUrl).hostname);
|
|
1833
|
-
validateReleaseUrl(manifestUrl, explicitLocalManifest);
|
|
1834
|
-
console.log(chalk.cyan(`Checking manifest channel ${channel}...`));
|
|
1835
|
-
const response = await axios.get(manifestUrl, {
|
|
1836
|
-
timeout: 10000,
|
|
1837
|
-
maxRedirects: 5,
|
|
1838
|
-
validateStatus: (status) => status >= 200 && status < 300,
|
|
1839
|
-
});
|
|
1840
|
-
manifestEntry = resolveManifestEntry(response.data || {}, channel);
|
|
1841
|
-
if (!manifestEntry?.version || !manifestEntry?.url) {
|
|
1842
|
-
console.log(chalk.yellow(`Manifest has no installable entry for channel ${channel}; will check npm.`));
|
|
1843
|
-
manifestEntry = null;
|
|
1844
|
-
}
|
|
1845
|
-
else {
|
|
1846
|
-
validateReleaseUrl(manifestEntry.url, explicitLocalManifest);
|
|
1847
|
-
if (!/^[a-f0-9]{64}$/i.test(String(manifestEntry.sha256 || ''))) {
|
|
1848
|
-
console.log(chalk.yellow('Manifest entry has no valid SHA-256 checksum; refusing it and checking npm.'));
|
|
1849
|
-
manifestEntry = null;
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
catch (error) {
|
|
1854
|
-
console.log(chalk.yellow(`Manifest check failed: ${error.message}`));
|
|
1855
|
-
console.log(chalk.gray('Continuing with npm registry check...'));
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
// The release manifest (server-curated, sha256-verified) is the
|
|
1859
|
-
// authoritative source of truth for this private CLI. Only fall back
|
|
1860
|
-
// to probing the public npm registry when the manifest itself has no
|
|
1861
|
-
// usable entry - never let an npm version number override a valid,
|
|
1862
|
-
// verified manifest entry (npm install -g against the public registry
|
|
1863
|
-
// is slower, unverified, and has previously caused the Workbench's
|
|
1864
|
-
// update button to hang for minutes).
|
|
1865
|
-
let npmVersion = null;
|
|
1866
|
-
if (manifestEntry?.version && manifestEntry?.url) {
|
|
1867
|
-
console.log(chalk.gray('Release manifest has a valid entry; skipping public npm registry lookup.'));
|
|
1868
|
-
}
|
|
1869
|
-
else {
|
|
1870
|
-
console.log(chalk.cyan('Checking npm registry...'));
|
|
1871
|
-
npmVersion = await fetchNpmLatestVersion();
|
|
1872
|
-
if (!npmVersion) {
|
|
1873
|
-
console.log(chalk.yellow('Could not read latest version from npm registry.'));
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
|
-
const effectiveLatest = maxVersion(manifestEntry?.version, npmVersion);
|
|
1877
|
-
if (!effectiveLatest) {
|
|
1878
|
-
console.error(chalk.red('Unable to determine the latest CLI version from manifest or npm.'));
|
|
1879
|
-
process.exitCode = 1;
|
|
1880
|
-
return;
|
|
1881
|
-
}
|
|
1882
|
-
if (!allowDowngrade && compareVersions(effectiveLatest, currentVersion) <= 0) {
|
|
1883
|
-
console.log(chalk.green(`You are running the latest version (${currentVersion})`));
|
|
1884
|
-
return;
|
|
1885
|
-
}
|
|
1886
|
-
console.log(chalk.yellow(`Update available: ${currentVersion} -> ${effectiveLatest}`));
|
|
1887
|
-
if (manifestEntry?.notes && compareVersions(manifestEntry.version, effectiveLatest) >= 0) {
|
|
1888
|
-
console.log(chalk.gray(` ${manifestEntry.notes}`));
|
|
1889
|
-
}
|
|
1890
|
-
if (options.check) {
|
|
1891
|
-
console.log(chalk.gray('Run `vigthoria update` to install the update'));
|
|
1892
|
-
return;
|
|
1893
|
-
}
|
|
1894
|
-
const manifestIsAuthoritative = Boolean(manifestEntry
|
|
1895
|
-
&& manifestEntry.url
|
|
1896
|
-
&& compareVersions(manifestEntry.version, effectiveLatest) >= 0
|
|
1897
|
-
&& compareVersions(manifestEntry.version, currentVersion) > 0);
|
|
1898
|
-
if (manifestIsAuthoritative && manifestEntry) {
|
|
1899
|
-
const tmpFile = path.join(os.tmpdir(), `vigthoria-update-${Date.now()}.tgz`);
|
|
1900
|
-
try {
|
|
1901
|
-
console.log(chalk.cyan(`Downloading release package (${manifestEntry.version})...`));
|
|
1902
|
-
await downloadFile(manifestEntry.url, tmpFile);
|
|
1903
|
-
const expected = String(manifestEntry.sha256).toLowerCase();
|
|
1904
|
-
const actual = sha256File(tmpFile).toLowerCase();
|
|
1905
|
-
if (actual !== expected) {
|
|
1906
|
-
console.error(chalk.red('Release checksum verification failed'));
|
|
1907
|
-
console.error(chalk.red(`Expected: ${expected}`));
|
|
1908
|
-
console.error(chalk.red(`Actual: ${actual}`));
|
|
1909
|
-
process.exitCode = 1;
|
|
1910
|
-
return;
|
|
1911
|
-
}
|
|
1912
|
-
console.log(chalk.green('Checksum verification passed'));
|
|
1913
|
-
console.log(chalk.cyan('Installing update...'));
|
|
1914
|
-
await installGlobalPackageWithNpm(tmpFile);
|
|
1915
|
-
console.log(chalk.green(`Updated to version ${manifestEntry.version}`));
|
|
1916
|
-
console.log(chalk.gray('Please restart the CLI to use the new version'));
|
|
1917
|
-
return;
|
|
1918
|
-
}
|
|
1919
|
-
finally {
|
|
1920
|
-
if (fs.existsSync(tmpFile)) {
|
|
1921
|
-
try {
|
|
1922
|
-
fs.unlinkSync(tmpFile);
|
|
1923
|
-
}
|
|
1924
|
-
catch { }
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
const npmSpec = npmVersion && compareVersions(npmVersion, currentVersion) > 0
|
|
1929
|
-
? `vigthoria-cli@${npmVersion}`
|
|
1930
|
-
: 'vigthoria-cli@latest';
|
|
1931
|
-
try {
|
|
1932
|
-
console.log(chalk.cyan(`Installing update from npm registry (${npmSpec})...`));
|
|
1933
|
-
await installGlobalPackageWithNpm(npmSpec);
|
|
1934
|
-
console.log(chalk.green(`Updated to version ${effectiveLatest}`));
|
|
1935
|
-
console.log(chalk.gray('Please restart the CLI to use the new version'));
|
|
1936
|
-
}
|
|
1937
|
-
catch (error) {
|
|
1938
|
-
console.error(chalk.red('Failed to install via npm registry:'), error.message);
|
|
1939
|
-
try {
|
|
1940
|
-
console.log(chalk.cyan('Attempting git package fallback...'));
|
|
1941
|
-
await installGlobalPackageWithNpm(gitPackageSpec);
|
|
1942
|
-
console.log(chalk.green('Updated via git package fallback'));
|
|
1943
|
-
console.log(chalk.gray('Please restart the CLI to use the new version'));
|
|
1944
|
-
}
|
|
1945
|
-
catch (fallbackError) {
|
|
1946
|
-
console.error(chalk.red('Fallback update also failed:'), fallbackError.message);
|
|
1947
|
-
console.log(chalk.gray(`Try manually: npm install -g ${npmSpec}`));
|
|
1948
|
-
console.log(chalk.gray(`Or: npm install -g ${gitPackageSpec}`));
|
|
1949
|
-
process.exitCode = 1;
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
});
|
|
459
|
+
registerUpdateCommand(program, VERSION);
|
|
1953
460
|
// Hyper Loop command alias (maps to legion --status for checklist gate 6.9)
|
|
1954
461
|
program
|
|
1955
462
|
.command('hyper-loop')
|
|
@@ -1970,12 +477,14 @@ Examples:
|
|
|
1970
477
|
project: process.cwd(),
|
|
1971
478
|
});
|
|
1972
479
|
});
|
|
1973
|
-
// DevTools
|
|
480
|
+
// DevTools status surface. Keep `connect` as a compatibility alias while
|
|
481
|
+
// making the canonical command accurately describe its read-only behavior.
|
|
1974
482
|
program
|
|
1975
483
|
.command('devtools')
|
|
1976
484
|
.description('DevTools Bridge commands for browser debugging')
|
|
1977
|
-
.command('
|
|
1978
|
-
.
|
|
485
|
+
.command('status')
|
|
486
|
+
.alias('connect')
|
|
487
|
+
.description('Show live DevTools Bridge status')
|
|
1979
488
|
.action(async () => {
|
|
1980
489
|
const bridge = new BridgeCommand(config, logger);
|
|
1981
490
|
await bridge.status();
|
|
@@ -2003,6 +512,27 @@ Examples:
|
|
|
2003
512
|
.action(() => {
|
|
2004
513
|
console.log(renderDynamicHelp(program));
|
|
2005
514
|
});
|
|
515
|
+
program.hook('preAction', async (_rootCommand, actionCommand) => {
|
|
516
|
+
const commandPath = commanderCommandPath(actionCommand);
|
|
517
|
+
activeCommandPath = commandPath;
|
|
518
|
+
const authRequirement = commandAuthRequirement(commandPath);
|
|
519
|
+
if (authRequirement === 'none')
|
|
520
|
+
return;
|
|
521
|
+
// On-box service identities are authenticated by their destination
|
|
522
|
+
// service and must not be confused with a user gateway session.
|
|
523
|
+
const hasServiceKey = Boolean(process.env.HYPERLOOP_SERVICE_KEY || process.env.V3_SERVICE_KEY);
|
|
524
|
+
const legionCortex = commandPath === 'legion' && actionCommand.opts().cortex === true;
|
|
525
|
+
if (hasServiceKey || legionCortex)
|
|
526
|
+
return;
|
|
527
|
+
const explicitEnvToken = Boolean(process.env.VIGTHORIA_TOKEN || process.env.VIGTHORIA_AUTH_TOKEN);
|
|
528
|
+
if (authRequirement === 'required-session' && !config.isAuthenticated() && !explicitEnvToken) {
|
|
529
|
+
throw new CliCommandError('Authentication required. Run `vigthoria login` first.', {
|
|
530
|
+
code: 'AUTH_REQUIRED',
|
|
531
|
+
category: 'authentication',
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
await enforceGatewayAuthSession(config, logger);
|
|
535
|
+
});
|
|
2006
536
|
try {
|
|
2007
537
|
// Default to chat if no command
|
|
2008
538
|
if (args.length === 2) {
|
|
@@ -2019,8 +549,11 @@ Examples:
|
|
|
2019
549
|
}
|
|
2020
550
|
}
|
|
2021
551
|
catch (error) {
|
|
2022
|
-
|
|
2023
|
-
|
|
552
|
+
if (error && typeof error === 'object' && error.exitCode === 0) {
|
|
553
|
+
process.exitCode = 0;
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
process.exitCode = reportCliError(error, jsonOutputRequested);
|
|
2024
557
|
}
|
|
2025
558
|
}
|
|
2026
559
|
setupErrorHandlers();
|
|
@@ -2030,7 +563,7 @@ async function bootstrapCli() {
|
|
|
2030
563
|
}
|
|
2031
564
|
catch (err) {
|
|
2032
565
|
handleFatalCliError(err, process.argv.includes('--json'));
|
|
2033
|
-
process.exit(1);
|
|
566
|
+
process.exit(process.exitCode || 1);
|
|
2034
567
|
}
|
|
2035
568
|
}
|
|
2036
569
|
void (async () => {
|
|
@@ -2039,7 +572,7 @@ void (async () => {
|
|
|
2039
572
|
}
|
|
2040
573
|
catch (err) {
|
|
2041
574
|
handleFatalCliError(err, process.argv.includes('--json'));
|
|
2042
|
-
process.exit(1);
|
|
575
|
+
process.exit(process.exitCode || 1);
|
|
2043
576
|
}
|
|
2044
577
|
})();
|
|
2045
578
|
export const __cliErrorHandlingReady = true;
|