netheriteai-code 0.1.0 → 0.2.3

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/image.png ADDED
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "netheriteai-code",
3
- "version": "0.1.0",
4
- "description": "NetheriteAI:Code by hurdacu",
3
+ "version": "0.2.3",
4
+ "description": "NetheriteAI:Code by hurdacu. High-performance coding assistant.",
5
5
  "author": "hurdacu",
6
6
  "type": "module",
7
7
  "bin": {
package/src/ollama.js CHANGED
@@ -2,7 +2,7 @@ import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
3
 
4
4
  const execFileAsync = promisify(execFile);
5
- const OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || "http://176.88.249.119:11434";
5
+ const BASE_URL = process.env.NETHERITE_BASE_URL || "http://176.88.249.119:11434";
6
6
  const PREFERRED_DEFAULT_MODELS = [
7
7
  "glm-5:cloud",
8
8
  ];
@@ -18,25 +18,29 @@ function getThinkMode(model) {
18
18
  }
19
19
 
20
20
  async function request(pathname, body, signal) {
21
- const response = await fetch(`${OLLAMA_BASE_URL}${pathname}`, {
22
- method: "POST",
23
- headers: { "content-type": "application/json" },
24
- body: JSON.stringify(body),
25
- signal,
26
- });
21
+ try {
22
+ const response = await fetch(`${BASE_URL}${pathname}`, {
23
+ method: "POST",
24
+ headers: { "content-type": "application/json" },
25
+ body: JSON.stringify(body),
26
+ signal,
27
+ });
27
28
 
28
- if (!response.ok) {
29
- throw new Error(`NetheriteAI request failed: ${response.status} ${response.statusText}`);
30
- }
29
+ if (!response.ok) {
30
+ throw new Error("Server down");
31
+ }
31
32
 
32
- return response;
33
+ return response;
34
+ } catch {
35
+ throw new Error("Server down");
36
+ }
33
37
  }
34
38
 
35
39
  export async function listModels() {
36
40
  try {
37
- const response = await fetch(`${OLLAMA_BASE_URL}/api/tags`);
41
+ const response = await fetch(`${BASE_URL}/api/tags`);
38
42
  if (!response.ok) {
39
- throw new Error(`NetheriteAI request failed: ${response.status} ${response.statusText}`);
43
+ throw new Error("Server down");
40
44
  }
41
45
  const json = await response.json();
42
46
  return (json.models || []).map((model) => ({
@@ -47,41 +51,28 @@ export async function listModels() {
47
51
  details: model.details || {},
48
52
  }));
49
53
  } catch {
50
- try {
51
- const { stdout } = await execFileAsync("ollama", ["list"], { encoding: "utf8" });
52
- const lines = stdout.trim().split("\n").slice(1).filter(Boolean);
53
- return lines.map((line) => {
54
- const parts = line.trim().split(/\s{2,}/);
55
- return {
56
- name: parts[0] || "",
57
- size: parts[2] || "",
58
- modifiedAt: parts[3] || "",
59
- digest: parts[1] || "",
60
- details: {},
61
- };
62
- });
63
- } catch {
64
- throw new Error(
65
- "Could not connect to NetheriteAI. Start it with `ollama serve`, or set OLLAMA_BASE_URL if it runs elsewhere.",
66
- );
67
- }
54
+ throw new Error("Server down");
68
55
  }
69
56
  }
70
57
 
71
58
  export async function pickDefaultModel() {
72
- const models = await listModels();
73
- if (!models.length) {
74
- throw new Error("No NetheriteAI models found. Pull one with `ollama pull <model>` first.");
75
- }
76
- const preferredMatch = models.find((model) =>
77
- PREFERRED_DEFAULT_MODELS.some((preferred) => preferred.toLowerCase() === model.name.toLowerCase()),
78
- );
79
- if (preferredMatch) {
80
- return preferredMatch.name;
81
- }
59
+ try {
60
+ const models = await listModels();
61
+ if (!models.length) {
62
+ throw new Error("Server down");
63
+ }
64
+ const preferredMatch = models.find((model) =>
65
+ PREFERRED_DEFAULT_MODELS.some((preferred) => preferred.toLowerCase() === model.name.toLowerCase()),
66
+ );
67
+ if (preferredMatch) {
68
+ return preferredMatch.name;
69
+ }
82
70
 
83
- const familyMatch = models.find((model) => model.name.toLowerCase().startsWith("netheriteai"));
84
- return familyMatch?.name || models[0].name;
71
+ const familyMatch = models.find((model) => model.name.toLowerCase().startsWith("netheriteai"));
72
+ return familyMatch?.name || models[0].name;
73
+ } catch {
74
+ throw new Error("Server down");
75
+ }
85
76
  }
86
77
 
87
78
  export async function chat({ model, messages, tools, signal }) {
@@ -181,7 +172,7 @@ export async function chatStream({ model, messages, tools, onChunk, onReasoningC
181
172
 
182
173
  const reader = response.body?.getReader();
183
174
  if (!reader) {
184
- throw new Error("NetheriteAI stream did not provide a readable body.");
175
+ throw new Error("Server down");
185
176
  }
186
177
 
187
178
  const decoder = new TextDecoder();
package/src/tui.js CHANGED
@@ -2,6 +2,8 @@ const ESC = "\u001b[";
2
2
  const RESET = "\u001b[0m";
3
3
  const BEL = "\u0007";
4
4
 
5
+ const isWin = process.platform === "win32";
6
+
5
7
  const COLORS = {
6
8
  fg: "\u001b[38;2;232;232;232m",
7
9
  muted: "\u001b[38;2;138;138;138m",
@@ -17,6 +19,18 @@ const COLORS = {
17
19
  blackFg: "\u001b[38;2;0;0;0m",
18
20
  };
19
21
 
22
+ const SYMBOLS = {
23
+ badge: isWin ? "[#]" : "▣",
24
+ dot: isWin ? "." : "·",
25
+ ellipsis: "...",
26
+ pointer: isWin ? ">" : "›",
27
+ meta: isWin ? "o" : "◻",
28
+ scroll: isWin ? "#" : "█",
29
+ busyFull: isWin ? "*" : "■",
30
+ busyDim: isWin ? "." : "▪",
31
+ bar: isWin ? "|" : "│",
32
+ };
33
+
20
34
  const LOGO_TOP = [
21
35
  "NN NN EEEEEEE TTTTTTT H H EEEEEEE RRRRR IIIII TTTTTTT EEEEEEE",
22
36
  "NNN NN EE T H H EE RR RR III T EE ",
@@ -62,7 +76,7 @@ function truncate(text, width) {
62
76
  const value = String(text ?? "");
63
77
  if (width <= 0) return "";
64
78
  if (value.length <= width) return value;
65
- return width === 1 ? value.slice(0, 1) : `${value.slice(0, width - 1)}…`;
79
+ return width <= 3 ? value.slice(0, width) : `${value.slice(0, width - 3)}${SYMBOLS.ellipsis}`;
66
80
  }
67
81
 
68
82
  function pad(text, width) {
@@ -87,9 +101,10 @@ function wrapText(text, width) {
87
101
  }
88
102
 
89
103
  function paintStatusBadge(frame, row, col, panelColor, agentName) {
90
- frame.paintText(row, col, "▣", `${panelColor}${COLORS.blue}`);
91
- frame.paintText(row, col + 2, agentName, `${panelColor}${COLORS.fg}`);
92
- frame.paintText(row, col + 2 + agentName.length + 1, "·", `${panelColor}${COLORS.dim}`);
104
+ const badge = SYMBOLS.badge;
105
+ frame.paintText(row, col, badge, `${panelColor}${COLORS.blue}`);
106
+ frame.paintText(row, col + badge.length + 1, agentName, `${panelColor}${COLORS.fg}`);
107
+ frame.paintText(row, col + badge.length + 1 + agentName.length + 1, SYMBOLS.dot, `${panelColor}${COLORS.dim}`);
93
108
  }
94
109
 
95
110
  function displayModelName(model) {
@@ -394,16 +409,16 @@ function paintBusyIndicator(frame, row, col, now = Date.now()) {
394
409
  for (let index = 0; index < width; index += 1) {
395
410
  const distance = Math.abs(index - head);
396
411
  let color = COLORS.dim;
397
- let char = "·";
412
+ let char = SYMBOLS.dot;
398
413
  if (distance < 0.7) {
399
414
  color = COLORS.blue;
400
- char = "■";
415
+ char = SYMBOLS.busyFull;
401
416
  } else if (distance < 1.5) {
402
417
  color = COLORS.blueSoft;
403
- char = "■";
418
+ char = SYMBOLS.busyFull;
404
419
  } else if (distance < 2.3) {
405
420
  color = COLORS.blueFaint;
406
- char = "▪";
421
+ char = SYMBOLS.busyDim;
407
422
  }
408
423
  frame.paintText(row, col + index, char, `${COLORS.bg}${color}`);
409
424
  }
@@ -437,23 +452,26 @@ function makeFrame(width, height) {
437
452
  }
438
453
 
439
454
  function flush(cursorRow, cursorCol) {
440
- const output = [`${ESC}2J${ESC}H${COLORS.bg}`];
455
+ const output = [`${ESC}H`];
441
456
  for (let r = 0; r < height; r += 1) {
442
457
  output.push(move(r + 1, 1));
443
458
  let currentStyle = "";
444
- let line = "";
459
+ let lineBuffer = "";
445
460
  for (let c = 0; c < width; c += 1) {
461
+ // Safe guard: never draw in the very last cell of the terminal to avoid scrolling
462
+ if (r === height - 1 && c === width - 1) break;
463
+
446
464
  const nextStyle = styles[r][c];
447
465
  if (nextStyle !== currentStyle) {
448
- if (line) output.push(line);
466
+ if (lineBuffer) output.push(lineBuffer);
449
467
  output.push(nextStyle);
450
- line = rows[r][c];
468
+ lineBuffer = rows[r][c];
451
469
  currentStyle = nextStyle;
452
470
  } else {
453
- line += rows[r][c];
471
+ lineBuffer += rows[r][c];
454
472
  }
455
473
  }
456
- output.push(line, RESET);
474
+ output.push(lineBuffer, RESET);
457
475
  }
458
476
  output.push(move(cursorRow, cursorCol));
459
477
  process.stdout.write(output.join(""));
@@ -478,7 +496,8 @@ export function runTui({
478
496
  }) {
479
497
  return new Promise((resolve) => {
480
498
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
481
- process.stdout.write("\u001b[?1049h\u001b[?25l\u001b[?1000h\u001b[?1006h");
499
+ // Disable wrap and enter alternate buffer
500
+ process.stdout.write("\u001b[?1049h\u001b[?25l\u001b[?1000h\u001b[?1006h\u001b[?7l\u001b[2J");
482
501
  setTerminalTitle("NetheriteAI:Code by hurdacu");
483
502
 
484
503
  let mode = "home";
@@ -507,6 +526,7 @@ export function runTui({
507
526
  let liveReasoning = "";
508
527
  let inputBuffer = "";
509
528
 
529
+ // Slower render loop for Windows stability (60ms = ~16fps)
510
530
  const timer = setInterval(() => {
511
531
  const now = Date.now();
512
532
  const hasAnimatingTool = transcript.some((item) =>
@@ -544,7 +564,7 @@ export function runTui({
544
564
  }
545
565
  }
546
566
  render();
547
- }, 45);
567
+ }, 60);
548
568
 
549
569
  function addPromptHistory(prompt) {
550
570
  if (!prompt.trim()) return;
@@ -557,7 +577,7 @@ export function runTui({
557
577
  const text = String(prompt || "").trim().replace(/\s+/g, " ");
558
578
  if (!text) return "";
559
579
  if (text.length <= maxLength) return text;
560
- return `${text.slice(0, Math.max(0, maxLength - 3))}...`;
580
+ return `${text.slice(0, Math.max(0, maxLength - 3))}${SYMBOLS.ellipsis}`;
561
581
  }
562
582
 
563
583
  function ensureSessionTitle(prompt) {
@@ -718,7 +738,6 @@ export function runTui({
718
738
  }
719
739
 
720
740
  function renderHome(frame, width, height) {
721
- const currentModel = displayModelName(getModel ? getModel() : model);
722
741
  const logoWidth = Math.max(LOGO_TOP[0].length, LOGO_BOTTOM[0].length);
723
742
  const logoLeft = center(width, logoWidth);
724
743
  const logoTop = Math.max(4, Math.floor(height / 2) - 10);
@@ -738,7 +757,7 @@ export function runTui({
738
757
  const panelHeight = promptLines.length + 3;
739
758
  const panelTop = logoTop + LOGO_TOP.length + LOGO_BOTTOM.length + 3;
740
759
  frame.fillRect(panelTop, panelLeft, panelWidth, panelHeight, `${COLORS.panel}${COLORS.fg}`);
741
- for (let y = 0; y < panelHeight; y += 1) frame.paintText(panelTop + y, panelLeft, "│", `${COLORS.panel}${COLORS.blue}`);
760
+ for (let y = 0; y < panelHeight; y += 1) frame.paintText(panelTop + y, panelLeft, SYMBOLS.bar, `${COLORS.panel}${COLORS.blue}`);
742
761
  promptLines.forEach((line, index) => {
743
762
  frame.paintText(panelTop + 1 + index, panelLeft + 2, pad(line, panelWidth - 4), `${COLORS.panel}${input ? COLORS.fg : COLORS.dim}`);
744
763
  });
@@ -771,7 +790,7 @@ export function runTui({
771
790
  frame.paintText(
772
791
  top + 2 + i,
773
792
  left + 2,
774
- `${active ? "›" : " "} ${truncate(item.label, boxWidth - 6)}`,
793
+ `${active ? SYMBOLS.pointer : " "} ${truncate(item.label, boxWidth - 6)}`,
775
794
  `${COLORS.panel}${active ? COLORS.blue : COLORS.fg}`,
776
795
  );
777
796
  }
@@ -913,7 +932,7 @@ export function runTui({
913
932
  frame.paintText(1, center(width, `NetheriteAI:Code | ${sessionTitle}`.length), `NetheriteAI:Code | ${sessionTitle}`, `${COLORS.bg}${COLORS.fg}`);
914
933
  if (hasSidebar) {
915
934
  for (let r = 2; r <= height; r += 1) {
916
- frame.paintText(r, contentWidth + 3, "│", `${COLORS.bg}${COLORS.dim}`);
935
+ frame.paintText(r, contentWidth + 3, SYMBOLS.bar, `${COLORS.bg}${COLORS.dim}`);
917
936
  }
918
937
  const sideCol = contentWidth + 6;
919
938
  const sideWidth = sidebarWidth - 8;
@@ -964,20 +983,20 @@ export function runTui({
964
983
  }
965
984
  if (item.kind === "user_top" || item.kind === "user_bottom") {
966
985
  frame.fillRect(row, 2, contentWidth - 1, 1, linePanel);
967
- frame.paintText(row, 2, "│", borderStyle);
986
+ frame.paintText(row, 2, SYMBOLS.bar, borderStyle);
968
987
  row += 1;
969
988
  continue;
970
989
  }
971
990
  if (item.kind === "user_line") {
972
991
  frame.fillRect(row, 2, contentWidth - 1, 1, linePanel);
973
- frame.paintText(row, 2, "│", borderStyle);
992
+ frame.paintText(row, 2, SYMBOLS.bar, borderStyle);
974
993
  frame.paintText(row, 4, pad(item.text, contentWidth - 5), linePanel);
975
994
  row += 1;
976
995
  continue;
977
996
  }
978
997
  if (item.kind === "thinking_line") {
979
998
  if (selected) frame.fillRect(row, 3, contentWidth - 3, 1, `${COLORS.bg}${COLORS.fg}`);
980
- frame.paintText(row, 3, "│", `${COLORS.bg}${COLORS.dim}`);
999
+ frame.paintText(row, 3, SYMBOLS.bar, `${COLORS.bg}${COLORS.dim}`);
981
1000
  if (item.first && item.text.startsWith("Thinking:")) {
982
1001
  frame.paintText(row, 5, "Thinking:", `${COLORS.bg}${COLORS.amber}`);
983
1002
  frame.paintText(row, 15, ` ${item.text.slice(9).trim()}`, `${COLORS.bg}${COLORS.muted}`);
@@ -1038,14 +1057,14 @@ export function runTui({
1038
1057
  }
1039
1058
  if (item.kind === "meta") {
1040
1059
  if (selected) frame.fillRect(row, 3, contentWidth - 3, 1, `${COLORS.bg}${COLORS.fg}`);
1041
- frame.paintText(row, 4, `◻ ${truncate(item.text, contentWidth - 8)}`, `${COLORS.bg}${selected ? COLORS.fg : COLORS.blue}`);
1060
+ frame.paintText(row, 4, `${SYMBOLS.meta} ${truncate(item.text, contentWidth - 8)}`, `${COLORS.bg}${selected ? COLORS.fg : COLORS.blue}`);
1042
1061
  row += 1;
1043
1062
  continue;
1044
1063
  }
1045
1064
  row += 1;
1046
1065
  }
1047
1066
  for (let r = 2; r < inputTop; r += 1) {
1048
- frame.paintText(r, scrollCol, "│", `${COLORS.bg}${COLORS.dim}`);
1067
+ frame.paintText(r, scrollCol, SYMBOLS.bar, `${COLORS.bg}${COLORS.dim}`);
1049
1068
  }
1050
1069
  if (maxOffset > 0) {
1051
1070
  const trackHeight = Math.max(1, inputTop - 2);
@@ -1053,18 +1072,18 @@ export function runTui({
1053
1072
  const thumbTravel = Math.max(0, trackHeight - thumbHeight);
1054
1073
  const thumbTop = 2 + Math.floor((scrollOffset / maxOffset) * thumbTravel);
1055
1074
  for (let r = 0; r < thumbHeight; r += 1) {
1056
- frame.paintText(thumbTop + r, scrollCol, "█", `${COLORS.bg}${COLORS.muted}`);
1075
+ frame.paintText(thumbTop + r, scrollCol, SYMBOLS.scroll, `${COLORS.bg}${COLORS.muted}`);
1057
1076
  }
1058
1077
  }
1059
1078
 
1060
1079
  frame.fillRect(inputTop, 2, contentWidth - 1, inputHeight, `${COLORS.panel}${COLORS.fg}`);
1061
- for (let y = 0; y < inputHeight; y += 1) frame.paintText(inputTop + y, 2, "│", `${COLORS.panel}${COLORS.blue}`);
1080
+ for (let y = 0; y < inputHeight; y += 1) frame.paintText(inputTop + y, 2, SYMBOLS.bar, `${COLORS.panel}${COLORS.blue}`);
1062
1081
  paintStatusBadge(frame, inputTop + inputLines.length + 1, 4, COLORS.panel, agentName);
1063
1082
  inputLines.forEach((line, index) => {
1064
1083
  frame.paintText(inputTop + 1 + index, 4, pad(line, contentWidth - 5), `${COLORS.panel}${COLORS.fg}`);
1065
1084
  });
1066
1085
  if (cursorVisible) {
1067
- const cursorLine = inputLines.length - 1;
1086
+ const cursorLine = promptLines.length - 1;
1068
1087
  const cursorCol = 4 + inputLines[cursorLine].length;
1069
1088
  paintInputCursor(frame, inputTop + 1 + cursorLine, cursorCol);
1070
1089
  }
@@ -1275,7 +1294,8 @@ export function runTui({
1275
1294
  process.stdin.removeListener("data", onData);
1276
1295
  process.stdout.removeListener("resize", render);
1277
1296
  if (process.stdin.isTTY) process.stdin.setRawMode(false);
1278
- process.stdout.write("\u001b[?1000l\u001b[?1006l\u001b[?25h\u001b[?1049l");
1297
+ // Restore wrapping and exit alternate buffer
1298
+ process.stdout.write("\u001b[?7h\u001b[?1000l\u001b[?1006l\u001b[?25h\u001b[?1049l");
1279
1299
  resolve({ reason, transcript: [...transcript] });
1280
1300
  }
1281
1301
 
@@ -1325,9 +1345,13 @@ export function runTui({
1325
1345
  activity = "Command palette not wired yet";
1326
1346
  return render();
1327
1347
  }
1328
- if (key.name === "escape" && busy) {
1329
- activity = "Interrupt requested";
1330
- currentAbortController?.abort();
1348
+ if (key.name === "escape") {
1349
+ if (busy) {
1350
+ activity = "Interrupt requested";
1351
+ currentAbortController?.abort();
1352
+ } else {
1353
+ input = "";
1354
+ }
1331
1355
  return render();
1332
1356
  }
1333
1357
  if (key.name === "pageup") {