devwing 0.1.5 → 0.1.7
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/dist/index.js +213 -66
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import ora from 'ora';
|
|
|
10
10
|
import boxen from 'boxen';
|
|
11
11
|
import gradient from 'gradient-string';
|
|
12
12
|
import terminalLink from 'terminal-link';
|
|
13
|
-
import {
|
|
13
|
+
import { readFileSync, promises } from 'fs';
|
|
14
14
|
import path, { dirname, join } from 'path';
|
|
15
15
|
import os from 'os';
|
|
16
16
|
import { exec, execSync } from 'child_process';
|
|
@@ -72,16 +72,21 @@ var ConfigManager = class {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
|
-
* Get API URL
|
|
75
|
+
* Get API URL — ensures /api/v1 suffix is always present
|
|
76
76
|
*/
|
|
77
77
|
getApiUrl() {
|
|
78
|
-
|
|
78
|
+
let url = this.conf.get("apiUrl");
|
|
79
|
+
if (url && !url.endsWith("/api/v1") && url.includes("devwing.ai")) {
|
|
80
|
+
url = url.replace(/\/+$/, "") + "/api/v1";
|
|
81
|
+
this.conf.set("apiUrl", url);
|
|
82
|
+
}
|
|
83
|
+
return url;
|
|
79
84
|
}
|
|
80
85
|
/**
|
|
81
86
|
* Set API URL (for development/testing)
|
|
82
87
|
*/
|
|
83
88
|
setApiUrl(url) {
|
|
84
|
-
this.conf.set("apiUrl", url);
|
|
89
|
+
this.conf.set("apiUrl", url.replace(/\/+$/, ""));
|
|
85
90
|
}
|
|
86
91
|
/**
|
|
87
92
|
* Get current workspace ID
|
|
@@ -157,6 +162,11 @@ var ConfigManager = class {
|
|
|
157
162
|
}
|
|
158
163
|
};
|
|
159
164
|
var configManager = new ConfigManager();
|
|
165
|
+
function extractErrorMessage(data) {
|
|
166
|
+
if (!data) return "Unknown error";
|
|
167
|
+
if (typeof data === "string") return data;
|
|
168
|
+
return data.detail || data.message || data.error || JSON.stringify(data);
|
|
169
|
+
}
|
|
160
170
|
var APIClient = class {
|
|
161
171
|
client;
|
|
162
172
|
jwtToken = null;
|
|
@@ -180,9 +190,18 @@ var APIClient = class {
|
|
|
180
190
|
(response) => response,
|
|
181
191
|
(error) => {
|
|
182
192
|
if (error.response?.data) {
|
|
183
|
-
|
|
193
|
+
const message = extractErrorMessage(error.response.data);
|
|
194
|
+
const err = new Error(message);
|
|
195
|
+
err.statusCode = error.response.status;
|
|
196
|
+
throw err;
|
|
197
|
+
}
|
|
198
|
+
if (error.code === "ECONNREFUSED") {
|
|
199
|
+
throw new Error("Cannot connect to DevWing API. Is the server running?");
|
|
200
|
+
}
|
|
201
|
+
if (error.code === "ENOTFOUND") {
|
|
202
|
+
throw new Error("Cannot resolve DevWing API hostname. Check your internet connection.");
|
|
184
203
|
}
|
|
185
|
-
throw error;
|
|
204
|
+
throw new Error(error.message || "Network error");
|
|
186
205
|
}
|
|
187
206
|
);
|
|
188
207
|
}
|
|
@@ -194,11 +213,22 @@ var APIClient = class {
|
|
|
194
213
|
}
|
|
195
214
|
/**
|
|
196
215
|
* Login with email and password
|
|
216
|
+
* Returns full login response including 2FA handling
|
|
197
217
|
*/
|
|
198
218
|
async login(email, password) {
|
|
199
219
|
const response = await this.client.post("/auth/login", { email, password });
|
|
200
220
|
return response.data;
|
|
201
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Complete 2FA login
|
|
224
|
+
*/
|
|
225
|
+
async complete2FA(tempToken, totpCode) {
|
|
226
|
+
const response = await this.client.post("/auth/2fa/complete", {
|
|
227
|
+
temp_token: tempToken,
|
|
228
|
+
totp_code: totpCode
|
|
229
|
+
});
|
|
230
|
+
return response.data;
|
|
231
|
+
}
|
|
202
232
|
/**
|
|
203
233
|
* Get current user profile
|
|
204
234
|
*/
|
|
@@ -249,8 +279,13 @@ var APIClient = class {
|
|
|
249
279
|
body: JSON.stringify(request)
|
|
250
280
|
});
|
|
251
281
|
if (!response.ok) {
|
|
252
|
-
|
|
253
|
-
|
|
282
|
+
let errorMsg = `API error (${response.status})`;
|
|
283
|
+
try {
|
|
284
|
+
const error = await response.json();
|
|
285
|
+
errorMsg = extractErrorMessage(error);
|
|
286
|
+
} catch {
|
|
287
|
+
}
|
|
288
|
+
throw new Error(errorMsg);
|
|
254
289
|
}
|
|
255
290
|
if (!response.body) {
|
|
256
291
|
throw new Error("No response body");
|
|
@@ -261,8 +296,7 @@ var APIClient = class {
|
|
|
261
296
|
try {
|
|
262
297
|
const message = JSON.parse(event.data);
|
|
263
298
|
yield message;
|
|
264
|
-
} catch
|
|
265
|
-
console.error("Failed to parse SSE message:", error);
|
|
299
|
+
} catch {
|
|
266
300
|
}
|
|
267
301
|
}
|
|
268
302
|
}
|
|
@@ -288,8 +322,13 @@ var APIClient = class {
|
|
|
288
322
|
})
|
|
289
323
|
});
|
|
290
324
|
if (!response.ok) {
|
|
291
|
-
|
|
292
|
-
|
|
325
|
+
let errorMsg = `API error (${response.status})`;
|
|
326
|
+
try {
|
|
327
|
+
const error = await response.json();
|
|
328
|
+
errorMsg = extractErrorMessage(error);
|
|
329
|
+
} catch {
|
|
330
|
+
}
|
|
331
|
+
throw new Error(errorMsg);
|
|
293
332
|
}
|
|
294
333
|
if (!response.body) {
|
|
295
334
|
throw new Error("No response body");
|
|
@@ -300,18 +339,25 @@ var APIClient = class {
|
|
|
300
339
|
try {
|
|
301
340
|
const message = JSON.parse(event.data);
|
|
302
341
|
yield message;
|
|
303
|
-
} catch
|
|
304
|
-
console.error("Failed to parse SSE message:", error);
|
|
342
|
+
} catch {
|
|
305
343
|
}
|
|
306
344
|
}
|
|
307
345
|
}
|
|
308
346
|
}
|
|
309
347
|
/**
|
|
310
348
|
* Get project memory
|
|
349
|
+
* Backend returns { memories: [...], total: N }
|
|
311
350
|
*/
|
|
312
351
|
async getProjectMemory(projectId) {
|
|
313
352
|
const response = await this.client.get(`/memory/${projectId}`);
|
|
314
|
-
|
|
353
|
+
const data = response.data;
|
|
354
|
+
if (data.memories && Array.isArray(data.memories)) {
|
|
355
|
+
return data.memories;
|
|
356
|
+
}
|
|
357
|
+
if (Array.isArray(data)) {
|
|
358
|
+
return data;
|
|
359
|
+
}
|
|
360
|
+
return [];
|
|
315
361
|
}
|
|
316
362
|
/**
|
|
317
363
|
* Add project memory
|
|
@@ -331,7 +377,14 @@ var APIClient = class {
|
|
|
331
377
|
*/
|
|
332
378
|
async getModels() {
|
|
333
379
|
const response = await this.client.get("/models");
|
|
334
|
-
|
|
380
|
+
const data = response.data;
|
|
381
|
+
if (data.models && Array.isArray(data.models)) {
|
|
382
|
+
return data.models;
|
|
383
|
+
}
|
|
384
|
+
if (Array.isArray(data)) {
|
|
385
|
+
return data;
|
|
386
|
+
}
|
|
387
|
+
return [];
|
|
335
388
|
}
|
|
336
389
|
/**
|
|
337
390
|
* Get usage stats
|
|
@@ -467,6 +520,12 @@ var Logger = class {
|
|
|
467
520
|
}
|
|
468
521
|
};
|
|
469
522
|
var logger = new Logger();
|
|
523
|
+
function getUserPlan(user) {
|
|
524
|
+
return user.subscription_plan || user.plan || "free";
|
|
525
|
+
}
|
|
526
|
+
function getUserDisplayName(user) {
|
|
527
|
+
return user.full_name || user.username || user.email;
|
|
528
|
+
}
|
|
470
529
|
async function loginCommand() {
|
|
471
530
|
try {
|
|
472
531
|
const existingKey = await configManager.getApiKey();
|
|
@@ -492,8 +551,8 @@ async function loginCommand() {
|
|
|
492
551
|
name: "method",
|
|
493
552
|
message: "How would you like to authenticate?",
|
|
494
553
|
choices: [
|
|
495
|
-
{ name: "Email & Password", value: "email" },
|
|
496
554
|
{ name: "Via Browser (easiest)", value: "browser" },
|
|
555
|
+
{ name: "Email & Password", value: "email" },
|
|
497
556
|
{ name: "API Key (from dashboard)", value: "apikey" }
|
|
498
557
|
]
|
|
499
558
|
}
|
|
@@ -506,7 +565,10 @@ async function loginCommand() {
|
|
|
506
565
|
await loginWithApiKey();
|
|
507
566
|
}
|
|
508
567
|
} catch (error) {
|
|
509
|
-
logger.error("Login failed"
|
|
568
|
+
logger.error("Login failed");
|
|
569
|
+
if (error.message) {
|
|
570
|
+
logger.error(error.message);
|
|
571
|
+
}
|
|
510
572
|
process.exit(1);
|
|
511
573
|
}
|
|
512
574
|
}
|
|
@@ -517,7 +579,7 @@ async function loginWithEmail() {
|
|
|
517
579
|
name: "email",
|
|
518
580
|
message: "Email:",
|
|
519
581
|
validate: (input) => {
|
|
520
|
-
if (!input.includes("@")) {
|
|
582
|
+
if (!input.includes("@") || !input.includes(".")) {
|
|
521
583
|
return "Please enter a valid email address";
|
|
522
584
|
}
|
|
523
585
|
return true;
|
|
@@ -527,23 +589,56 @@ async function loginWithEmail() {
|
|
|
527
589
|
type: "password",
|
|
528
590
|
name: "password",
|
|
529
591
|
message: "Password:",
|
|
530
|
-
mask: "*"
|
|
592
|
+
mask: "*",
|
|
593
|
+
validate: (input) => {
|
|
594
|
+
if (!input || input.length < 1) {
|
|
595
|
+
return "Password is required";
|
|
596
|
+
}
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
531
599
|
}
|
|
532
600
|
]);
|
|
533
601
|
logger.startSpinner("Authenticating...");
|
|
534
602
|
try {
|
|
535
|
-
|
|
536
|
-
|
|
603
|
+
let loginResult = await apiClient.login(email, password);
|
|
604
|
+
if (loginResult.requires_2fa) {
|
|
605
|
+
logger.succeedSpinner("Credentials verified");
|
|
606
|
+
logger.info("Two-factor authentication is enabled on your account");
|
|
607
|
+
const { totpCode } = await inquirer.prompt([
|
|
608
|
+
{
|
|
609
|
+
type: "input",
|
|
610
|
+
name: "totpCode",
|
|
611
|
+
message: "2FA Code (from authenticator app):",
|
|
612
|
+
validate: (input) => {
|
|
613
|
+
if (!/^\d{6}$/.test(input)) {
|
|
614
|
+
return "Please enter a 6-digit code";
|
|
615
|
+
}
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
]);
|
|
620
|
+
logger.startSpinner("Verifying 2FA code...");
|
|
621
|
+
loginResult = await apiClient.complete2FA(loginResult.temp_token, totpCode);
|
|
622
|
+
}
|
|
623
|
+
if (!loginResult.access_token || !loginResult.user) {
|
|
624
|
+
logger.failSpinner("Login failed");
|
|
625
|
+
logger.error("Unexpected response from server");
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
apiClient.setJwtToken(loginResult.access_token);
|
|
537
629
|
logger.updateSpinner("Creating API key...");
|
|
538
|
-
const
|
|
630
|
+
const apiKeyResult = await apiClient.createApiKey("CLI - Auto-generated");
|
|
539
631
|
apiClient.setJwtToken(null);
|
|
540
|
-
await configManager.setApiKey(key);
|
|
541
|
-
|
|
542
|
-
|
|
632
|
+
await configManager.setApiKey(apiKeyResult.key);
|
|
633
|
+
const user = loginResult.user;
|
|
634
|
+
const plan = getUserPlan(user);
|
|
635
|
+
logger.succeedSpinner(`Welcome back, ${getUserDisplayName(user)}!`);
|
|
636
|
+
logger.success(`Plan: ${plan.toUpperCase()}`);
|
|
543
637
|
logger.newline();
|
|
544
638
|
showQuickStart();
|
|
545
639
|
} catch (error) {
|
|
546
640
|
logger.failSpinner("Authentication failed");
|
|
641
|
+
logger.error(error.message || "Unknown error occurred");
|
|
547
642
|
throw error;
|
|
548
643
|
}
|
|
549
644
|
}
|
|
@@ -552,21 +647,23 @@ async function loginWithBrowser() {
|
|
|
552
647
|
const open = await import('open');
|
|
553
648
|
logger.startSpinner("Initiating browser authentication...");
|
|
554
649
|
try {
|
|
555
|
-
const deviceInfo = `DevWing CLI on ${os2.platform()}`;
|
|
650
|
+
const deviceInfo = `DevWing CLI on ${os2.platform()} (${os2.hostname()})`;
|
|
556
651
|
const initResponse = await apiClient.initiateCliAuth(deviceInfo);
|
|
557
|
-
logger.succeedSpinner("
|
|
652
|
+
logger.succeedSpinner("Device code created");
|
|
558
653
|
logger.newline();
|
|
654
|
+
const verifyUrl = initResponse.verification_url_complete;
|
|
559
655
|
try {
|
|
560
|
-
await open.default(
|
|
561
|
-
logger.success("Browser opened! Please log in to
|
|
562
|
-
} catch
|
|
563
|
-
logger.warn("Could not open browser automatically");
|
|
564
|
-
logger.info(`Please visit: ${chalk3.cyan(
|
|
656
|
+
await open.default(verifyUrl);
|
|
657
|
+
logger.success("Browser opened! Please log in to authorize this device.");
|
|
658
|
+
} catch {
|
|
659
|
+
logger.warn("Could not open browser automatically.");
|
|
660
|
+
logger.info(`Please visit: ${chalk3.cyan(verifyUrl)}`);
|
|
565
661
|
}
|
|
566
662
|
logger.newline();
|
|
663
|
+
logger.info(chalk3.dim(`Device code: ${initResponse.user_code.substring(0, 8)}...`));
|
|
567
664
|
logger.startSpinner("Waiting for authorization...");
|
|
568
|
-
const pollInterval = initResponse.interval * 1e3;
|
|
569
|
-
const maxAttempts = Math.floor(initResponse.expires_in / initResponse.interval);
|
|
665
|
+
const pollInterval = (initResponse.interval || 5) * 1e3;
|
|
666
|
+
const maxAttempts = Math.floor((initResponse.expires_in || 900) / (initResponse.interval || 5));
|
|
570
667
|
let attempts = 0;
|
|
571
668
|
const pollForAuth = async () => {
|
|
572
669
|
return new Promise((resolve, reject) => {
|
|
@@ -574,13 +671,11 @@ async function loginWithBrowser() {
|
|
|
574
671
|
attempts++;
|
|
575
672
|
if (attempts >= maxAttempts) {
|
|
576
673
|
clearInterval(interval);
|
|
577
|
-
reject(new Error(
|
|
674
|
+
reject(new Error('Authentication timed out. Please try again with "devwing login".'));
|
|
578
675
|
return;
|
|
579
676
|
}
|
|
580
677
|
try {
|
|
581
|
-
const pollResponse = await apiClient.pollCliAuth(
|
|
582
|
-
initResponse.device_code
|
|
583
|
-
);
|
|
678
|
+
const pollResponse = await apiClient.pollCliAuth(initResponse.device_code);
|
|
584
679
|
if (pollResponse.status === "authorized" && pollResponse.api_key) {
|
|
585
680
|
clearInterval(interval);
|
|
586
681
|
resolve(pollResponse.api_key);
|
|
@@ -589,11 +684,12 @@ async function loginWithBrowser() {
|
|
|
589
684
|
reject(new Error("Authorization denied by user."));
|
|
590
685
|
} else if (pollResponse.status === "expired") {
|
|
591
686
|
clearInterval(interval);
|
|
592
|
-
reject(
|
|
593
|
-
new Error('Device code expired. Please run "devwing login" again.')
|
|
594
|
-
);
|
|
687
|
+
reject(new Error('Device code expired. Please run "devwing login" again.'));
|
|
595
688
|
}
|
|
596
689
|
} catch (error) {
|
|
690
|
+
if (error.statusCode === 429) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
597
693
|
clearInterval(interval);
|
|
598
694
|
reject(error);
|
|
599
695
|
}
|
|
@@ -604,12 +700,14 @@ async function loginWithBrowser() {
|
|
|
604
700
|
await configManager.setApiKey(apiKey);
|
|
605
701
|
logger.updateSpinner("Fetching profile...");
|
|
606
702
|
const user = await apiClient.getProfile();
|
|
607
|
-
|
|
608
|
-
logger.
|
|
703
|
+
const plan = getUserPlan(user);
|
|
704
|
+
logger.succeedSpinner(`Welcome, ${getUserDisplayName(user)}!`);
|
|
705
|
+
logger.success(`Plan: ${plan.toUpperCase()}`);
|
|
609
706
|
logger.newline();
|
|
610
707
|
showQuickStart();
|
|
611
708
|
} catch (error) {
|
|
612
709
|
logger.failSpinner("Browser authentication failed");
|
|
710
|
+
logger.error(error.message || "Unknown error");
|
|
613
711
|
throw error;
|
|
614
712
|
}
|
|
615
713
|
}
|
|
@@ -642,13 +740,15 @@ async function loginWithApiKey() {
|
|
|
642
740
|
try {
|
|
643
741
|
await configManager.setApiKey(apiKey);
|
|
644
742
|
const user = await apiClient.getProfile();
|
|
645
|
-
|
|
646
|
-
logger.
|
|
743
|
+
const plan = getUserPlan(user);
|
|
744
|
+
logger.succeedSpinner(`Authenticated as ${getUserDisplayName(user)}!`);
|
|
745
|
+
logger.success(`Plan: ${plan.toUpperCase()}`);
|
|
647
746
|
logger.newline();
|
|
648
747
|
showQuickStart();
|
|
649
748
|
} catch (error) {
|
|
650
749
|
await configManager.deleteApiKey();
|
|
651
750
|
logger.failSpinner("Invalid API key");
|
|
751
|
+
logger.error(error.message || "Could not verify API key");
|
|
652
752
|
throw error;
|
|
653
753
|
}
|
|
654
754
|
}
|
|
@@ -677,7 +777,8 @@ async function logoutCommand() {
|
|
|
677
777
|
configManager.setApiUrl(apiUrl);
|
|
678
778
|
logger.success("Successfully logged out");
|
|
679
779
|
} catch (error) {
|
|
680
|
-
logger.error("Logout failed"
|
|
780
|
+
logger.error("Logout failed");
|
|
781
|
+
logger.error(error.message || "Unknown error");
|
|
681
782
|
process.exit(1);
|
|
682
783
|
}
|
|
683
784
|
}
|
|
@@ -692,15 +793,19 @@ async function statusCommand() {
|
|
|
692
793
|
logger.startSpinner("Fetching profile...");
|
|
693
794
|
try {
|
|
694
795
|
const user = await apiClient.getProfile();
|
|
796
|
+
const plan = getUserPlan(user);
|
|
695
797
|
logger.succeedSpinner("Authenticated");
|
|
696
798
|
logger.newline();
|
|
697
799
|
console.log(chalk3.bold("User Profile:"));
|
|
698
800
|
console.log(` Email: ${user.email}`);
|
|
699
|
-
console.log(` Name: ${user
|
|
700
|
-
console.log(` Plan: ${
|
|
701
|
-
|
|
702
|
-
`
|
|
703
|
-
|
|
801
|
+
console.log(` Name: ${getUserDisplayName(user)}`);
|
|
802
|
+
console.log(` Plan: ${plan.toUpperCase()}`);
|
|
803
|
+
if (user.is_verified !== void 0) {
|
|
804
|
+
console.log(` Verified: ${user.is_verified ? chalk3.green("Yes") : chalk3.yellow("No")}`);
|
|
805
|
+
}
|
|
806
|
+
if (user.totp_enabled !== void 0) {
|
|
807
|
+
console.log(` 2FA: ${user.totp_enabled ? chalk3.green("Enabled") : chalk3.dim("Disabled")}`);
|
|
808
|
+
}
|
|
704
809
|
const config = configManager.getAll();
|
|
705
810
|
if (config.workspaceId) {
|
|
706
811
|
console.log(` Workspace: ${config.workspaceId}`);
|
|
@@ -711,11 +816,12 @@ async function statusCommand() {
|
|
|
711
816
|
logger.newline();
|
|
712
817
|
} catch (error) {
|
|
713
818
|
logger.failSpinner("Failed to fetch profile");
|
|
714
|
-
logger.error("API key may be invalid or expired");
|
|
819
|
+
logger.error(error.message || "API key may be invalid or expired");
|
|
715
820
|
logger.info('Run "devwing login" to re-authenticate');
|
|
716
821
|
}
|
|
717
822
|
} catch (error) {
|
|
718
|
-
logger.error("Failed to check status"
|
|
823
|
+
logger.error("Failed to check status");
|
|
824
|
+
logger.error(error.message || "Unknown error");
|
|
719
825
|
process.exit(1);
|
|
720
826
|
}
|
|
721
827
|
}
|
|
@@ -725,6 +831,7 @@ function showQuickStart() {
|
|
|
725
831
|
"Quick Start:",
|
|
726
832
|
"",
|
|
727
833
|
' devwing "fix the auth bug" Ask anything',
|
|
834
|
+
" devwing chat Interactive chat mode",
|
|
728
835
|
" devwing scan Security scan",
|
|
729
836
|
" devwing review Code review",
|
|
730
837
|
" devwing explain file.js Explain code",
|
|
@@ -2115,9 +2222,21 @@ var __filename2 = fileURLToPath(import.meta.url);
|
|
|
2115
2222
|
var __dirname2 = dirname(__filename2);
|
|
2116
2223
|
function getCurrentVersion() {
|
|
2117
2224
|
try {
|
|
2118
|
-
const
|
|
2119
|
-
|
|
2120
|
-
|
|
2225
|
+
const possiblePaths = [
|
|
2226
|
+
join(__dirname2, "../package.json"),
|
|
2227
|
+
// Production: dist/ -> package.json
|
|
2228
|
+
join(__dirname2, "../../package.json")
|
|
2229
|
+
// Development: src/commands/ -> package.json
|
|
2230
|
+
];
|
|
2231
|
+
for (const packageJsonPath of possiblePaths) {
|
|
2232
|
+
try {
|
|
2233
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
2234
|
+
return packageJson.version;
|
|
2235
|
+
} catch {
|
|
2236
|
+
continue;
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
throw new Error("package.json not found in any expected location");
|
|
2121
2240
|
} catch (error) {
|
|
2122
2241
|
logger.error("Failed to read package version", error);
|
|
2123
2242
|
return "0.1.0";
|
|
@@ -2125,12 +2244,24 @@ function getCurrentVersion() {
|
|
|
2125
2244
|
}
|
|
2126
2245
|
function getPackageName() {
|
|
2127
2246
|
try {
|
|
2128
|
-
const
|
|
2129
|
-
|
|
2130
|
-
|
|
2247
|
+
const possiblePaths = [
|
|
2248
|
+
join(__dirname2, "../package.json"),
|
|
2249
|
+
// Production: dist/ -> package.json
|
|
2250
|
+
join(__dirname2, "../../package.json")
|
|
2251
|
+
// Development: src/commands/ -> package.json
|
|
2252
|
+
];
|
|
2253
|
+
for (const packageJsonPath of possiblePaths) {
|
|
2254
|
+
try {
|
|
2255
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
2256
|
+
return packageJson.name;
|
|
2257
|
+
} catch {
|
|
2258
|
+
continue;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
throw new Error("package.json not found in any expected location");
|
|
2131
2262
|
} catch (error) {
|
|
2132
2263
|
logger.error("Failed to read package name", error);
|
|
2133
|
-
return "
|
|
2264
|
+
return "devwing";
|
|
2134
2265
|
}
|
|
2135
2266
|
}
|
|
2136
2267
|
async function getLatestVersion(packageName) {
|
|
@@ -2254,10 +2385,24 @@ async function updateCommand(options) {
|
|
|
2254
2385
|
process.exit(1);
|
|
2255
2386
|
}
|
|
2256
2387
|
}
|
|
2257
|
-
|
|
2258
|
-
// src/index.ts
|
|
2259
2388
|
var program = new Command();
|
|
2260
|
-
var
|
|
2389
|
+
var __cliFilename = fileURLToPath(import.meta.url);
|
|
2390
|
+
var __cliDirname = dirname(__cliFilename);
|
|
2391
|
+
function getVersion() {
|
|
2392
|
+
const paths = [
|
|
2393
|
+
join(__cliDirname, "../package.json"),
|
|
2394
|
+
join(__cliDirname, "../../package.json")
|
|
2395
|
+
];
|
|
2396
|
+
for (const p of paths) {
|
|
2397
|
+
try {
|
|
2398
|
+
return JSON.parse(readFileSync(p, "utf-8")).version;
|
|
2399
|
+
} catch {
|
|
2400
|
+
continue;
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
return "0.1.6";
|
|
2404
|
+
}
|
|
2405
|
+
var VERSION = getVersion();
|
|
2261
2406
|
program.name("devwing").description("DevWing.ai - Your AI Wingman in the Terminal").version(VERSION, "-v, --version", "Display version number").helpOption("-h, --help", "Display help information");
|
|
2262
2407
|
program.option("--mode <mode>", "AI mode: general|frontend|backend|security|devops").option("--model <model>", "Specific model to use").option("--project <id>", "Project ID").option("--workspace <id>", "Workspace ID").option("--verbose", "Verbose output for debugging").option("-y, --yes", "Auto-confirm destructive actions");
|
|
2263
2408
|
program.command("login").description("Authenticate with DevWing").action(async () => {
|
|
@@ -2324,10 +2469,12 @@ program.exitOverride();
|
|
|
2324
2469
|
try {
|
|
2325
2470
|
await program.parseAsync(process.argv);
|
|
2326
2471
|
} catch (error) {
|
|
2327
|
-
if (error.code === "commander.help" || error.code === "commander.version") {
|
|
2472
|
+
if (error.code === "commander.help" || error.code === "commander.helpDisplayed" || error.code === "commander.version") {
|
|
2328
2473
|
process.exit(0);
|
|
2329
2474
|
}
|
|
2330
|
-
|
|
2475
|
+
if (error.message && !error.message.includes("process.exit")) {
|
|
2476
|
+
logger.error(error.message);
|
|
2477
|
+
}
|
|
2331
2478
|
if (process.env.DEBUG) {
|
|
2332
2479
|
console.error(error);
|
|
2333
2480
|
}
|