sarangai-cli 1.0.13 → 1.0.14
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 +109 -41
- 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.14",
|
|
39
39
|
description: "CLI resmi SarangAI \u2014 Gateway multi-model AI langsung dari terminal Anda",
|
|
40
40
|
main: "dist/index.js",
|
|
41
41
|
bin: {
|
|
@@ -206,7 +206,25 @@ async function fetchUserMeta(baseUrl2, apiKey) {
|
|
|
206
206
|
}
|
|
207
207
|
} catch {
|
|
208
208
|
}
|
|
209
|
-
return
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
async function refreshUserMeta() {
|
|
212
|
+
const fresh = await fetchUserMeta(baseUrl, cfg?.apiKey || "");
|
|
213
|
+
if (!fresh) return;
|
|
214
|
+
const changed = !userMeta || fresh.balance !== userMeta.balance || fresh.tier !== userMeta.tier || fresh.accountId !== userMeta.accountId;
|
|
215
|
+
userMeta = fresh;
|
|
216
|
+
if (!changed) return;
|
|
217
|
+
if (inWorkspace) {
|
|
218
|
+
if (isExecuting) drawSessionScreen(lastTurn);
|
|
219
|
+
else if (sessionScreen) drawSessionScreen(lastTurn, { inputMode: "typing" });
|
|
220
|
+
else drawWorkspaceScreen(activeModel, sessionTimeLeft, currentTaskInput);
|
|
221
|
+
} else {
|
|
222
|
+
drawSelectionModal(activeModel, userMeta, expanded, cursorIdx, copiedNotice);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function redrawWithFreshBalance() {
|
|
226
|
+
drawSelectionModal(activeModel, userMeta, expanded, cursorIdx, copiedNotice);
|
|
227
|
+
refreshUserMeta();
|
|
210
228
|
}
|
|
211
229
|
function getDisplayDir() {
|
|
212
230
|
const cwd = process.cwd();
|
|
@@ -259,7 +277,7 @@ function drawSelectionModal(activeModel2, userMeta2, expanded2, cursorIdx2, copi
|
|
|
259
277
|
if (!expanded2) {
|
|
260
278
|
lines.push(...renderCard(activeModel2, cursorIdx2 === 0, cardWidth, pad));
|
|
261
279
|
lines.push("");
|
|
262
|
-
const balanceStr = `${Number(userMeta2
|
|
280
|
+
const balanceStr = `${Number(userMeta2?.balance ?? 0).toLocaleString()} Credits`;
|
|
263
281
|
lines.push(
|
|
264
282
|
pad + import_chalk.default.bold.white(userMeta2.tier) + import_chalk.default.gray(" \u2022 ") + import_chalk.default.yellow.bold(balanceStr) + import_chalk.default.gray(" remaining \u2022 ") + import_chalk.default.hex("#a855f7")(userMeta2.accountId)
|
|
265
283
|
);
|
|
@@ -283,7 +301,7 @@ function drawSelectionModal(activeModel2, userMeta2, expanded2, cursorIdx2, copi
|
|
|
283
301
|
lines.push(...renderCard(m, cursorIdx2 === idx + 10, cardWidth, pad));
|
|
284
302
|
});
|
|
285
303
|
lines.push("");
|
|
286
|
-
const balanceStr = `${Number(userMeta2
|
|
304
|
+
const balanceStr = `${Number(userMeta2?.balance ?? 0).toLocaleString()} Credits`;
|
|
287
305
|
lines.push(
|
|
288
306
|
pad + import_chalk.default.bold.white(userMeta2.tier) + import_chalk.default.gray(" \u2022 ") + import_chalk.default.yellow.bold(balanceStr) + import_chalk.default.gray(" remaining \u2022 ") + import_chalk.default.hex("#a855f7")(userMeta2.accountId)
|
|
289
307
|
);
|
|
@@ -349,7 +367,7 @@ var expanded = false;
|
|
|
349
367
|
var cursorIdx = 0;
|
|
350
368
|
var copiedNotice = false;
|
|
351
369
|
var activeModel;
|
|
352
|
-
var userMeta;
|
|
370
|
+
var userMeta = null;
|
|
353
371
|
var sessionTimeLeft = 3600;
|
|
354
372
|
var timerInterval = null;
|
|
355
373
|
var currentTaskInput = "";
|
|
@@ -361,12 +379,14 @@ var respError;
|
|
|
361
379
|
var respStreaming = false;
|
|
362
380
|
var respScroll = null;
|
|
363
381
|
var respTotalLines = 0;
|
|
382
|
+
var respViewRows = 12;
|
|
364
383
|
var conversationHistory = [];
|
|
365
384
|
var sessionScreen = false;
|
|
366
385
|
var respLastNotes = [];
|
|
386
|
+
var keyBuffer = "";
|
|
367
387
|
function cleanupAndExit() {
|
|
368
388
|
if (timerInterval) clearInterval(timerInterval);
|
|
369
|
-
process.stdout.write("\x1B[?1049l\x1B[?25h\n");
|
|
389
|
+
process.stdout.write("\x1B[?1000l\x1B[?1006l\x1B[?1049l\x1B[?25h\n");
|
|
370
390
|
process.exit(0);
|
|
371
391
|
}
|
|
372
392
|
function triggerCopyLink() {
|
|
@@ -380,21 +400,23 @@ function leaveWorkspace() {
|
|
|
380
400
|
sessionScreen = false;
|
|
381
401
|
if (timerInterval) clearInterval(timerInterval);
|
|
382
402
|
currentTaskInput = "";
|
|
403
|
+
respScroll = null;
|
|
383
404
|
conversationHistory = [];
|
|
384
405
|
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
385
|
-
|
|
406
|
+
redrawWithFreshBalance();
|
|
386
407
|
}
|
|
387
408
|
function enterWorkspace() {
|
|
388
409
|
inWorkspace = true;
|
|
389
410
|
isExecuting = false;
|
|
390
411
|
currentTaskInput = "";
|
|
391
|
-
|
|
412
|
+
sessionScreen = true;
|
|
413
|
+
redrawInputScreen();
|
|
392
414
|
if (timerInterval) clearInterval(timerInterval);
|
|
393
415
|
timerInterval = setInterval(() => {
|
|
394
416
|
if (sessionTimeLeft > 0) {
|
|
395
417
|
sessionTimeLeft--;
|
|
396
418
|
if (inWorkspace && !isExecuting) {
|
|
397
|
-
|
|
419
|
+
redrawInputScreen();
|
|
398
420
|
}
|
|
399
421
|
}
|
|
400
422
|
}, 1e3);
|
|
@@ -561,6 +583,17 @@ var AGENT_SYSTEM_PROMPT = [
|
|
|
561
583
|
].join("\n");
|
|
562
584
|
var lastTurn = { query: "", text: "", streaming: false, notes: [] };
|
|
563
585
|
var activeAbort = null;
|
|
586
|
+
function buildTranscript(width) {
|
|
587
|
+
const out = [];
|
|
588
|
+
for (let i = 0; i + 1 < conversationHistory.length; i += 2) {
|
|
589
|
+
const q = conversationHistory[i]?.content ?? "";
|
|
590
|
+
const a = conversationHistory[i + 1]?.content ?? "";
|
|
591
|
+
out.push({ text: `\u203A Anda: ${q}`, style: "query" });
|
|
592
|
+
if (a && a !== "(error)") out.push(...wrapWithStyles(a, width));
|
|
593
|
+
out.push({ text: "", style: "plain" });
|
|
594
|
+
}
|
|
595
|
+
return out;
|
|
596
|
+
}
|
|
564
597
|
function drawSessionScreen(turn, opts = { inputMode: "idle" }) {
|
|
565
598
|
const boxWidth = 72;
|
|
566
599
|
const termRows = process.stdout.rows || 30;
|
|
@@ -574,17 +607,27 @@ function drawSessionScreen(turn, opts = { inputMode: "idle" }) {
|
|
|
574
607
|
const ss = sessionTimeLeft % 60;
|
|
575
608
|
lines.push(pad + import_chalk.default.gray(`${conversationHistory.length} pesan \u2022 ${activeModel.name} \u2022 ${mm}m ${ss < 10 ? "0" : ""}${ss}s left`));
|
|
576
609
|
lines.push("");
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
610
|
+
const transcript = buildTranscript(contentW);
|
|
611
|
+
if (!turn.streaming) {
|
|
612
|
+
transcript.push({ text: `\u203A Anda: ${turn.query}`, style: "query" });
|
|
613
|
+
if (turn.error) {
|
|
614
|
+
transcript.push(...wrapWithStyles(turn.error, contentW).map((l) => ({ ...l, style: "error" })));
|
|
615
|
+
} else if (turn.text) {
|
|
616
|
+
transcript.push(...wrapWithStyles(turn.text, contentW));
|
|
617
|
+
}
|
|
618
|
+
for (const note of turn.notes) transcript.push({ text: note, style: "plain" });
|
|
619
|
+
}
|
|
620
|
+
respTotalLines = transcript.length;
|
|
621
|
+
const headerRows = lines.length;
|
|
622
|
+
const footerRows = opts.inputMode === "typing" ? 8 : 4;
|
|
581
623
|
const viewRows = Math.max(4, termRows - headerRows - footerRows);
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
const visible =
|
|
624
|
+
const maxStart = Math.max(0, respTotalLines - viewRows);
|
|
625
|
+
const start = turn.streaming ? maxStart : Math.min(respScroll ?? maxStart, maxStart);
|
|
626
|
+
const visible = transcript.slice(start, start + viewRows);
|
|
627
|
+
respViewRows = viewRows;
|
|
585
628
|
lines.push(pad + import_chalk.default.gray("\u250C" + "\u2500".repeat(innerW) + "\u2510"));
|
|
586
629
|
for (const l of visible) {
|
|
587
|
-
const styled = styleBodyLine(l);
|
|
630
|
+
const styled = l.style === "query" ? import_chalk.default.bold.hex("#c084fc")(l.text) : styleBodyLine(l);
|
|
588
631
|
const fill = Math.max(0, contentW - visibleLength(styled));
|
|
589
632
|
lines.push(pad + import_chalk.default.gray("\u2502 ") + styled + " ".repeat(fill) + import_chalk.default.gray(" \u2502"));
|
|
590
633
|
}
|
|
@@ -592,11 +635,12 @@ function drawSessionScreen(turn, opts = { inputMode: "idle" }) {
|
|
|
592
635
|
lines.push(pad + import_chalk.default.gray("\u2502 ") + " ".repeat(contentW) + import_chalk.default.gray(" \u2502"));
|
|
593
636
|
}
|
|
594
637
|
lines.push(pad + import_chalk.default.gray("\u2514" + "\u2500".repeat(innerW) + "\u2518"));
|
|
595
|
-
for (const note of turn.notes) lines.push(pad + " " + note);
|
|
596
638
|
if (opts.inputMode === "typing") {
|
|
597
639
|
lines.push("");
|
|
598
|
-
const
|
|
599
|
-
|
|
640
|
+
const scrollTag = start > 0 || start < maxStart ? import_chalk.default.gray(`[\u2191/\u2193/mouse scroll \u2022 ${start + 1}-${Math.min(respTotalLines, start + viewRows)}/${respTotalLines}] `) : "";
|
|
641
|
+
lines.push(pad + scrollTag + import_chalk.default.gray("(/new = sesi baru \u2022 exit = keluar \u2022 Esc = menu)"));
|
|
642
|
+
const textContent = currentTaskInput ? currentTaskInput : import_chalk.default.gray("Ketik follow-up\u2026");
|
|
643
|
+
const visibleLen = currentTaskInput ? currentTaskInput.length : 17;
|
|
600
644
|
const paddingRight = Math.max(0, innerW - 2 - visibleLen);
|
|
601
645
|
lines.push(pad + import_chalk.default.gray("\u250C" + "\u2500".repeat(innerW) + "\u2510"));
|
|
602
646
|
const inputRowNumber = lines.length + 1;
|
|
@@ -607,33 +651,30 @@ function drawSessionScreen(turn, opts = { inputMode: "idle" }) {
|
|
|
607
651
|
process.stdout.write(`\x1B[${inputRowNumber};${cursorCol}H\x1B[?25h`);
|
|
608
652
|
} else {
|
|
609
653
|
lines.push("");
|
|
610
|
-
const
|
|
654
|
+
const range = start > 0 || start < maxStart ? ` \u2022 ${start + 1}-${Math.min(respTotalLines, start + viewRows)}/${respTotalLines}` : ` \u2022 ${respTotalLines} baris`;
|
|
655
|
+
const status = turn.streaming ? import_chalk.default.gray(`[Streaming \u2022 ${activeModel.name}${range}] Esc = batalkan`) : turn.error ? import_chalk.default.red("\u2715 Error") + import_chalk.default.gray(`${range} \u2022 tekan tombol untuk lanjut`) : import_chalk.default.green("\u2714 Selesai") + import_chalk.default.gray(`${range} \u2022 ketik follow-up di bawah \u2022 Esc = menu`);
|
|
611
656
|
lines.push(pad + status);
|
|
612
657
|
process.stdout.write("\x1B[2J\x1B[3J\x1B[H" + lines.join("\n"));
|
|
613
658
|
}
|
|
614
659
|
}
|
|
615
|
-
function
|
|
616
|
-
|
|
617
|
-
|
|
660
|
+
function scrollSession(delta) {
|
|
661
|
+
const maxStart = Math.max(0, respTotalLines - respViewRows);
|
|
662
|
+
const current = respScroll ?? maxStart;
|
|
663
|
+
respScroll = Math.max(0, Math.min(maxStart, current + delta));
|
|
618
664
|
drawSessionScreen(lastTurn, { inputMode: "typing" });
|
|
619
665
|
}
|
|
620
666
|
function redrawInputScreen() {
|
|
621
667
|
if (sessionScreen) drawSessionScreen(lastTurn, { inputMode: "typing" });
|
|
622
668
|
else drawWorkspaceScreen(activeModel, sessionTimeLeft, currentTaskInput);
|
|
623
669
|
}
|
|
624
|
-
function
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
leaveWorkspace();
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
enterSessionInput();
|
|
636
|
-
});
|
|
670
|
+
function finishTurnToInput() {
|
|
671
|
+
refreshUserMeta();
|
|
672
|
+
isExecuting = false;
|
|
673
|
+
sessionScreen = true;
|
|
674
|
+
currentTaskInput = keyBuffer;
|
|
675
|
+
keyBuffer = "";
|
|
676
|
+
respScroll = null;
|
|
677
|
+
drawSessionScreen(lastTurn, { inputMode: "typing" });
|
|
637
678
|
}
|
|
638
679
|
async function runPromptStream(query, isRetry = false) {
|
|
639
680
|
isExecuting = true;
|
|
@@ -692,7 +733,7 @@ async function runPromptStream(query, isRetry = false) {
|
|
|
692
733
|
const newKey = await handleAutoAuth(baseUrl);
|
|
693
734
|
if (newKey) {
|
|
694
735
|
cfg = getConfig();
|
|
695
|
-
userMeta = await fetchUserMeta(baseUrl, cfg.apiKey || "");
|
|
736
|
+
userMeta = await fetchUserMeta(baseUrl, cfg.apiKey || "") ?? userMeta;
|
|
696
737
|
return runPromptStream(query, true);
|
|
697
738
|
}
|
|
698
739
|
respError = "Otorisasi ulang dibatalkan.";
|
|
@@ -740,13 +781,12 @@ async function runPromptStream(query, isRetry = false) {
|
|
|
740
781
|
}
|
|
741
782
|
respStreaming = false;
|
|
742
783
|
lastTurn = { query, text: respText, streaming: false, error: respError, notes };
|
|
743
|
-
drawSessionScreen(lastTurn);
|
|
744
784
|
if (respError && !isRetry) {
|
|
745
785
|
conversationHistory.push({ role: "assistant", content: respText || "(error)" });
|
|
746
786
|
} else if (respText) {
|
|
747
787
|
conversationHistory.push({ role: "assistant", content: respText });
|
|
748
788
|
}
|
|
749
|
-
|
|
789
|
+
finishTurnToInput();
|
|
750
790
|
}
|
|
751
791
|
async function handleAutoAuth(baseUrl2) {
|
|
752
792
|
const sessionCode = "SA-" + import_crypto.default.randomBytes(4).toString("hex").toUpperCase();
|
|
@@ -796,7 +836,7 @@ async function startInteractiveSession() {
|
|
|
796
836
|
}
|
|
797
837
|
userMeta = await fetchUserMeta(baseUrl, cfg.apiKey || "");
|
|
798
838
|
activeModel = CODING_MODELS.find((m) => m.id === cfg.defaultModel) || CODING_MODELS[0];
|
|
799
|
-
process.stdout.write("\x1B[?1049h\x1B[2J\x1B[3J\x1B[H");
|
|
839
|
+
process.stdout.write("\x1B[?1049h\x1B[2J\x1B[3J\x1B[H\x1B[?1000h\x1B[?1006h");
|
|
800
840
|
import_readline.default.emitKeypressEvents(process.stdin);
|
|
801
841
|
if (process.stdin.isTTY) {
|
|
802
842
|
process.stdin.setRawMode(true);
|
|
@@ -813,6 +853,26 @@ async function startInteractiveSession() {
|
|
|
813
853
|
drawSelectionModal(activeModel, userMeta, expanded, cursorIdx, copiedNotice);
|
|
814
854
|
}
|
|
815
855
|
});
|
|
856
|
+
process.stdin.on("data", (buf) => {
|
|
857
|
+
const s = buf.toString("utf8");
|
|
858
|
+
if (!inWorkspace) return;
|
|
859
|
+
let delta = 0;
|
|
860
|
+
const m = s.match(/^\x1b\[(?:M([\s\S]{3})|<(?:0|64|65);(\d+);(\d+)[Mm])/);
|
|
861
|
+
if (m) {
|
|
862
|
+
if (m[1]) {
|
|
863
|
+
const b = m[1].charCodeAt(0) - 32;
|
|
864
|
+
delta = b === 64 ? -3 : b === 65 ? 3 : 0;
|
|
865
|
+
} else {
|
|
866
|
+
delta = s.includes("<64") ? -3 : s.includes("<65") ? 3 : 0;
|
|
867
|
+
}
|
|
868
|
+
} else if (s === "\x1B[A") delta = -3;
|
|
869
|
+
else if (s === "\x1B[B") delta = 3;
|
|
870
|
+
if (delta !== 0 && !isExecuting && sessionScreen) {
|
|
871
|
+
scrollSession(delta);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
if (delta !== 0) return;
|
|
875
|
+
});
|
|
816
876
|
process.stdin.on("keypress", (str, key) => {
|
|
817
877
|
if (key.ctrl && key.name === "c") {
|
|
818
878
|
cleanupAndExit();
|
|
@@ -822,6 +882,10 @@ async function startInteractiveSession() {
|
|
|
822
882
|
if (isExecuting) {
|
|
823
883
|
if (key.name === "escape" && activeAbort) {
|
|
824
884
|
activeAbort.abort();
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (str && !key.ctrl && !key.meta && !str.startsWith("\x1B")) {
|
|
888
|
+
keyBuffer += str;
|
|
825
889
|
}
|
|
826
890
|
return;
|
|
827
891
|
}
|
|
@@ -830,6 +894,10 @@ async function startInteractiveSession() {
|
|
|
830
894
|
leaveWorkspace();
|
|
831
895
|
return;
|
|
832
896
|
}
|
|
897
|
+
if (key.name === "up" || key.name === "down") {
|
|
898
|
+
scrollSession(key.name === "up" ? -3 : 3);
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
833
901
|
if (key.name === "return") {
|
|
834
902
|
const query = currentTaskInput.trim();
|
|
835
903
|
if (!query) return;
|