vigthoria-cli 1.9.5 → 1.9.8
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/commands/auth.js +87 -8
- package/dist/commands/bridge.js +3 -8
- package/dist/commands/chat.js +31 -19
- package/dist/commands/legion.js +20 -11
- package/dist/commands/security.d.ts +20 -0
- package/dist/commands/security.js +98 -0
- package/dist/index.js +41 -15
- package/dist/utils/api.js +133 -40
- package/dist/utils/config.js +1 -1
- package/dist/utils/tools.js +29 -5
- package/install.ps1 +322 -0
- package/install.sh +314 -0
- package/package.json +12 -3
- package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +159 -0
- package/scripts/release/publish-cli-release.sh +73 -0
- package/scripts/release/validate-no-go-gates.sh +129 -0
- package/scripts/release/verify-runtime-consistency.mjs +64 -0
package/dist/utils/api.js
CHANGED
|
@@ -100,6 +100,38 @@ function sanitizeUserFacingErrorText(input) {
|
|
|
100
100
|
const withoutTags = raw.replace(/<[^>]+>/g, ' ');
|
|
101
101
|
return withoutTags.replace(/\s+/g, ' ').trim();
|
|
102
102
|
}
|
|
103
|
+
const TRUSTED_TOKEN_HOST_PATTERN = /(^|\.)vigthoria\.io$/i;
|
|
104
|
+
function isLoopbackHost(hostname) {
|
|
105
|
+
const host = String(hostname || '').toLowerCase();
|
|
106
|
+
return host === 'localhost' || host === '127.0.0.1';
|
|
107
|
+
}
|
|
108
|
+
function isTrustedTokenDestination(rawUrl) {
|
|
109
|
+
try {
|
|
110
|
+
const parsed = new URL(rawUrl);
|
|
111
|
+
const host = parsed.hostname.toLowerCase();
|
|
112
|
+
return TRUSTED_TOKEN_HOST_PATTERN.test(host) || isLoopbackHost(host);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function resolveAxiosRequestUrl(req) {
|
|
119
|
+
const direct = String(req?.url || '').trim();
|
|
120
|
+
const base = String(req?.baseURL || '').trim();
|
|
121
|
+
if (!direct && !base)
|
|
122
|
+
return '';
|
|
123
|
+
if (/^https?:\/\//i.test(direct))
|
|
124
|
+
return direct;
|
|
125
|
+
if (base) {
|
|
126
|
+
try {
|
|
127
|
+
return new URL(direct || '', base).toString();
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return base;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return direct;
|
|
134
|
+
}
|
|
103
135
|
function isServerRuntime() {
|
|
104
136
|
if (process.env.VIGTHORIA_ALLOW_LOCAL_SERVICES === '1') {
|
|
105
137
|
return true;
|
|
@@ -146,9 +178,12 @@ function propagateError(err) {
|
|
|
146
178
|
};
|
|
147
179
|
}
|
|
148
180
|
const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
|
|
149
|
-
const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS
|
|
181
|
+
const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
|
|
182
|
+
if (!rawValue) {
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
150
185
|
const parsed = Number.parseInt(rawValue, 10);
|
|
151
|
-
return Number.isFinite(parsed) && parsed
|
|
186
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
|
|
152
187
|
})();
|
|
153
188
|
const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
|
|
154
189
|
const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS || '90000';
|
|
@@ -156,9 +191,12 @@ const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
|
|
|
156
191
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 90000;
|
|
157
192
|
})();
|
|
158
193
|
const DEFAULT_OPERATOR_TIMEOUT_MS = (() => {
|
|
159
|
-
const rawValue = process.env.VIGTHORIA_OPERATOR_TIMEOUT_MS || process.env.OPERATOR_TIMEOUT_MS
|
|
194
|
+
const rawValue = process.env.VIGTHORIA_OPERATOR_TIMEOUT_MS || process.env.OPERATOR_TIMEOUT_MS;
|
|
195
|
+
if (!rawValue) {
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
160
198
|
const parsed = Number.parseInt(rawValue, 10);
|
|
161
|
-
return Number.isFinite(parsed) && parsed
|
|
199
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
|
|
162
200
|
})();
|
|
163
201
|
class APIClient {
|
|
164
202
|
client;
|
|
@@ -212,24 +250,27 @@ class APIClient {
|
|
|
212
250
|
}) : null;
|
|
213
251
|
// Add auth interceptor
|
|
214
252
|
this.client.interceptors.request.use((req) => {
|
|
215
|
-
const token = this.
|
|
216
|
-
|
|
253
|
+
const token = this.getAccessToken();
|
|
254
|
+
const destination = resolveAxiosRequestUrl(req);
|
|
255
|
+
if (token && isTrustedTokenDestination(destination)) {
|
|
217
256
|
req.headers.Authorization = `Bearer ${token}`;
|
|
218
257
|
req.headers.Cookie = `vigthoria-auth-token=${token}`;
|
|
219
258
|
}
|
|
220
259
|
return req;
|
|
221
260
|
});
|
|
222
261
|
this.modelRouterClient.interceptors.request.use((req) => {
|
|
223
|
-
const token = this.
|
|
224
|
-
|
|
262
|
+
const token = this.getAccessToken();
|
|
263
|
+
const destination = resolveAxiosRequestUrl(req);
|
|
264
|
+
if (token && isTrustedTokenDestination(destination)) {
|
|
225
265
|
req.headers.Authorization = `Bearer ${token}`;
|
|
226
266
|
req.headers.Cookie = `vigthoria-auth-token=${token}`;
|
|
227
267
|
}
|
|
228
268
|
return req;
|
|
229
269
|
});
|
|
230
270
|
this.selfHostedModelRouterClient?.interceptors.request.use((req) => {
|
|
231
|
-
const token = this.
|
|
232
|
-
|
|
271
|
+
const token = this.getAccessToken();
|
|
272
|
+
const destination = resolveAxiosRequestUrl(req);
|
|
273
|
+
if (token && isTrustedTokenDestination(destination)) {
|
|
233
274
|
req.headers.Authorization = `Bearer ${token}`;
|
|
234
275
|
}
|
|
235
276
|
return req;
|
|
@@ -430,18 +471,22 @@ class APIClient {
|
|
|
430
471
|
if (!token) {
|
|
431
472
|
return { valid: false, error: 'No auth token configured. Run: vigthoria login' };
|
|
432
473
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
474
|
+
const explicitEnvToken = Boolean(process.env.VIGTHORIA_TOKEN || process.env.VIGTHORIA_AUTH_TOKEN);
|
|
475
|
+
const headers = {
|
|
476
|
+
Authorization: `Bearer ${token}`,
|
|
477
|
+
Cookie: `vigthoria-auth-token=${token}`,
|
|
478
|
+
};
|
|
479
|
+
const canonicalBaseUrl = String(this.config.get('apiUrl') || 'https://coder.vigthoria.io').replace(/\/$/, '');
|
|
480
|
+
// Probe protected canonical endpoints in parallel so stale local endpoint overrides
|
|
481
|
+
// cannot mask an invalid gateway token during preflight.
|
|
436
482
|
const results = await Promise.allSettled([
|
|
437
|
-
|
|
438
|
-
|
|
483
|
+
axios_1.default.get(`${canonicalBaseUrl}/api/user/profile`, { timeout: 5000, headers, httpsAgent: this._httpsAgent ?? undefined }),
|
|
484
|
+
axios_1.default.get(`${canonicalBaseUrl}/api/user/subscription`, { timeout: 5000, headers, httpsAgent: this._httpsAgent ?? undefined }),
|
|
439
485
|
]);
|
|
440
486
|
for (const r of results) {
|
|
441
487
|
if (r.status === 'fulfilled')
|
|
442
488
|
return { valid: true };
|
|
443
489
|
}
|
|
444
|
-
// Both failed — check why
|
|
445
490
|
for (const r of results) {
|
|
446
491
|
if (r.status === 'rejected') {
|
|
447
492
|
const err = r.reason;
|
|
@@ -453,7 +498,10 @@ class APIClient {
|
|
|
453
498
|
}
|
|
454
499
|
}
|
|
455
500
|
}
|
|
456
|
-
|
|
501
|
+
if (explicitEnvToken) {
|
|
502
|
+
return { valid: false, error: 'Auth token expired or invalid. Run: vigthoria login' };
|
|
503
|
+
}
|
|
504
|
+
// Both unreachable — don't assume the stored token is bad when running offline.
|
|
457
505
|
return { valid: true };
|
|
458
506
|
}
|
|
459
507
|
getV3AgentBaseUrls(preferLocal = false) {
|
|
@@ -464,6 +512,7 @@ class APIClient {
|
|
|
464
512
|
process.env.V3_AGENT_URL,
|
|
465
513
|
...(allowLocalV3Agent ? ['http://127.0.0.1:8030'] : []),
|
|
466
514
|
configuredApiUrl,
|
|
515
|
+
'https://coder.vigthoria.io',
|
|
467
516
|
].filter(Boolean).map((url) => String(url).replace(/\/$/, ''));
|
|
468
517
|
return [...new Set(urls)];
|
|
469
518
|
}
|
|
@@ -486,6 +535,7 @@ class APIClient {
|
|
|
486
535
|
process.env.OPERATOR_URL,
|
|
487
536
|
'http://127.0.0.1:4009',
|
|
488
537
|
configuredModelsApiUrl,
|
|
538
|
+
'https://api.vigthoria.io',
|
|
489
539
|
].filter(Boolean).map((url) => String(url).replace(/\/$/, ''));
|
|
490
540
|
return [...new Set(urls)];
|
|
491
541
|
}
|
|
@@ -2769,7 +2819,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2769
2819
|
}
|
|
2770
2820
|
async runV3AgentWorkflow(message, context = {}) {
|
|
2771
2821
|
const executionContext = await this.bindExecutionContext(context);
|
|
2772
|
-
const
|
|
2822
|
+
const requestedTimeoutMs = Number(executionContext.agentTimeoutMs ?? DEFAULT_V3_AGENT_TIMEOUT_MS);
|
|
2823
|
+
const baseTimeoutMs = Number.isFinite(requestedTimeoutMs) && requestedTimeoutMs > 0 ? requestedTimeoutMs : 0;
|
|
2773
2824
|
const expectedFiles = this.extractExpectedWorkspaceFiles(message, executionContext);
|
|
2774
2825
|
const requestedModel = String(executionContext.model || executionContext.requestedModel || 'agent');
|
|
2775
2826
|
const resolvedModel = this.resolvePermittedModelId(requestedModel);
|
|
@@ -2777,7 +2828,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2777
2828
|
&& context.localMachineCapable !== false;
|
|
2778
2829
|
const rescueEligibleSaaS = preferLocalV3
|
|
2779
2830
|
&& /(saas|dashboard|analytics|billing|team management|activity feed|login screen)/i.test(message);
|
|
2780
|
-
const timeoutMs = rescueEligibleSaaS ? Math.min(baseTimeoutMs, 210000) : baseTimeoutMs;
|
|
2831
|
+
const timeoutMs = baseTimeoutMs > 0 && rescueEligibleSaaS ? Math.min(baseTimeoutMs, 210000) : baseTimeoutMs;
|
|
2781
2832
|
const maxAttempts = preferLocalV3 ? 2 : 1;
|
|
2782
2833
|
let lastErrors = [];
|
|
2783
2834
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
@@ -2811,7 +2862,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2811
2862
|
};
|
|
2812
2863
|
for (const baseUrl of this.getV3AgentBaseUrls(preferLocalV3)) {
|
|
2813
2864
|
const controller = new AbortController();
|
|
2814
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
2865
|
+
const timeoutId = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
2815
2866
|
try {
|
|
2816
2867
|
const response = await this.executeV3AgentRunRequest(baseUrl, requestBody, requestExecutionContext, controller.signal);
|
|
2817
2868
|
if (!response.ok) {
|
|
@@ -2843,7 +2894,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2843
2894
|
stream: true,
|
|
2844
2895
|
};
|
|
2845
2896
|
const continueController = new AbortController();
|
|
2846
|
-
const continueTimeoutId = setTimeout(() => continueController.abort(), timeoutMs);
|
|
2897
|
+
const continueTimeoutId = timeoutMs > 0 ? setTimeout(() => continueController.abort(), timeoutMs) : null;
|
|
2847
2898
|
try {
|
|
2848
2899
|
const continueHeaders = await this.getV3AgentHeaders();
|
|
2849
2900
|
const continueResponse = await fetch(this.getV3AgentContinueUrl(baseUrl), {
|
|
@@ -2861,7 +2912,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2861
2912
|
break; // Fall through to normal completion with partial data
|
|
2862
2913
|
}
|
|
2863
2914
|
finally {
|
|
2864
|
-
|
|
2915
|
+
if (continueTimeoutId)
|
|
2916
|
+
clearTimeout(continueTimeoutId);
|
|
2865
2917
|
}
|
|
2866
2918
|
}
|
|
2867
2919
|
// Use the final continuation data for workspace recovery
|
|
@@ -2915,7 +2967,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2915
2967
|
errors.push(`${baseUrl}: ${error?.message || String(error)}`);
|
|
2916
2968
|
}
|
|
2917
2969
|
finally {
|
|
2918
|
-
|
|
2970
|
+
if (timeoutId)
|
|
2971
|
+
clearTimeout(timeoutId);
|
|
2919
2972
|
}
|
|
2920
2973
|
}
|
|
2921
2974
|
lastErrors = errors;
|
|
@@ -2982,7 +3035,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2982
3035
|
}
|
|
2983
3036
|
async runOperatorWorkflow(message, context = {}) {
|
|
2984
3037
|
const executionContext = await this.bindExecutionContext(context);
|
|
2985
|
-
const
|
|
3038
|
+
const requestedOperatorTimeoutMs = Number(context.operatorTimeoutMs ?? DEFAULT_OPERATOR_TIMEOUT_MS);
|
|
3039
|
+
const timeoutMs = Number.isFinite(requestedOperatorTimeoutMs) && requestedOperatorTimeoutMs > 0
|
|
3040
|
+
? requestedOperatorTimeoutMs
|
|
3041
|
+
: 0;
|
|
2986
3042
|
const errors = [];
|
|
2987
3043
|
const authToken = this.config.get('authToken');
|
|
2988
3044
|
// Collect a lightweight workspace file listing so the operator can
|
|
@@ -2991,7 +3047,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2991
3047
|
const workspaceSummary = this.buildLocalWorkspaceSummary(workspacePath);
|
|
2992
3048
|
for (const baseUrl of this.getOperatorBaseUrls()) {
|
|
2993
3049
|
const controller = new AbortController();
|
|
2994
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
3050
|
+
const timeoutId = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
2995
3051
|
try {
|
|
2996
3052
|
const response = await fetch(this.getOperatorStreamUrl(baseUrl), {
|
|
2997
3053
|
method: 'POST',
|
|
@@ -3138,7 +3194,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3138
3194
|
errors.push(`${baseUrl}: ${error?.message || String(error)}`);
|
|
3139
3195
|
}
|
|
3140
3196
|
finally {
|
|
3141
|
-
|
|
3197
|
+
if (timeoutId)
|
|
3198
|
+
clearTimeout(timeoutId);
|
|
3142
3199
|
}
|
|
3143
3200
|
}
|
|
3144
3201
|
throw new CLIError(`Operator workflow failed on all endpoints: ${errors.join(' | ')}`, 'model_backend');
|
|
@@ -3190,7 +3247,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3190
3247
|
if (!this.shouldSkipCloudRoutes(resolvedModel)) {
|
|
3191
3248
|
try {
|
|
3192
3249
|
this.logger.debug(`Direct Vigthoria Models API: ${resolvedModel}`);
|
|
3193
|
-
const token = this.
|
|
3250
|
+
const token = this.getAccessToken();
|
|
3194
3251
|
const response = await this.modelRouterClient.post('/v1/chat/completions', {
|
|
3195
3252
|
model: resolvedModel,
|
|
3196
3253
|
messages,
|
|
@@ -3254,6 +3311,35 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3254
3311
|
const errMsg = error.response?.data?.error || error.message || 'Unknown error';
|
|
3255
3312
|
this.logger.debug(`Vigthoria Cloud API failed for ${resolvedModel}: ${errMsg}`);
|
|
3256
3313
|
}
|
|
3314
|
+
try {
|
|
3315
|
+
this.logger.debug(`Canonical Vigthoria Cloud fallback: ${resolvedModel}`);
|
|
3316
|
+
const token = this.getAccessToken();
|
|
3317
|
+
const response = await axios_1.default.post('https://coder.vigthoria.io/api/ai/chat', {
|
|
3318
|
+
messages,
|
|
3319
|
+
model: resolvedModel,
|
|
3320
|
+
maxTokens: this.config.get('preferences').maxTokens,
|
|
3321
|
+
temperature: 0.7,
|
|
3322
|
+
}, {
|
|
3323
|
+
timeout: 180000,
|
|
3324
|
+
httpsAgent: this._httpsAgent ?? undefined,
|
|
3325
|
+
headers: token ? { Authorization: `Bearer ${token}`, Cookie: `vigthoria-auth-token=${token}` } : {},
|
|
3326
|
+
});
|
|
3327
|
+
if (response.data?.success !== false) {
|
|
3328
|
+
const content = response.data.response || response.data.message || response.data.content;
|
|
3329
|
+
if (typeof content === 'string' && content.trim()) {
|
|
3330
|
+
return {
|
|
3331
|
+
id: response.data.id || `vigthoria-coder-canonical-${Date.now()}`,
|
|
3332
|
+
message: content,
|
|
3333
|
+
model: response.data.model || resolvedModel || requestedModel,
|
|
3334
|
+
usage: response.data.usage,
|
|
3335
|
+
};
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
catch (error) {
|
|
3340
|
+
const errMsg = error.response?.data?.error || error.message || 'Unknown error';
|
|
3341
|
+
this.logger.debug(`Canonical Vigthoria Cloud fallback failed for ${resolvedModel}: ${errMsg}`);
|
|
3342
|
+
}
|
|
3257
3343
|
}
|
|
3258
3344
|
if (!preferSelfHostedFirst) {
|
|
3259
3345
|
const selfHostedResponse = await this.trySelfHostedChatWithModel(messages, resolvedModel, requestedModel);
|
|
@@ -3311,7 +3397,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3311
3397
|
'vigthoria-cloud-ultra',
|
|
3312
3398
|
]);
|
|
3313
3399
|
if (cloudModels.has(resolvedModel)) {
|
|
3314
|
-
return 'vigthoria-v3-code-
|
|
3400
|
+
return 'vigthoria-v3-code-35b';
|
|
3315
3401
|
}
|
|
3316
3402
|
return null;
|
|
3317
3403
|
}
|
|
@@ -3326,6 +3412,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3326
3412
|
return this.config.hasCloudAccess();
|
|
3327
3413
|
}
|
|
3328
3414
|
resolvePermittedModelId(shortName) {
|
|
3415
|
+
const normalizedRequested = String(shortName || '').trim().toLowerCase();
|
|
3416
|
+
const blockedModels = new Set(['fast', 'mini', 'creative', 'creative-v3', 'creative-v4']);
|
|
3417
|
+
if (blockedModels.has(normalizedRequested)) {
|
|
3418
|
+
this.logger.debug(`Blocked governed model ${shortName}; using fallback vigthoria-v3-code-35b`);
|
|
3419
|
+
return 'vigthoria-v3-code-35b';
|
|
3420
|
+
}
|
|
3329
3421
|
const resolvedModel = this.resolveModelId(shortName);
|
|
3330
3422
|
if (this.isCloudModelId(resolvedModel) && !this.canUseCloudModel()) {
|
|
3331
3423
|
const fallbackModel = this.getSelfHostedFallbackModelId(resolvedModel, shortName);
|
|
@@ -3350,8 +3442,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3350
3442
|
isSelfHostedPreferredModel(resolvedModel, requestedModel) {
|
|
3351
3443
|
const normalizedRequested = String(requestedModel || '').toLowerCase();
|
|
3352
3444
|
const selfHostedModels = new Set([
|
|
3353
|
-
'vigthoria-v3-code-
|
|
3354
|
-
'vigthoria-v3-code-
|
|
3445
|
+
'vigthoria-v3-code-35b',
|
|
3446
|
+
'vigthoria-v3-code-35b:latest',
|
|
3355
3447
|
'qwen3-coder:latest',
|
|
3356
3448
|
'vigthoria-v2-code-8b',
|
|
3357
3449
|
]);
|
|
@@ -3363,9 +3455,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3363
3455
|
}
|
|
3364
3456
|
getSelfHostedFallbackModelId(resolvedModel, requestedModel) {
|
|
3365
3457
|
if (this.isSelfHostedPreferredModel(resolvedModel, requestedModel)) {
|
|
3366
|
-
return resolvedModel === 'qwen3-coder:latest' ? 'vigthoria-v3-code-
|
|
3458
|
+
return resolvedModel === 'qwen3-coder:latest' ? 'vigthoria-v3-code-35b' : resolvedModel;
|
|
3367
3459
|
}
|
|
3368
|
-
return 'vigthoria-v3-code-
|
|
3460
|
+
return 'vigthoria-v3-code-35b';
|
|
3369
3461
|
}
|
|
3370
3462
|
// Streaming chat
|
|
3371
3463
|
async *chatStream(messages, model) {
|
|
@@ -3448,7 +3540,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3448
3540
|
// (/v1/chat/completions on api.vigthoria.io) which is the only
|
|
3449
3541
|
// backend that reliably accepts our auth token.
|
|
3450
3542
|
async chatComplete(systemPrompt, userPrompt, model, maxTokens) {
|
|
3451
|
-
const resolvedModel = model ? this.resolvePermittedModelId(model) : 'vigthoria-v3-code-
|
|
3543
|
+
const resolvedModel = model ? this.resolvePermittedModelId(model) : 'vigthoria-v3-code-35b';
|
|
3452
3544
|
const response = await this.modelRouterClient.post('/v1/chat/completions', {
|
|
3453
3545
|
model: resolvedModel,
|
|
3454
3546
|
messages: [
|
|
@@ -4301,15 +4393,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
4301
4393
|
'fast': 'vigthoria-fast-1.7b',
|
|
4302
4394
|
'mini': 'vigthoria-mini-0.6b',
|
|
4303
4395
|
'balanced': 'vigthoria-balanced-4b',
|
|
4396
|
+
'balanced-4b': 'vigthoria-balanced-4b',
|
|
4304
4397
|
'creative': 'vigthoria-creative-9b-v4',
|
|
4305
4398
|
// Code Models - 30B is the default powerhouse
|
|
4306
|
-
'code': 'vigthoria-v3-code-
|
|
4307
|
-
'code-30b': 'vigthoria-v3-code-
|
|
4399
|
+
'code': 'vigthoria-v3-code-35b', // Internal: self-hosted 35B on Blackwell
|
|
4400
|
+
'code-30b': 'vigthoria-v3-code-35b',
|
|
4308
4401
|
'code-8b': 'vigthoria-v2-code-8b',
|
|
4309
|
-
'pro': 'vigthoria-v3-code-
|
|
4310
|
-
'agent': 'vigthoria-v3-code-
|
|
4311
|
-
'vigthoria-code': 'vigthoria-v3-code-
|
|
4312
|
-
'vigthoria-agent': 'vigthoria-v3-code-
|
|
4402
|
+
'pro': 'vigthoria-v3-code-35b',
|
|
4403
|
+
'agent': 'vigthoria-v3-code-35b',
|
|
4404
|
+
'vigthoria-code': 'vigthoria-v3-code-35b',
|
|
4405
|
+
'vigthoria-agent': 'vigthoria-v3-code-35b',
|
|
4313
4406
|
// ═══════════════════════════════════════════════════════════════
|
|
4314
4407
|
// VIGTHORIA CLOUD - Premium cloud models (internal routing)
|
|
4315
4408
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -4324,7 +4417,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
4324
4417
|
}
|
|
4325
4418
|
return shortName;
|
|
4326
4419
|
}
|
|
4327
|
-
return modelMap[shortName] || 'vigthoria-v3-code-
|
|
4420
|
+
return modelMap[shortName] || 'vigthoria-v3-code-35b';
|
|
4328
4421
|
}
|
|
4329
4422
|
async getCoderHealth() {
|
|
4330
4423
|
try {
|
package/dist/utils/config.js
CHANGED
|
@@ -47,7 +47,7 @@ const defaultConfig = {
|
|
|
47
47
|
};
|
|
48
48
|
class Config {
|
|
49
49
|
store;
|
|
50
|
-
static OPERATOR_PLANS = new Set(['enterprise', 'admin', 'master_admin']);
|
|
50
|
+
static OPERATOR_PLANS = new Set(['pro', 'professional', 'ultra', 'enterprise', 'admin', 'master_admin']);
|
|
51
51
|
static CLOUD_PLANS = new Set(['pro', 'professional', 'ultra', 'enterprise', 'master_admin', 'admin']);
|
|
52
52
|
constructor() {
|
|
53
53
|
this.store = new conf_1.default({
|
package/dist/utils/tools.js
CHANGED
|
@@ -61,6 +61,17 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
61
61
|
const logger_js_1 = require("./logger.js");
|
|
62
62
|
const api_js_1 = require("./api.js");
|
|
63
63
|
const STREAM_RESPONSE_MAX_YIELD_CHARS = 32 * 1024;
|
|
64
|
+
const POWERSHELL_SAFE_PATH_PATTERN = /^[A-Za-z0-9_:\\/.\-\s]+$/;
|
|
65
|
+
const POWERSHELL_SAFE_INCLUDE_PATTERN = /^[A-Za-z0-9_*?.\-]+$/;
|
|
66
|
+
const SSH_SAFE_HOST_PATTERN = /^[A-Za-z0-9.-]+$/;
|
|
67
|
+
function getSshAllowedHosts() {
|
|
68
|
+
const configured = String(process.env.VIGTHORIA_SSH_ALLOWED_HOSTS || '')
|
|
69
|
+
.split(',')
|
|
70
|
+
.map((v) => v.trim().toLowerCase())
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
const defaults = ['vigthoria-server', 'localhost', '127.0.0.1'];
|
|
73
|
+
return new Set([...defaults, ...configured]);
|
|
74
|
+
}
|
|
64
75
|
function isNodeError(error) {
|
|
65
76
|
return error instanceof Error && 'code' in error;
|
|
66
77
|
}
|
|
@@ -1798,9 +1809,14 @@ class AgenticTools {
|
|
|
1798
1809
|
grepWithSelectString(args, searchPath) {
|
|
1799
1810
|
// Normalize path for PowerShell
|
|
1800
1811
|
const psPath = searchPath.replace(/\//g, '\\');
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1812
|
+
if (!POWERSHELL_SAFE_PATH_PATTERN.test(psPath)) {
|
|
1813
|
+
return this.createErrorResult(ToolErrorType.INVALID_ARGS, 'Unsafe search path for PowerShell backend', 'Use a normal workspace path containing only letters, numbers, separators, dot, dash, and spaces.');
|
|
1814
|
+
}
|
|
1815
|
+
const includeArg = args.include ? String(args.include) : '*';
|
|
1816
|
+
if (!POWERSHELL_SAFE_INCLUDE_PATTERN.test(includeArg)) {
|
|
1817
|
+
return this.createErrorResult(ToolErrorType.INVALID_ARGS, 'Unsafe include filter for PowerShell backend', 'Use simple wildcard filters like *.ts or *.py.');
|
|
1818
|
+
}
|
|
1819
|
+
const includeFilter = `-Include "${includeArg}"`;
|
|
1804
1820
|
const escapedPattern = args.pattern.replace(/'/g, "''");
|
|
1805
1821
|
const cmd = `powershell -NoProfile -Command "Get-ChildItem -Path '${psPath}' -Recurse -File ${includeFilter} | Select-String -Pattern '${escapedPattern}' | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }"`;
|
|
1806
1822
|
try {
|
|
@@ -2453,6 +2469,14 @@ class AgenticTools {
|
|
|
2453
2469
|
const command = args.command;
|
|
2454
2470
|
const host = args.host || 'vigthoria-server';
|
|
2455
2471
|
const timeout = args.timeout ? parseInt(args.timeout) * 1000 : 60000;
|
|
2472
|
+
const normalizedHost = String(host).trim().toLowerCase();
|
|
2473
|
+
if (!SSH_SAFE_HOST_PATTERN.test(normalizedHost)) {
|
|
2474
|
+
return this.createErrorResult(ToolErrorType.INVALID_ARGS, 'Invalid SSH host format', 'Host can only include letters, numbers, dots, and dashes.');
|
|
2475
|
+
}
|
|
2476
|
+
const allowedHosts = getSshAllowedHosts();
|
|
2477
|
+
if (!allowedHosts.has(normalizedHost)) {
|
|
2478
|
+
return this.createErrorResult(ToolErrorType.PERMISSION_DENIED, `SSH host is not allowlisted: ${host}`, 'Add the host to VIGTHORIA_SSH_ALLOWED_HOSTS to permit access.');
|
|
2479
|
+
}
|
|
2456
2480
|
// Security checks for SSH commands
|
|
2457
2481
|
const blockedPatterns = [
|
|
2458
2482
|
/\brm\s+-rf?\s+\//i, // Dangerous rm commands
|
|
@@ -2478,12 +2502,12 @@ class AgenticTools {
|
|
|
2478
2502
|
};
|
|
2479
2503
|
if (platform === 'win32') {
|
|
2480
2504
|
// On Windows, use the ssh command from OpenSSH
|
|
2481
|
-
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=
|
|
2505
|
+
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 ${host} "${command.replace(/"/g, '\\"')}"`;
|
|
2482
2506
|
execOptions.shell = true;
|
|
2483
2507
|
}
|
|
2484
2508
|
else {
|
|
2485
2509
|
// On Unix-like systems
|
|
2486
|
-
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=
|
|
2510
|
+
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 ${host} '${command.replace(/'/g, "'\\''")}'`;
|
|
2487
2511
|
execOptions.shell = '/bin/sh';
|
|
2488
2512
|
}
|
|
2489
2513
|
const output = (0, child_process_1.execSync)(sshCommand, execOptions);
|