@aigne/cli 1.50.0-beta.1 → 1.50.0-beta.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.
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Calculates the maximum width of a multi-line ASCII art string.
3
+ * @param asciiArt The ASCII art string.
4
+ * @returns The length of the longest line in the ASCII art.
5
+ */
6
+ export declare const getAsciiArtWidth: (asciiArt: string) => number;
7
+ export declare function toCodePoints(str: string): string[];
8
+ export declare function cpLen(str: string): number;
9
+ export declare function cpSlice(str: string, start: number, end?: number): string;
10
+ /**
11
+ * Strip characters that can break terminal rendering.
12
+ *
13
+ * Uses Node.js built-in stripVTControlCharacters to handle VT sequences,
14
+ * then filters remaining control characters that can disrupt display.
15
+ *
16
+ * Characters stripped:
17
+ * - ANSI escape sequences (via strip-ansi)
18
+ * - VT control sequences (via Node.js util.stripVTControlCharacters)
19
+ * - C0 control chars (0x00-0x1F) except CR/LF which are handled elsewhere
20
+ * - C1 control chars (0x80-0x9F) that can cause display issues
21
+ *
22
+ * Characters preserved:
23
+ * - All printable Unicode including emojis
24
+ * - DEL (0x7F) - handled functionally by applyOperations, not a display issue
25
+ * - CR/LF (0x0D/0x0A) - needed for line breaks
26
+ */
27
+ export declare function stripUnsafeCharacters(str: string): string;
28
+ /**
29
+ * Cached version of stringWidth function for better performance
30
+ * Follows Ink's approach with unlimited cache (no eviction)
31
+ */
32
+ export declare const getCachedStringWidth: (str: string) => number;
33
+ /**
34
+ * Clear the string width cache
35
+ */
36
+ export declare const clearStringWidthCache: () => void;
37
+ export declare function escapeAnsiCtrlCodes<T>(obj: T): T;
@@ -0,0 +1,185 @@
1
+ // biome-ignore-all lint/style/noNonNullAssertion: code is from gemini-cli
2
+ /**
3
+ * @license
4
+ * Copyright 2025 Google LLC
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import { stripVTControlCharacters } from "node:util";
8
+ import ansiRegex from "ansi-regex";
9
+ import stringWidth from "string-width";
10
+ import stripAnsi from "strip-ansi";
11
+ /**
12
+ * Calculates the maximum width of a multi-line ASCII art string.
13
+ * @param asciiArt The ASCII art string.
14
+ * @returns The length of the longest line in the ASCII art.
15
+ */
16
+ export const getAsciiArtWidth = (asciiArt) => {
17
+ if (!asciiArt) {
18
+ return 0;
19
+ }
20
+ const lines = asciiArt.split("\n");
21
+ return Math.max(...lines.map((line) => line.length));
22
+ };
23
+ /*
24
+ * -------------------------------------------------------------------------
25
+ * Unicode‑aware helpers (work at the code‑point level rather than UTF‑16
26
+ * code units so that surrogate‑pair emoji count as one "column".)
27
+ * ---------------------------------------------------------------------- */
28
+ // Cache for code points to reduce GC pressure
29
+ const codePointsCache = new Map();
30
+ const MAX_STRING_LENGTH_TO_CACHE = 1000;
31
+ export function toCodePoints(str) {
32
+ // ASCII fast path - check if all chars are ASCII (0-127)
33
+ let isAscii = true;
34
+ for (let i = 0; i < str.length; i++) {
35
+ if (str.charCodeAt(i) > 127) {
36
+ isAscii = false;
37
+ break;
38
+ }
39
+ }
40
+ if (isAscii) {
41
+ return str.split("");
42
+ }
43
+ // Cache short strings
44
+ if (str.length <= MAX_STRING_LENGTH_TO_CACHE) {
45
+ const cached = codePointsCache.get(str);
46
+ if (cached) {
47
+ return cached;
48
+ }
49
+ }
50
+ const result = Array.from(str);
51
+ // Cache result (unlimited like Ink)
52
+ if (str.length <= MAX_STRING_LENGTH_TO_CACHE) {
53
+ codePointsCache.set(str, result);
54
+ }
55
+ return result;
56
+ }
57
+ export function cpLen(str) {
58
+ return toCodePoints(str).length;
59
+ }
60
+ export function cpSlice(str, start, end) {
61
+ // Slice by code‑point indices and re‑join.
62
+ const arr = toCodePoints(str).slice(start, end);
63
+ return arr.join("");
64
+ }
65
+ /**
66
+ * Strip characters that can break terminal rendering.
67
+ *
68
+ * Uses Node.js built-in stripVTControlCharacters to handle VT sequences,
69
+ * then filters remaining control characters that can disrupt display.
70
+ *
71
+ * Characters stripped:
72
+ * - ANSI escape sequences (via strip-ansi)
73
+ * - VT control sequences (via Node.js util.stripVTControlCharacters)
74
+ * - C0 control chars (0x00-0x1F) except CR/LF which are handled elsewhere
75
+ * - C1 control chars (0x80-0x9F) that can cause display issues
76
+ *
77
+ * Characters preserved:
78
+ * - All printable Unicode including emojis
79
+ * - DEL (0x7F) - handled functionally by applyOperations, not a display issue
80
+ * - CR/LF (0x0D/0x0A) - needed for line breaks
81
+ */
82
+ export function stripUnsafeCharacters(str) {
83
+ const strippedAnsi = stripAnsi(str);
84
+ const strippedVT = stripVTControlCharacters(strippedAnsi);
85
+ return toCodePoints(strippedVT)
86
+ .filter((char) => {
87
+ const code = char.codePointAt(0);
88
+ if (code === undefined)
89
+ return false;
90
+ // Preserve CR/LF for line handling
91
+ if (code === 0x0a || code === 0x0d)
92
+ return true;
93
+ // Remove C0 control chars (except CR/LF) that can break display
94
+ // Examples: BELL(0x07) makes noise, BS(0x08) moves cursor, VT(0x0B), FF(0x0C)
95
+ if (code >= 0x00 && code <= 0x1f)
96
+ return false;
97
+ // Remove C1 control chars (0x80-0x9f) - legacy 8-bit control codes
98
+ if (code >= 0x80 && code <= 0x9f)
99
+ return false;
100
+ // Preserve DEL (0x7f) - it's handled functionally by applyOperations as backspace
101
+ // and doesn't cause rendering issues when displayed
102
+ // Preserve all other characters including Unicode/emojis
103
+ return true;
104
+ })
105
+ .join("");
106
+ }
107
+ // String width caching for performance optimization
108
+ const stringWidthCache = new Map();
109
+ /**
110
+ * Cached version of stringWidth function for better performance
111
+ * Follows Ink's approach with unlimited cache (no eviction)
112
+ */
113
+ export const getCachedStringWidth = (str) => {
114
+ // ASCII printable chars have width 1
115
+ if (/^[\x20-\x7E]*$/.test(str)) {
116
+ return str.length;
117
+ }
118
+ if (stringWidthCache.has(str)) {
119
+ return stringWidthCache.get(str);
120
+ }
121
+ const width = stringWidth(str);
122
+ stringWidthCache.set(str, width);
123
+ return width;
124
+ };
125
+ /**
126
+ * Clear the string width cache
127
+ */
128
+ export const clearStringWidthCache = () => {
129
+ stringWidthCache.clear();
130
+ };
131
+ const regex = ansiRegex();
132
+ /* Recursively traverses a JSON-like structure (objects, arrays, primitives)
133
+ * and escapes all ANSI control characters found in any string values.
134
+ *
135
+ * This function is designed to be robust, handling deeply nested objects and
136
+ * arrays. It applies a regex-based replacement to all string values to
137
+ * safely escape control characters.
138
+ *
139
+ * To optimize performance, this function uses a "copy-on-write" strategy.
140
+ * It avoids allocating new objects or arrays if no nested string values
141
+ * required escaping, returning the original object reference in such cases.
142
+ *
143
+ * @param obj The JSON-like value (object, array, string, etc.) to traverse.
144
+ * @returns A new value with all nested string fields escaped, or the
145
+ * original `obj` reference if no changes were necessary.
146
+ */
147
+ export function escapeAnsiCtrlCodes(obj) {
148
+ if (typeof obj === "string") {
149
+ if (obj.search(regex) === -1) {
150
+ return obj; // No changes return original string
151
+ }
152
+ regex.lastIndex = 0; // needed for global regex
153
+ return obj.replace(regex, (match) => JSON.stringify(match).slice(1, -1));
154
+ }
155
+ if (obj === null || typeof obj !== "object") {
156
+ return obj;
157
+ }
158
+ if (Array.isArray(obj)) {
159
+ let newArr = null;
160
+ for (let i = 0; i < obj.length; i++) {
161
+ const value = obj[i];
162
+ const escapedValue = escapeAnsiCtrlCodes(value);
163
+ if (escapedValue !== value) {
164
+ if (newArr === null) {
165
+ newArr = [...obj];
166
+ }
167
+ newArr[i] = escapedValue;
168
+ }
169
+ }
170
+ return (newArr !== null ? newArr : obj);
171
+ }
172
+ let newObj = null;
173
+ const keys = Object.keys(obj);
174
+ for (const key of keys) {
175
+ const value = obj[key];
176
+ const escapedValue = escapeAnsiCtrlCodes(value);
177
+ if (escapedValue !== value) {
178
+ if (newObj === null) {
179
+ newObj = { ...obj };
180
+ }
181
+ newObj[key] = escapedValue;
182
+ }
183
+ }
184
+ return newObj !== null ? newObj : obj;
185
+ }
@@ -1,5 +1,5 @@
1
1
  import type { AgentResponseStream, Message } from "@aigne/core";
2
- import type { PromiseOrValue } from "@aigne/core/utils/type-utils";
2
+ import type { PromiseOrValue } from "@aigne/core/utils/type-utils.js";
3
3
  import { DefaultRenderer, Listr, type ListrDefaultRendererOptions, ListrLogger, type ListrSimpleRendererOptions, type ListrTaskWrapper, SimpleRenderer, Spinner } from "@aigne/listr2";
4
4
  import type { createLogUpdate } from "log-update";
5
5
  export type AIGNEListrTaskWrapper = ListrTaskWrapper<unknown, typeof AIGNEListrRenderer, typeof AIGNEListrFallbackRenderer>;
@@ -2,12 +2,11 @@ import { readFile } from "node:fs/promises";
2
2
  import { basename } from "node:path";
3
3
  import { ChatModel } from "@aigne/core";
4
4
  import { isNonNullable, omit } from "@aigne/core/utils/type-utils.js";
5
- import inquirer from "inquirer";
6
5
  import { TerminalTracer } from "../tracer/terminal.js";
6
+ import { terminalInput } from "../ui/utils/terminal-input.js";
7
7
  export const DEFAULT_CHAT_INPUT_KEY = "message";
8
8
  export async function runChatLoopInTerminal(userAgent, options = {}) {
9
9
  const { initialCall } = options;
10
- let prompt;
11
10
  if (options?.welcome)
12
11
  console.log(options.welcome);
13
12
  if (initialCall) {
@@ -17,21 +16,10 @@ export async function runChatLoopInTerminal(userAgent, options = {}) {
17
16
  }
18
17
  }
19
18
  for (let i = 0;; i++) {
20
- prompt = inquirer.prompt([
21
- {
22
- type: "input",
23
- name: "question",
24
- message: "💬",
25
- default: i === 0 ? options?.defaultQuestion : undefined,
26
- },
27
- ]);
28
- let question;
29
- try {
30
- question = (await prompt).question;
31
- }
32
- catch {
33
- // ignore abort error from inquirer
34
- }
19
+ const question = await terminalInput({
20
+ message: "💬",
21
+ default: i === 0 ? options?.defaultQuestion : undefined,
22
+ });
35
23
  if (!question?.trim())
36
24
  continue;
37
25
  const cmd = COMMANDS[question.trim()];
@@ -1,6 +1,7 @@
1
1
  import assert from "node:assert";
2
2
  import { AIGNE } from "@aigne/core";
3
3
  import { findCliAgent, mapCliAgent } from "@aigne/core/utils/agent-utils.js";
4
+ import { logger } from "@aigne/core/utils/logger.js";
4
5
  import { loadAIGNE } from "../load-aigne.js";
5
6
  import { runAgentWithAIGNE } from "../run-with-aigne.js";
6
7
  import { parseAgentInput } from "../yargs.js";
@@ -9,7 +10,9 @@ const METHODS = {
9
10
  loadAIGNE: loadAIGNEInChildProcess,
10
11
  invokeCLIAgentFromDir: invokeCLIAgentFromDirInChildProcess,
11
12
  };
12
- process.on("message", async ({ method, args }) => {
13
+ process.on("message", async ({ method, args, ...options }) => {
14
+ if (options.logLevel)
15
+ logger.level = options.logLevel;
13
16
  const send = (message) => new Promise((resolve, reject) => {
14
17
  assert(process.send);
15
18
  process.send(message, undefined, undefined, (error) => {
@@ -1,6 +1,7 @@
1
1
  import { fork } from "node:child_process";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { logger } from "@aigne/core/utils/logger.js";
4
5
  import { zodToJsonSchema } from "zod-to-json-schema";
5
6
  export function serializeAgent(agent) {
6
7
  return {
@@ -25,6 +26,6 @@ export async function runAIGNEInChildProcess(method, ...args) {
25
26
  child.on("exit", (code) => {
26
27
  reject(new Error(`Child process exited with code ${code}`));
27
28
  });
28
- child.send({ method, args });
29
+ child.send({ method, args, logLevel: logger.level });
29
30
  });
30
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/cli",
3
- "version": "1.50.0-beta.1",
3
+ "version": "1.50.0-beta.3",
4
4
  "description": "Your command center for agent development",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -58,6 +58,7 @@
58
58
  "@ocap/mcrypto": "^1.25.1",
59
59
  "@smithy/node-http-handler": "^4.2.1",
60
60
  "ansi-escapes": "^7.1.0",
61
+ "ansi-regex": "^6.2.2",
61
62
  "boxen": "^8.0.1",
62
63
  "chalk": "^5.6.2",
63
64
  "cli-table3": "^0.6.5",
@@ -67,6 +68,7 @@
67
68
  "glob": "^11.0.3",
68
69
  "gradient-string": "^3.0.0",
69
70
  "https-proxy-agent": "^7.0.6",
71
+ "ink": "^6.3.1",
70
72
  "inquirer": "^12.9.6",
71
73
  "log-update": "^7.0.0",
72
74
  "marked": "^16.3.0",
@@ -75,6 +77,9 @@
75
77
  "openai": "^5.20.3",
76
78
  "p-wait-for": "^5.0.2",
77
79
  "prettier": "^3.6.2",
80
+ "react": "^19.1.1",
81
+ "string-width": "^8.1.0",
82
+ "strip-ansi": "^7.1.2",
78
83
  "tar": "^7.4.3",
79
84
  "terminal-image": "^4.0.0",
80
85
  "terminal-link": "^5.0.0",
@@ -84,13 +89,13 @@
84
89
  "yoctocolors-cjs": "^2.1.3",
85
90
  "zod": "^3.25.67",
86
91
  "zod-to-json-schema": "^3.24.6",
87
- "@aigne/agent-library": "^1.21.47-beta.1",
88
- "@aigne/aigne-hub": "^0.10.1-beta.1",
89
- "@aigne/agentic-memory": "^1.0.47-beta.1",
90
- "@aigne/core": "^1.62.0-beta",
91
- "@aigne/default-memory": "^1.2.10-beta.1",
92
- "@aigne/openai": "^0.16.1-beta.1",
93
- "@aigne/observability-api": "^0.11.1-beta"
92
+ "@aigne/agent-library": "^1.21.47-beta.2",
93
+ "@aigne/agentic-memory": "^1.0.47-beta.2",
94
+ "@aigne/default-memory": "^1.2.10-beta.2",
95
+ "@aigne/core": "^1.62.0-beta.1",
96
+ "@aigne/aigne-hub": "^0.10.1-beta.2",
97
+ "@aigne/observability-api": "^0.11.1-beta",
98
+ "@aigne/openai": "^0.16.1-beta.2"
94
99
  },
95
100
  "devDependencies": {
96
101
  "@inquirer/testing": "^2.1.50",
@@ -107,7 +112,7 @@
107
112
  "rimraf": "^6.0.1",
108
113
  "typescript": "^5.9.2",
109
114
  "ufo": "^1.6.1",
110
- "@aigne/test-utils": "^0.5.54-beta.1"
115
+ "@aigne/test-utils": "^0.5.54-beta.2"
111
116
  },
112
117
  "scripts": {
113
118
  "lint": "tsc --noEmit",