vigthoria-cli 1.9.8 → 1.9.10
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 +1 -1
- package/dist/utils/api.d.ts +6 -5
- package/dist/utils/api.js +58 -18
- package/package.json +1 -1
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.
|
|
102
|
+
npm install -g https://coder.vigthoria.io/releases/vigthoria-cli-1.9.10.tgz
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
5. **Use Git clone method (no npm registry needed):**
|
package/dist/utils/api.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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(
|
|
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,17 +462,25 @@ class APIClient {
|
|
|
461
462
|
}
|
|
462
463
|
/**
|
|
463
464
|
* Validate the current auth token against the Coder API.
|
|
464
|
-
*
|
|
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
|
+
const explicitEnvToken = Boolean(process.env.VIGTHORIA_TOKEN || process.env.VIGTHORIA_AUTH_TOKEN);
|
|
470
471
|
const token = this.getAccessToken();
|
|
471
472
|
if (!token) {
|
|
472
473
|
return { valid: false, error: 'No auth token configured. Run: vigthoria login' };
|
|
473
474
|
}
|
|
474
|
-
|
|
475
|
+
// Fast-fail obviously malformed ENV override tokens so invalid-token checks
|
|
476
|
+
// don't get masked by unrelated transport outages. Persisted login tokens may
|
|
477
|
+
// be non-JWT in some deployments and must still be gateway-validated server-side.
|
|
478
|
+
if (enforceTokenShape && explicitEnvToken) {
|
|
479
|
+
const looksLikeJwt = token.split('.').length === 3;
|
|
480
|
+
if (!looksLikeJwt || token.length < 40) {
|
|
481
|
+
return { valid: false, error: 'Auth token expired or invalid. Run: vigthoria login' };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
475
484
|
const headers = {
|
|
476
485
|
Authorization: `Bearer ${token}`,
|
|
477
486
|
Cookie: `vigthoria-auth-token=${token}`,
|
|
@@ -487,18 +496,32 @@ class APIClient {
|
|
|
487
496
|
if (r.status === 'fulfilled')
|
|
488
497
|
return { valid: true };
|
|
489
498
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
499
|
+
const sawUnauthorized = results.some((r) => r.status === 'rejected' && ((r.reason?.response?.status === 401) || (r.reason?.response?.status === 403) || (r.reason instanceof CLIError && r.reason.category === 'auth')));
|
|
500
|
+
if (sawUnauthorized) {
|
|
501
|
+
// For persisted CLI sessions, attempt one refresh before failing auth.
|
|
502
|
+
if (!explicitEnvToken) {
|
|
503
|
+
const refreshed = await this.refreshToken();
|
|
504
|
+
if (refreshed) {
|
|
505
|
+
const retryToken = this.getAccessToken();
|
|
506
|
+
if (retryToken) {
|
|
507
|
+
const retryHeaders = {
|
|
508
|
+
Authorization: `Bearer ${retryToken}`,
|
|
509
|
+
Cookie: `vigthoria-auth-token=${retryToken}`,
|
|
510
|
+
};
|
|
511
|
+
const retryResults = await Promise.allSettled([
|
|
512
|
+
axios_1.default.get(`${canonicalBaseUrl}/api/user/profile`, { timeout: 5000, headers: retryHeaders, httpsAgent: this._httpsAgent ?? undefined }),
|
|
513
|
+
axios_1.default.get(`${canonicalBaseUrl}/api/user/subscription`, { timeout: 5000, headers: retryHeaders, httpsAgent: this._httpsAgent ?? undefined }),
|
|
514
|
+
]);
|
|
515
|
+
for (const rr of retryResults) {
|
|
516
|
+
if (rr.status === 'fulfilled')
|
|
517
|
+
return { valid: true };
|
|
518
|
+
}
|
|
519
|
+
}
|
|
498
520
|
}
|
|
499
521
|
}
|
|
522
|
+
return { valid: false, error: 'Auth token expired or invalid. Run: vigthoria login' };
|
|
500
523
|
}
|
|
501
|
-
if (explicitEnvToken) {
|
|
524
|
+
if (explicitEnvToken || !allowNetworkFailOpen) {
|
|
502
525
|
return { valid: false, error: 'Auth token expired or invalid. Run: vigthoria login' };
|
|
503
526
|
}
|
|
504
527
|
// Both unreachable — don't assume the stored token is bad when running offline.
|
|
@@ -958,6 +981,8 @@ class APIClient {
|
|
|
958
981
|
if (authToken) {
|
|
959
982
|
headers.Authorization = `Bearer ${authToken}`;
|
|
960
983
|
headers.Cookie = `vigthoria-auth-token=${authToken}`;
|
|
984
|
+
headers['X-Vigthoria-Token'] = authToken;
|
|
985
|
+
headers['X-Auth-Token'] = authToken;
|
|
961
986
|
}
|
|
962
987
|
return headers;
|
|
963
988
|
}
|
|
@@ -2987,8 +3012,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2987
3012
|
&& !process.env.VIGTHORIA_AUTH_TOKEN
|
|
2988
3013
|
&& Boolean(this.config.get('authToken'));
|
|
2989
3014
|
if (onlyUnauthorizedErrors && usingStoredConfigToken) {
|
|
2990
|
-
this.
|
|
2991
|
-
|
|
3015
|
+
const gatewayTokenCheck = await this.validateToken({ allowNetworkFailOpen: true, enforceTokenShape: true });
|
|
3016
|
+
if (!gatewayTokenCheck.valid) {
|
|
3017
|
+
this.config.clearAuth();
|
|
3018
|
+
throw new Error('V3 agent authentication failed. The stored CLI login token is invalid or expired. Run vigthoria login again.');
|
|
3019
|
+
}
|
|
3020
|
+
throw new Error('V3 agent authentication failed at the V3 service layer while your gateway login token is still valid. Please retry shortly.');
|
|
2992
3021
|
}
|
|
2993
3022
|
if (preferLocalV3
|
|
2994
3023
|
&& !this.hasAgentWorkspaceOutput(executionContext)
|
|
@@ -3212,6 +3241,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3212
3241
|
* NO localhost fallbacks - CLI is for external users, not server-side!
|
|
3213
3242
|
*/
|
|
3214
3243
|
async chat(messages, model, useLocal = false) {
|
|
3244
|
+
this.lastChatTransportErrors = [];
|
|
3215
3245
|
const resolvedModel = this.resolveModelId(model);
|
|
3216
3246
|
const candidateModels = this.isCloudModelId(resolvedModel) && !this.canUseCloudModel()
|
|
3217
3247
|
? [this.getSelfHostedFallbackModelId(resolvedModel, model)]
|
|
@@ -3230,12 +3260,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3230
3260
|
}
|
|
3231
3261
|
}
|
|
3232
3262
|
// No more localhost fallbacks - CLI is for external users!
|
|
3233
|
-
|
|
3263
|
+
const detail = this.lastChatTransportErrors.length > 0
|
|
3264
|
+
? ` Tried routes: ${this.lastChatTransportErrors.slice(0, 4).join(' | ')}`
|
|
3265
|
+
: '';
|
|
3266
|
+
throw new CLIError(`AI service unavailable. Please check your internet connection or try again later.${detail}`, 'model_backend');
|
|
3234
3267
|
}
|
|
3235
3268
|
shouldSkipCloudRoutes(resolvedModel) {
|
|
3236
3269
|
return this.shouldSimulateCloudFailure() && this.isCloudModelId(resolvedModel);
|
|
3237
3270
|
}
|
|
3238
3271
|
async tryChatWithModel(messages, resolvedModel, requestedModel) {
|
|
3272
|
+
const routeFailures = [];
|
|
3239
3273
|
const preferSelfHostedFirst = this.isSelfHostedPreferredModel(resolvedModel, requestedModel);
|
|
3240
3274
|
if (preferSelfHostedFirst) {
|
|
3241
3275
|
const selfHostedResponse = await this.trySelfHostedChatWithModel(messages, resolvedModel, requestedModel);
|
|
@@ -3276,6 +3310,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3276
3310
|
catch (error) {
|
|
3277
3311
|
const errMsg = error.response?.data?.error || error.message || 'Unknown error';
|
|
3278
3312
|
this.logger.debug(`Direct Vigthoria Models API failed for ${resolvedModel}: ${errMsg}`);
|
|
3313
|
+
routeFailures.push(`models:${String(errMsg).slice(0, 120)}`);
|
|
3279
3314
|
}
|
|
3280
3315
|
}
|
|
3281
3316
|
else {
|
|
@@ -3310,6 +3345,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3310
3345
|
catch (error) {
|
|
3311
3346
|
const errMsg = error.response?.data?.error || error.message || 'Unknown error';
|
|
3312
3347
|
this.logger.debug(`Vigthoria Cloud API failed for ${resolvedModel}: ${errMsg}`);
|
|
3348
|
+
routeFailures.push(`coder:${String(errMsg).slice(0, 120)}`);
|
|
3313
3349
|
}
|
|
3314
3350
|
try {
|
|
3315
3351
|
this.logger.debug(`Canonical Vigthoria Cloud fallback: ${resolvedModel}`);
|
|
@@ -3339,6 +3375,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3339
3375
|
catch (error) {
|
|
3340
3376
|
const errMsg = error.response?.data?.error || error.message || 'Unknown error';
|
|
3341
3377
|
this.logger.debug(`Canonical Vigthoria Cloud fallback failed for ${resolvedModel}: ${errMsg}`);
|
|
3378
|
+
routeFailures.push(`coder-canonical:${String(errMsg).slice(0, 120)}`);
|
|
3342
3379
|
}
|
|
3343
3380
|
}
|
|
3344
3381
|
if (!preferSelfHostedFirst) {
|
|
@@ -3347,6 +3384,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3347
3384
|
return selfHostedResponse;
|
|
3348
3385
|
}
|
|
3349
3386
|
}
|
|
3387
|
+
if (routeFailures.length > 0) {
|
|
3388
|
+
this.lastChatTransportErrors = routeFailures;
|
|
3389
|
+
}
|
|
3350
3390
|
return null;
|
|
3351
3391
|
}
|
|
3352
3392
|
async trySelfHostedChatWithModel(messages, resolvedModel, requestedModel) {
|