sarangai-cli 1.0.12 → 1.0.13
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 +257 -44
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ var require_package = __commonJS({
|
|
|
35
35
|
"package.json"(exports2, module2) {
|
|
36
36
|
module2.exports = {
|
|
37
37
|
name: "sarangai-cli",
|
|
38
|
-
version: "1.0.
|
|
38
|
+
version: "1.0.13",
|
|
39
39
|
description: "CLI resmi SarangAI \u2014 Gateway multi-model AI langsung dari terminal Anda",
|
|
40
40
|
main: "dist/index.js",
|
|
41
41
|
bin: {
|
|
@@ -96,6 +96,8 @@ var import_chalk = __toESM(require("chalk"));
|
|
|
96
96
|
var import_readline = __toESM(require("readline"));
|
|
97
97
|
var import_child_process = require("child_process");
|
|
98
98
|
var import_crypto = __toESM(require("crypto"));
|
|
99
|
+
var import_fs2 = __toESM(require("fs"));
|
|
100
|
+
var import_path2 = __toESM(require("path"));
|
|
99
101
|
var import_clipboardy = __toESM(require("clipboardy"));
|
|
100
102
|
|
|
101
103
|
// src/config.ts
|
|
@@ -359,7 +361,9 @@ var respError;
|
|
|
359
361
|
var respStreaming = false;
|
|
360
362
|
var respScroll = null;
|
|
361
363
|
var respTotalLines = 0;
|
|
362
|
-
var
|
|
364
|
+
var conversationHistory = [];
|
|
365
|
+
var sessionScreen = false;
|
|
366
|
+
var respLastNotes = [];
|
|
363
367
|
function cleanupAndExit() {
|
|
364
368
|
if (timerInterval) clearInterval(timerInterval);
|
|
365
369
|
process.stdout.write("\x1B[?1049l\x1B[?25h\n");
|
|
@@ -373,8 +377,10 @@ function triggerCopyLink() {
|
|
|
373
377
|
function leaveWorkspace() {
|
|
374
378
|
inWorkspace = false;
|
|
375
379
|
isExecuting = false;
|
|
380
|
+
sessionScreen = false;
|
|
376
381
|
if (timerInterval) clearInterval(timerInterval);
|
|
377
382
|
currentTaskInput = "";
|
|
383
|
+
conversationHistory = [];
|
|
378
384
|
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
379
385
|
drawSelectionModal(activeModel, userMeta, expanded, cursorIdx, copiedNotice);
|
|
380
386
|
}
|
|
@@ -432,7 +438,130 @@ function styleBodyLine(l) {
|
|
|
432
438
|
if (l.style === "error") return import_chalk.default.red(l.text);
|
|
433
439
|
return import_chalk.default.hex("#e2e8f0")(l.text);
|
|
434
440
|
}
|
|
435
|
-
|
|
441
|
+
var EXT_BY_LANG = {
|
|
442
|
+
python: "py",
|
|
443
|
+
py: "py",
|
|
444
|
+
javascript: "js",
|
|
445
|
+
js: "js",
|
|
446
|
+
typescript: "ts",
|
|
447
|
+
ts: "ts",
|
|
448
|
+
tsx: "tsx",
|
|
449
|
+
jsx: "jsx",
|
|
450
|
+
go: "go",
|
|
451
|
+
rust: "rs",
|
|
452
|
+
rs: "rs",
|
|
453
|
+
java: "java",
|
|
454
|
+
c: "c",
|
|
455
|
+
cpp: "cpp",
|
|
456
|
+
"c++": "cpp",
|
|
457
|
+
csharp: "cs",
|
|
458
|
+
"c#": "cs",
|
|
459
|
+
cs: "cs",
|
|
460
|
+
php: "php",
|
|
461
|
+
ruby: "rb",
|
|
462
|
+
rb: "rb",
|
|
463
|
+
swift: "swift",
|
|
464
|
+
kotlin: "kt",
|
|
465
|
+
kt: "kt",
|
|
466
|
+
bash: "sh",
|
|
467
|
+
sh: "sh",
|
|
468
|
+
shell: "sh",
|
|
469
|
+
zsh: "sh",
|
|
470
|
+
powershell: "ps1",
|
|
471
|
+
ps1: "ps1",
|
|
472
|
+
html: "html",
|
|
473
|
+
css: "css",
|
|
474
|
+
scss: "scss",
|
|
475
|
+
sass: "sass",
|
|
476
|
+
less: "less",
|
|
477
|
+
sql: "sql",
|
|
478
|
+
json: "json",
|
|
479
|
+
yaml: "yaml",
|
|
480
|
+
yml: "yml",
|
|
481
|
+
toml: "toml",
|
|
482
|
+
xml: "xml",
|
|
483
|
+
markdown: "md",
|
|
484
|
+
md: "md",
|
|
485
|
+
dockerfile: "",
|
|
486
|
+
makefile: "",
|
|
487
|
+
ini: "ini",
|
|
488
|
+
env: "",
|
|
489
|
+
txt: "txt",
|
|
490
|
+
text: "txt",
|
|
491
|
+
csv: "csv"
|
|
492
|
+
};
|
|
493
|
+
function extractFilesFromMarkdown(text) {
|
|
494
|
+
const files = [];
|
|
495
|
+
const fence = /^[ \t]*```[ \t]*([A-Za-z0-9_#+.-]*)(?::[ \t]*([^\n`]+))?[^\n]*\n([\s\S]*?)^[ \t]*```[ \t]*$/gm;
|
|
496
|
+
let m;
|
|
497
|
+
while ((m = fence.exec(text)) !== null) {
|
|
498
|
+
const lang = (m[1] || "").toLowerCase().trim();
|
|
499
|
+
const fname = (m[2] || "").trim().replace(/^["']|["']$/g, "");
|
|
500
|
+
if (!fname) continue;
|
|
501
|
+
files.push({ filename: fname, content: m[3], lang: lang || "text" });
|
|
502
|
+
}
|
|
503
|
+
return files;
|
|
504
|
+
}
|
|
505
|
+
function sanitizeFilename(name) {
|
|
506
|
+
const base = import_path2.default.basename(name.trim());
|
|
507
|
+
if (!base || base === "." || base === "..") return null;
|
|
508
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(base)) return null;
|
|
509
|
+
if (base.includes("..")) return null;
|
|
510
|
+
return base;
|
|
511
|
+
}
|
|
512
|
+
function defaultFilename(lang) {
|
|
513
|
+
if (lang === "dockerfile") return "Dockerfile";
|
|
514
|
+
if (lang === "makefile") return "Makefile";
|
|
515
|
+
const ext = EXT_BY_LANG[lang];
|
|
516
|
+
return ext === void 0 ? null : `snippet.${ext || "txt"}`;
|
|
517
|
+
}
|
|
518
|
+
function extractAllFiles(text) {
|
|
519
|
+
const extracted = extractFilesFromMarkdown(text);
|
|
520
|
+
if (extracted.length) return extracted;
|
|
521
|
+
const fence2 = /^[ \t]*```[ \t]*([A-Za-z0-9_#+.-]+)[ \t]*\n([\s\S]*?)^[ \t]*```[ \t]*$/gm;
|
|
522
|
+
let best = null;
|
|
523
|
+
let m;
|
|
524
|
+
while ((m = fence2.exec(text)) !== null) {
|
|
525
|
+
const lang = (m[1] || "").toLowerCase();
|
|
526
|
+
if (!EXT_BY_LANG.hasOwnProperty(lang) || lang === "text") continue;
|
|
527
|
+
const content = m[2];
|
|
528
|
+
if (content.trim().split("\n").length < 2) continue;
|
|
529
|
+
if (!best || content.length > best.content.length) {
|
|
530
|
+
best = { filename: defaultFilename(lang) || "snippet.txt", content, lang };
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return best ? [best] : [];
|
|
534
|
+
}
|
|
535
|
+
function writeExtractedFiles(files) {
|
|
536
|
+
const notes = [];
|
|
537
|
+
for (const f of files) {
|
|
538
|
+
const safe = sanitizeFilename(f.filename);
|
|
539
|
+
if (!safe) {
|
|
540
|
+
notes.push(import_chalk.default.red(`\u2715 Skip nama file tidak valid: ${f.filename}`));
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
try {
|
|
544
|
+
const target = import_path2.default.join(process.cwd(), safe);
|
|
545
|
+
import_fs2.default.writeFileSync(target, f.content, "utf-8");
|
|
546
|
+
const bytes = Buffer.byteLength(f.content, "utf-8");
|
|
547
|
+
notes.push(import_chalk.default.green(`\u2714 File created: ${safe}`) + import_chalk.default.gray(` (${bytes} bytes)`));
|
|
548
|
+
} catch (err) {
|
|
549
|
+
notes.push(import_chalk.default.red(`\u2715 Gagal menulis ${safe}: ${err.message}`));
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return notes;
|
|
553
|
+
}
|
|
554
|
+
var AGENT_SYSTEM_PROMPT = [
|
|
555
|
+
"Anda adalah asisten coding agent SarangAI yang berjalan di terminal pengguna.",
|
|
556
|
+
`Direktori kerja aktif: ${process.cwd()}.`,
|
|
557
|
+
"Jika permintaan pengguna melibatkan pembuatan atau perubahan file, SELALU output kode dalam fenced code block dengan format ````bahasa:nama-file` pada baris pembuka, contoh: ```python:binary_search.py",
|
|
558
|
+
"Gunakan SATU file per code block dan jangan menambah teks setelah baris penutup ``` milik block tersebut.",
|
|
559
|
+
"Untuk file konfigurasi tanpa ekstensi gunakan ```dockerfile:Dockerfile atau ```makefile:Makefile.",
|
|
560
|
+
"Jelaskan secara singkat sebelum/sesudah code block, tanpa membungkus nama file di backtick atau bold."
|
|
561
|
+
].join("\n");
|
|
562
|
+
var lastTurn = { query: "", text: "", streaming: false, notes: [] };
|
|
563
|
+
var activeAbort = null;
|
|
564
|
+
function drawSessionScreen(turn, opts = { inputMode: "idle" }) {
|
|
436
565
|
const boxWidth = 72;
|
|
437
566
|
const termRows = process.stdout.rows || 30;
|
|
438
567
|
const pad = getCenterPad(boxWidth);
|
|
@@ -441,20 +570,19 @@ function drawResponseScreen(query, text, opts) {
|
|
|
441
570
|
const lines = [];
|
|
442
571
|
SARANG_BANNER.forEach((l) => lines.push(pad + import_chalk.default.white.bold(l)));
|
|
443
572
|
lines.push("");
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
573
|
+
const mm = Math.floor(sessionTimeLeft / 60);
|
|
574
|
+
const ss = sessionTimeLeft % 60;
|
|
575
|
+
lines.push(pad + import_chalk.default.gray(`${conversationHistory.length} pesan \u2022 ${activeModel.name} \u2022 ${mm}m ${ss < 10 ? "0" : ""}${ss}s left`));
|
|
576
|
+
lines.push("");
|
|
577
|
+
const qLabel = turn.query.length > 64 ? turn.query.slice(0, 61) + "\u2026" : turn.query;
|
|
578
|
+
lines.push(pad + import_chalk.default.bold.hex("#c084fc")(`\u203A Task: ${qLabel}`));
|
|
579
|
+
const headerRows = lines.length + 1;
|
|
580
|
+
const footerRows = opts.inputMode === "typing" ? 7 : 3;
|
|
581
|
+
const viewRows = Math.max(4, termRows - headerRows - footerRows);
|
|
582
|
+
const body = turn.error ? wrapWithStyles(turn.error, contentW).map((l) => ({ ...l, style: "error" })) : wrapWithStyles(turn.text, contentW);
|
|
453
583
|
respTotalLines = body.length;
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
const visible = body.slice(start, start + viewRows);
|
|
457
|
-
respViewRows = viewRows;
|
|
584
|
+
const visible = turn.streaming ? body.slice(0, viewRows) : body.slice(Math.max(0, body.length - viewRows));
|
|
585
|
+
lines.push(pad + import_chalk.default.gray("\u250C" + "\u2500".repeat(innerW) + "\u2510"));
|
|
458
586
|
for (const l of visible) {
|
|
459
587
|
const styled = styleBodyLine(l);
|
|
460
588
|
const fill = Math.max(0, contentW - visibleLength(styled));
|
|
@@ -464,31 +592,47 @@ function drawResponseScreen(query, text, opts) {
|
|
|
464
592
|
lines.push(pad + import_chalk.default.gray("\u2502 ") + " ".repeat(contentW) + import_chalk.default.gray(" \u2502"));
|
|
465
593
|
}
|
|
466
594
|
lines.push(pad + import_chalk.default.gray("\u2514" + "\u2500".repeat(innerW) + "\u2518"));
|
|
467
|
-
lines.push("");
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
595
|
+
for (const note of turn.notes) lines.push(pad + " " + note);
|
|
596
|
+
if (opts.inputMode === "typing") {
|
|
597
|
+
lines.push("");
|
|
598
|
+
const textContent = currentTaskInput ? currentTaskInput : import_chalk.default.gray("Ketik follow-up\u2026 (/new = sesi baru, exit = keluar)");
|
|
599
|
+
const visibleLen = currentTaskInput ? currentTaskInput.length : 55;
|
|
600
|
+
const paddingRight = Math.max(0, innerW - 2 - visibleLen);
|
|
601
|
+
lines.push(pad + import_chalk.default.gray("\u250C" + "\u2500".repeat(innerW) + "\u2510"));
|
|
602
|
+
const inputRowNumber = lines.length + 1;
|
|
603
|
+
lines.push(pad + import_chalk.default.gray(`\u2502 ${textContent}${" ".repeat(paddingRight)}\u2502`));
|
|
604
|
+
lines.push(pad + import_chalk.default.gray("\u2514" + "\u2500".repeat(innerW) + "\u2518"));
|
|
605
|
+
process.stdout.write("\x1B[2J\x1B[3J\x1B[H" + lines.join("\n"));
|
|
606
|
+
const cursorCol = pad.length + 5 + currentTaskInput.length;
|
|
607
|
+
process.stdout.write(`\x1B[${inputRowNumber};${cursorCol}H\x1B[?25h`);
|
|
608
|
+
} else {
|
|
609
|
+
lines.push("");
|
|
610
|
+
const status = turn.streaming ? import_chalk.default.gray(`[Streaming \u2022 ${activeModel.name} \u2022 ${respTotalLines} baris] Esc = batalkan`) : turn.error ? import_chalk.default.red("\u2715 Error") + import_chalk.default.gray(" \u2022 tekan tombol untuk lanjut") : import_chalk.default.green("\u2714 Selesai") + import_chalk.default.gray(` \u2022 ${respTotalLines} baris \u2022 ketik follow-up di bawah \u2022 Esc = menu`);
|
|
611
|
+
lines.push(pad + status);
|
|
612
|
+
process.stdout.write("\x1B[2J\x1B[3J\x1B[H" + lines.join("\n"));
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function enterSessionInput() {
|
|
616
|
+
currentTaskInput = "";
|
|
617
|
+
sessionScreen = true;
|
|
618
|
+
drawSessionScreen(lastTurn, { inputMode: "typing" });
|
|
619
|
+
}
|
|
620
|
+
function redrawInputScreen() {
|
|
621
|
+
if (sessionScreen) drawSessionScreen(lastTurn, { inputMode: "typing" });
|
|
622
|
+
else drawWorkspaceScreen(activeModel, sessionTimeLeft, currentTaskInput);
|
|
473
623
|
}
|
|
474
|
-
function
|
|
624
|
+
function waitSessionKeypress() {
|
|
475
625
|
process.stdin.once("keypress", (str, key) => {
|
|
476
626
|
if (key?.ctrl && key?.name === "c") {
|
|
477
627
|
cleanupAndExit();
|
|
478
628
|
return;
|
|
479
629
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
respScroll = key.name === "up" ? Math.max(0, current - 3) : Math.min(maxStart, current + 3);
|
|
484
|
-
drawResponseScreen(respQuery, respText, { streaming: false, error: respError });
|
|
485
|
-
waitResponseDismiss();
|
|
630
|
+
isExecuting = false;
|
|
631
|
+
if (key?.name === "escape") {
|
|
632
|
+
leaveWorkspace();
|
|
486
633
|
return;
|
|
487
634
|
}
|
|
488
|
-
|
|
489
|
-
currentTaskInput = "";
|
|
490
|
-
respScroll = null;
|
|
491
|
-
drawWorkspaceScreen(activeModel, sessionTimeLeft, currentTaskInput);
|
|
635
|
+
enterSessionInput();
|
|
492
636
|
});
|
|
493
637
|
}
|
|
494
638
|
async function runPromptStream(query, isRetry = false) {
|
|
@@ -498,15 +642,18 @@ async function runPromptStream(query, isRetry = false) {
|
|
|
498
642
|
respText = "";
|
|
499
643
|
respError = void 0;
|
|
500
644
|
respScroll = null;
|
|
645
|
+
activeAbort = new AbortController();
|
|
501
646
|
process.stdout.write("\x1B[?25l");
|
|
647
|
+
lastTurn = { query, text: "", streaming: true, notes: [] };
|
|
648
|
+
drawSessionScreen(lastTurn);
|
|
502
649
|
let lastPaint = 0;
|
|
503
650
|
const repaint = (streaming, force = false) => {
|
|
504
651
|
const now = Date.now();
|
|
505
652
|
if (!force && now - lastPaint < 80) return;
|
|
506
653
|
lastPaint = now;
|
|
507
|
-
|
|
654
|
+
lastTurn = { query, text: respText, streaming, error: respError, notes: lastTurn.notes };
|
|
655
|
+
drawSessionScreen(lastTurn);
|
|
508
656
|
};
|
|
509
|
-
repaint(true, true);
|
|
510
657
|
try {
|
|
511
658
|
const res = await fetch(`${baseUrl}/api/gateway/v1/chat/completions`, {
|
|
512
659
|
method: "POST",
|
|
@@ -514,9 +661,13 @@ async function runPromptStream(query, isRetry = false) {
|
|
|
514
661
|
"Content-Type": "application/json",
|
|
515
662
|
Authorization: `Bearer ${cfg.apiKey}`
|
|
516
663
|
},
|
|
664
|
+
signal: activeAbort.signal,
|
|
517
665
|
body: JSON.stringify({
|
|
518
666
|
model: activeModel.id,
|
|
519
|
-
messages: [
|
|
667
|
+
messages: [
|
|
668
|
+
{ role: "system", content: AGENT_SYSTEM_PROMPT },
|
|
669
|
+
...conversationHistory
|
|
670
|
+
],
|
|
520
671
|
stream: true
|
|
521
672
|
})
|
|
522
673
|
});
|
|
@@ -576,11 +727,26 @@ async function runPromptStream(query, isRetry = false) {
|
|
|
576
727
|
}
|
|
577
728
|
}
|
|
578
729
|
} catch (err) {
|
|
579
|
-
|
|
580
|
-
|
|
730
|
+
if (err?.name !== "AbortError") {
|
|
731
|
+
respError = err.message;
|
|
732
|
+
repaint(false, true);
|
|
733
|
+
} else {
|
|
734
|
+
respText += "\n[ dibatalkan oleh pengguna ]";
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
let notes = [];
|
|
738
|
+
if (respText && !respError) {
|
|
739
|
+
notes = writeExtractedFiles(extractAllFiles(respText));
|
|
581
740
|
}
|
|
582
741
|
respStreaming = false;
|
|
583
|
-
|
|
742
|
+
lastTurn = { query, text: respText, streaming: false, error: respError, notes };
|
|
743
|
+
drawSessionScreen(lastTurn);
|
|
744
|
+
if (respError && !isRetry) {
|
|
745
|
+
conversationHistory.push({ role: "assistant", content: respText || "(error)" });
|
|
746
|
+
} else if (respText) {
|
|
747
|
+
conversationHistory.push({ role: "assistant", content: respText });
|
|
748
|
+
}
|
|
749
|
+
waitSessionKeypress();
|
|
584
750
|
}
|
|
585
751
|
async function handleAutoAuth(baseUrl2) {
|
|
586
752
|
const sessionCode = "SA-" + import_crypto.default.randomBytes(4).toString("hex").toUpperCase();
|
|
@@ -639,9 +805,9 @@ async function startInteractiveSession() {
|
|
|
639
805
|
process.stdout.on("resize", () => {
|
|
640
806
|
if (inWorkspace) {
|
|
641
807
|
if (isExecuting) {
|
|
642
|
-
|
|
808
|
+
drawSessionScreen(lastTurn);
|
|
643
809
|
} else {
|
|
644
|
-
|
|
810
|
+
redrawInputScreen();
|
|
645
811
|
}
|
|
646
812
|
} else {
|
|
647
813
|
drawSelectionModal(activeModel, userMeta, expanded, cursorIdx, copiedNotice);
|
|
@@ -654,7 +820,47 @@ async function startInteractiveSession() {
|
|
|
654
820
|
}
|
|
655
821
|
if (inWorkspace) {
|
|
656
822
|
if (isExecuting) {
|
|
657
|
-
if (key.name === "escape")
|
|
823
|
+
if (key.name === "escape" && activeAbort) {
|
|
824
|
+
activeAbort.abort();
|
|
825
|
+
}
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (sessionScreen) {
|
|
829
|
+
if (key.name === "escape") {
|
|
830
|
+
leaveWorkspace();
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
if (key.name === "return") {
|
|
834
|
+
const query = currentTaskInput.trim();
|
|
835
|
+
if (!query) return;
|
|
836
|
+
if (query === "/exit" || query === "exit" || query === "quit" || query === ":q") {
|
|
837
|
+
leaveWorkspace();
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
if (query === "/model" || query === "/back") {
|
|
841
|
+
leaveWorkspace();
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
if (query === "/new" || query === "/reset") {
|
|
845
|
+
conversationHistory = [];
|
|
846
|
+
respLastNotes = [];
|
|
847
|
+
enterWorkspace();
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
conversationHistory.push({ role: "user", content: query });
|
|
851
|
+
runPromptStream(query);
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
if (key.name === "backspace") {
|
|
855
|
+
currentTaskInput = currentTaskInput.slice(0, -1);
|
|
856
|
+
redrawInputScreen();
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (str && !key.ctrl && !key.meta && !str.startsWith("\x1B")) {
|
|
860
|
+
currentTaskInput += str;
|
|
861
|
+
redrawInputScreen();
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
658
864
|
return;
|
|
659
865
|
}
|
|
660
866
|
if (key.name === "escape") {
|
|
@@ -664,14 +870,21 @@ async function startInteractiveSession() {
|
|
|
664
870
|
if (key.name === "return") {
|
|
665
871
|
const query = currentTaskInput.trim();
|
|
666
872
|
if (!query) return;
|
|
667
|
-
if (query === "/exit" || query === "exit" || query === ":q") {
|
|
668
|
-
|
|
873
|
+
if (query === "/exit" || query === "exit" || query === "quit" || query === ":q") {
|
|
874
|
+
leaveWorkspace();
|
|
669
875
|
return;
|
|
670
876
|
}
|
|
671
877
|
if (query === "/model" || query === "/back") {
|
|
672
878
|
leaveWorkspace();
|
|
673
879
|
return;
|
|
674
880
|
}
|
|
881
|
+
if (query === "/new" || query === "/reset") {
|
|
882
|
+
conversationHistory = [];
|
|
883
|
+
respLastNotes = [];
|
|
884
|
+
enterWorkspace();
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
conversationHistory.push({ role: "user", content: query });
|
|
675
888
|
runPromptStream(query);
|
|
676
889
|
return;
|
|
677
890
|
}
|