vigthoria-cli 1.13.13 → 1.13.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ import * as fs from 'fs';
49
49
  import * as path from 'path';
50
50
  import * as os from 'os';
51
51
  import { fileURLToPath } from 'url';
52
+ import { createRequire } from 'module';
52
53
  import { createHash } from 'crypto';
53
54
  import axios from 'axios';
54
55
  import { renderDynamicHelp, selectInteractiveCommand } from './utils/command-menu.js';
@@ -472,10 +473,10 @@ async function enforceGatewayAuthSession(config, logger, jsonOutputRequested) {
472
473
  if (isLocalTestfarmMode()) {
473
474
  return true;
474
475
  }
475
- if (!config.isAuthenticated()) {
476
+ const explicitEnvToken = Boolean(process.env.VIGTHORIA_TOKEN || process.env.VIGTHORIA_AUTH_TOKEN);
477
+ if (!config.isAuthenticated() && !explicitEnvToken) {
476
478
  return true;
477
479
  }
478
- const explicitEnvToken = Boolean(process.env.VIGTHORIA_TOKEN || process.env.VIGTHORIA_AUTH_TOKEN);
479
480
  // Offline mode disables every outbound preflight call.
480
481
  if (isOfflineMode()) {
481
482
  return true;
@@ -1317,6 +1318,7 @@ Examples:
1317
1318
  .command('start <prompt...>')
1318
1319
  .description('Start an Agent job in the background and return immediately')
1319
1320
  .option('--workspace <path>', 'Local workspace path', process.cwd())
1321
+ .option('--model <model>', 'Execution lane: agent, cloud, cloud-reason, or ultra', 'agent')
1320
1322
  .option('--json', 'Emit JSON output', false)
1321
1323
  .action(async (prompt, options) => {
1322
1324
  const bg = new BackgroundCommand(config, logger);
@@ -1357,6 +1359,15 @@ Examples:
1357
1359
  const bg = new BackgroundCommand(config, logger);
1358
1360
  await bg.cancel(jobId, options);
1359
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
+ });
1360
1371
  backgroundCommand.action(() => {
1361
1372
  backgroundCommand.outputHelp();
1362
1373
  });
@@ -1598,9 +1609,57 @@ Examples:
1598
1609
  : process.platform === 'win32' || process.platform === 'linux'
1599
1610
  ? 'supported-desktop'
1600
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;
1601
1658
  const report = {
1602
1659
  cliVersion: VERSION,
1603
1660
  nodeVersion: process.version,
1661
+ nodeCompatible,
1662
+ runtimeDependencies: missingRuntimePackages.length === 0 ? 'complete' : `missing: ${missingRuntimePackages.join(', ')}`,
1604
1663
  platform: `${process.platform} ${process.arch}`,
1605
1664
  clientOs: resolveClientOsSlug(),
1606
1665
  platformSupport,
@@ -1614,6 +1673,14 @@ Examples:
1614
1673
  apiUrl,
1615
1674
  modelsApiUrl,
1616
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
+ },
1617
1684
  subscriptionPlan: subscription.plan || null,
1618
1685
  subscriptionStatus: subscription.status || null,
1619
1686
  offlineMode: offline,
@@ -1628,20 +1695,52 @@ Examples:
1628
1695
  },
1629
1696
  };
1630
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
+ }
1631
1711
  try {
1632
- const probe = await axios.get(`${apiUrl}/api/health`, { timeout: 4000, validateStatus: () => true });
1712
+ const probe = await axios.get(`${apiUrl}/api/health`, { timeout: 12000, validateStatus: () => true });
1633
1713
  report.apiHealth = probe.status >= 200 && probe.status < 400 ? 'online' : `status ${probe.status}`;
1634
1714
  }
1635
1715
  catch (error) {
1636
1716
  report.apiHealth = `unreachable (${error.message})`;
1637
1717
  }
1638
1718
  try {
1639
- const probe = await axios.get(`${modelsApiUrl}/health`, { timeout: 4000, validateStatus: () => true });
1640
- report.modelsApiHealth = probe.status >= 200 && probe.status < 400 ? 'online' : `status ${probe.status}`;
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}`;
1641
1726
  }
1642
1727
  catch (error) {
1643
1728
  report.modelsApiHealth = `unreachable (${error.message})`;
1644
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
+ }
1645
1744
  }
1646
1745
  if (options.json) {
1647
1746
  console.log(JSON.stringify(report, null, 2));
@@ -1663,7 +1762,9 @@ Examples:
1663
1762
  console.log(chalk.gray('\nTip: pass --check-api to verify API reachability.'));
1664
1763
  }
1665
1764
  }
1666
- process.exitCode = 0;
1765
+ const requiredLocalChecksPassed = nodeCompatible && missingRuntimePackages.length === 0 && sessionStorageWritable;
1766
+ const coderReachable = !options.checkApi || offline || report.apiHealth === 'online';
1767
+ process.exitCode = requiredLocalChecksPassed && coderReachable ? 0 : 1;
1667
1768
  });
1668
1769
  // Config command
1669
1770
  program
@@ -261,6 +261,7 @@ export declare class APIClient {
261
261
  getV3AgentRunUrl(baseUrl: string): string;
262
262
  getV3AgentContinueUrl(baseUrl: string): string;
263
263
  getV3AgentBackgroundUrl(baseUrl: string, suffix?: string): string;
264
+ getV3AgentApprovalUrl(baseUrl: string): string;
264
265
  getOperatorBaseUrls(): string[];
265
266
  getOperatorStreamUrl(baseUrl: string): string;
266
267
  getMcpBaseUrls(): string[];
@@ -359,6 +360,11 @@ export declare class APIClient {
359
360
  private buildPublicWorkspaceDescriptor;
360
361
  private buildPublicRuntimeEnvironment;
361
362
  private buildLocalWorkspaceSummary;
363
+ /**
364
+ * Build complete workspace hydration separately from model context.
365
+ * Context compaction must not become filesystem compaction.
366
+ */
367
+ private buildOutOfBandWorkspaceFiles;
362
368
  /**
363
369
  * Collect text file contents from the workspace for V3 agent hydration.
364
370
  * Budget: up to ~2 MB total, per-file cap 200 KB, skip binary extensions.
@@ -405,6 +411,7 @@ export declare class APIClient {
405
411
  listV3BackgroundJobs(limit?: number): Promise<any[]>;
406
412
  getV3BackgroundJob(jobId: string): Promise<any>;
407
413
  cancelV3BackgroundJob(jobId: string): Promise<any>;
414
+ resolveV3BackgroundApproval(jobId: string, approvalId: string, approved: boolean, allowMode?: string): Promise<any>;
408
415
  getV3BackgroundJobFiles(jobId: string): Promise<{
409
416
  status: string;
410
417
  files: Record<string, string>;