@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.
- package/CHANGELOG.md +28 -0
- package/dist/commands/aigne.d.ts +1 -2
- package/dist/tracer/terminal.js +5 -2
- package/dist/ui/utils/error.d.ts +2 -0
- package/dist/ui/utils/error.js +2 -0
- package/dist/ui/utils/terminal-input.d.ts +5 -0
- package/dist/ui/utils/terminal-input.js +81 -0
- package/dist/ui/utils/text-buffer.d.ts +87 -0
- package/dist/ui/utils/text-buffer.js +1059 -0
- package/dist/ui/utils/text-utils.d.ts +37 -0
- package/dist/ui/utils/text-utils.js +185 -0
- package/dist/utils/listr.d.ts +1 -1
- package/dist/utils/run-chat-loop.js +5 -17
- package/dist/utils/workers/run-aigne-in-child-process-worker.js +4 -1
- package/dist/utils/workers/run-aigne-in-child-process.js +2 -1
- package/package.json +14 -9
|
@@ -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
|
+
}
|
package/dist/utils/listr.d.ts
CHANGED
|
@@ -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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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.
|
|
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.
|
|
88
|
-
"@aigne/
|
|
89
|
-
"@aigne/
|
|
90
|
-
"@aigne/core": "^1.62.0-beta",
|
|
91
|
-
"@aigne/
|
|
92
|
-
"@aigne/
|
|
93
|
-
"@aigne/
|
|
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.
|
|
115
|
+
"@aigne/test-utils": "^0.5.54-beta.2"
|
|
111
116
|
},
|
|
112
117
|
"scripts": {
|
|
113
118
|
"lint": "tsc --noEmit",
|