devwing 0.1.22 → 0.1.24
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 +326 -416
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22,7 +22,6 @@ import util from 'util';
|
|
|
22
22
|
import Table3 from 'cli-table3';
|
|
23
23
|
import { fileURLToPath } from 'url';
|
|
24
24
|
import semver from 'semver';
|
|
25
|
-
import readline from 'readline';
|
|
26
25
|
|
|
27
26
|
var SERVICE_NAME = "DevWing CLI";
|
|
28
27
|
var API_KEY_ACCOUNT = "api-key";
|
|
@@ -2772,7 +2771,7 @@ function isDemoMode() {
|
|
|
2772
2771
|
}
|
|
2773
2772
|
var __sessionFile = fileURLToPath(import.meta.url);
|
|
2774
2773
|
var __sessionDir = dirname(__sessionFile);
|
|
2775
|
-
var sleep2 = (ms) => new Promise((
|
|
2774
|
+
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2776
2775
|
function getVersion() {
|
|
2777
2776
|
const paths = [
|
|
2778
2777
|
join(__sessionDir, "../package.json"),
|
|
@@ -2787,10 +2786,10 @@ function getVersion() {
|
|
|
2787
2786
|
continue;
|
|
2788
2787
|
}
|
|
2789
2788
|
}
|
|
2790
|
-
return "0.1.
|
|
2789
|
+
return "0.1.24";
|
|
2791
2790
|
}
|
|
2792
2791
|
var DEMO_USER2 = {
|
|
2793
|
-
id: "usr_a1b2c3d4
|
|
2792
|
+
id: "usr_a1b2c3d4",
|
|
2794
2793
|
email: "aliyu@devwing.ai",
|
|
2795
2794
|
full_name: "Aliyu Mohammed",
|
|
2796
2795
|
subscription_plan: "pro",
|
|
@@ -2801,18 +2800,16 @@ var DEMO_USER2 = {
|
|
|
2801
2800
|
tokens_used_today: 47832,
|
|
2802
2801
|
tokens_reset_at: "2026-04-10T00:00:00Z"
|
|
2803
2802
|
};
|
|
2804
|
-
var DEMO_WORKSPACE2 = {
|
|
2805
|
-
|
|
2806
|
-
var DEMO_PROJECT2 = {
|
|
2807
|
-
name: "Tallon SaaS ERP"};
|
|
2803
|
+
var DEMO_WORKSPACE2 = { name: "Kano State Government"};
|
|
2804
|
+
var DEMO_PROJECT2 = { name: "Tallon SaaS ERP"};
|
|
2808
2805
|
var DEMO_MODELS = [
|
|
2809
|
-
{ name: "devwing-general-1", display_name: "DevWing General", domain: "general", status: "active", min_plan: "free", context_window: 32768, tokens_per_sec: 85
|
|
2810
|
-
{ name: "devwing-frontend-1", display_name: "DevWing Frontend", domain: "frontend", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 78
|
|
2811
|
-
{ name: "devwing-backend-1", display_name: "DevWing Backend", domain: "backend", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 82
|
|
2812
|
-
{ name: "devwing-security-1", display_name: "DevWing Security", domain: "security", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 74
|
|
2813
|
-
{ name: "devwing-devops-1", display_name: "DevWing DevOps", domain: "devops", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 70
|
|
2814
|
-
{ name: "devwing-mobile-1", display_name: "DevWing Mobile", domain: "mobile", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 72
|
|
2815
|
-
{ name: "devwing-data-1", display_name: "DevWing Data", domain: "data", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 68
|
|
2806
|
+
{ name: "devwing-general-1", display_name: "DevWing General", domain: "general", status: "active", min_plan: "free", context_window: 32768, tokens_per_sec: 85 },
|
|
2807
|
+
{ name: "devwing-frontend-1", display_name: "DevWing Frontend", domain: "frontend", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 78 },
|
|
2808
|
+
{ name: "devwing-backend-1", display_name: "DevWing Backend", domain: "backend", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 82 },
|
|
2809
|
+
{ name: "devwing-security-1", display_name: "DevWing Security", domain: "security", status: "active", min_plan: "pro", context_window: 32768, tokens_per_sec: 74 },
|
|
2810
|
+
{ name: "devwing-devops-1", display_name: "DevWing DevOps", domain: "devops", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 70 },
|
|
2811
|
+
{ name: "devwing-mobile-1", display_name: "DevWing Mobile", domain: "mobile", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 72 },
|
|
2812
|
+
{ name: "devwing-data-1", display_name: "DevWing Data", domain: "data", status: "beta", min_plan: "pro", context_window: 32768, tokens_per_sec: 68 }
|
|
2816
2813
|
];
|
|
2817
2814
|
var DEMO_PLANS = [
|
|
2818
2815
|
{ name: "Free", price: "$0/mo", tokens_day: "50,000", models: "General only", projects: "1", seats: "1" },
|
|
@@ -2825,6 +2822,65 @@ var DEMO_USAGE = {
|
|
|
2825
2822
|
week: { requests: 147, tokens_in: 198340, tokens_out: 102890, cost: 0.7213 },
|
|
2826
2823
|
month: { requests: 612, tokens_in: 824100, tokens_out: 428750, cost: 3.0012 }
|
|
2827
2824
|
};
|
|
2825
|
+
function readLine() {
|
|
2826
|
+
return new Promise((resolve) => {
|
|
2827
|
+
try {
|
|
2828
|
+
if (typeof process.stdin.setRawMode === "function") {
|
|
2829
|
+
process.stdin.setRawMode(false);
|
|
2830
|
+
}
|
|
2831
|
+
} catch {
|
|
2832
|
+
}
|
|
2833
|
+
process.stdin.resume();
|
|
2834
|
+
process.stdin.setEncoding("utf8");
|
|
2835
|
+
let buf = "";
|
|
2836
|
+
const onData = (chunk) => {
|
|
2837
|
+
for (const ch of chunk) {
|
|
2838
|
+
if (ch === "\n" || ch === "\r") {
|
|
2839
|
+
cleanup();
|
|
2840
|
+
resolve(buf);
|
|
2841
|
+
return;
|
|
2842
|
+
} else if (ch === "") {
|
|
2843
|
+
cleanup();
|
|
2844
|
+
resolve("");
|
|
2845
|
+
return;
|
|
2846
|
+
} else if (ch === "\x7F" || ch === "\b") {
|
|
2847
|
+
if (buf.length > 0) {
|
|
2848
|
+
buf = buf.slice(0, -1);
|
|
2849
|
+
process.stdout.write("\b \b");
|
|
2850
|
+
}
|
|
2851
|
+
} else if (ch >= " ") {
|
|
2852
|
+
buf += ch;
|
|
2853
|
+
process.stdout.write(ch);
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
};
|
|
2857
|
+
const onEnd = () => {
|
|
2858
|
+
cleanup();
|
|
2859
|
+
resolve("");
|
|
2860
|
+
};
|
|
2861
|
+
const cleanup = () => {
|
|
2862
|
+
process.stdin.removeListener("data", onData);
|
|
2863
|
+
process.stdin.removeListener("end", onEnd);
|
|
2864
|
+
process.stdin.pause();
|
|
2865
|
+
};
|
|
2866
|
+
process.stdin.on("data", onData);
|
|
2867
|
+
process.stdin.once("end", onEnd);
|
|
2868
|
+
});
|
|
2869
|
+
}
|
|
2870
|
+
function restoreStdin() {
|
|
2871
|
+
try {
|
|
2872
|
+
if (typeof process.stdin.setRawMode === "function") {
|
|
2873
|
+
try {
|
|
2874
|
+
process.stdin.setRawMode(false);
|
|
2875
|
+
} catch {
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
process.stdin.removeAllListeners("keypress");
|
|
2879
|
+
process.stdin.removeAllListeners("data");
|
|
2880
|
+
if (process.stdin.isPaused()) process.stdin.resume();
|
|
2881
|
+
} catch {
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2828
2884
|
var InteractiveSession = class {
|
|
2829
2885
|
isAuthenticated = false;
|
|
2830
2886
|
userProfile = null;
|
|
@@ -2835,12 +2891,10 @@ var InteractiveSession = class {
|
|
|
2835
2891
|
sessionId = null;
|
|
2836
2892
|
isDemo;
|
|
2837
2893
|
options;
|
|
2838
|
-
rl;
|
|
2839
2894
|
commands = /* @__PURE__ */ new Map();
|
|
2840
|
-
sigintCount = 0;
|
|
2841
|
-
sigintTimer = null;
|
|
2842
2895
|
version;
|
|
2843
|
-
|
|
2896
|
+
running = true;
|
|
2897
|
+
ctrlCCount = 0;
|
|
2844
2898
|
constructor(options) {
|
|
2845
2899
|
this.options = options;
|
|
2846
2900
|
this.isDemo = isDemoMode();
|
|
@@ -2849,109 +2903,48 @@ var InteractiveSession = class {
|
|
|
2849
2903
|
if (options.model) this.currentModel = options.model;
|
|
2850
2904
|
this.registerCommands();
|
|
2851
2905
|
}
|
|
2852
|
-
//
|
|
2853
|
-
// START
|
|
2854
|
-
// ============================================================
|
|
2906
|
+
// ─── START ───────────────────────────────────────────────
|
|
2855
2907
|
async start() {
|
|
2856
2908
|
console.clear();
|
|
2857
|
-
this.
|
|
2858
|
-
await this.
|
|
2909
|
+
this.printBanner();
|
|
2910
|
+
await this.checkAuth();
|
|
2859
2911
|
this.printSystemInfo();
|
|
2860
|
-
this.createReadline();
|
|
2861
2912
|
console.log();
|
|
2862
|
-
this.
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
return;
|
|
2878
|
-
}
|
|
2879
|
-
this.sigintCount++;
|
|
2880
|
-
if (this.sigintCount >= 2) {
|
|
2881
|
-
this.exitSession();
|
|
2882
|
-
return;
|
|
2913
|
+
while (this.running) {
|
|
2914
|
+
this.printPrompt();
|
|
2915
|
+
const input = await readLine();
|
|
2916
|
+
process.stdout.write("\n");
|
|
2917
|
+
if (input === "") {
|
|
2918
|
+
this.ctrlCCount++;
|
|
2919
|
+
if (this.ctrlCCount >= 2) {
|
|
2920
|
+
this.exit();
|
|
2921
|
+
return;
|
|
2922
|
+
}
|
|
2923
|
+
console.log(chalk7.dim(" Press Ctrl+C again to exit, or type /exit"));
|
|
2924
|
+
setTimeout(() => {
|
|
2925
|
+
this.ctrlCCount = 0;
|
|
2926
|
+
}, 1500);
|
|
2927
|
+
continue;
|
|
2883
2928
|
}
|
|
2884
|
-
|
|
2885
|
-
this.sigintTimer = setTimeout(() => {
|
|
2886
|
-
this.sigintCount = 0;
|
|
2887
|
-
}, 1500);
|
|
2888
|
-
this.prompt();
|
|
2889
|
-
});
|
|
2890
|
-
this.rl.on("line", async (input) => {
|
|
2891
|
-
if (this.isRunningCommand) return;
|
|
2929
|
+
this.ctrlCCount = 0;
|
|
2892
2930
|
const trimmed = input.trim();
|
|
2893
|
-
if (!trimmed)
|
|
2894
|
-
this.prompt();
|
|
2895
|
-
return;
|
|
2896
|
-
}
|
|
2897
|
-
this.isRunningCommand = true;
|
|
2898
|
-
this.rl.pause();
|
|
2931
|
+
if (!trimmed) continue;
|
|
2899
2932
|
try {
|
|
2900
2933
|
if (trimmed.startsWith("/")) {
|
|
2901
|
-
await this.
|
|
2934
|
+
await this.handleSlash(trimmed);
|
|
2902
2935
|
} else {
|
|
2903
|
-
await this.
|
|
2936
|
+
await this.handlePrompt(trimmed);
|
|
2904
2937
|
}
|
|
2905
|
-
} catch (
|
|
2906
|
-
logger.error(
|
|
2938
|
+
} catch (err) {
|
|
2939
|
+
logger.error(err.message || "An error occurred");
|
|
2907
2940
|
}
|
|
2908
|
-
|
|
2909
|
-
await sleep2(
|
|
2910
|
-
this.isRunningCommand = false;
|
|
2911
|
-
this.rl.resume();
|
|
2912
|
-
this.prompt();
|
|
2913
|
-
});
|
|
2914
|
-
this.rl.on("close", () => {
|
|
2915
|
-
if (!this.isRunningCommand) {
|
|
2916
|
-
this.exitSession();
|
|
2917
|
-
}
|
|
2918
|
-
});
|
|
2919
|
-
}
|
|
2920
|
-
// Pause readline, run fn (which may use inquirer), restore
|
|
2921
|
-
async withInquirer(fn) {
|
|
2922
|
-
this.rl.pause();
|
|
2923
|
-
try {
|
|
2924
|
-
return await fn();
|
|
2925
|
-
} finally {
|
|
2926
|
-
this.restoreStdin();
|
|
2927
|
-
await sleep2(80);
|
|
2928
|
-
this.rl.resume();
|
|
2941
|
+
restoreStdin();
|
|
2942
|
+
await sleep2(50);
|
|
2929
2943
|
}
|
|
2930
2944
|
}
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
try {
|
|
2935
|
-
process.stdin.setRawMode(false);
|
|
2936
|
-
} catch {
|
|
2937
|
-
}
|
|
2938
|
-
}
|
|
2939
|
-
process.stdin.removeAllListeners("keypress");
|
|
2940
|
-
process.stdin.removeAllListeners("data");
|
|
2941
|
-
if (process.stdin.isPaused()) process.stdin.resume();
|
|
2942
|
-
} catch {
|
|
2943
|
-
}
|
|
2944
|
-
}
|
|
2945
|
-
prompt() {
|
|
2946
|
-
const p = this.buildPrompt();
|
|
2947
|
-
process.stdout.write(p);
|
|
2948
|
-
this.rl.setPrompt(p);
|
|
2949
|
-
}
|
|
2950
|
-
// ============================================================
|
|
2951
|
-
// STARTUP UI — Claude Code style
|
|
2952
|
-
// ============================================================
|
|
2953
|
-
printStartupBanner() {
|
|
2954
|
-
const banner = gradient.pastel.multiline([
|
|
2945
|
+
// ─── BANNER ──────────────────────────────────────────────
|
|
2946
|
+
printBanner() {
|
|
2947
|
+
const art = gradient.pastel.multiline([
|
|
2955
2948
|
"",
|
|
2956
2949
|
" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
|
|
2957
2950
|
" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ",
|
|
@@ -2961,102 +2954,81 @@ var InteractiveSession = class {
|
|
|
2961
2954
|
" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D ",
|
|
2962
2955
|
""
|
|
2963
2956
|
].join("\n"));
|
|
2964
|
-
console.log(
|
|
2957
|
+
console.log(art);
|
|
2965
2958
|
console.log(
|
|
2966
|
-
chalk7.dim(" Your AI Wingman in the Terminal") +
|
|
2959
|
+
chalk7.dim(" Your AI Wingman in the Terminal") + " " + chalk7.dim(`v${this.version}`)
|
|
2967
2960
|
);
|
|
2968
2961
|
console.log();
|
|
2969
2962
|
}
|
|
2963
|
+
// ─── SYSTEM INFO PANEL ───────────────────────────────────
|
|
2970
2964
|
printSystemInfo() {
|
|
2971
|
-
const W =
|
|
2972
|
-
const
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
const labelPart2 = chalk7.dim("\u2502 ") + chalk7.bold(label.padEnd(11)) + chalk7.dim(" \u2502 ");
|
|
2977
|
-
const rawValue = value.replace(/\x1B\[[0-9;]*m/g, "");
|
|
2978
|
-
const padLen2 = W - 4 - 11 - 3 - rawValue.length;
|
|
2979
|
-
return labelPart2 + value + " ".repeat(Math.max(0, padLen2)) + chalk7.dim(" \u2502");
|
|
2965
|
+
const W = 62;
|
|
2966
|
+
const ln = (s = "") => {
|
|
2967
|
+
const raw = s.replace(/\x1B\[[0-9;]*m/g, "");
|
|
2968
|
+
const pad = W - 4 - raw.length;
|
|
2969
|
+
return chalk7.dim("\u2502 ") + s + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
|
|
2980
2970
|
};
|
|
2971
|
+
const div = chalk7.dim("\u251C" + "\u2500".repeat(W - 2) + "\u2524");
|
|
2972
|
+
const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
|
|
2973
|
+
const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
|
|
2974
|
+
const kv = (k, v) => ln(chalk7.bold(k.padEnd(12)) + v);
|
|
2981
2975
|
console.log(top);
|
|
2982
2976
|
if (this.isAuthenticated && this.userProfile) {
|
|
2983
2977
|
const name = this.userProfile.full_name || this.userProfile.email;
|
|
2984
2978
|
const plan = (this.userProfile.subscription_plan || "free").toUpperCase();
|
|
2985
|
-
const
|
|
2986
|
-
console.log(
|
|
2979
|
+
const badge = plan === "PRO" ? chalk7.bgCyan.black(` ${plan} `) : plan === "ENTERPRISE" ? chalk7.bgMagenta.white(` ${plan} `) : plan === "TEAM" ? chalk7.bgYellow.black(` ${plan} `) : chalk7.bgGray.white(` ${plan} `);
|
|
2980
|
+
console.log(ln(chalk7.bold("Auth".padEnd(12)) + chalk7.green("\u25CF ") + chalk7.white(name) + " " + badge));
|
|
2987
2981
|
} else {
|
|
2988
|
-
console.log(
|
|
2982
|
+
console.log(ln(chalk7.bold("Auth".padEnd(12)) + chalk7.red("\u25CF ") + chalk7.dim("Not logged in") + " " + chalk7.cyan("/login")));
|
|
2989
2983
|
}
|
|
2990
2984
|
console.log(div);
|
|
2991
|
-
|
|
2992
|
-
const modelVal = this.currentModel ? chalk7.cyan(this.currentModel) : chalk7.dim("auto");
|
|
2993
|
-
const labelPart = chalk7.dim("\u2502 ") + chalk7.bold("Mode".padEnd(11)) + chalk7.dim(" \u2502 ");
|
|
2994
|
-
const raw1 = this.currentMode;
|
|
2995
|
-
const mid = " " + chalk7.bold("Model".padEnd(8)) + " " + modelVal;
|
|
2996
|
-
const raw2 = mid.replace(/\x1B\[[0-9;]*m/g, "");
|
|
2997
|
-
const padLen = W - 4 - 11 - 3 - raw1.length - raw2.length;
|
|
2998
|
-
console.log(labelPart + modeVal + mid + " ".repeat(Math.max(0, padLen)) + chalk7.dim(" \u2502"));
|
|
2985
|
+
console.log(kv("Mode", chalk7.yellow(this.currentMode) + " " + chalk7.bold("Model".padEnd(8)) + (this.currentModel ? chalk7.cyan(this.currentModel) : chalk7.dim("auto"))));
|
|
2999
2986
|
console.log(div);
|
|
3000
2987
|
if (this.isDemo) {
|
|
3001
|
-
console.log(
|
|
2988
|
+
console.log(kv("Project", chalk7.cyan(DEMO_PROJECT2.name)));
|
|
3002
2989
|
} else {
|
|
3003
2990
|
const pid = configManager.getProjectId();
|
|
3004
|
-
console.log(
|
|
2991
|
+
console.log(kv("Project", pid ? chalk7.cyan(pid) : chalk7.dim("none")));
|
|
3005
2992
|
}
|
|
3006
2993
|
const cwd = process.cwd().replace(process.env.HOME || "", "~");
|
|
3007
|
-
console.log(
|
|
2994
|
+
console.log(kv("Directory", chalk7.dim(cwd)));
|
|
3008
2995
|
if (this.isDemo) {
|
|
3009
2996
|
console.log(div);
|
|
3010
|
-
console.log(
|
|
2997
|
+
console.log(ln(chalk7.yellow("\u25C6 ") + chalk7.yellow("Demo mode") + chalk7.dim(" \u2014 no backend required")));
|
|
3011
2998
|
}
|
|
3012
|
-
console.log(
|
|
2999
|
+
console.log(bot);
|
|
3013
3000
|
console.log();
|
|
3014
|
-
console.log(
|
|
3015
|
-
chalk7.dim(" Chat freely, or use ") + chalk7.cyan("/help") + chalk7.dim(" to see all commands.")
|
|
3016
|
-
);
|
|
3001
|
+
console.log(chalk7.dim(" Type a message to chat, or ") + chalk7.cyan("/help") + chalk7.dim(" for commands."));
|
|
3017
3002
|
}
|
|
3018
|
-
//
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
const
|
|
3023
|
-
const
|
|
3024
|
-
let
|
|
3003
|
+
// ─── PROMPT LINE ─────────────────────────────────────────
|
|
3004
|
+
printPrompt() {
|
|
3005
|
+
const sep = chalk7.dim("\n " + "\u2500".repeat(50));
|
|
3006
|
+
const icon = chalk7.bold.cyan("\u2B21");
|
|
3007
|
+
const name = chalk7.bold.white("devwing");
|
|
3008
|
+
const mode = chalk7.dim("[") + chalk7.yellow(this.currentMode) + chalk7.dim("]");
|
|
3009
|
+
let user = "";
|
|
3025
3010
|
if (this.isAuthenticated && this.userProfile) {
|
|
3026
|
-
const first = (this.userProfile.full_name || this.userProfile.email).split(" ")[0].split("@")[0];
|
|
3027
|
-
|
|
3011
|
+
const first = (this.userProfile.full_name || this.userProfile.email).split(" ")[0].split("@")[0].toLowerCase();
|
|
3012
|
+
user = " " + chalk7.dim("@") + chalk7.green(first);
|
|
3028
3013
|
}
|
|
3029
|
-
const
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
parts.push(" " + chalk7.bold.cyan("\u276F") + " ");
|
|
3037
|
-
return parts.join("\n");
|
|
3038
|
-
}
|
|
3039
|
-
// ============================================================
|
|
3040
|
-
// AUTH CHECK
|
|
3041
|
-
// ============================================================
|
|
3042
|
-
async checkAuthStatus() {
|
|
3014
|
+
const demo = this.isDemo ? chalk7.dim.yellow(" demo") : "";
|
|
3015
|
+
process.stdout.write(`${sep}
|
|
3016
|
+
${icon} ${name} ${mode}${user}${demo}
|
|
3017
|
+
${chalk7.bold.cyan("\u276F")} `);
|
|
3018
|
+
}
|
|
3019
|
+
// ─── AUTH CHECK ──────────────────────────────────────────
|
|
3020
|
+
async checkAuth() {
|
|
3043
3021
|
try {
|
|
3044
|
-
const
|
|
3022
|
+
const key = await configManager.getApiKey();
|
|
3045
3023
|
if (this.isDemo) {
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
this.userProfile = { ...DEMO_USER2 };
|
|
3049
|
-
} else {
|
|
3050
|
-
this.isAuthenticated = false;
|
|
3051
|
-
this.userProfile = null;
|
|
3052
|
-
}
|
|
3024
|
+
this.isAuthenticated = !!key;
|
|
3025
|
+
this.userProfile = key ? { ...DEMO_USER2 } : null;
|
|
3053
3026
|
return;
|
|
3054
3027
|
}
|
|
3055
|
-
if (!
|
|
3028
|
+
if (!key) return;
|
|
3056
3029
|
try {
|
|
3057
|
-
|
|
3030
|
+
this.userProfile = await apiClient.getProfile();
|
|
3058
3031
|
this.isAuthenticated = true;
|
|
3059
|
-
this.userProfile = profile;
|
|
3060
3032
|
} catch {
|
|
3061
3033
|
this.isAuthenticated = true;
|
|
3062
3034
|
this.userProfile = null;
|
|
@@ -3066,10 +3038,8 @@ ${sep}`);
|
|
|
3066
3038
|
this.userProfile = null;
|
|
3067
3039
|
}
|
|
3068
3040
|
}
|
|
3069
|
-
//
|
|
3070
|
-
|
|
3071
|
-
// ============================================================
|
|
3072
|
-
async handleSlashCommand(input) {
|
|
3041
|
+
// ─── SLASH COMMAND DISPATCH ──────────────────────────────
|
|
3042
|
+
async handleSlash(input) {
|
|
3073
3043
|
const parts = input.split(/\s+/);
|
|
3074
3044
|
const cmdName = parts[0].substring(1).toLowerCase();
|
|
3075
3045
|
const args = parts.slice(1).join(" ").trim();
|
|
@@ -3087,19 +3057,17 @@ ${sep}`);
|
|
|
3087
3057
|
}
|
|
3088
3058
|
if (command.requiresAuth && !this.isAuthenticated) {
|
|
3089
3059
|
console.log();
|
|
3090
|
-
logger.warn("You need to be logged in
|
|
3060
|
+
logger.warn("You need to be logged in. Type /login first.");
|
|
3091
3061
|
return;
|
|
3092
3062
|
}
|
|
3093
3063
|
console.log();
|
|
3094
3064
|
await command.handler(args);
|
|
3095
3065
|
}
|
|
3096
|
-
//
|
|
3097
|
-
|
|
3098
|
-
// ============================================================
|
|
3099
|
-
async handleAIPrompt(input) {
|
|
3066
|
+
// ─── AI PROMPT ───────────────────────────────────────────
|
|
3067
|
+
async handlePrompt(input) {
|
|
3100
3068
|
if (!this.isAuthenticated) {
|
|
3101
3069
|
console.log();
|
|
3102
|
-
logger.warn("You need to log in
|
|
3070
|
+
logger.warn("You need to log in first. Type /login to authenticate.");
|
|
3103
3071
|
return;
|
|
3104
3072
|
}
|
|
3105
3073
|
console.log();
|
|
@@ -3114,12 +3082,12 @@ ${sep}`);
|
|
|
3114
3082
|
if (!this.sessionContext) {
|
|
3115
3083
|
logger.startSpinner("Loading codebase context...");
|
|
3116
3084
|
try {
|
|
3117
|
-
const
|
|
3118
|
-
this.sessionContext = await
|
|
3085
|
+
const col = new ContextCollector(process.cwd());
|
|
3086
|
+
this.sessionContext = await col.collect(input);
|
|
3119
3087
|
logger.succeedSpinner(`Context loaded \u2014 ${this.sessionContext.files.length} files`);
|
|
3120
|
-
} catch (
|
|
3088
|
+
} catch (err) {
|
|
3121
3089
|
logger.failSpinner("Failed to load context");
|
|
3122
|
-
logger.error(
|
|
3090
|
+
logger.error(err.message);
|
|
3123
3091
|
return;
|
|
3124
3092
|
}
|
|
3125
3093
|
}
|
|
@@ -3127,8 +3095,8 @@ ${sep}`);
|
|
|
3127
3095
|
logger.info(chalk7.dim("DevWing is thinking..."));
|
|
3128
3096
|
console.log();
|
|
3129
3097
|
try {
|
|
3130
|
-
const
|
|
3131
|
-
prompt: this.
|
|
3098
|
+
const req = {
|
|
3099
|
+
prompt: this.buildHistory(),
|
|
3132
3100
|
mode: this.currentMode,
|
|
3133
3101
|
model: this.currentModel || void 0,
|
|
3134
3102
|
project_id: this.options.project || configManager.getProjectId(),
|
|
@@ -3136,149 +3104,130 @@ ${sep}`);
|
|
|
3136
3104
|
stream: true,
|
|
3137
3105
|
max_tokens: 4096
|
|
3138
3106
|
};
|
|
3139
|
-
let
|
|
3107
|
+
let reply = "";
|
|
3140
3108
|
streamingRenderer.reset();
|
|
3141
3109
|
streamingRenderer.clearPendingTools();
|
|
3142
|
-
for await (const
|
|
3143
|
-
await streamingRenderer.processMessage(
|
|
3144
|
-
if (
|
|
3145
|
-
|
|
3146
|
-
}
|
|
3147
|
-
if (message.session_id) {
|
|
3148
|
-
this.sessionId = message.session_id;
|
|
3149
|
-
}
|
|
3110
|
+
for await (const msg of apiClient.streamCompletion(req)) {
|
|
3111
|
+
await streamingRenderer.processMessage(msg);
|
|
3112
|
+
if (msg.type === "token" && msg.content) reply += msg.content;
|
|
3113
|
+
if (msg.session_id) this.sessionId = msg.session_id;
|
|
3150
3114
|
}
|
|
3151
|
-
if (
|
|
3152
|
-
this.conversationHistory.push({ role: "assistant", content:
|
|
3115
|
+
if (reply) {
|
|
3116
|
+
this.conversationHistory.push({ role: "assistant", content: reply, timestamp: /* @__PURE__ */ new Date() });
|
|
3153
3117
|
}
|
|
3154
|
-
const
|
|
3155
|
-
if (
|
|
3118
|
+
const pending = streamingRenderer.getPendingToolCalls();
|
|
3119
|
+
if (pending.length > 0) {
|
|
3156
3120
|
logger.newline();
|
|
3157
|
-
|
|
3158
|
-
const toolResults = await streamingRenderer.executePendingTools();
|
|
3121
|
+
const results = await streamingRenderer.executePendingTools();
|
|
3159
3122
|
if (this.sessionId) {
|
|
3160
|
-
for await (const
|
|
3161
|
-
await streamingRenderer.processMessage(
|
|
3123
|
+
for await (const msg of apiClient.continueCompletion(this.sessionId, results)) {
|
|
3124
|
+
await streamingRenderer.processMessage(msg);
|
|
3162
3125
|
}
|
|
3163
3126
|
}
|
|
3164
3127
|
}
|
|
3165
3128
|
console.log();
|
|
3166
|
-
} catch (
|
|
3167
|
-
logger.error(
|
|
3129
|
+
} catch (err) {
|
|
3130
|
+
logger.error(err.message || "AI request failed");
|
|
3168
3131
|
}
|
|
3169
3132
|
}
|
|
3170
|
-
|
|
3171
|
-
const
|
|
3172
|
-
if (
|
|
3173
|
-
const recent =
|
|
3174
|
-
const
|
|
3133
|
+
buildHistory() {
|
|
3134
|
+
const h = this.conversationHistory;
|
|
3135
|
+
if (h.length <= 1) return h[h.length - 1]?.content || "";
|
|
3136
|
+
const recent = h.slice(-10);
|
|
3137
|
+
const ctx = recent.slice(0, -1).map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${m.content}`).join("\n\n");
|
|
3175
3138
|
return `Previous conversation:
|
|
3176
|
-
${
|
|
3139
|
+
${ctx}
|
|
3177
3140
|
|
|
3178
3141
|
Current question:
|
|
3179
3142
|
${recent[recent.length - 1].content}`;
|
|
3180
3143
|
}
|
|
3181
|
-
//
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
exitSession() {
|
|
3185
|
-
if (this.sigintTimer) clearTimeout(this.sigintTimer);
|
|
3186
|
-
this.restoreStdin();
|
|
3144
|
+
// ─── EXIT ────────────────────────────────────────────────
|
|
3145
|
+
exit() {
|
|
3146
|
+
restoreStdin();
|
|
3187
3147
|
console.log();
|
|
3188
|
-
console.log(chalk7.dim(" Goodbye! Happy shipping.
|
|
3148
|
+
console.log(chalk7.dim(" Goodbye! Happy shipping."));
|
|
3189
3149
|
console.log();
|
|
3190
3150
|
process.exit(0);
|
|
3191
3151
|
}
|
|
3192
|
-
//
|
|
3193
|
-
// COMMAND REGISTRATION
|
|
3194
|
-
// ============================================================
|
|
3152
|
+
// ─── COMMAND REGISTRATION ────────────────────────────────
|
|
3195
3153
|
registerCommands() {
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
}
|
|
3218
|
-
// ============================================================
|
|
3219
|
-
// COMMAND IMPLEMENTATIONS
|
|
3220
|
-
// ============================================================
|
|
3154
|
+
const add = (c) => this.commands.set(c.name, c);
|
|
3155
|
+
add({ name: "help", aliases: ["h", "?"], description: "Show all available commands", category: "session", requiresAuth: false, handler: async () => this.cmdHelp() });
|
|
3156
|
+
add({ name: "exit", aliases: ["quit", "q"], description: "Exit DevWing", category: "session", requiresAuth: false, handler: async () => this.exit() });
|
|
3157
|
+
add({ name: "clear", aliases: ["cls"], description: "Clear screen and reset conversation", category: "session", requiresAuth: false, handler: async () => this.cmdClear() });
|
|
3158
|
+
add({ name: "login", aliases: ["signin"], description: "Authenticate with DevWing", category: "session", requiresAuth: false, handler: async () => this.cmdLogin() });
|
|
3159
|
+
add({ name: "logout", aliases: ["signout"], description: "Clear credentials and log out", category: "session", requiresAuth: true, handler: async () => this.cmdLogout() });
|
|
3160
|
+
add({ name: "status", aliases: ["whoami"], description: "Show auth status and profile", category: "session", requiresAuth: false, handler: async () => this.cmdStatus() });
|
|
3161
|
+
add({ name: "scan", aliases: [], description: "Run security vulnerability scan", category: "tools", requiresAuth: true, handler: async () => this.cmdScan() });
|
|
3162
|
+
add({ name: "review", aliases: [], description: "Perform code review on recent changes", category: "tools", requiresAuth: true, handler: async () => this.cmdReview() });
|
|
3163
|
+
add({ name: "explain", aliases: [], description: "Explain code, file, or concept", category: "tools", requiresAuth: true, handler: async (a) => this.cmdExplain(a) });
|
|
3164
|
+
add({ name: "memory", aliases: ["mem"], description: "Project memory save / show / clear", category: "tools", requiresAuth: true, handler: async (a) => this.cmdMemory(a) });
|
|
3165
|
+
add({ name: "mode", aliases: [], description: "Show or set AI mode", category: "config", requiresAuth: false, handler: async (a) => this.cmdMode(a) });
|
|
3166
|
+
add({ name: "model", aliases: [], description: "Show or set AI model", category: "config", requiresAuth: false, handler: async (a) => this.cmdModel(a) });
|
|
3167
|
+
add({ name: "models", aliases: [], description: "List all available AI models", category: "config", requiresAuth: false, handler: async () => this.cmdModels() });
|
|
3168
|
+
add({ name: "usage", aliases: ["stats"], description: "Show token usage statistics", category: "config", requiresAuth: true, handler: async () => this.cmdUsage() });
|
|
3169
|
+
add({ name: "plans", aliases: ["pricing"], description: "Show available plans and pricing", category: "config", requiresAuth: false, handler: async () => this.cmdPlans() });
|
|
3170
|
+
add({ name: "context", aliases: ["ctx"], description: "Show loaded codebase context", category: "config", requiresAuth: false, handler: async () => this.cmdContext() });
|
|
3171
|
+
add({ name: "history", aliases: [], description: "Show conversation history", category: "config", requiresAuth: false, handler: async () => this.cmdHistory() });
|
|
3172
|
+
add({ name: "config", aliases: ["settings"], description: "Show or set CLI configuration", category: "config", requiresAuth: false, handler: async (a) => this.cmdConfig(a) });
|
|
3173
|
+
}
|
|
3174
|
+
// ─── COMMAND IMPLEMENTATIONS ─────────────────────────────
|
|
3221
3175
|
async cmdHelp() {
|
|
3222
|
-
const W =
|
|
3176
|
+
const W = 62;
|
|
3223
3177
|
const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
|
|
3224
|
-
const
|
|
3178
|
+
const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
|
|
3225
3179
|
const div = chalk7.dim("\u251C" + "\u2500".repeat(W - 2) + "\u2524");
|
|
3226
|
-
const
|
|
3227
|
-
const
|
|
3228
|
-
const rawRight = right.replace(/\x1B\[[0-9;]*m/g, "");
|
|
3229
|
-
const pad = W - 2 - rawLeft.length - rawRight.length - 2;
|
|
3230
|
-
return chalk7.dim("\u2502 ") + left + " ".repeat(Math.max(0, pad)) + right + chalk7.dim(" \u2502");
|
|
3231
|
-
};
|
|
3232
|
-
const catRow = (label) => {
|
|
3233
|
-
const raw = label.replace(/\x1B\[[0-9;]*m/g, "");
|
|
3180
|
+
const ln = (s = "") => {
|
|
3181
|
+
const raw = s.replace(/\x1B\[[0-9;]*m/g, "");
|
|
3234
3182
|
const pad = W - 4 - raw.length;
|
|
3235
|
-
return chalk7.dim("\u2502 ") +
|
|
3183
|
+
return chalk7.dim("\u2502 ") + s + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
|
|
3236
3184
|
};
|
|
3237
|
-
|
|
3185
|
+
console.log(top);
|
|
3186
|
+
console.log(ln(chalk7.bold.white("Commands")));
|
|
3187
|
+
const cats = [
|
|
3238
3188
|
{ key: "session", label: "Session", color: chalk7.bold.green },
|
|
3239
3189
|
{ key: "tools", label: "AI Tools", color: chalk7.bold.magenta },
|
|
3240
3190
|
{ key: "config", label: "Configuration", color: chalk7.bold.yellow }
|
|
3241
3191
|
];
|
|
3242
|
-
|
|
3243
|
-
console.log(catRow(chalk7.bold.white(" Commands")));
|
|
3244
|
-
for (const cat of categories) {
|
|
3192
|
+
for (const cat of cats) {
|
|
3245
3193
|
console.log(div);
|
|
3246
|
-
console.log(
|
|
3194
|
+
console.log(ln(" " + cat.color(cat.label)));
|
|
3247
3195
|
for (const cmd of this.commands.values()) {
|
|
3248
3196
|
if (cmd.category !== cat.key) continue;
|
|
3249
|
-
const cmdStr = chalk7.cyan(("/" + cmd.name).padEnd(
|
|
3197
|
+
const cmdStr = chalk7.cyan(("/" + cmd.name).padEnd(13));
|
|
3250
3198
|
const descStr = chalk7.dim(cmd.description);
|
|
3251
|
-
const
|
|
3252
|
-
console.log(
|
|
3199
|
+
const alias = cmd.aliases.length ? chalk7.dim(" \xB7 " + cmd.aliases.map((a) => "/" + a).join(", ")) : "";
|
|
3200
|
+
console.log(ln(" " + cmdStr + " " + descStr + alias));
|
|
3253
3201
|
}
|
|
3254
3202
|
}
|
|
3255
3203
|
console.log(div);
|
|
3256
|
-
console.log(
|
|
3257
|
-
console.log(
|
|
3204
|
+
console.log(ln(chalk7.dim(" Type any message to chat with DevWing AI")));
|
|
3205
|
+
console.log(bot);
|
|
3258
3206
|
}
|
|
3259
3207
|
async cmdClear() {
|
|
3260
3208
|
this.conversationHistory = [];
|
|
3261
3209
|
this.sessionContext = null;
|
|
3262
3210
|
this.sessionId = null;
|
|
3263
3211
|
console.clear();
|
|
3264
|
-
this.
|
|
3212
|
+
this.printBanner();
|
|
3265
3213
|
this.printSystemInfo();
|
|
3214
|
+
console.log();
|
|
3266
3215
|
}
|
|
3267
3216
|
async cmdLogin() {
|
|
3268
3217
|
if (this.isDemo) {
|
|
3269
|
-
await
|
|
3218
|
+
await demoLoginCommand({ fromSession: true });
|
|
3270
3219
|
} else {
|
|
3271
|
-
await
|
|
3220
|
+
await loginCommand();
|
|
3272
3221
|
}
|
|
3273
|
-
await this.
|
|
3222
|
+
await this.checkAuth();
|
|
3274
3223
|
}
|
|
3275
3224
|
async cmdLogout() {
|
|
3276
3225
|
if (this.isDemo) {
|
|
3277
|
-
await
|
|
3226
|
+
await demoLogoutCommand();
|
|
3278
3227
|
} else {
|
|
3279
|
-
await
|
|
3228
|
+
await logoutCommand();
|
|
3280
3229
|
}
|
|
3281
|
-
await this.
|
|
3230
|
+
await this.checkAuth();
|
|
3282
3231
|
}
|
|
3283
3232
|
async cmdStatus() {
|
|
3284
3233
|
if (!this.isAuthenticated) {
|
|
@@ -3288,22 +3237,22 @@ ${recent[recent.length - 1].content}`;
|
|
|
3288
3237
|
if (this.isDemo) {
|
|
3289
3238
|
const W = 54;
|
|
3290
3239
|
const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
|
|
3291
|
-
const
|
|
3292
|
-
const
|
|
3240
|
+
const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
|
|
3241
|
+
const kv = (k, v) => {
|
|
3293
3242
|
const rawV = v.replace(/\x1B\[[0-9;]*m/g, "");
|
|
3294
3243
|
const pad = W - 4 - k.length - rawV.length;
|
|
3295
3244
|
return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
|
|
3296
3245
|
};
|
|
3297
3246
|
console.log(top);
|
|
3298
|
-
console.log(
|
|
3299
|
-
console.log(
|
|
3300
|
-
console.log(
|
|
3301
|
-
console.log(
|
|
3302
|
-
console.log(
|
|
3303
|
-
console.log(
|
|
3304
|
-
console.log(
|
|
3305
|
-
console.log(
|
|
3306
|
-
console.log(
|
|
3247
|
+
console.log(kv("Email", DEMO_USER2.email));
|
|
3248
|
+
console.log(kv("Name", DEMO_USER2.full_name));
|
|
3249
|
+
console.log(kv("Plan", chalk7.bgCyan.black(" PRO ")));
|
|
3250
|
+
console.log(kv("Verified", chalk7.green("\u2713 Yes")));
|
|
3251
|
+
console.log(kv("2FA", chalk7.dim("Disabled")));
|
|
3252
|
+
console.log(kv("Workspace", DEMO_WORKSPACE2.name + chalk7.dim(" (enterprise)")));
|
|
3253
|
+
console.log(kv("Project", DEMO_PROJECT2.name));
|
|
3254
|
+
console.log(kv("Tokens today", `${(DEMO_USER2.tokens_used_today).toLocaleString()} / 500,000`));
|
|
3255
|
+
console.log(bot);
|
|
3307
3256
|
} else {
|
|
3308
3257
|
await statusCommand();
|
|
3309
3258
|
}
|
|
@@ -3324,8 +3273,7 @@ ${recent[recent.length - 1].content}`;
|
|
|
3324
3273
|
}
|
|
3325
3274
|
async cmdExplain(args) {
|
|
3326
3275
|
if (!args) {
|
|
3327
|
-
logger.error("Usage: /explain <file
|
|
3328
|
-
logger.info("Example: /explain src/auth/middleware.ts");
|
|
3276
|
+
logger.error("Usage: /explain <file or concept>");
|
|
3329
3277
|
return;
|
|
3330
3278
|
}
|
|
3331
3279
|
if (this.isDemo) {
|
|
@@ -3338,7 +3286,6 @@ ${recent[recent.length - 1].content}`;
|
|
|
3338
3286
|
const parts = args.split(/\s+/);
|
|
3339
3287
|
const sub = parts[0]?.toLowerCase();
|
|
3340
3288
|
if (!sub || !["save", "show", "clear"].includes(sub)) {
|
|
3341
|
-
console.log();
|
|
3342
3289
|
console.log(` ${chalk7.cyan("/memory show")} \u2014 Show all project memories`);
|
|
3343
3290
|
console.log(` ${chalk7.cyan("/memory save <text>")} \u2014 Save a memory`);
|
|
3344
3291
|
console.log(` ${chalk7.cyan("/memory clear")} \u2014 Clear all memories`);
|
|
@@ -3346,59 +3293,44 @@ ${recent[recent.length - 1].content}`;
|
|
|
3346
3293
|
}
|
|
3347
3294
|
const content = parts.slice(1).join(" ").trim();
|
|
3348
3295
|
if (this.isDemo) {
|
|
3349
|
-
await
|
|
3350
|
-
() => demoMemoryCommand(sub, content || void 0)
|
|
3351
|
-
);
|
|
3296
|
+
await demoMemoryCommand(sub, content || void 0);
|
|
3352
3297
|
} else {
|
|
3353
3298
|
await memoryCommand(sub, content || void 0, this.options);
|
|
3354
3299
|
}
|
|
3355
3300
|
}
|
|
3356
3301
|
async cmdMode(args) {
|
|
3357
|
-
const
|
|
3302
|
+
const valid = ["general", "frontend", "backend", "security", "devops"];
|
|
3358
3303
|
if (!args) {
|
|
3359
|
-
const
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
const pad = W - 2 - rawLeft.length;
|
|
3366
|
-
return chalk7.dim("\u2502") + left + " ".repeat(Math.max(0, pad)) + chalk7.dim("\u2502");
|
|
3304
|
+
const descs = {
|
|
3305
|
+
general: "All-purpose coding assistant",
|
|
3306
|
+
frontend: "React, Vue, CSS, TypeScript",
|
|
3307
|
+
backend: "APIs, databases, microservices",
|
|
3308
|
+
security: "OWASP, CVE, penetration testing",
|
|
3309
|
+
devops: "Docker, Kubernetes, CI/CD"
|
|
3367
3310
|
};
|
|
3368
|
-
|
|
3369
|
-
console.log(chalk7.dim("\u2502 ") + chalk7.bold("AI Mode".padEnd(W - 4)) + chalk7.dim(" \u2502"));
|
|
3370
|
-
for (const m of validModes) {
|
|
3311
|
+
for (const m of valid) {
|
|
3371
3312
|
const active = m === this.currentMode;
|
|
3372
|
-
const
|
|
3373
|
-
const
|
|
3374
|
-
|
|
3375
|
-
general: "All-purpose coding assistant",
|
|
3376
|
-
frontend: "React, Vue, CSS, TypeScript",
|
|
3377
|
-
backend: "APIs, databases, microservices",
|
|
3378
|
-
security: "OWASP, CVE, penetration testing",
|
|
3379
|
-
devops: "Docker, Kubernetes, CI/CD"
|
|
3380
|
-
};
|
|
3381
|
-
console.log(row(icon, name, descs[m] || ""));
|
|
3313
|
+
const dot = active ? chalk7.green("\u25CF") : chalk7.dim("\u25CB");
|
|
3314
|
+
const label = active ? chalk7.yellow.bold(m.padEnd(10)) : chalk7.white(m.padEnd(10));
|
|
3315
|
+
console.log(` ${dot} ${label} ${chalk7.dim(descs[m])}`);
|
|
3382
3316
|
}
|
|
3383
|
-
console.log(
|
|
3317
|
+
console.log();
|
|
3384
3318
|
console.log(chalk7.dim(" Usage: /mode <name>"));
|
|
3385
3319
|
return;
|
|
3386
3320
|
}
|
|
3387
3321
|
const mode = args.toLowerCase();
|
|
3388
|
-
if (!
|
|
3389
|
-
logger.error(`Invalid mode "${args}". Valid: ${
|
|
3322
|
+
if (!valid.includes(mode)) {
|
|
3323
|
+
logger.error(`Invalid mode "${args}". Valid: ${valid.join(", ")}`);
|
|
3390
3324
|
return;
|
|
3391
3325
|
}
|
|
3392
3326
|
this.currentMode = mode;
|
|
3393
3327
|
this.options.mode = mode;
|
|
3394
|
-
console.log();
|
|
3395
3328
|
console.log(` ${chalk7.green("\u2713")} Mode \u2192 ${chalk7.yellow.bold(mode)}`);
|
|
3396
3329
|
}
|
|
3397
3330
|
async cmdModel(args) {
|
|
3398
3331
|
if (!args) {
|
|
3399
|
-
const
|
|
3400
|
-
console.log();
|
|
3401
|
-
console.log(` ${chalk7.bold("Current model:")} ${chalk7.cyan(current)}`);
|
|
3332
|
+
const cur = this.currentModel || "auto";
|
|
3333
|
+
console.log(` Current model: ${chalk7.cyan.bold(cur)}`);
|
|
3402
3334
|
console.log(chalk7.dim(" /model <name> \xB7 /model auto \xB7 /models to list all"));
|
|
3403
3335
|
return;
|
|
3404
3336
|
}
|
|
@@ -3410,28 +3342,20 @@ ${recent[recent.length - 1].content}`;
|
|
|
3410
3342
|
}
|
|
3411
3343
|
this.currentModel = args;
|
|
3412
3344
|
this.options.model = args;
|
|
3413
|
-
console.log();
|
|
3414
3345
|
console.log(` ${chalk7.green("\u2713")} Model \u2192 ${chalk7.cyan.bold(args)}`);
|
|
3415
3346
|
}
|
|
3416
3347
|
async cmdModels() {
|
|
3417
3348
|
if (this.isDemo) {
|
|
3418
3349
|
const table = new Table3({
|
|
3419
|
-
head: [
|
|
3420
|
-
|
|
3421
|
-
chalk7.bold("Domain"),
|
|
3422
|
-
chalk7.bold("Status"),
|
|
3423
|
-
chalk7.bold("Plan"),
|
|
3424
|
-
chalk7.bold("Context"),
|
|
3425
|
-
chalk7.bold("Speed")
|
|
3426
|
-
],
|
|
3427
|
-
style: { head: [], border: ["dim"] }
|
|
3350
|
+
head: [chalk7.bold("Model"), chalk7.bold("Domain"), chalk7.bold("Status"), chalk7.bold("Plan"), chalk7.bold("Context"), chalk7.bold("Speed")],
|
|
3351
|
+
style: { border: ["dim"] }
|
|
3428
3352
|
});
|
|
3429
3353
|
for (const m of DEMO_MODELS) {
|
|
3430
|
-
const
|
|
3431
|
-
const
|
|
3432
|
-
const
|
|
3433
|
-
const
|
|
3434
|
-
table.push([
|
|
3354
|
+
const cur = m.name === (this.currentModel || "");
|
|
3355
|
+
const nm = cur ? chalk7.cyan.bold(m.display_name + " \u25CF") : m.display_name;
|
|
3356
|
+
const status = m.status === "active" ? chalk7.green("active") : chalk7.yellow("beta");
|
|
3357
|
+
const plan = m.min_plan === "free" ? chalk7.green("free") : chalk7.cyan("pro");
|
|
3358
|
+
table.push([nm, chalk7.yellow(m.domain), status, plan, `${(m.context_window / 1024).toFixed(0)}k`, `${m.tokens_per_sec} t/s`]);
|
|
3435
3359
|
}
|
|
3436
3360
|
console.log(table.toString());
|
|
3437
3361
|
console.log(chalk7.dim(" /model <name> to select \xB7 /model auto for automatic"));
|
|
@@ -3452,29 +3376,28 @@ ${recent[recent.length - 1].content}`;
|
|
|
3452
3376
|
}
|
|
3453
3377
|
async cmdUsage() {
|
|
3454
3378
|
if (this.isDemo) {
|
|
3455
|
-
const
|
|
3379
|
+
const t = new Table3({
|
|
3456
3380
|
head: [chalk7.bold("Period"), chalk7.bold("Requests"), chalk7.bold("Tokens In"), chalk7.bold("Tokens Out"), chalk7.bold("Cost")],
|
|
3457
|
-
style: {
|
|
3381
|
+
style: { border: ["dim"] }
|
|
3458
3382
|
});
|
|
3459
|
-
|
|
3383
|
+
t.push(
|
|
3460
3384
|
["Today", String(DEMO_USAGE.today.requests), DEMO_USAGE.today.tokens_in.toLocaleString(), DEMO_USAGE.today.tokens_out.toLocaleString(), `$${DEMO_USAGE.today.cost.toFixed(4)}`],
|
|
3461
3385
|
["This Week", String(DEMO_USAGE.week.requests), DEMO_USAGE.week.tokens_in.toLocaleString(), DEMO_USAGE.week.tokens_out.toLocaleString(), `$${DEMO_USAGE.week.cost.toFixed(4)}`],
|
|
3462
3386
|
["This Month", String(DEMO_USAGE.month.requests), DEMO_USAGE.month.tokens_in.toLocaleString(), DEMO_USAGE.month.tokens_out.toLocaleString(), `$${DEMO_USAGE.month.cost.toFixed(4)}`]
|
|
3463
3387
|
);
|
|
3464
|
-
console.log(
|
|
3388
|
+
console.log(t.toString());
|
|
3465
3389
|
const used = DEMO_USER2.tokens_used_today;
|
|
3466
3390
|
const limit = 5e5;
|
|
3467
3391
|
const pct = Math.round(used / limit * 100);
|
|
3468
|
-
const barW =
|
|
3469
|
-
const
|
|
3470
|
-
const bar = chalk7.cyan("\u2588".repeat(
|
|
3392
|
+
const barW = 30;
|
|
3393
|
+
const fill = Math.round(pct / 100 * barW);
|
|
3394
|
+
const bar = chalk7.cyan("\u2588".repeat(fill)) + chalk7.dim("\u2591".repeat(barW - fill));
|
|
3471
3395
|
console.log();
|
|
3472
|
-
console.log(` Daily Quota ${bar} ${chalk7.bold(pct + "%")} ${chalk7.dim(`${used.toLocaleString()} / ${limit.toLocaleString()}
|
|
3396
|
+
console.log(` Daily Quota ${bar} ${chalk7.bold(pct + "%")} ${chalk7.dim(`${used.toLocaleString()} / ${limit.toLocaleString()}`)}`);
|
|
3473
3397
|
console.log();
|
|
3474
|
-
console.log(chalk7.bold(" By Model"));
|
|
3475
3398
|
const mt = new Table3({
|
|
3476
3399
|
head: [chalk7.bold("Model"), chalk7.bold("Requests"), chalk7.bold("Tokens"), chalk7.bold("Cost")],
|
|
3477
|
-
style: {
|
|
3400
|
+
style: { border: ["dim"] }
|
|
3478
3401
|
});
|
|
3479
3402
|
mt.push(
|
|
3480
3403
|
["devwing-backend-1", "312", "421,000", "$1.4721"],
|
|
@@ -3486,10 +3409,10 @@ ${recent[recent.length - 1].content}`;
|
|
|
3486
3409
|
console.log(mt.toString());
|
|
3487
3410
|
} else {
|
|
3488
3411
|
try {
|
|
3489
|
-
const
|
|
3490
|
-
console.log(` Requests: ${
|
|
3491
|
-
console.log(` Tokens: ${
|
|
3492
|
-
console.log(` Cost: $${
|
|
3412
|
+
const u = await apiClient.getUsage();
|
|
3413
|
+
console.log(` Requests: ${u.total_requests}`);
|
|
3414
|
+
console.log(` Tokens: ${u.total_tokens_in.toLocaleString()} in, ${u.total_tokens_out.toLocaleString()} out`);
|
|
3415
|
+
console.log(` Cost: $${u.total_cost_usd.toFixed(4)}`);
|
|
3493
3416
|
} catch (e) {
|
|
3494
3417
|
logger.error("Failed to fetch usage: " + e.message);
|
|
3495
3418
|
}
|
|
@@ -3497,71 +3420,60 @@ ${recent[recent.length - 1].content}`;
|
|
|
3497
3420
|
}
|
|
3498
3421
|
async cmdPlans() {
|
|
3499
3422
|
const table = new Table3({
|
|
3500
|
-
head: [
|
|
3501
|
-
|
|
3502
|
-
chalk7.bold("Price"),
|
|
3503
|
-
chalk7.bold("Tokens/Day"),
|
|
3504
|
-
chalk7.bold("Models"),
|
|
3505
|
-
chalk7.bold("Projects"),
|
|
3506
|
-
chalk7.bold("Seats")
|
|
3507
|
-
],
|
|
3508
|
-
style: { head: [], border: ["dim"] }
|
|
3423
|
+
head: [chalk7.bold("Plan"), chalk7.bold("Price"), chalk7.bold("Tokens/Day"), chalk7.bold("Models"), chalk7.bold("Projects"), chalk7.bold("Seats")],
|
|
3424
|
+
style: { border: ["dim"] }
|
|
3509
3425
|
});
|
|
3510
|
-
const
|
|
3511
|
-
for (const
|
|
3512
|
-
const
|
|
3513
|
-
const
|
|
3514
|
-
table.push([
|
|
3426
|
+
const cur = this.userProfile?.subscription_plan?.toLowerCase() || "";
|
|
3427
|
+
for (const p of DEMO_PLANS) {
|
|
3428
|
+
const isCur = this.isAuthenticated && cur === p.name.toLowerCase();
|
|
3429
|
+
const nm = isCur ? chalk7.cyan.bold(p.name) + chalk7.dim(" \u2190") : p.name;
|
|
3430
|
+
table.push([nm, p.price, p.tokens_day, p.models, p.projects, p.seats]);
|
|
3515
3431
|
}
|
|
3516
3432
|
console.log(table.toString());
|
|
3517
3433
|
console.log();
|
|
3518
3434
|
if (this.isAuthenticated) {
|
|
3519
|
-
console.log(chalk7.dim(` Your plan: ${
|
|
3435
|
+
console.log(chalk7.dim(` Your plan: ${cur.toUpperCase()} \xB7 Upgrade at devwing.ai/dashboard/billing`));
|
|
3520
3436
|
} else {
|
|
3521
3437
|
console.log(chalk7.dim(" Sign up at devwing.ai"));
|
|
3522
3438
|
}
|
|
3523
3439
|
}
|
|
3524
3440
|
async cmdContext() {
|
|
3525
3441
|
if (this.isDemo) {
|
|
3526
|
-
const W =
|
|
3442
|
+
const W = 56;
|
|
3527
3443
|
const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
|
|
3528
|
-
const
|
|
3444
|
+
const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
|
|
3529
3445
|
const div = chalk7.dim("\u251C" + "\u2500".repeat(W - 2) + "\u2524");
|
|
3530
|
-
const
|
|
3531
|
-
const
|
|
3532
|
-
const pad = W - 4 - k.length -
|
|
3446
|
+
const kv = (k, v) => {
|
|
3447
|
+
const rv = v.replace(/\x1B\[[0-9;]*m/g, "");
|
|
3448
|
+
const pad = W - 4 - k.length - rv.length;
|
|
3533
3449
|
return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
|
|
3534
3450
|
};
|
|
3535
|
-
const
|
|
3536
|
-
const pad = W -
|
|
3537
|
-
return chalk7.dim("\u2502
|
|
3451
|
+
const fl = (f) => {
|
|
3452
|
+
const pad = W - 6 - f.length;
|
|
3453
|
+
return chalk7.dim("\u2502 \xB7 ") + chalk7.cyan(f) + " ".repeat(Math.max(0, pad)) + chalk7.dim("\u2502");
|
|
3538
3454
|
};
|
|
3539
|
-
const
|
|
3540
|
-
const
|
|
3541
|
-
|
|
3455
|
+
const ln = (s) => {
|
|
3456
|
+
const raw = s.replace(/\x1B\[[0-9;]*m/g, "");
|
|
3457
|
+
const pad = W - 4 - raw.length;
|
|
3458
|
+
return chalk7.dim("\u2502 ") + s + " ".repeat(Math.max(0, pad)) + chalk7.dim(" \u2502");
|
|
3542
3459
|
};
|
|
3543
3460
|
console.log(top);
|
|
3544
|
-
console.log(
|
|
3545
|
-
console.log(
|
|
3546
|
-
console.log(
|
|
3547
|
-
console.log(
|
|
3548
|
-
console.log(row("Shell", process.env.SHELL?.split("/").pop() || "unknown"));
|
|
3461
|
+
console.log(kv("Directory", chalk7.dim(process.cwd().replace(process.env.HOME || "", "~"))));
|
|
3462
|
+
console.log(kv("Git Branch", "main"));
|
|
3463
|
+
console.log(kv("Files", "34 files loaded"));
|
|
3464
|
+
console.log(kv("Framework", "FastAPI + TypeScript"));
|
|
3549
3465
|
console.log(div);
|
|
3550
|
-
console.log(
|
|
3551
|
-
const
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
console.log(
|
|
3466
|
+
console.log(ln(chalk7.bold("Top files")));
|
|
3467
|
+
for (const f of ["src/auth/auth_controller.ts", "src/middleware/jwt.middleware.ts", "src/services/user_service.ts", "package.json", "docker-compose.yml"]) {
|
|
3468
|
+
console.log(fl(f));
|
|
3469
|
+
}
|
|
3470
|
+
console.log(ln(chalk7.dim("... and 29 more")));
|
|
3471
|
+
console.log(bot);
|
|
3555
3472
|
} else if (this.sessionContext) {
|
|
3556
|
-
console.log(` Dir: ${
|
|
3557
|
-
if (this.sessionContext.git_branch) console.log(` Branch: ${this.sessionContext.git_branch}`);
|
|
3558
|
-
console.log();
|
|
3473
|
+
console.log(` Dir: ${this.sessionContext.cwd} \xB7 Files: ${this.sessionContext.files.length}`);
|
|
3559
3474
|
for (const f of this.sessionContext.files.slice(0, 10)) {
|
|
3560
3475
|
console.log(` ${chalk7.dim("\xB7")} ${chalk7.cyan(f.path)}`);
|
|
3561
3476
|
}
|
|
3562
|
-
if (this.sessionContext.files.length > 10) {
|
|
3563
|
-
console.log(chalk7.dim(` ... and ${this.sessionContext.files.length - 10} more`));
|
|
3564
|
-
}
|
|
3565
3477
|
} else {
|
|
3566
3478
|
logger.info("No context loaded yet. Send a message to load context.");
|
|
3567
3479
|
}
|
|
@@ -3571,43 +3483,41 @@ ${recent[recent.length - 1].content}`;
|
|
|
3571
3483
|
logger.info("No conversation history yet.");
|
|
3572
3484
|
return;
|
|
3573
3485
|
}
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
const
|
|
3577
|
-
const
|
|
3578
|
-
const preview = msg.content.length > 100 ? msg.content.substring(0, 100) + "\u2026" : msg.content;
|
|
3486
|
+
for (const m of this.conversationHistory.slice(-20)) {
|
|
3487
|
+
const label = m.role === "user" ? chalk7.green.bold("You ") : chalk7.cyan.bold("DevWing ");
|
|
3488
|
+
const time = chalk7.dim(m.timestamp.toLocaleTimeString());
|
|
3489
|
+
const preview = m.content.length > 100 ? m.content.slice(0, 100) + "\u2026" : m.content;
|
|
3579
3490
|
console.log(` ${time} ${label} ${preview}`);
|
|
3580
3491
|
}
|
|
3581
3492
|
}
|
|
3582
3493
|
async cmdConfig(args) {
|
|
3583
3494
|
if (!args) {
|
|
3584
|
-
const
|
|
3585
|
-
const W =
|
|
3495
|
+
const cfg = configManager.getAll();
|
|
3496
|
+
const W = 54;
|
|
3586
3497
|
const top = chalk7.dim("\u256D" + "\u2500".repeat(W - 2) + "\u256E");
|
|
3587
|
-
const
|
|
3588
|
-
const
|
|
3589
|
-
const
|
|
3590
|
-
const pad = W - 4 - k.length -
|
|
3498
|
+
const bot = chalk7.dim("\u2570" + "\u2500".repeat(W - 2) + "\u256F");
|
|
3499
|
+
const kv = (k, v) => {
|
|
3500
|
+
const rv = v.replace(/\x1B\[[0-9;]*m/g, "");
|
|
3501
|
+
const pad = W - 4 - k.length - rv.length;
|
|
3591
3502
|
return chalk7.dim("\u2502 ") + chalk7.bold(k) + " ".repeat(Math.max(0, pad)) + v + chalk7.dim(" \u2502");
|
|
3592
3503
|
};
|
|
3593
3504
|
console.log(top);
|
|
3594
|
-
console.log(
|
|
3595
|
-
console.log(
|
|
3596
|
-
console.log(
|
|
3597
|
-
console.log(
|
|
3598
|
-
console.log(
|
|
3599
|
-
console.log(
|
|
3600
|
-
console.log(
|
|
3505
|
+
console.log(kv("API URL", chalk7.dim(cfg.apiUrl || "default")));
|
|
3506
|
+
console.log(kv("Workspace", chalk7.dim(cfg.workspaceId || "none")));
|
|
3507
|
+
console.log(kv("Project", chalk7.dim(cfg.projectId || "none")));
|
|
3508
|
+
console.log(kv("Model", chalk7.dim(cfg.model || "auto")));
|
|
3509
|
+
console.log(kv("Mode", chalk7.dim(cfg.mode || "general")));
|
|
3510
|
+
console.log(kv("Config", chalk7.dim(configManager.getPath())));
|
|
3511
|
+
console.log(bot);
|
|
3601
3512
|
console.log(chalk7.dim(" /config set <key> <value> \xB7 /config get <key>"));
|
|
3602
3513
|
return;
|
|
3603
3514
|
}
|
|
3604
3515
|
const parts = args.split(/\s+/);
|
|
3605
|
-
|
|
3606
|
-
if (sub === "list") {
|
|
3516
|
+
if (parts[0] === "list") {
|
|
3607
3517
|
await configCommand("list");
|
|
3608
|
-
} else if (
|
|
3518
|
+
} else if (parts[0] === "get" && parts[1]) {
|
|
3609
3519
|
await configCommand("get", parts[1]);
|
|
3610
|
-
} else if (
|
|
3520
|
+
} else if (parts[0] === "set" && parts[1] && parts[2]) {
|
|
3611
3521
|
await configCommand("set", parts[1], parts[2]);
|
|
3612
3522
|
if (parts[1] === "mode") this.currentMode = parts[2];
|
|
3613
3523
|
if (parts[1] === "model") this.currentModel = parts[2];
|