vigthoria-cli 1.10.1 → 1.10.36

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.
@@ -2,6 +2,7 @@ import { Command } from 'commander';
2
2
  export interface AuthConfig {
3
3
  apiUrl: string;
4
4
  token?: string;
5
+ v3ServiceKey?: string;
5
6
  user?: {
6
7
  id?: string;
7
8
  email?: string;
@@ -7,7 +7,7 @@ import { Config } from '../utils/config.js';
7
7
  const DEFAULT_API_URL = 'https://coder.vigthoria.io';
8
8
  const CONFIG_DIR = path.join(homedir(), '.vigthoria');
9
9
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
10
- const KNOWN_AUTH_BASE_URLS = ['https://coder.vigthoria.io', 'https://api.vigthoria.io'];
10
+ const KNOWN_AUTH_BASE_URLS = ['https://coder.vigthoria.io'];
11
11
  class HttpError extends Error {
12
12
  status;
13
13
  constructor(status, message) {
@@ -62,6 +62,9 @@ function syncSharedConfig(config) {
62
62
  if (config.user?.email) {
63
63
  shared.set('email', String(config.user.email));
64
64
  }
65
+ if (config.v3ServiceKey) {
66
+ shared.set('v3ServiceKey', config.v3ServiceKey);
67
+ }
65
68
  }
66
69
  catch {
67
70
  // Keep legacy auth flow working even if shared config write fails.
@@ -74,6 +77,7 @@ function clearSharedConfigAuth() {
74
77
  shared.set('refreshToken', null);
75
78
  shared.set('userId', null);
76
79
  shared.set('email', null);
80
+ shared.set('v3ServiceKey', null);
77
81
  }
78
82
  catch {
79
83
  // Ignore shared config clear failures during logout.
@@ -125,6 +129,7 @@ export function loadAuthConfig() {
125
129
  return {
126
130
  apiUrl: trimTrailingSlash(parsed.apiUrl || getApiUrl()),
127
131
  token: parsed.token || parsed.authToken,
132
+ v3ServiceKey: typeof parsed.v3ServiceKey === 'string' ? parsed.v3ServiceKey : undefined,
128
133
  user: parsed.user,
129
134
  };
130
135
  }
@@ -203,6 +208,23 @@ async function requestJson(url, init) {
203
208
  }
204
209
  return body;
205
210
  }
211
+ async function fetchV3ServiceKey(apiBase, token) {
212
+ try {
213
+ const endpoint = `${trimTrailingSlash(apiBase)}/api/cli/v3-service-key`;
214
+ const result = await requestJson(endpoint, {
215
+ method: 'GET',
216
+ headers: { authorization: `Bearer ${token}` },
217
+ });
218
+ const key = result?.v3ServiceKey;
219
+ if (typeof key === 'string' && key.trim()) {
220
+ return key.trim();
221
+ }
222
+ }
223
+ catch {
224
+ // Non-fatal during login; V3 preflight can still fetch later via API utility.
225
+ }
226
+ return undefined;
227
+ }
206
228
  async function ask(question, hidden = false) {
207
229
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
208
230
  if (!hidden) {
@@ -214,18 +236,20 @@ async function ask(question, hidden = false) {
214
236
  }
215
237
  }
216
238
  const output = process.stdout;
239
+ // Write the label directly so masking doesn't hide it
240
+ output.write(question);
217
241
  const mutableRl = rl;
218
242
  const originalWrite = mutableRl._writeToOutput;
219
243
  mutableRl._writeToOutput = function writeMasked(value) {
220
244
  if (value.includes('\n') || value.includes('\r')) {
221
245
  output.write(value);
222
246
  }
223
- else {
247
+ else if (value.length > 0) {
224
248
  output.write('*');
225
249
  }
226
250
  };
227
251
  try {
228
- return await new Promise((resolve) => rl.question(question, resolve));
252
+ return await new Promise((resolve) => rl.question('', resolve));
229
253
  }
230
254
  finally {
231
255
  if (originalWrite) {
@@ -271,10 +295,13 @@ export async function login(email, password) {
271
295
  failures.push(`${endpoint} -> success without token`);
272
296
  continue;
273
297
  }
298
+ const normalizedBase = trimTrailingSlash(base);
299
+ const v3ServiceKey = await fetchV3ServiceKey(normalizedBase, token);
274
300
  const config = {
275
- apiUrl: trimTrailingSlash(base),
301
+ apiUrl: normalizedBase,
276
302
  token,
277
303
  user: extractAuthUser(result, resolvedEmail),
304
+ ...(v3ServiceKey ? { v3ServiceKey } : {}),
278
305
  success: true,
279
306
  };
280
307
  saveAuthConfig(config);
@@ -287,11 +314,7 @@ export async function login(email, password) {
287
314
  }
288
315
  }
289
316
  }
290
- throw new Error([
291
- 'Login failed on all known auth endpoints.',
292
- `Tried: ${attempted.join(', ')}`,
293
- `Last errors: ${failures.slice(-4).join(' | ')}`,
294
- ].join(' '));
317
+ throw new Error('Invalid email or password. Please check your credentials and try again.');
295
318
  }
296
319
  catch (error) {
297
320
  const message = humanMessage(error);
@@ -399,6 +422,10 @@ export async function handleLogin(options = {}) {
399
422
  token: options.token,
400
423
  success: true,
401
424
  };
425
+ const v3ServiceKey = await fetchV3ServiceKey(config.apiUrl, options.token);
426
+ if (v3ServiceKey) {
427
+ config.v3ServiceKey = v3ServiceKey;
428
+ }
402
429
  saveAuthConfig(config);
403
430
  console.log(chalk.green('Logged in successfully with API token.'));
404
431
  process.exitCode = 0;
@@ -413,7 +440,7 @@ export async function handleLogin(options = {}) {
413
440
  email = (await ask('Email: ')).trim();
414
441
  }
415
442
  if (!password) {
416
- password = await ask('Password: ', true);
443
+ password = (await ask('Password: ', true)).trim();
417
444
  }
418
445
  if (!email || !password) {
419
446
  throw new Error('Email and password are required. Use --email and --password, or run vigthoria login interactively.');
@@ -423,6 +450,36 @@ export async function handleLogin(options = {}) {
423
450
  if (config.user?.email) {
424
451
  console.log(`Account: ${config.user.email}`);
425
452
  }
453
+ // Fetch and display subscription tier + role
454
+ try {
455
+ const subResponse = await fetch(`${config.apiUrl}/api/user/subscription`, {
456
+ headers: { Authorization: `Bearer ${config.token}`, Cookie: `vigthoria-auth-token=${config.token}` },
457
+ signal: AbortSignal.timeout(5000),
458
+ });
459
+ if (subResponse.ok) {
460
+ const sub = await subResponse.json();
461
+ const subRecord = (sub.subscription && typeof sub.subscription === 'object') ? sub.subscription : {};
462
+ const userRecord = (sub.user && typeof sub.user === 'object') ? sub.user : {};
463
+ const plan = String(sub.plan || subRecord.plan || userRecord.plan || userRecord.subscription_plan || '').trim();
464
+ const role = String(sub.role || subRecord.role || userRecord.role || userRecord.user_role || '').trim();
465
+ if (plan) {
466
+ console.log(`Plan: ${plan.toUpperCase()}`);
467
+ }
468
+ if (role) {
469
+ console.log(`Role: ${role}`);
470
+ }
471
+ }
472
+ }
473
+ catch { /* non-fatal — login already succeeded */ }
474
+ try {
475
+ const sharedConfig = new Config();
476
+ await sharedConfig.refreshHubModelPreferences();
477
+ const hubPrefs = sharedConfig.get('hubModelPrefs');
478
+ if (hubPrefs?.balance !== undefined) {
479
+ console.log(`Cloud credits: ${hubPrefs.balance}`);
480
+ }
481
+ }
482
+ catch { /* non-fatal */ }
426
483
  console.log(`API: ${config.apiUrl}`);
427
484
  process.exitCode = 0;
428
485
  return config;
@@ -446,27 +503,52 @@ export async function handleLogout(_options) {
446
503
  }
447
504
  export async function statusAction() {
448
505
  try {
449
- const config = loadAuthConfig();
450
- if (!config.token) {
506
+ const legacyConfig = loadAuthConfig();
507
+ const sharedConfig = new Config();
508
+ const sharedApiUrl = String(sharedConfig.get('apiUrl') || '').trim();
509
+ const sharedToken = String(sharedConfig.get('authToken') || '').trim();
510
+ const effectiveApiUrl = trimTrailingSlash(sharedApiUrl || legacyConfig.apiUrl || getApiUrl());
511
+ const effectiveToken = sharedToken || legacyConfig.token || '';
512
+ if (!effectiveToken) {
451
513
  console.log(chalk.yellow('Not logged in.'));
452
514
  process.exitCode = 0;
453
515
  return;
454
516
  }
455
- let user = config.user;
517
+ const authProbeHeaders = {
518
+ Authorization: `Bearer ${effectiveToken}`,
519
+ Cookie: `vigthoria-auth-token=${effectiveToken}`,
520
+ };
521
+ let authLooksValid = true;
522
+ try {
523
+ const authProbe = await fetch(`${effectiveApiUrl}/api/user/subscription`, { headers: authProbeHeaders, signal: AbortSignal.timeout(6000) });
524
+ if (authProbe.status === 401 || authProbe.status === 403) {
525
+ authLooksValid = false;
526
+ }
527
+ }
528
+ catch {
529
+ // Network failures should not force logout status.
530
+ }
531
+ if (!authLooksValid) {
532
+ console.log(chalk.yellow('Not logged in. Session expired or invalid.'));
533
+ console.log(chalk.gray('Run: vigthoria login'));
534
+ process.exitCode = 1;
535
+ return;
536
+ }
537
+ let user = legacyConfig.user;
456
538
  try {
457
539
  user = await whoami();
458
540
  }
459
541
  catch {
460
542
  // Keep local status available even if remote whoami endpoint is down.
461
543
  }
462
- const sharedConfig = new Config();
463
544
  let overallStatus = 'Unknown';
464
545
  let coderStatus = 'Unknown';
465
546
  let modelsStatus = 'Unknown';
547
+ const modelsApiBase = trimTrailingSlash(String(sharedConfig.get('modelsApiUrl') || 'https://api.vigthoria.io'));
466
548
  try {
467
549
  const [coderResponse, modelsResponse] = await Promise.all([
468
- fetch(`${trimTrailingSlash(config.apiUrl)}/api/health`),
469
- fetch('https://api.vigthoria.io/health'),
550
+ fetch(`${effectiveApiUrl}/api/health`, { signal: AbortSignal.timeout(8000) }),
551
+ fetch(`${modelsApiBase}/health`, { signal: AbortSignal.timeout(8000) }),
470
552
  ]);
471
553
  const coderJson = await coderResponse.json().catch(() => ({}));
472
554
  const modelsJson = await modelsResponse.json().catch(() => ({}));
@@ -491,7 +573,7 @@ export async function statusAction() {
491
573
  console.log(`Overall: ${overallStatus}`);
492
574
  console.log(`Coder API: ${coderStatus}`);
493
575
  console.log(`Models API: ${modelsStatus}`);
494
- console.log(`API: ${config.apiUrl}`);
576
+ console.log(`API: ${effectiveApiUrl}`);
495
577
  process.exitCode = 0;
496
578
  }
497
579
  catch (error) {
@@ -16,6 +16,7 @@ interface ChatOptions {
16
16
  prompt?: string;
17
17
  json?: boolean;
18
18
  bridge?: string;
19
+ grant?: boolean;
19
20
  }
20
21
  export declare class ChatCommand {
21
22
  private config;
@@ -34,6 +35,7 @@ export declare class ChatCommand {
34
35
  private currentModel;
35
36
  private modelExplicitlySelected;
36
37
  private autoApprove;
38
+ private personaOverride;
37
39
  private agentToolEvidence;
38
40
  private operatorMode;
39
41
  private workflowTarget;
@@ -59,12 +61,26 @@ export declare class ChatCommand {
59
61
  private isLegacyAgentFallbackAllowed;
60
62
  private resolveAgentExecutionPolicy;
61
63
  private getMessagesForModel;
64
+ private normalizeClientV3ToolPath;
65
+ private resolveClientV3ToolPath;
66
+ private normalizeClientV3ToolArgs;
67
+ private executeClientV3Tool;
68
+ private getActivePersonaMode;
69
+ private buildActivePersonaOverlay;
62
70
  private isDiagnosticPrompt;
63
71
  /**
64
72
  * Returns true when the prompt is a simple lookup / analysis / read-only
65
73
  * question — these should use analysis_only workflow, not full_autonomy.
66
74
  */
67
75
  private isAnalysisLookupPrompt;
76
+ private isImplementationPrompt;
77
+ private getWindowsPromptPathRoots;
78
+ private findPromptDirectoryByName;
79
+ private extractExplicitLocalPath;
80
+ private isUnscopedPromptPathOverrideAllowed;
81
+ private isPathWithinRoot;
82
+ private getPromptPathAllowedRoots;
83
+ private resolvePromptWorkspacePath;
68
84
  private isBrowserTaskPrompt;
69
85
  /**
70
86
  * Returns true when a prompt can be answered directly without the full
@@ -81,6 +97,7 @@ export declare class ChatCommand {
81
97
  */
82
98
  private isRepoGroundedPrompt;
83
99
  private inferAgentTaskType;
100
+ private bindPromptWorkspace;
84
101
  private buildTaskShapingInstructions;
85
102
  private buildExecutionPrompt;
86
103
  private isProjectBrainRuntimeDisabled;
@@ -91,15 +108,26 @@ export declare class ChatCommand {
91
108
  private v3ToolCallCount;
92
109
  private v3LastActivity;
93
110
  private v3StreamingStarted;
111
+ private v3StreamedTextBuffer;
112
+ private v3LiveToolEvidence;
113
+ private v3PendingToolCalls;
94
114
  /**
95
115
  * Strip server-internal path prefixes from tool output strings.
96
116
  * Prevents exposing paths like /var/www/V3-Code-Agent/... to end users.
97
117
  */
98
118
  private sanitizeServerPath;
119
+ private stripHiddenThoughtBlocks;
99
120
  private describeV3AgentTool;
100
121
  private isRawV3StreamPayload;
101
122
  private consumeV3StreamPayload;
102
123
  private writeV3StreamText;
124
+ private isGenericV3AgentContent;
125
+ private hasAlreadyStreamedV3Content;
126
+ private isThinV3Summary;
127
+ private shouldPrintV3FinalContent;
128
+ private rememberV3ToolEvidence;
129
+ private buildUserFacingV3RunReport;
130
+ private printV3UserReport;
103
131
  private updateV3AgentSpinner;
104
132
  private updateOperatorSpinner;
105
133
  constructor(config: Config, logger: Logger);
@@ -107,6 +135,7 @@ export declare class ChatCommand {
107
135
  /** Handle an inbound admin command from the Commando Bridge. */
108
136
  private handleAdminCommand;
109
137
  private ensureProjectWorkspace;
138
+ private shouldStartWorkspaceWatcher;
110
139
  private resolveProjectPath;
111
140
  private shouldUseManagedWorkspace;
112
141
  private getManagedWorkspaceRoot;
@@ -134,7 +163,11 @@ export declare class ChatCommand {
134
163
  private runOperatorDirectAnswer;
135
164
  private runSimplePrompt;
136
165
  private runAgentTurn;
166
+ private localAgentIterationCount;
167
+ private buildLocalAgentChatOptions;
168
+ private printChatModelPreflight;
137
169
  private runLocalAgentLoop;
170
+ private primeBypassedTargetFileContext;
138
171
  private tryDirectSingleFileFlow;
139
172
  private isConfirmationFollowUp;
140
173
  private getPreviousActionablePrompt;
@@ -210,6 +243,7 @@ export declare class ChatCommand {
210
243
  private buildLocalAnalysisFallback;
211
244
  private tryDeterministicSingleFileRewrite;
212
245
  private extractExactTextRequirement;
246
+ private tryLocalToolFallback;
213
247
  private executeToolCalls;
214
248
  private formatToolResult;
215
249
  private truncateText;