vigthoria-cli 1.9.2 → 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 +15 -5
- package/dist/commands/auth.d.ts +28 -38
- package/dist/commands/auth.js +461 -313
- package/dist/commands/bridge.js +3 -8
- package/dist/commands/chat.d.ts +3 -0
- package/dist/commands/chat.js +97 -34
- package/dist/commands/index.js +1 -1
- package/dist/commands/legion.d.ts +22 -19
- package/dist/commands/legion.js +561 -134
- package/dist/commands/preview.js +32 -7
- package/dist/commands/repo.js +19 -13
- package/dist/commands/security.d.ts +20 -0
- package/dist/commands/security.js +98 -0
- package/dist/commands/update.d.ts +9 -0
- package/dist/commands/update.js +235 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +147 -40
- package/dist/utils/api.d.ts +25 -70
- package/dist/utils/api.js +875 -693
- package/dist/utils/config.js +1 -1
- package/dist/utils/tools.d.ts +11 -0
- package/dist/utils/tools.js +251 -5
- package/install.ps1 +322 -0
- package/install.sh +314 -0
- package/package.json +18 -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/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.d.ts
CHANGED
|
@@ -14,6 +14,17 @@
|
|
|
14
14
|
* @author Vigthoria Labs
|
|
15
15
|
*/
|
|
16
16
|
import { Logger } from './logger.js';
|
|
17
|
+
export type StreamChunk = {
|
|
18
|
+
type: 'text' | 'delta' | 'error';
|
|
19
|
+
content: string;
|
|
20
|
+
};
|
|
21
|
+
export type UpdateInstallerResult = {
|
|
22
|
+
success: boolean;
|
|
23
|
+
platform: string;
|
|
24
|
+
error?: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function installUpdateWindows(): Promise<UpdateInstallerResult>;
|
|
27
|
+
export declare function robustifyStreamResponse(res: any): AsyncIterable<StreamChunk>;
|
|
17
28
|
export type RiskLevel = 'low' | 'medium' | 'high' | 'critical';
|
|
18
29
|
export type SearchStatus = 'search_matches_found' | 'search_no_matches' | 'search_failed';
|
|
19
30
|
export interface ToolResult {
|
package/dist/utils/tools.js
CHANGED
|
@@ -52,12 +52,245 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
52
52
|
};
|
|
53
53
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
54
|
exports.AgenticTools = exports.ToolErrorType = void 0;
|
|
55
|
+
exports.installUpdateWindows = installUpdateWindows;
|
|
56
|
+
exports.robustifyStreamResponse = robustifyStreamResponse;
|
|
55
57
|
const fs = __importStar(require("fs"));
|
|
56
58
|
const path = __importStar(require("path"));
|
|
57
59
|
const child_process_1 = require("child_process");
|
|
58
60
|
const chalk_1 = __importDefault(require("chalk"));
|
|
59
61
|
const logger_js_1 = require("./logger.js");
|
|
60
62
|
const api_js_1 = require("./api.js");
|
|
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
|
+
}
|
|
75
|
+
function isNodeError(error) {
|
|
76
|
+
return error instanceof Error && 'code' in error;
|
|
77
|
+
}
|
|
78
|
+
function resolveWindowsInstallerPath() {
|
|
79
|
+
const configuredPath = process.env.VIGTHORIA_WINDOWS_INSTALLER || process.env.VIGTHORIA_UPDATE_INSTALLER;
|
|
80
|
+
if (configuredPath && configuredPath.trim().length > 0) {
|
|
81
|
+
return path.resolve(configuredPath);
|
|
82
|
+
}
|
|
83
|
+
const executableDir = path.dirname(process.execPath);
|
|
84
|
+
const cwd = process.cwd();
|
|
85
|
+
const candidates = [
|
|
86
|
+
path.resolve(cwd, 'dist', 'VigthoriaSetup.exe'),
|
|
87
|
+
path.resolve(cwd, 'release', 'VigthoriaSetup.exe'),
|
|
88
|
+
path.resolve(cwd, 'VigthoriaSetup.exe'),
|
|
89
|
+
path.resolve(executableDir, 'VigthoriaSetup.exe'),
|
|
90
|
+
path.resolve(executableDir, '..', 'VigthoriaSetup.exe'),
|
|
91
|
+
];
|
|
92
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) ?? candidates[0];
|
|
93
|
+
}
|
|
94
|
+
async function installUpdateWindows() {
|
|
95
|
+
const installerPath = resolveWindowsInstallerPath();
|
|
96
|
+
try {
|
|
97
|
+
if (!fs.existsSync(installerPath)) {
|
|
98
|
+
return { success: false, platform: 'windows', error: 'ENOENT' };
|
|
99
|
+
}
|
|
100
|
+
await fs.promises.access(installerPath, fs.constants.F_OK);
|
|
101
|
+
await new Promise((resolve, reject) => {
|
|
102
|
+
const child = (0, child_process_1.spawn)(installerPath, [], {
|
|
103
|
+
cwd: path.dirname(installerPath),
|
|
104
|
+
detached: true,
|
|
105
|
+
stdio: 'ignore',
|
|
106
|
+
windowsHide: false,
|
|
107
|
+
});
|
|
108
|
+
child.once('error', reject);
|
|
109
|
+
child.once('spawn', () => {
|
|
110
|
+
child.unref();
|
|
111
|
+
resolve();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
return { success: true, platform: 'windows' };
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
118
|
+
return { success: false, platform: 'windows', error: 'ENOENT' };
|
|
119
|
+
}
|
|
120
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
121
|
+
return { success: false, platform: 'windows', error: message };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function* robustifyStreamResponse(res) {
|
|
125
|
+
const MAX_YIELD_CHARS = STREAM_RESPONSE_MAX_YIELD_CHARS;
|
|
126
|
+
const MAX_BUFFER_CHARS = 256 * 1024;
|
|
127
|
+
const body = res?.body ?? res;
|
|
128
|
+
const decoder = new TextDecoder('utf-8');
|
|
129
|
+
let reader = null;
|
|
130
|
+
let lineBuffer = '';
|
|
131
|
+
const emitContent = async function* (type, content) {
|
|
132
|
+
for (let offset = 0; offset < content.length; offset += MAX_YIELD_CHARS) {
|
|
133
|
+
const part = content.slice(offset, offset + MAX_YIELD_CHARS);
|
|
134
|
+
if (part.length > 0) {
|
|
135
|
+
yield { type, content: part };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const textFromObject = (value) => {
|
|
140
|
+
if (!value || typeof value !== 'object')
|
|
141
|
+
return '';
|
|
142
|
+
if (typeof value.content === 'string')
|
|
143
|
+
return value.content;
|
|
144
|
+
if (typeof value.text === 'string')
|
|
145
|
+
return value.text;
|
|
146
|
+
if (typeof value.delta === 'string')
|
|
147
|
+
return value.delta;
|
|
148
|
+
if (typeof value.delta?.text === 'string')
|
|
149
|
+
return value.delta.text;
|
|
150
|
+
if (typeof value.message?.content === 'string')
|
|
151
|
+
return value.message.content;
|
|
152
|
+
if (typeof value.choices?.[0]?.delta?.content === 'string')
|
|
153
|
+
return value.choices[0].delta.content;
|
|
154
|
+
if (typeof value.choices?.[0]?.message?.content === 'string')
|
|
155
|
+
return value.choices[0].message.content;
|
|
156
|
+
if (typeof value.error === 'string')
|
|
157
|
+
return value.error;
|
|
158
|
+
if (typeof value.error?.message === 'string')
|
|
159
|
+
return value.error.message;
|
|
160
|
+
return '';
|
|
161
|
+
};
|
|
162
|
+
const stringifyChunk = (chunk) => {
|
|
163
|
+
if (typeof chunk === 'string')
|
|
164
|
+
return chunk;
|
|
165
|
+
if (Buffer.isBuffer(chunk))
|
|
166
|
+
return chunk.toString('utf8');
|
|
167
|
+
if (chunk instanceof Uint8Array)
|
|
168
|
+
return Buffer.from(chunk).toString('utf8');
|
|
169
|
+
if (chunk instanceof ArrayBuffer)
|
|
170
|
+
return Buffer.from(chunk).toString('utf8');
|
|
171
|
+
if (chunk === null || chunk === undefined)
|
|
172
|
+
return '';
|
|
173
|
+
const objectText = textFromObject(chunk);
|
|
174
|
+
return objectText || String(chunk);
|
|
175
|
+
};
|
|
176
|
+
const decodeLine = (line) => {
|
|
177
|
+
const trimmed = line.trim();
|
|
178
|
+
if (!trimmed || trimmed === 'event: ping' || trimmed === ':' || trimmed === 'data: [DONE]' || trimmed === '[DONE]') {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const payload = trimmed.startsWith('data:') ? trimmed.slice(5).trimStart() : trimmed;
|
|
182
|
+
if (!payload || payload === '[DONE]')
|
|
183
|
+
return null;
|
|
184
|
+
if ((payload.startsWith('{') && payload.endsWith('}')) || (payload.startsWith('[') && payload.endsWith(']'))) {
|
|
185
|
+
try {
|
|
186
|
+
const parsed = JSON.parse(payload);
|
|
187
|
+
const parsedText = textFromObject(parsed);
|
|
188
|
+
if (parsedText) {
|
|
189
|
+
const type = parsed.error ? 'error' : 'delta';
|
|
190
|
+
return { type, content: parsedText };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
console.debug(`Stream JSON fragment preserved as text: ${error instanceof Error ? error.message : String(error)}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (payload.startsWith('event:') || payload.startsWith('id:') || payload.startsWith('retry:')) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
return { type: 'delta', content: payload };
|
|
201
|
+
};
|
|
202
|
+
const consumeText = async function* (text, flush = false) {
|
|
203
|
+
if (text) {
|
|
204
|
+
lineBuffer += text;
|
|
205
|
+
}
|
|
206
|
+
while (lineBuffer.length > MAX_BUFFER_CHARS) {
|
|
207
|
+
const newlineIndex = lineBuffer.indexOf('\n', Math.max(0, MAX_BUFFER_CHARS - MAX_YIELD_CHARS));
|
|
208
|
+
const cutIndex = newlineIndex >= 0 ? newlineIndex + 1 : MAX_BUFFER_CHARS;
|
|
209
|
+
const overflow = lineBuffer.slice(0, cutIndex);
|
|
210
|
+
lineBuffer = lineBuffer.slice(cutIndex);
|
|
211
|
+
const decoded = decodeLine(overflow);
|
|
212
|
+
if (decoded) {
|
|
213
|
+
yield* emitContent(decoded.type, decoded.content);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
yield* emitContent('delta', overflow);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const lines = lineBuffer.split(/\r?\n/);
|
|
220
|
+
const partialLine = lines.pop() ?? '';
|
|
221
|
+
for (const line of lines) {
|
|
222
|
+
const decoded = decodeLine(line);
|
|
223
|
+
if (decoded) {
|
|
224
|
+
yield* emitContent(decoded.type, decoded.content);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (flush) {
|
|
228
|
+
lineBuffer = '';
|
|
229
|
+
if (partialLine) {
|
|
230
|
+
const decoded = decodeLine(partialLine);
|
|
231
|
+
if (decoded) {
|
|
232
|
+
yield* emitContent(decoded.type, decoded.content);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
lineBuffer = partialLine;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
try {
|
|
241
|
+
if (!body) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (typeof body.getReader === 'function') {
|
|
245
|
+
reader = body.getReader();
|
|
246
|
+
const streamReader = reader;
|
|
247
|
+
const decoder = new TextDecoder();
|
|
248
|
+
while (true) {
|
|
249
|
+
const { done, value } = await streamReader.read();
|
|
250
|
+
if (done)
|
|
251
|
+
break;
|
|
252
|
+
const content = decoder.decode(value, { stream: true });
|
|
253
|
+
if (content.length > 0) {
|
|
254
|
+
yield* consumeText(content);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const tail = decoder.decode();
|
|
258
|
+
if (tail.length > 0) {
|
|
259
|
+
yield* consumeText(tail);
|
|
260
|
+
}
|
|
261
|
+
yield* consumeText('', true);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (typeof body[Symbol.asyncIterator] === 'function') {
|
|
265
|
+
for await (const chunk of body) {
|
|
266
|
+
const content = stringifyChunk(chunk);
|
|
267
|
+
if (content.length > 0) {
|
|
268
|
+
yield* consumeText(content);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
yield* consumeText('', true);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const content = stringifyChunk(body);
|
|
275
|
+
if (content.length > 0) {
|
|
276
|
+
yield* emitContent('text', content);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
const content = error instanceof Error ? error.message : String(error);
|
|
281
|
+
yield { type: 'error', content };
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
lineBuffer = '';
|
|
285
|
+
try {
|
|
286
|
+
reader?.releaseLock();
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
290
|
+
console.debug(`Stream reader release failed: ${message}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
61
294
|
const TOOL_ARG_ALIASES = {
|
|
62
295
|
read_file: {
|
|
63
296
|
path: ['file', 'filePath', 'filepath', 'target', 'targetPath'],
|
|
@@ -1576,9 +1809,14 @@ class AgenticTools {
|
|
|
1576
1809
|
grepWithSelectString(args, searchPath) {
|
|
1577
1810
|
// Normalize path for PowerShell
|
|
1578
1811
|
const psPath = searchPath.replace(/\//g, '\\');
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
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}"`;
|
|
1582
1820
|
const escapedPattern = args.pattern.replace(/'/g, "''");
|
|
1583
1821
|
const cmd = `powershell -NoProfile -Command "Get-ChildItem -Path '${psPath}' -Recurse -File ${includeFilter} | Select-String -Pattern '${escapedPattern}' | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }"`;
|
|
1584
1822
|
try {
|
|
@@ -2231,6 +2469,14 @@ class AgenticTools {
|
|
|
2231
2469
|
const command = args.command;
|
|
2232
2470
|
const host = args.host || 'vigthoria-server';
|
|
2233
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
|
+
}
|
|
2234
2480
|
// Security checks for SSH commands
|
|
2235
2481
|
const blockedPatterns = [
|
|
2236
2482
|
/\brm\s+-rf?\s+\//i, // Dangerous rm commands
|
|
@@ -2256,12 +2502,12 @@ class AgenticTools {
|
|
|
2256
2502
|
};
|
|
2257
2503
|
if (platform === 'win32') {
|
|
2258
2504
|
// On Windows, use the ssh command from OpenSSH
|
|
2259
|
-
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=
|
|
2505
|
+
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 ${host} "${command.replace(/"/g, '\\"')}"`;
|
|
2260
2506
|
execOptions.shell = true;
|
|
2261
2507
|
}
|
|
2262
2508
|
else {
|
|
2263
2509
|
// On Unix-like systems
|
|
2264
|
-
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=
|
|
2510
|
+
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 ${host} '${command.replace(/'/g, "'\\''")}'`;
|
|
2265
2511
|
execOptions.shell = '/bin/sh';
|
|
2266
2512
|
}
|
|
2267
2513
|
const output = (0, child_process_1.execSync)(sshCommand, execOptions);
|
package/install.ps1
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# Vigthoria CLI Installer for Windows
|
|
2
|
+
# Usage: irm https://cli.vigthoria.io/install.ps1 | iex
|
|
3
|
+
# Alternative: PowerShell -ExecutionPolicy Bypass -File install.ps1
|
|
4
|
+
|
|
5
|
+
$ErrorActionPreference = "Stop"
|
|
6
|
+
|
|
7
|
+
# Configuration
|
|
8
|
+
$CLI_VERSION = "1.9.1"
|
|
9
|
+
$INSTALL_DIR = "$env:USERPROFILE\.vigthoria"
|
|
10
|
+
$NPM_PACKAGE = "vigthoria-cli"
|
|
11
|
+
$GIT_PACKAGE_URL = "git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git"
|
|
12
|
+
$HOSTED_TARBALL_URL = "https://coder.vigthoria.io/releases/vigthoria-cli-$CLI_VERSION.tgz"
|
|
13
|
+
|
|
14
|
+
function Write-Banner {
|
|
15
|
+
Write-Host ""
|
|
16
|
+
Write-Host " ╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
|
17
|
+
Write-Host " ║ ║" -ForegroundColor Cyan
|
|
18
|
+
Write-Host " ║ VIGTHORIA CLI INSTALLER v$CLI_VERSION ║" -ForegroundColor Cyan
|
|
19
|
+
Write-Host " ║ AI-Powered Terminal Coding Assistant ║" -ForegroundColor Cyan
|
|
20
|
+
Write-Host " ║ ║" -ForegroundColor Cyan
|
|
21
|
+
Write-Host " ╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
|
|
22
|
+
Write-Host ""
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function Test-NetworkConnectivity {
|
|
26
|
+
Write-Host "🔍 Checking network connectivity..." -ForegroundColor Yellow
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
$response = Invoke-WebRequest -Uri $HOSTED_TARBALL_URL -Method Head -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
|
|
30
|
+
Write-Host "✓ hosted CLI package is reachable" -ForegroundColor Green
|
|
31
|
+
return $true
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
Write-Host "⚠ Hosted package unavailable, checking npm registry..." -ForegroundColor Yellow
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
$response = Invoke-WebRequest -Uri "https://registry.npmjs.org/vigthoria-cli" -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
|
|
39
|
+
Write-Host "✓ npm registry is reachable" -ForegroundColor Green
|
|
40
|
+
return $true
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
Write-Host "✗ Cannot reach hosted package or npm registry" -ForegroundColor Red
|
|
44
|
+
return $false
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Test-Prerequisites {
|
|
49
|
+
Write-Host "🔍 Checking prerequisites..." -ForegroundColor Yellow
|
|
50
|
+
|
|
51
|
+
# Check Node.js
|
|
52
|
+
try {
|
|
53
|
+
$nodeVersion = node -v 2>$null
|
|
54
|
+
if ($nodeVersion) {
|
|
55
|
+
$major = [int]($nodeVersion -replace 'v(\d+)\..*', '$1')
|
|
56
|
+
if ($major -ge 18) {
|
|
57
|
+
Write-Host "✓ Node.js $nodeVersion" -ForegroundColor Green
|
|
58
|
+
} else {
|
|
59
|
+
Write-Host "✗ Node.js 18+ required (found $nodeVersion)" -ForegroundColor Red
|
|
60
|
+
Write-Host " Download from: https://nodejs.org/" -ForegroundColor Yellow
|
|
61
|
+
return $false
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
Write-Host "✗ Node.js is not installed" -ForegroundColor Red
|
|
67
|
+
Write-Host ""
|
|
68
|
+
Write-Host "Please install Node.js first:" -ForegroundColor Yellow
|
|
69
|
+
Write-Host " Option 1: winget install OpenJS.NodeJS" -ForegroundColor White
|
|
70
|
+
Write-Host " Option 2: Download from https://nodejs.org/" -ForegroundColor White
|
|
71
|
+
Write-Host ""
|
|
72
|
+
return $false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Check npm
|
|
76
|
+
try {
|
|
77
|
+
$npmVersion = npm -v 2>$null
|
|
78
|
+
if ($npmVersion) {
|
|
79
|
+
Write-Host "✓ npm v$npmVersion" -ForegroundColor Green
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
Write-Host "✗ npm is not installed" -ForegroundColor Red
|
|
84
|
+
return $false
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return $true
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function Install-VigthoriaCLI-NPM {
|
|
91
|
+
Write-Host ""
|
|
92
|
+
Write-Host "📦 Installing Vigthoria CLI..." -ForegroundColor Cyan
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
npm install -g $HOSTED_TARBALL_URL
|
|
96
|
+
Write-Host "✓ Vigthoria CLI installed successfully!" -ForegroundColor Green
|
|
97
|
+
return $true
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
Write-Host "⚠ Hosted package install failed, trying npm registry..." -ForegroundColor Yellow
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
npm install -g vigthoria-cli
|
|
105
|
+
Write-Host "✓ Vigthoria CLI installed successfully!" -ForegroundColor Green
|
|
106
|
+
return $true
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
Write-Host "⚠ npm registry install failed, trying git package..." -ForegroundColor Yellow
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
npm install -g $GIT_PACKAGE_URL
|
|
114
|
+
Write-Host "✓ Vigthoria CLI installed successfully from git package!" -ForegroundColor Green
|
|
115
|
+
return $true
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
Write-Host "✗ install failed (hosted package + npm registry + git package): $_" -ForegroundColor Red
|
|
119
|
+
return $false
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function Install-VigthoriaCLI-Direct {
|
|
124
|
+
Write-Host ""
|
|
125
|
+
Write-Host "📦 Installing Vigthoria CLI (Direct Download)..." -ForegroundColor Cyan
|
|
126
|
+
|
|
127
|
+
# Create install directory
|
|
128
|
+
if (-not (Test-Path $INSTALL_DIR)) {
|
|
129
|
+
New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
$tarballUrl = $HOSTED_TARBALL_URL
|
|
133
|
+
$tarballPath = "$INSTALL_DIR\vigthoria-cli.tgz"
|
|
134
|
+
$extractPath = "$INSTALL_DIR\cli"
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
# Download tarball
|
|
138
|
+
Write-Host " Downloading from $tarballUrl..." -ForegroundColor Gray
|
|
139
|
+
Invoke-WebRequest -Uri $tarballUrl -OutFile $tarballPath -UseBasicParsing
|
|
140
|
+
|
|
141
|
+
# Extract
|
|
142
|
+
Write-Host " Extracting..." -ForegroundColor Gray
|
|
143
|
+
if (Test-Path $extractPath) {
|
|
144
|
+
Remove-Item -Recurse -Force $extractPath
|
|
145
|
+
}
|
|
146
|
+
New-Item -ItemType Directory -Path $extractPath -Force | Out-Null
|
|
147
|
+
|
|
148
|
+
# Use tar to extract (available in Windows 10+)
|
|
149
|
+
tar -xzf $tarballPath -C $extractPath
|
|
150
|
+
|
|
151
|
+
# The tarball extracts to a 'package' folder
|
|
152
|
+
$packagePath = "$extractPath\package"
|
|
153
|
+
|
|
154
|
+
# Install dependencies
|
|
155
|
+
Write-Host " Installing dependencies..." -ForegroundColor Gray
|
|
156
|
+
Push-Location $packagePath
|
|
157
|
+
npm install --production
|
|
158
|
+
Pop-Location
|
|
159
|
+
|
|
160
|
+
# Create batch file wrapper
|
|
161
|
+
$batContent = @"
|
|
162
|
+
@echo off
|
|
163
|
+
node "$packagePath\dist\index.js" %*
|
|
164
|
+
"@
|
|
165
|
+
Set-Content -Path "$INSTALL_DIR\vigthoria.bat" -Value $batContent
|
|
166
|
+
Set-Content -Path "$INSTALL_DIR\vig.bat" -Value $batContent
|
|
167
|
+
|
|
168
|
+
# Add to PATH if not already there
|
|
169
|
+
$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
|
|
170
|
+
if ($userPath -notlike "*$INSTALL_DIR*") {
|
|
171
|
+
[Environment]::SetEnvironmentVariable("PATH", "$userPath;$INSTALL_DIR", "User")
|
|
172
|
+
Write-Host " Added $INSTALL_DIR to PATH" -ForegroundColor Gray
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# Cleanup
|
|
176
|
+
Remove-Item $tarballPath -Force
|
|
177
|
+
|
|
178
|
+
Write-Host "✓ Vigthoria CLI installed successfully!" -ForegroundColor Green
|
|
179
|
+
return $true
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
Write-Host "✗ Direct install failed: $_" -ForegroundColor Red
|
|
183
|
+
return $false
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function Install-VigthoriaCLI-GitHub {
|
|
188
|
+
Write-Host ""
|
|
189
|
+
Write-Host "📦 Installing Vigthoria CLI (from Vigthoria Market)..." -ForegroundColor Cyan
|
|
190
|
+
|
|
191
|
+
# Create install directory
|
|
192
|
+
if (-not (Test-Path $INSTALL_DIR)) {
|
|
193
|
+
New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
$clonePath = "$INSTALL_DIR\cli"
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
# Check if git is available
|
|
200
|
+
$gitVersion = git --version 2>$null
|
|
201
|
+
if (-not $gitVersion) {
|
|
202
|
+
Write-Host "✗ Git is not installed" -ForegroundColor Red
|
|
203
|
+
return $false
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# Clone repository
|
|
207
|
+
if (Test-Path $clonePath) {
|
|
208
|
+
Remove-Item -Recurse -Force $clonePath
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
Write-Host " Cloning from Vigthoria Market..." -ForegroundColor Gray
|
|
212
|
+
git clone --depth 1 https://market.vigthoria.io/vigthoria/vigthoria-cli.git $clonePath
|
|
213
|
+
|
|
214
|
+
# Install and build
|
|
215
|
+
Push-Location $clonePath
|
|
216
|
+
Write-Host " Installing dependencies..." -ForegroundColor Gray
|
|
217
|
+
npm install
|
|
218
|
+
Write-Host " Building..." -ForegroundColor Gray
|
|
219
|
+
npm run build
|
|
220
|
+
Write-Host " Linking globally..." -ForegroundColor Gray
|
|
221
|
+
npm link
|
|
222
|
+
Pop-Location
|
|
223
|
+
|
|
224
|
+
Write-Host "✓ Vigthoria CLI installed successfully!" -ForegroundColor Green
|
|
225
|
+
return $true
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
Write-Host "✗ GitHub install failed: $_" -ForegroundColor Red
|
|
229
|
+
return $false
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function Show-NetworkTroubleshooting {
|
|
234
|
+
Write-Host ""
|
|
235
|
+
Write-Host "═══════════════════════════════════════════════════════════════" -ForegroundColor Yellow
|
|
236
|
+
Write-Host " NETWORK TROUBLESHOOTING" -ForegroundColor Yellow
|
|
237
|
+
Write-Host "═══════════════════════════════════════════════════════════════" -ForegroundColor Yellow
|
|
238
|
+
Write-Host ""
|
|
239
|
+
Write-Host "The hosted package and npm registry could not be reached. Try these solutions:" -ForegroundColor White
|
|
240
|
+
Write-Host ""
|
|
241
|
+
Write-Host "1. Check your internet connection" -ForegroundColor Cyan
|
|
242
|
+
Write-Host " ping registry.npmjs.org" -ForegroundColor Gray
|
|
243
|
+
Write-Host ""
|
|
244
|
+
Write-Host "2. If behind a corporate proxy, configure npm:" -ForegroundColor Cyan
|
|
245
|
+
Write-Host " npm config set proxy http://proxy.company.com:8080" -ForegroundColor Gray
|
|
246
|
+
Write-Host " npm config set https-proxy http://proxy.company.com:8080" -ForegroundColor Gray
|
|
247
|
+
Write-Host ""
|
|
248
|
+
Write-Host "3. Try a different npm registry:" -ForegroundColor Cyan
|
|
249
|
+
Write-Host " npm config set registry https://registry.npmmirror.com" -ForegroundColor Gray
|
|
250
|
+
Write-Host ""
|
|
251
|
+
Write-Host "4. Check Windows Firewall/Antivirus settings" -ForegroundColor Cyan
|
|
252
|
+
Write-Host ""
|
|
253
|
+
Write-Host "5. Try using a VPN" -ForegroundColor Cyan
|
|
254
|
+
Write-Host ""
|
|
255
|
+
Write-Host "6. Manual download alternatives:" -ForegroundColor Cyan
|
|
256
|
+
Write-Host " Hosted package: $HOSTED_TARBALL_URL" -ForegroundColor Gray
|
|
257
|
+
Write-Host " npm package: https://www.npmjs.com/package/vigthoria-cli" -ForegroundColor Gray
|
|
258
|
+
Write-Host " git package: $GIT_PACKAGE_URL" -ForegroundColor Gray
|
|
259
|
+
Write-Host ""
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function Show-Success {
|
|
263
|
+
Write-Host ""
|
|
264
|
+
Write-Host "═══════════════════════════════════════════════════════════════" -ForegroundColor Green
|
|
265
|
+
Write-Host " INSTALLATION COMPLETE!" -ForegroundColor Green
|
|
266
|
+
Write-Host "═══════════════════════════════════════════════════════════════" -ForegroundColor Green
|
|
267
|
+
Write-Host ""
|
|
268
|
+
Write-Host " Get started:" -ForegroundColor White
|
|
269
|
+
Write-Host " 1. Open a new terminal" -ForegroundColor Gray
|
|
270
|
+
Write-Host " 2. Login: vigthoria login" -ForegroundColor Gray
|
|
271
|
+
Write-Host " 3. Start: vigthoria chat" -ForegroundColor Gray
|
|
272
|
+
Write-Host " Or local project start: npx vigthoria-chat" -ForegroundColor Gray
|
|
273
|
+
Write-Host ""
|
|
274
|
+
Write-Host " Commands:" -ForegroundColor White
|
|
275
|
+
Write-Host " vigthoria chat - Start AI chat" -ForegroundColor Gray
|
|
276
|
+
Write-Host " vigthoria-chat - Start chat directly" -ForegroundColor Gray
|
|
277
|
+
Write-Host " vigthoria edit - Edit files with AI" -ForegroundColor Gray
|
|
278
|
+
Write-Host " vigthoria repo - Manage cloud repos" -ForegroundColor Gray
|
|
279
|
+
Write-Host " vig --help - Show all commands" -ForegroundColor Gray
|
|
280
|
+
Write-Host ""
|
|
281
|
+
Write-Host " Documentation: https://docs.vigthoria.io/cli" -ForegroundColor Cyan
|
|
282
|
+
Write-Host ""
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# Main installation flow
|
|
286
|
+
Write-Banner
|
|
287
|
+
|
|
288
|
+
if (-not (Test-Prerequisites)) {
|
|
289
|
+
exit 1
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
$networkOk = Test-NetworkConnectivity
|
|
293
|
+
|
|
294
|
+
if ($networkOk) {
|
|
295
|
+
# Try npm install first
|
|
296
|
+
if (Install-VigthoriaCLI-NPM) {
|
|
297
|
+
Show-Success
|
|
298
|
+
exit 0
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
# Network failed or npm install failed
|
|
303
|
+
Show-NetworkTroubleshooting
|
|
304
|
+
|
|
305
|
+
Write-Host "Would you like to try alternative installation methods?" -ForegroundColor Yellow
|
|
306
|
+
$choice = Read-Host "Enter 'y' to try direct download, 'g' for git clone, or 'n' to exit"
|
|
307
|
+
|
|
308
|
+
switch ($choice.ToLower()) {
|
|
309
|
+
'y' {
|
|
310
|
+
if (Install-VigthoriaCLI-Direct) {
|
|
311
|
+
Show-Success
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
'g' {
|
|
315
|
+
if (Install-VigthoriaCLI-GitHub) {
|
|
316
|
+
Show-Success
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
default {
|
|
320
|
+
Write-Host "Installation cancelled." -ForegroundColor Gray
|
|
321
|
+
}
|
|
322
|
+
}
|