vigthoria-cli 1.9.8 → 1.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -99,7 +99,7 @@ If you see `ENOTFOUND registry.npmjs.org`, try these solutions:
99
99
  4. **Direct tarball download:**
100
100
  ```bash
101
101
  # Download directly
102
- npm install -g https://coder.vigthoria.io/releases/vigthoria-cli-1.9.8.tgz
102
+ npm install -g https://coder.vigthoria.io/releases/vigthoria-cli-1.9.9.tgz
103
103
  ```
104
104
 
105
105
  5. **Use Git clone method (no npm registry needed):**
@@ -200,6 +200,7 @@ export declare class APIClient {
200
200
  private ws;
201
201
  private vigFlowTokens;
202
202
  private _httpsAgent;
203
+ private lastChatTransportErrors;
203
204
  constructor(config: Config, logger: Logger);
204
205
  /**
205
206
  * Destroy keep-alive sockets so the Node.js event loop can drain
@@ -217,12 +218,12 @@ export declare class APIClient {
217
218
  private getAccessToken;
218
219
  /**
219
220
  * Validate the current auth token against the Coder API.
220
- * Returns { valid: true } when the server accepts the token,
221
- * { valid: false, error } when the token is rejected (401/403),
222
- * and { valid: true } when the server is unreachable (network error)
223
- * so that offline/degraded scenarios don't block the user.
221
+ * By default this fails open on network errors to keep offline commands usable.
224
222
  */
225
- validateToken(): Promise<{
223
+ validateToken(options?: {
224
+ allowNetworkFailOpen?: boolean;
225
+ enforceTokenShape?: boolean;
226
+ }): Promise<{
226
227
  valid: boolean;
227
228
  error?: string;
228
229
  }>;
package/dist/utils/api.js CHANGED
@@ -207,6 +207,7 @@ class APIClient {
207
207
  ws = null;
208
208
  vigFlowTokens = new Map();
209
209
  _httpsAgent = null;
210
+ lastChatTransportErrors = [];
210
211
  constructor(config, logger) {
211
212
  this.config = config;
212
213
  this.logger = logger;
@@ -461,16 +462,23 @@ class APIClient {
461
462
  }
462
463
  /**
463
464
  * Validate the current auth token against the Coder API.
464
- * Returns { valid: true } when the server accepts the token,
465
- * { valid: false, error } when the token is rejected (401/403),
466
- * and { valid: true } when the server is unreachable (network error)
467
- * so that offline/degraded scenarios don't block the user.
465
+ * By default this fails open on network errors to keep offline commands usable.
468
466
  */
469
- async validateToken() {
467
+ async validateToken(options = {}) {
468
+ const allowNetworkFailOpen = options.allowNetworkFailOpen !== false;
469
+ const enforceTokenShape = options.enforceTokenShape !== false;
470
470
  const token = this.getAccessToken();
471
471
  if (!token) {
472
472
  return { valid: false, error: 'No auth token configured. Run: vigthoria login' };
473
473
  }
474
+ // Fast-fail obviously malformed tokens so invalid-token checks don't get
475
+ // masked by unrelated transport outages.
476
+ if (enforceTokenShape) {
477
+ const looksLikeJwt = token.split('.').length === 3;
478
+ if (!looksLikeJwt || token.length < 40) {
479
+ return { valid: false, error: 'Auth token expired or invalid. Run: vigthoria login' };
480
+ }
481
+ }
474
482
  const explicitEnvToken = Boolean(process.env.VIGTHORIA_TOKEN || process.env.VIGTHORIA_AUTH_TOKEN);
475
483
  const headers = {
476
484
  Authorization: `Bearer ${token}`,
@@ -498,7 +506,7 @@ class APIClient {
498
506
  }
499
507
  }
500
508
  }
501
- if (explicitEnvToken) {
509
+ if (explicitEnvToken || !allowNetworkFailOpen) {
502
510
  return { valid: false, error: 'Auth token expired or invalid. Run: vigthoria login' };
503
511
  }
504
512
  // Both unreachable — don't assume the stored token is bad when running offline.
@@ -958,6 +966,8 @@ class APIClient {
958
966
  if (authToken) {
959
967
  headers.Authorization = `Bearer ${authToken}`;
960
968
  headers.Cookie = `vigthoria-auth-token=${authToken}`;
969
+ headers['X-Vigthoria-Token'] = authToken;
970
+ headers['X-Auth-Token'] = authToken;
961
971
  }
962
972
  return headers;
963
973
  }
@@ -2987,8 +2997,12 @@ document.addEventListener('DOMContentLoaded', () => {
2987
2997
  && !process.env.VIGTHORIA_AUTH_TOKEN
2988
2998
  && Boolean(this.config.get('authToken'));
2989
2999
  if (onlyUnauthorizedErrors && usingStoredConfigToken) {
2990
- this.config.clearAuth();
2991
- throw new Error('V3 agent authentication failed. The stored CLI login token is invalid or expired. Run vigthoria login again.');
3000
+ const gatewayTokenCheck = await this.validateToken({ allowNetworkFailOpen: true, enforceTokenShape: true });
3001
+ if (!gatewayTokenCheck.valid) {
3002
+ this.config.clearAuth();
3003
+ throw new Error('V3 agent authentication failed. The stored CLI login token is invalid or expired. Run vigthoria login again.');
3004
+ }
3005
+ throw new Error('V3 agent authentication failed at the V3 service layer while your gateway login token is still valid. Please retry shortly.');
2992
3006
  }
2993
3007
  if (preferLocalV3
2994
3008
  && !this.hasAgentWorkspaceOutput(executionContext)
@@ -3212,6 +3226,7 @@ document.addEventListener('DOMContentLoaded', () => {
3212
3226
  * NO localhost fallbacks - CLI is for external users, not server-side!
3213
3227
  */
3214
3228
  async chat(messages, model, useLocal = false) {
3229
+ this.lastChatTransportErrors = [];
3215
3230
  const resolvedModel = this.resolveModelId(model);
3216
3231
  const candidateModels = this.isCloudModelId(resolvedModel) && !this.canUseCloudModel()
3217
3232
  ? [this.getSelfHostedFallbackModelId(resolvedModel, model)]
@@ -3230,12 +3245,16 @@ document.addEventListener('DOMContentLoaded', () => {
3230
3245
  }
3231
3246
  }
3232
3247
  // No more localhost fallbacks - CLI is for external users!
3233
- throw new CLIError('AI service unavailable. Please check your internet connection or try again later.', 'model_backend');
3248
+ const detail = this.lastChatTransportErrors.length > 0
3249
+ ? ` Tried routes: ${this.lastChatTransportErrors.slice(0, 4).join(' | ')}`
3250
+ : '';
3251
+ throw new CLIError(`AI service unavailable. Please check your internet connection or try again later.${detail}`, 'model_backend');
3234
3252
  }
3235
3253
  shouldSkipCloudRoutes(resolvedModel) {
3236
3254
  return this.shouldSimulateCloudFailure() && this.isCloudModelId(resolvedModel);
3237
3255
  }
3238
3256
  async tryChatWithModel(messages, resolvedModel, requestedModel) {
3257
+ const routeFailures = [];
3239
3258
  const preferSelfHostedFirst = this.isSelfHostedPreferredModel(resolvedModel, requestedModel);
3240
3259
  if (preferSelfHostedFirst) {
3241
3260
  const selfHostedResponse = await this.trySelfHostedChatWithModel(messages, resolvedModel, requestedModel);
@@ -3276,6 +3295,7 @@ document.addEventListener('DOMContentLoaded', () => {
3276
3295
  catch (error) {
3277
3296
  const errMsg = error.response?.data?.error || error.message || 'Unknown error';
3278
3297
  this.logger.debug(`Direct Vigthoria Models API failed for ${resolvedModel}: ${errMsg}`);
3298
+ routeFailures.push(`models:${String(errMsg).slice(0, 120)}`);
3279
3299
  }
3280
3300
  }
3281
3301
  else {
@@ -3310,6 +3330,7 @@ document.addEventListener('DOMContentLoaded', () => {
3310
3330
  catch (error) {
3311
3331
  const errMsg = error.response?.data?.error || error.message || 'Unknown error';
3312
3332
  this.logger.debug(`Vigthoria Cloud API failed for ${resolvedModel}: ${errMsg}`);
3333
+ routeFailures.push(`coder:${String(errMsg).slice(0, 120)}`);
3313
3334
  }
3314
3335
  try {
3315
3336
  this.logger.debug(`Canonical Vigthoria Cloud fallback: ${resolvedModel}`);
@@ -3339,6 +3360,7 @@ document.addEventListener('DOMContentLoaded', () => {
3339
3360
  catch (error) {
3340
3361
  const errMsg = error.response?.data?.error || error.message || 'Unknown error';
3341
3362
  this.logger.debug(`Canonical Vigthoria Cloud fallback failed for ${resolvedModel}: ${errMsg}`);
3363
+ routeFailures.push(`coder-canonical:${String(errMsg).slice(0, 120)}`);
3342
3364
  }
3343
3365
  }
3344
3366
  if (!preferSelfHostedFirst) {
@@ -3347,6 +3369,9 @@ document.addEventListener('DOMContentLoaded', () => {
3347
3369
  return selfHostedResponse;
3348
3370
  }
3349
3371
  }
3372
+ if (routeFailures.length > 0) {
3373
+ this.lastChatTransportErrors = routeFailures;
3374
+ }
3350
3375
  return null;
3351
3376
  }
3352
3377
  async trySelfHostedChatWithModel(messages, resolvedModel, requestedModel) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.9.8",
3
+ "version": "1.9.9",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [