@easbot/terminal 0.3.11
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/LICENSE +21 -0
- package/README.en.md +238 -0
- package/README.md +238 -0
- package/dist/index.cjs +11 -0
- package/dist/index.d.cts +131 -0
- package/dist/index.d.ts +131 -0
- package/dist/index.mjs +11 -0
- package/package.json +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 houjallen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
[中文](./README.md) | English
|
|
2
|
+
|
|
3
|
+
# @easbot/terminal
|
|
4
|
+
|
|
5
|
+
> Terminal interaction toolkit — unified terminal output, formatting, prompts, and state recovery for EASBOT
|
|
6
|
+
|
|
7
|
+
## Introduction
|
|
8
|
+
|
|
9
|
+
`@easbot/terminal` is a terminal interaction toolkit for the EASBOT ecosystem. It encapsulates the common capabilities required by CLI / TUI scenarios: themed color output, ANSI handling, OSC-8 hyperlinks, table rendering, styled prompts, safe stream writes, progress-line management, and terminal-state recovery. Every module auto-detects terminal capability (`NO_COLOR` / `FORCE_COLOR` / TTY) and degrades gracefully in non-TTY environments (pipes, redirected files, CI logs).
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Themed color output** — chalk + Lobster palette with consistent accent / info / success / warning / error colors
|
|
14
|
+
- **Capability detection** — auto-detects TTY, color level, `NO_COLOR` / `FORCE_COLOR`, and degrades to plain text when needed
|
|
15
|
+
- **ANSI utilities** — strips SGR / OSC-8 sequences and computes visible character width (Unicode & emoji safe)
|
|
16
|
+
- **Path helpers** — `~` and `$EASBOT_HOME` shortening for home-directory paths
|
|
17
|
+
- **OSC-8 hyperlinks** — cross-platform terminal hyperlinks with graceful fallback
|
|
18
|
+
- **Table rendering** — `renderTable` with auto-fit columns, Unicode / ASCII / borderless styles, ANSI-safe wrapping
|
|
19
|
+
- **Command output formatting** — success / error / warning / info / heading / key-value / command / list / status / count helpers
|
|
20
|
+
- **Styled prompts** — styled wrappers around `@clack/prompts` (`selectStyled` / `note` / `stylePromptMessage` …)
|
|
21
|
+
- **Safe stream writer** — `createSafeStreamWriter` swallows `EPIPE` / `EIO`, so a closed pipe never throws
|
|
22
|
+
- **Progress line** — register / clear / unregister an active progress line (TTY-only)
|
|
23
|
+
- **Terminal-state recovery** — `restoreTerminalState` resets cursor, mouse tracking, and bracketed paste on exit
|
|
24
|
+
- **CLI JSON output** — `emitJsonOk` / `emitJsonError` implement the EASBOT CLI `--json` contract (`{ ok, data | error }`)
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm add @easbot/terminal
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### Theme & capability detection
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { theme, isRich, colorize } from '@easbot/terminal';
|
|
38
|
+
|
|
39
|
+
const rich = isRich();
|
|
40
|
+
console.log(colorize(rich, theme.accent, 'Important notice'));
|
|
41
|
+
console.log(colorize(rich, theme.success, '✓ Operation succeeded'));
|
|
42
|
+
console.log(colorize(rich, theme.error, '✗ Operation failed'));
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Command output formatting
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import {
|
|
49
|
+
formatSuccess,
|
|
50
|
+
formatError,
|
|
51
|
+
formatWarning,
|
|
52
|
+
formatInfo,
|
|
53
|
+
formatHeading,
|
|
54
|
+
formatKeyValue,
|
|
55
|
+
formatCommandExample,
|
|
56
|
+
formatListItem,
|
|
57
|
+
formatStatus,
|
|
58
|
+
} from '@easbot/terminal';
|
|
59
|
+
|
|
60
|
+
console.log(formatHeading('Build Output'));
|
|
61
|
+
console.log(formatKeyValue('entry', './dist/index.mjs'));
|
|
62
|
+
console.log(formatKeyValue('format', 'esm / cjs'));
|
|
63
|
+
console.log(formatSuccess('Build complete'));
|
|
64
|
+
console.log(formatError('Missing required flag --name'));
|
|
65
|
+
console.log(formatWarning('Detected uncommitted changes'));
|
|
66
|
+
console.log(formatCommandExample('pnpm build', 'Build all packages'));
|
|
67
|
+
console.log(formatListItem('◇', 'lint', 'biome check .'));
|
|
68
|
+
console.log(formatStatus('running', 'Building...'));
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Paths & hyperlinks
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { resolveUserPath, resolveConfigDir, displayPath, formatTerminalLink } from '@easbot/terminal';
|
|
75
|
+
|
|
76
|
+
const configDir = resolveConfigDir(); // ~/.openclaw or $OPENCLAW_STATE_DIR
|
|
77
|
+
console.log(displayPath(configDir));
|
|
78
|
+
|
|
79
|
+
console.log(formatTerminalLink('EASBOT repo', 'https://github.com/houjallen/easbot'));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Table rendering
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { renderTable } from '@easbot/terminal';
|
|
86
|
+
|
|
87
|
+
console.log(
|
|
88
|
+
renderTable({
|
|
89
|
+
columns: [
|
|
90
|
+
{ key: 'name', header: 'Name', align: 'left' },
|
|
91
|
+
{ key: 'version', header: 'Version', align: 'left' },
|
|
92
|
+
{ key: 'status', header: 'Status', align: 'center' },
|
|
93
|
+
],
|
|
94
|
+
rows: [
|
|
95
|
+
{ name: '@easbot/terminal', version: '0.3.11', status: '✓' },
|
|
96
|
+
{ name: '@easbot/note', version: '0.3.11', status: '✓' },
|
|
97
|
+
],
|
|
98
|
+
style: 'unicode',
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Styled prompts
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { selectStyled, note, wrapNoteMessage } from '@easbot/terminal';
|
|
107
|
+
|
|
108
|
+
const choice = await selectStyled({
|
|
109
|
+
message: 'Choose an action',
|
|
110
|
+
options: [
|
|
111
|
+
{ value: 'build', label: 'Build', hint: 'Run pnpm build' },
|
|
112
|
+
{ value: 'test', label: 'Test', hint: 'Run pnpm test:run' },
|
|
113
|
+
],
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
note(wrapNoteMessage('Config file saved to ~/.config/app/config.json'), 'Success');
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Safe stream writes & state recovery
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { createSafeStreamWriter, restoreTerminalState, registerActiveProgressLine } from '@easbot/terminal';
|
|
123
|
+
|
|
124
|
+
const writer = createSafeStreamWriter();
|
|
125
|
+
registerActiveProgressLine(process.stdout);
|
|
126
|
+
|
|
127
|
+
process.on('exit', () => restoreTerminalState('process exit'));
|
|
128
|
+
process.on('SIGINT', () => restoreTerminalState('SIGINT', { exit: true }));
|
|
129
|
+
|
|
130
|
+
// Writing after the pipe closes won't throw EPIPE
|
|
131
|
+
writer.write('first\n');
|
|
132
|
+
writer.write('second\n');
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### CLI `--json` contract
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { emitJsonOk, emitJsonError } from '@easbot/terminal';
|
|
139
|
+
|
|
140
|
+
emitJsonOk({ items: [{ id: 1 }, { id: 2 }] }, { count: 2 });
|
|
141
|
+
// {"ok":true,"data":{"items":[{"id":1},{"id":2}]},"meta":{"count":2}}
|
|
142
|
+
|
|
143
|
+
emitJsonError('E_NOT_FOUND', 'Resource not found');
|
|
144
|
+
// {"ok":false,"error":{"code":"E_NOT_FOUND","message":"Resource not found"}}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## API Overview
|
|
148
|
+
|
|
149
|
+
### Theme & styling
|
|
150
|
+
|
|
151
|
+
- `theme` — themed color helpers (`accent`, `info`, `success`, `warn`, `error`, `muted`, `heading`, `command`, `option`)
|
|
152
|
+
- `isRich()` — whether the terminal supports rich color output
|
|
153
|
+
- `colorize(rich, color, value)` — conditional coloring helper
|
|
154
|
+
- `LOBSTER_PALETTE` — raw Lobster palette values
|
|
155
|
+
|
|
156
|
+
### ANSI utilities
|
|
157
|
+
|
|
158
|
+
- `stripAnsi(input)` — strip SGR / OSC-8 sequences from a string
|
|
159
|
+
- `visibleWidth(input)` — visible character width after stripping ANSI
|
|
160
|
+
|
|
161
|
+
### Paths & display
|
|
162
|
+
|
|
163
|
+
- `resolveUserPath(input)` — resolve user paths (supports `~`)
|
|
164
|
+
- `resolveConfigDir(env?, homedir?)` — resolve the config dir (supports `OPENCLAW_STATE_DIR` / `CLAWDBOT_STATE_DIR`)
|
|
165
|
+
- `resolveHomeDir()` — resolve the effective home directory
|
|
166
|
+
- `shortenHomePath(input)` / `displayPath(input)` — shorten the home prefix in a single path
|
|
167
|
+
- `shortenHomeInString(input)` / `displayString(input)` — replace every occurrence of the home prefix in a string
|
|
168
|
+
- `resolveEffectiveHomeDir` / `resolveRequiredHomeDir` / `expandHomePrefix` — low-level home helpers
|
|
169
|
+
- `formatTerminalLink(label, url, opts?)` — format an OSC-8 hyperlink
|
|
170
|
+
- `formatDocsLink(path, label?, opts?)` / `formatDocsRootLink(label?)` / `DOCS_ROOT` — docs-link helpers
|
|
171
|
+
|
|
172
|
+
### Command output formatting
|
|
173
|
+
|
|
174
|
+
- `formatSuccess` / `formatError` / `formatWarning` / `formatInfo`
|
|
175
|
+
- `formatHeading` / `formatKeyValue` / `formatCommandExample` / `formatListItem`
|
|
176
|
+
- `formatStatus` / `formatSeparator` / `formatEmptyMessage` / `formatCount`
|
|
177
|
+
|
|
178
|
+
### Tables
|
|
179
|
+
|
|
180
|
+
- `TableColumn` / `RenderTableOptions` — column and render-option types
|
|
181
|
+
- `renderTable(opts)` — render a table string
|
|
182
|
+
|
|
183
|
+
### Styled prompts
|
|
184
|
+
|
|
185
|
+
- `stylePromptMessage` / `stylePromptTitle` / `stylePromptHint` — prompt text styling
|
|
186
|
+
- `selectStyled<T>(params)` — themed select prompt
|
|
187
|
+
- `note(message, title?)` — themed note box
|
|
188
|
+
- `wrapNoteMessage(message, options?)` — auto-wrap a note's message
|
|
189
|
+
|
|
190
|
+
### CLI JSON output
|
|
191
|
+
|
|
192
|
+
- `emitJsonOk(data, meta?)` — emit `{ ok: true, data, meta? }`
|
|
193
|
+
- `emitJsonError(code, message)` — emit `{ ok: false, error: { code, message } }`
|
|
194
|
+
|
|
195
|
+
### Streams & state
|
|
196
|
+
|
|
197
|
+
- `SafeStreamWriterOptions` / `SafeStreamWriter` — safe stream-writer types
|
|
198
|
+
- `createSafeStreamWriter(options?)` — create a writer that swallows `EPIPE` / `EIO`
|
|
199
|
+
- `registerActiveProgressLine(stream)` / `clearActiveProgressLine()` / `unregisterActiveProgressLine(stream?)` — progress line management
|
|
200
|
+
- `restoreTerminalState(reason?, options?)` — restore cursor / mouse / bracketed-paste state
|
|
201
|
+
|
|
202
|
+
### Misc
|
|
203
|
+
|
|
204
|
+
- `styleHealthChannelLine(line, rich)` — themed health-check channel line
|
|
205
|
+
|
|
206
|
+
## Environment Variables
|
|
207
|
+
|
|
208
|
+
| Variable | Description |
|
|
209
|
+
|----------|-------------|
|
|
210
|
+
| `NO_COLOR` | Disables colors when set (unless `FORCE_COLOR` is also set) — see [no-color.org](https://no-color.org/) |
|
|
211
|
+
| `FORCE_COLOR` | Forces colors when non-empty and not equal to `'0'` |
|
|
212
|
+
| `HOME` / `USERPROFILE` | Fallback sources for home directory resolution |
|
|
213
|
+
| `EASBOT_HOME` | Explicit override for the home directory; changes the display prefix from `~` to `$EASBOT_HOME` |
|
|
214
|
+
| `OPENCLAW_STATE_DIR` / `CLAWDBOT_STATE_DIR` | Override the resolved config directory |
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Install dependencies
|
|
220
|
+
pnpm install
|
|
221
|
+
|
|
222
|
+
# Build
|
|
223
|
+
pnpm build
|
|
224
|
+
|
|
225
|
+
# Test
|
|
226
|
+
pnpm test:run
|
|
227
|
+
|
|
228
|
+
# Type-check
|
|
229
|
+
pnpm type-check
|
|
230
|
+
|
|
231
|
+
# Lint / Format
|
|
232
|
+
pnpm lint
|
|
233
|
+
pnpm format:fix
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
[English](./README.en.md) | 中文
|
|
2
|
+
|
|
3
|
+
# @easbot/terminal
|
|
4
|
+
|
|
5
|
+
> 终端交互工具库 — 为 EASBOT 生态提供统一的终端输出、格式化、提示与状态恢复能力
|
|
6
|
+
|
|
7
|
+
## 简介
|
|
8
|
+
|
|
9
|
+
`@easbot/terminal` 是一个面向 EASBOT 生态的终端交互工具库,封装了命令行 / TUI 场景下常用的能力:终端主题色、ANSI 处理、超链接、表格渲染、提示样式、安全流写入、进度行管理以及终端状态恢复。所有能力都基于终端能力自动检测(`NO_COLOR` / `FORCE_COLOR` / TTY)进行降级,可在管道、文件重定向等非 TTY 场景下安全使用。
|
|
10
|
+
|
|
11
|
+
## 特性
|
|
12
|
+
|
|
13
|
+
- **终端主题**:基于 chalk + Lobster 调色板,提供统一的强调色 / 信息 / 成功 / 警告 / 错误等样式
|
|
14
|
+
- **终端能力检测**:自动识别 TTY、颜色等级、`NO_COLOR` / `FORCE_COLOR`,不支持富文本时自动降级为纯文本
|
|
15
|
+
- **ANSI 工具**:剥离 SGR / OSC-8 序列,计算可见字符宽度,处理 Unicode / emoji 文本对齐
|
|
16
|
+
- **路径显示**:支持 `~` / `$EASBOT_HOME` 缩短用户主目录路径(`resolveUserPath` / `shortenHomePath` / `displayPath`)
|
|
17
|
+
- **OSC-8 超链接**:跨平台终端超链接,自动检测终端能力,提供降级文本
|
|
18
|
+
- **表格渲染**:`renderTable` 支持自适应列宽、Unicode / ASCII / 无边框三种样式、文本换行、ANSI 样式保持
|
|
19
|
+
- **命令输出格式化**:成功 / 错误 / 警告 / 信息 / 标题 / 键值对 / 命令示例 / 列表项 / 进度状态 / 计数等
|
|
20
|
+
- **提示样式化**:`@clack/prompts` 的样式化封装(`selectStyled` / `note` / `stylePromptMessage` 等)
|
|
21
|
+
- **安全流写入**:`createSafeStreamWriter` 自动处理 `EPIPE` / `EIO`,流关闭后静默忽略后续写入
|
|
22
|
+
- **进度行管理**:注册 / 清除 / 注销活动进度行,仅在 TTY 下生效
|
|
23
|
+
- **终端状态恢复**:`restoreTerminalState` 在退出 / 异常时重置光标、鼠标跟踪、括号粘贴模式
|
|
24
|
+
- **JSON 规范输出**:`emitJsonOk` / `emitJsonError` 实现 CLI `--json` 规范的 `{ ok, data | error }` 协议
|
|
25
|
+
|
|
26
|
+
## 安装
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm add @easbot/terminal
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 快速开始
|
|
33
|
+
|
|
34
|
+
### 主题与终端能力
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { theme, isRich, colorize } from '@easbot/terminal';
|
|
38
|
+
|
|
39
|
+
const rich = isRich();
|
|
40
|
+
console.log(colorize(rich, theme.accent, '重要提示'));
|
|
41
|
+
console.log(colorize(rich, theme.success, '✓ 操作成功'));
|
|
42
|
+
console.log(colorize(rich, theme.error, '✗ 操作失败'));
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 命令输出格式化
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import {
|
|
49
|
+
formatSuccess,
|
|
50
|
+
formatError,
|
|
51
|
+
formatWarning,
|
|
52
|
+
formatInfo,
|
|
53
|
+
formatHeading,
|
|
54
|
+
formatKeyValue,
|
|
55
|
+
formatCommandExample,
|
|
56
|
+
formatListItem,
|
|
57
|
+
formatStatus,
|
|
58
|
+
} from '@easbot/terminal';
|
|
59
|
+
|
|
60
|
+
console.log(formatHeading('构建产物'));
|
|
61
|
+
console.log(formatKeyValue('入口', './dist/index.mjs'));
|
|
62
|
+
console.log(formatKeyValue('格式', 'esm / cjs'));
|
|
63
|
+
console.log(formatSuccess('构建完成'));
|
|
64
|
+
console.log(formatError('缺少必填参数 --name'));
|
|
65
|
+
console.log(formatWarning('检测到未提交变更'));
|
|
66
|
+
console.log(formatCommandExample('pnpm build', '构建所有包'));
|
|
67
|
+
console.log(formatListItem('◇', 'lint', 'biome check .'));
|
|
68
|
+
console.log(formatStatus('running', '正在执行构建...'));
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 路径与超链接
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { resolveUserPath, resolveConfigDir, displayPath, formatTerminalLink } from '@easbot/terminal';
|
|
75
|
+
|
|
76
|
+
const configDir = resolveConfigDir(); // ~/.openclaw 或 $OPENCLAW_STATE_DIR
|
|
77
|
+
console.log(displayPath(configDir));
|
|
78
|
+
|
|
79
|
+
console.log(formatTerminalLink('查看文档', 'https://github.com/houjallen/easbot'));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 表格渲染
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { renderTable } from '@easbot/terminal';
|
|
86
|
+
|
|
87
|
+
console.log(
|
|
88
|
+
renderTable({
|
|
89
|
+
columns: [
|
|
90
|
+
{ key: 'name', header: '名称', align: 'left' },
|
|
91
|
+
{ key: 'version', header: '版本', align: 'left' },
|
|
92
|
+
{ key: 'status', header: '状态', align: 'center' },
|
|
93
|
+
],
|
|
94
|
+
rows: [
|
|
95
|
+
{ name: '@easbot/terminal', version: '0.3.11', status: '✓' },
|
|
96
|
+
{ name: '@easbot/note', version: '0.3.11', status: '✓' },
|
|
97
|
+
],
|
|
98
|
+
style: 'unicode',
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 样式化提示
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { selectStyled, note, wrapNoteMessage } from '@easbot/terminal';
|
|
107
|
+
|
|
108
|
+
const choice = await selectStyled({
|
|
109
|
+
message: '请选择要执行的操作',
|
|
110
|
+
options: [
|
|
111
|
+
{ value: 'build', label: '构建', hint: '运行 pnpm build' },
|
|
112
|
+
{ value: 'test', label: '测试', hint: '运行 pnpm test:run' },
|
|
113
|
+
],
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
note(wrapNoteMessage('配置文件已成功保存到 ~/.config/app/config.json'), '成功');
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 安全流写入与状态恢复
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { createSafeStreamWriter, restoreTerminalState, registerActiveProgressLine } from '@easbot/terminal';
|
|
123
|
+
|
|
124
|
+
const writer = createSafeStreamWriter();
|
|
125
|
+
registerActiveProgressLine(process.stdout);
|
|
126
|
+
|
|
127
|
+
process.on('exit', () => restoreTerminalState('process exit'));
|
|
128
|
+
process.on('SIGINT', () => restoreTerminalState('SIGINT', { exit: true }));
|
|
129
|
+
|
|
130
|
+
// 流断开(如管道关闭)后再次写入不会抛 EPIPE
|
|
131
|
+
writer.write('first\n');
|
|
132
|
+
writer.write('second\n');
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### CLI `--json` 规范输出
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { emitJsonOk, emitJsonError } from '@easbot/terminal';
|
|
139
|
+
|
|
140
|
+
emitJsonOk({ items: [{ id: 1 }, { id: 2 }] }, { count: 2 });
|
|
141
|
+
// {"ok":true,"data":{"items":[{"id":1},{"id":2}]},"meta":{"count":2}}
|
|
142
|
+
|
|
143
|
+
emitJsonError('E_NOT_FOUND', '资源不存在');
|
|
144
|
+
// {"ok":false,"error":{"code":"E_NOT_FOUND","message":"资源不存在"}}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## API 总览
|
|
148
|
+
|
|
149
|
+
### 主题与样式
|
|
150
|
+
|
|
151
|
+
- `theme` — 主题颜色对象(accent / info / success / warn / error / muted / heading / command / option)
|
|
152
|
+
- `isRich()` — 判断终端是否支持富文本颜色
|
|
153
|
+
- `colorize(rich, color, value)` — 条件着色工具
|
|
154
|
+
- `LOBSTER_PALETTE` — Lobster 调色板原始色值
|
|
155
|
+
|
|
156
|
+
### ANSI 工具
|
|
157
|
+
|
|
158
|
+
- `stripAnsi(input)` — 剥离 ANSI SGR / OSC-8 序列,返回纯文本
|
|
159
|
+
- `visibleWidth(input)` — 计算剥离 ANSI 后的可见字符宽度
|
|
160
|
+
|
|
161
|
+
### 路径与显示
|
|
162
|
+
|
|
163
|
+
- `resolveUserPath(input)` — 解析用户路径(支持 `~` 波浪号)
|
|
164
|
+
- `resolveConfigDir(env?, homedir?)` — 解析配置目录(支持 `OPENCLAW_STATE_DIR` / `CLAWDBOT_STATE_DIR` 覆盖)
|
|
165
|
+
- `resolveHomeDir()` — 解析用户主目录
|
|
166
|
+
- `shortenHomePath(input)` / `displayPath(input)` — 缩短路径中的主目录部分
|
|
167
|
+
- `shortenHomeInString(input)` / `displayString(input)` — 替换字符串中所有匹配的主目录路径
|
|
168
|
+
- `resolveEffectiveHomeDir` / `resolveRequiredHomeDir` / `expandHomePrefix` — 主目录解析工具
|
|
169
|
+
- `formatTerminalLink(label, url, opts?)` — 格式化 OSC-8 终端超链接
|
|
170
|
+
- `formatDocsLink(path, label?, opts?)` / `formatDocsRootLink(label?)` / `DOCS_ROOT` — 文档链接辅助
|
|
171
|
+
|
|
172
|
+
### 命令输出格式化
|
|
173
|
+
|
|
174
|
+
- `formatSuccess` / `formatError` / `formatWarning` / `formatInfo`
|
|
175
|
+
- `formatHeading` / `formatKeyValue` / `formatCommandExample` / `formatListItem`
|
|
176
|
+
- `formatStatus` / `formatSeparator` / `formatEmptyMessage` / `formatCount`
|
|
177
|
+
|
|
178
|
+
### 表格
|
|
179
|
+
|
|
180
|
+
- `TableColumn` / `RenderTableOptions` — 表格列配置与渲染选项类型
|
|
181
|
+
- `renderTable(opts)` — 渲染表格字符串
|
|
182
|
+
|
|
183
|
+
### 提示样式化
|
|
184
|
+
|
|
185
|
+
- `stylePromptMessage` / `stylePromptTitle` / `stylePromptHint` — 提示文本样式化
|
|
186
|
+
- `selectStyled<T>(params)` — 样式化的选择提示
|
|
187
|
+
- `note(message, title?)` — 样式化的注释框
|
|
188
|
+
- `wrapNoteMessage(message, options?)` — 注释消息自动换行
|
|
189
|
+
|
|
190
|
+
### JSON 规范输出
|
|
191
|
+
|
|
192
|
+
- `emitJsonOk(data, meta?)` — 输出 `{ ok: true, data, meta? }` JSON 响应
|
|
193
|
+
- `emitJsonError(code, message)` — 输出 `{ ok: false, error: { code, message } }` JSON 响应
|
|
194
|
+
|
|
195
|
+
### 流与状态
|
|
196
|
+
|
|
197
|
+
- `SafeStreamWriterOptions` / `SafeStreamWriter` — 安全流写入器类型
|
|
198
|
+
- `createSafeStreamWriter(options?)` — 创建安全流写入器,自动处理 `EPIPE` / `EIO`
|
|
199
|
+
- `registerActiveProgressLine(stream)` / `clearActiveProgressLine()` / `unregisterActiveProgressLine(stream?)` — 进度行管理
|
|
200
|
+
- `restoreTerminalState(reason?, options?)` — 恢复终端状态(光标 / 鼠标 / 括号粘贴模式)
|
|
201
|
+
|
|
202
|
+
### 其它
|
|
203
|
+
|
|
204
|
+
- `styleHealthChannelLine(line, rich)` — 健康检查通道行样式化
|
|
205
|
+
|
|
206
|
+
## 环境变量
|
|
207
|
+
|
|
208
|
+
| 变量 | 作用 |
|
|
209
|
+
|------|------|
|
|
210
|
+
| `NO_COLOR` | 存在时禁用终端颜色(除非同时设置了 `FORCE_COLOR`),遵循 [no-color.org](https://no-color.org/) 规范 |
|
|
211
|
+
| `FORCE_COLOR` | 非空且不为 `'0'` 时强制启用颜色 |
|
|
212
|
+
| `HOME` / `USERPROFILE` | 主目录解析的备选来源(OS 默认值) |
|
|
213
|
+
| `EASBOT_HOME` | 显式指定主目录;设置后路径显示前缀由 `~` 切换为 `$EASBOT_HOME` |
|
|
214
|
+
| `OPENCLAW_STATE_DIR` / `CLAWDBOT_STATE_DIR` | 配置目录覆盖项 |
|
|
215
|
+
|
|
216
|
+
## 开发
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# 安装依赖
|
|
220
|
+
pnpm install
|
|
221
|
+
|
|
222
|
+
# 构建
|
|
223
|
+
pnpm build
|
|
224
|
+
|
|
225
|
+
# 测试
|
|
226
|
+
pnpm test:run
|
|
227
|
+
|
|
228
|
+
# 类型检查
|
|
229
|
+
pnpm type-check
|
|
230
|
+
|
|
231
|
+
# Lint / Format
|
|
232
|
+
pnpm lint
|
|
233
|
+
pnpm format:fix
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## 许可证
|
|
237
|
+
|
|
238
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';var xe=require('chalk'),Ne=require('fs'),B=require('os'),K=require('path'),utils=require('@easbot/utils'),prompts=require('@clack/prompts');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var xe__default=/*#__PURE__*/_interopDefault(xe);var Ne__default=/*#__PURE__*/_interopDefault(Ne);var B__default=/*#__PURE__*/_interopDefault(B);var K__default=/*#__PURE__*/_interopDefault(K);var de="\\x1b\\[[0-9;]*m",pe="\\x1b\\]8;;.*?\\x1b\\\\|\\x1b\\]8;;\\x1b\\\\",he=new RegExp(de,"g"),ge=new RegExp(pe,"g");function re(e){return !e||e.length===0?"":e.replace(ge,"").replace(he,"")}function O(e){return !e||e.length===0?0:Array.from(re(e)).length}var T={accent:"#FF5A2D",accentBright:"#FF7A3D",accentDim:"#D14A22",info:"#FF8A5B",success:"#2FBF71",warn:"#FFB020",error:"#E23D2D",muted:"#8B7F77"};var Se=typeof process.env.FORCE_COLOR=="string"&&process.env.FORCE_COLOR.trim().length>0&&process.env.FORCE_COLOR.trim()!=="0",z=process.env.NO_COLOR&&!Se?new xe.Chalk({level:0}):xe__default.default,R=e=>z.hex(e),m={accent:R(T.accent),accentBright:R(T.accentBright),accentDim:R(T.accentDim),info:R(T.info),success:R(T.success),warn:R(T.warn),error:R(T.error),muted:R(T.muted),heading:z.bold.hex(T.accent),command:R(T.accentBright),option:R(T.warn)},S=()=>z.level>0,E=(e,r,n)=>e?r(n):n;function Ee(e){let r=S();return E(r,m.success,"\u2713")+` ${e}`}function ke(e){let r=S();return E(r,m.error,"\u2717")+` ${e}`}function ve(e){let r=S();return E(r,m.warn,"\u26A0")+` ${e}`}function $e(e){let r=S();return E(r,m.info,"\u2139")+` ${e}`}function We(e){let r=S();return E(r,m.heading,e)}function we(e,r,n=15){let i=S(),o=e.padEnd(n);return `${E(i,m.muted,o)} ${r}`}function Te(e,r){let n=S(),i=E(n,m.command,e);if(!r)return ` ${i}`;let o=E(n,m.muted,`# ${r}`);return ` ${i} ${o}`}function ye(e,r,n){let i=S(),o=E(i,m.info,e),l=E(i,m.command,r),d=n?` ${E(i,m.muted,n)}`:"";return `${o} ${l}${d}`}function Pe(e,r){let n=S(),i,o;switch(e){case "pending":i="\u25CB",o=m.muted;break;case "running":i="\u25CF",o=m.info;break;case "success":i="\u2713",o=m.success;break;case "error":i="\u2717",o=m.error;break;case "skipped":i="\u25CB",o=m.muted;break}return E(n,o,i)+` ${r}`}function Oe(e=80,r="\u2500"){let n=S();return E(n,m.muted,r.repeat(e))}function Re(e){let r=S();return E(r,m.muted,e)}function Le(e,r,n){let i=S(),o=e===1?r:n||`${r}s`;return `${E(i,m.accent,String(e))} ${o}`}function L(e){let r=e?.trim();return r?utils.Filesystem.toUnixPath(r):void 0}function M(e=process.env,r=B__default.default.homedir){let n=Ce(e,r);return n?utils.Filesystem.resolve(n):void 0}function Ce(e,r){let n=L(e.EASBOT_HOME);if(n){if(n==="~"||n.startsWith("~/")||n.startsWith("~\\")){let l=L(e.HOME)??L(e.USERPROFILE)??te(r);return l?n.replace(/^~(?=$|[\\/])/,l):void 0}return n}let i=L(e.HOME);if(i)return i;let o=L(e.USERPROFILE);return o||te(r)}function te(e){try{return L(e())}catch{return}}function F(e=process.env,r=B__default.default.homedir){return M(e,r)??utils.Filesystem.resolve(process.cwd())}function Y(e,r){if(!e.startsWith("~"))return L(e);let n=process.env.EASBOT_TEST_HOME?.trim();return n||(n=L(r?.home)??M(r?.env??process.env,r?.homedir??B__default.default.homedir)),n?e.replace(/^~(?=$|[\\/])/,n):L(e)}function ne(e){if(!e)return "";let r=e.trim();if(!r)return r;if(r.startsWith("~")){let n=Y(r,{home:F(process.env,B__default.default.homedir),env:process.env,homedir:B__default.default.homedir});return K__default.default.resolve(n)}return K__default.default.resolve(r)}function Ae(e=process.env,r=B__default.default.homedir){let n=e.OPENCLAW_STATE_DIR?.trim()||e.CLAWDBOT_STATE_DIR?.trim();if(n)return ne(n);let i=K__default.default.join(F(e,r),".openclaw");try{if(Ne__default.default.existsSync(i))return i}catch{}return i}function oe(){return M(process.env,B__default.default.homedir)}function ie(){let e=oe();return e?process.env.EASBOT_HOME?.trim()?{home:e,prefix:"$EASBOT_HOME"}:{home:e,prefix:"~"}:void 0}function se(e){if(!e)return e;let r=ie();if(!r)return e;let{home:n,prefix:i}=r;return e===n?i:e.startsWith(`${n}/`)||e.startsWith(`${n}\\`)?`${i}${e.slice(n.length)}`:e}function ce(e){if(!e)return e;let r=ie();return r?e.split(r.home).join(r.prefix):e}function Ie(e){return se(e)}function q(e){return ce(e)}function J(e,r,n){let o=e.replaceAll("\x1B",""),l=r.replaceAll("\x1B","");return (n?.force===true?true:n?.force===false?false:!!process.stdout.isTTY)?`\x1B]8;;${l}\x07${o}\x1B]8;;\x07`:n?.fallback??`${o} (${l})`}var D="https://docs.openclaw.ai";function _e(e,r,n){let i=e.trim(),o=i.startsWith("http")?i:`${D}${i.startsWith("/")?i:`/${i}`}`;return J(r??o,o,{fallback:n?.fallback??o,force:n?.force})}function He(e){return J(e??D,D,{fallback:D})}function Me(e,r){let n={ok:true,data:e};r!==void 0&&(n.meta=r),console.log(JSON.stringify(n,null,2));}function De(e,r){console.log(JSON.stringify({ok:false,error:{code:e,message:r}},null,2));}var G=e=>S()?m.accent(e):e,X=e=>e&&S()?m.heading(e):e,Z=e=>e&&S()?m.muted(e):e;var Be=/^(https?:\/\/|file:\/\/)/i,Je=/^[a-zA-Z]:[\\/]/,je=/^[a-zA-Z0-9._-]+$/;function ae(e,r){if(r<=0)return [e];let n=Array.from(e),i=[];for(let o=0;o<n.length;o+=r)i.push(n.slice(o,o+r).join(""));return i.length>0?i:[e]}function fe(e){return e?Be.test(e)||e.startsWith("/")||e.startsWith("~/")||e.startsWith("./")||e.startsWith("../")||Je.test(e)||e.startsWith("\\\\")||e.includes("/")||e.includes("\\")?true:e.includes("_")&&je.test(e):false}function ze(e,r){if(e.trim().length===0)return [e];if(r<=0)return [e];let n=e.match(/^(\s*)([-*\u2022]\s+)?(.*)$/),i=n?.[1]??"",o=n?.[2]??"",l=n?.[3]??"",d=`${i}${o}`,g=`${i}${o?" ".repeat(o.length):""}`,$=Math.max(10,r-O(d)),W=Math.max(10,r-O(g)),A=l.split(/\s+/).filter(Boolean),y=[],p="",x=d,k=$;for(let b of A){if(!p){if(O(b)>k){if(fe(b)){p=b;continue}let w=ae(b,k),f=w.shift()??"";y.push(x+f),x=g,k=W;for(let u of w)y.push(x+u);continue}p=b;continue}let C=`${p} ${b}`;if(O(C)<=k){p=C;continue}if(y.push(x+p),x=g,k=W,O(b)>k){if(fe(b)){p=b;continue}let w=ae(b,k),f=w.shift()??"";y.push(x+f);for(let u of w)y.push(x+u);p="";continue}p=b;}return (p||A.length===0)&&y.push(x+p),y}function ue(e,r={}){let n=r.columns??process.stdout.columns??80,i=r.maxWidth??Math.max(40,Math.min(88,n-10));return e.split(`
|
|
2
|
+
`).flatMap(o=>ze(o,i)).join(`
|
|
3
|
+
`)}function Ue(e,r){prompts.note(ue(e),X(r));}var _=null;function Ve(e){e.isTTY&&(_=e);}function Q(){_?.isTTY&&_.write("\r\x1B[2K");}function Ye(e){_&&(e&&_!==e||(_=null));}function qe(e){return prompts.select({...e,message:G(e.message),options:e.options.map(r=>r.hint===void 0?r:{...r,hint:Z(r.hint)})})}var Ge="\x1B[0m\x1B[?25h\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?2004l";function j(e,r,n){let i=n?` (${n})`:"",o=`[terminal] restore ${e} failed${i}: ${String(r)}`;try{process.stderr.write(`${o}
|
|
4
|
+
`);}catch(l){console.error(`[terminal] restore reporting failed${i}: ${String(l)}`);}}function Xe(e,r={}){let n=r.resumeStdinIfPaused??r.resumeStdin??false;try{Q();}catch(o){j("progress line",o,e);}let i=process.stdin;if(i.isTTY&&typeof i.setRawMode=="function"){try{i.setRawMode(!1);}catch(o){j("raw mode",o,e);}if(n&&typeof i.isPaused=="function"&&i.isPaused())try{i.resume();}catch(o){j("stdin resume",o,e);}}if(process.stdout.isTTY)try{process.stdout.write(Ge);}catch(o){j("stdout reset",o,e);}}function Ze(e){let r=e?.code;return r==="EPIPE"||r==="EIO"}function Qe(e={}){let r=false,n=false,i=(g,$)=>{n||(n=true,e.onBrokenPipe?.(g,$));},o=(g,$)=>{if(!Ze(g))throw g;return r=true,i(g,$),false},l=(g,$)=>{if(r)return false;try{e.beforeWrite?.();}catch(W){return o(W,process.stderr)}try{return g.write($),!r}catch(W){return o(W,g)}};return {write:l,writeLine:(g,$)=>l(g,`${$}
|
|
5
|
+
`),reset:()=>{r=false,n=false;},isClosed:()=>r}}function H(e,r){return !Number.isFinite(r)||r<=0?"":e.repeat(r)}function er(e,r,n){let i=O(e);if(i>=r)return e;let o=r-i;if(n==="right")return `${H(" ",o)}${e}`;if(n==="center"){let l=Math.floor(o/2),d=o-l;return `${H(" ",l)}${e}${H(" ",d)}`}return `${e}${H(" ",o)}`}function rr(e,r){if(!e||e.length===0)return [""];if(!Number.isFinite(r)||r<=0)return [e];let n="\x1B",i=[];for(let t=0;t<e.length;){if(e[t]===n){if(e[t+1]==="["){let a=t+2;for(;a<e.length;){let h=e[a];if(h==="m")break;if(h&&h>="0"&&h<="9"){a+=1;continue}if(h===";"){a+=1;continue}break}if(e[a]==="m"){i.push({kind:"ansi",value:e.slice(t,a+1)}),t=a+1;continue}}if(e[t+1]==="]"&&e.slice(t+2,t+5)==="8;;"){let a=e.indexOf(`${n}\\`,t+5);if(a>=0){i.push({kind:"ansi",value:e.slice(t,a+2)}),t=a+2;continue}}}let s=e.codePointAt(t);if(!s)break;let c=String.fromCodePoint(s);i.push({kind:"char",value:c}),t+=c.length;}let o=i.findIndex(t=>t.kind==="char");if(o<0)return [e];let l=-1;for(let t=i.length-1;t>=0;t-=1){let s=i[t];if(s&&s.kind==="char"){l=t;break}}let d=i.slice(0,o).filter(t=>t.kind==="ansi").map(t=>t.value).join(""),g=i.slice(l+1).filter(t=>t.kind==="ansi").map(t=>t.value).join(""),$=i.slice(o,l+1),W=[],A=t=>t===" "||t===" "||t==="/"||t==="-"||t==="_"||t===".",y=t=>t===" "||t===" ",p=false,x=[],k=0,b=null,C=t=>(t??x).map(s=>s.value).join(""),w=t=>t.reduce((s,c)=>s+(c.kind==="char"?1:0),0),f=t=>{let s=t.replace(/\s+$/,"");s.trim().length!==0&&W.push(s);},u=t=>{if(x.length===0)return;if(t==null||t<=0){f(C()),x.length=0,k=0,b=null;return}let s=x.slice(0,t),c=x.slice(t);for(f(C(s));c.length>0;){let a=c[0];if(a?.kind!=="char"||!y(a.value))break;c.shift();}x.length=0,x.push(...c),k=w(x),b=null;};for(let t of $){if(t.kind==="ansi"){x.push(t);continue}let s=t.value;if(!(p&&(p=false,s===`
|
|
6
|
+
`))){if(s===`
|
|
7
|
+
`||s==="\r"){u(x.length),s==="\r"&&(p=true);continue}k+1>r&&k>0&&u(b),x.push(t),k+=1,A(s)&&(b=x.length);}}return u(x.length),W.length===0?[""]:!d&&!g?W:W.map(t=>t&&`${d}${t}${g}`)}function tr(e){if(e!=null&&!(!Number.isFinite(e)||e<=0))return Math.floor(e)}function nr(e){let r=e.rows.map(f=>{let u={};for(let[t,s]of Object.entries(f))u[t]=q(s);return u}),n=e.border??"unicode";if(n==="none"){let f=e.columns;return `${[f.map(s=>s.header).join(" | "),...r.map(s=>f.map(c=>s[c.key]??"").join(" | "))].join(`
|
|
8
|
+
`)}
|
|
9
|
+
`}let i=Math.max(0,e.padding??1),o=e.columns,l=o.map(f=>{let u=O(f.header),t=Math.max(0,...r.map(s=>O(s[f.key]??"")));return {headerW:u,cellW:t}}),d=o.map((f,u)=>{let t=l[u];if(!t)throw new Error(`Internal error: metrics[${u}] is undefined`);let s=Math.max(t.headerW,t.cellW)+i*2,c=f.maxWidth?Math.min(s,f.maxWidth):s;return Math.max(f.minWidth??3,c)}),g=tr(e.width),$=o.length+1,W=d.reduce((f,u)=>f+u,0)+$,A=o.map((f,u)=>{let t=l[u];if(!t)throw new Error(`Internal error: metrics[${u}] is undefined`);return Math.max(f.minWidth??3,t.headerW+i*2,3)}),y=o.map((f,u)=>{let t=l[u];if(!t)throw new Error(`Internal error: metrics[${u}] is undefined`);return Math.max(t.headerW+i*2,3)});if(g&&W>g){let f=W-g,u=o.map((c,a)=>{let h=d[a];if(h===void 0)throw new Error(`Internal error: widths[${a}] is undefined`);return {i:a,w:h}}).filter(({i:c})=>!!o[c]?.flex).toSorted((c,a)=>a.w-c.w).map(c=>c.i),t=o.map((c,a)=>{let h=d[a];if(h===void 0)throw new Error(`Internal error: widths[${a}] is undefined`);return {i:a,w:h}}).filter(({i:c})=>!o[c]?.flex).toSorted((c,a)=>a.w-c.w).map(c=>c.i),s=(c,a)=>{for(;f>0;){let h=false;for(let P of c){let N=d[P],I=a[P];if(!(N===void 0||I===void 0)&&!(N<=I)&&(d[P]=N-1,f-=1,h=true,f<=0))break}if(!h)break}};s(u,A),s(u,y),s(t,A),s(t,y);}if(g){let f=o.length+1,u=d.reduce((s,c)=>s+c,0)+f,t=g-u;if(t>0){let s=o.map((c,a)=>({c,i:a})).filter(({c})=>!!c.flex).map(({i:c})=>c);if(s.length>0){let c=o.map(a=>typeof a.maxWidth=="number"&&a.maxWidth>0?Math.floor(a.maxWidth):Number.POSITIVE_INFINITY);for(;t>0;){let a=false;for(let h of s){let P=d[h],N=c[h];if(!(P===void 0||N===void 0)&&!(P>=N)&&(d[h]=P+1,t-=1,a=true,t<=0))break}if(!a)break}}}}let p=n==="ascii"?{tl:"+",tr:"+",bl:"+",br:"+",h:"-",v:"|",t:"+",ml:"+",m:"+",mr:"+",b:"+"}:{tl:"\u250C",tr:"\u2510",bl:"\u2514",br:"\u2518",h:"\u2500",v:"\u2502",t:"\u252C",ml:"\u251C",m:"\u253C",mr:"\u2524",b:"\u2534"},x=(f,u,t)=>`${f}${d.map(s=>{if(s===void 0)throw new Error("Internal error: width is undefined");return H(p.h,s)}).join(u)}${t}`,k=f=>{let u=d[f];if(u===void 0)throw new Error(`Internal error: widths[${f}] is undefined`);return Math.max(1,u-i*2)},b=H(" ",i),C=(f,u=false)=>{let s=o.map(h=>u?h.header:f[h.key]??"").map((h,P)=>rr(h,k(P))),c=Math.max(...s.map(h=>h.length)),a=[];for(let h=0;h<c;h+=1){let P=s.map((N,I)=>{let le=N[h]??"",ee=o[I];if(!ee)throw new Error(`Internal error: columns[${I}] is undefined`);let me=er(le,k(I),ee.align??"left");return `${b}${me}${b}`});a.push(`${p.v}${P.join(p.v)}${p.v}`);}return a},w=[];w.push(x(p.tl,p.t,p.tr)),w.push(...C({},true)),w.push(x(p.ml,p.m,p.mr));for(let f of r)w.push(...C(f,false));return w.push(x(p.bl,p.b,p.br)),`${w.join(`
|
|
10
|
+
`)}
|
|
11
|
+
`}function or(e,r){if(!r)return e;let n=e.indexOf(":");if(n===-1)return e;let i=e.slice(0,n+1),o=e.slice(n+1).trimStart(),l=o.toLowerCase(),d=(g,$)=>`${i} ${$(o.slice(0,g.length))}${o.slice(g.length)}`;return l.startsWith("failed")?d("failed",m.error):l.startsWith("ok")?d("ok",m.success):l.startsWith("linked")?d("linked",m.success):l.startsWith("configured")?d("configured",m.success):l.startsWith("not linked")?d("not linked",m.warn):l.startsWith("not configured")?d("not configured",m.muted):l.startsWith("unknown")?d("unknown",m.warn):e}exports.DOCS_ROOT=D;exports.LOBSTER_PALETTE=T;exports.clearActiveProgressLine=Q;exports.colorize=E;exports.createSafeStreamWriter=Qe;exports.displayPath=Ie;exports.displayString=q;exports.emitJsonError=De;exports.emitJsonOk=Me;exports.expandHomePrefix=Y;exports.formatCommandExample=Te;exports.formatCount=Le;exports.formatDocsLink=_e;exports.formatDocsRootLink=He;exports.formatEmptyMessage=Re;exports.formatError=ke;exports.formatHeading=We;exports.formatInfo=$e;exports.formatKeyValue=we;exports.formatListItem=ye;exports.formatSeparator=Oe;exports.formatStatus=Pe;exports.formatSuccess=Ee;exports.formatTerminalLink=J;exports.formatWarning=ve;exports.isRich=S;exports.note=Ue;exports.registerActiveProgressLine=Ve;exports.renderTable=nr;exports.resolveConfigDir=Ae;exports.resolveEffectiveHomeDir=M;exports.resolveHomeDir=oe;exports.resolveRequiredHomeDir=F;exports.resolveUserPath=ne;exports.restoreTerminalState=Xe;exports.selectStyled=qe;exports.shortenHomeInString=ce;exports.shortenHomePath=se;exports.stripAnsi=re;exports.styleHealthChannelLine=or;exports.stylePromptHint=Z;exports.stylePromptMessage=G;exports.stylePromptTitle=X;exports.theme=m;exports.unregisterActiveProgressLine=Ye;exports.visibleWidth=O;exports.wrapNoteMessage=ue;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as chalk from 'chalk';
|
|
2
|
+
import { select } from '@clack/prompts';
|
|
3
|
+
|
|
4
|
+
declare function stripAnsi(input: string): string;
|
|
5
|
+
declare function visibleWidth(input: string): number;
|
|
6
|
+
|
|
7
|
+
declare const LOBSTER_PALETTE: {
|
|
8
|
+
readonly accent: "#FF5A2D";
|
|
9
|
+
readonly accentBright: "#FF7A3D";
|
|
10
|
+
readonly accentDim: "#D14A22";
|
|
11
|
+
readonly info: "#FF8A5B";
|
|
12
|
+
readonly success: "#2FBF71";
|
|
13
|
+
readonly warn: "#FFB020";
|
|
14
|
+
readonly error: "#E23D2D";
|
|
15
|
+
readonly muted: "#8B7F77";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
declare const theme: {
|
|
19
|
+
readonly accent: chalk.ChalkInstance;
|
|
20
|
+
readonly accentBright: chalk.ChalkInstance;
|
|
21
|
+
readonly accentDim: chalk.ChalkInstance;
|
|
22
|
+
readonly info: chalk.ChalkInstance;
|
|
23
|
+
readonly success: chalk.ChalkInstance;
|
|
24
|
+
readonly warn: chalk.ChalkInstance;
|
|
25
|
+
readonly error: chalk.ChalkInstance;
|
|
26
|
+
readonly muted: chalk.ChalkInstance;
|
|
27
|
+
readonly heading: chalk.ChalkInstance;
|
|
28
|
+
readonly command: chalk.ChalkInstance;
|
|
29
|
+
readonly option: chalk.ChalkInstance;
|
|
30
|
+
};
|
|
31
|
+
declare const isRich: () => boolean;
|
|
32
|
+
declare const colorize: (rich: boolean, color: (value: string) => string, value: string) => string;
|
|
33
|
+
|
|
34
|
+
declare function formatSuccess(message: string): string;
|
|
35
|
+
declare function formatError(message: string): string;
|
|
36
|
+
declare function formatWarning(message: string): string;
|
|
37
|
+
declare function formatInfo(message: string): string;
|
|
38
|
+
declare function formatHeading(title: string): string;
|
|
39
|
+
declare function formatKeyValue(key: string, value: string, keyWidth?: number): string;
|
|
40
|
+
declare function formatCommandExample(command: string, description?: string): string;
|
|
41
|
+
declare function formatListItem(icon: string, text: string, description?: string): string;
|
|
42
|
+
declare function formatStatus(status: 'pending' | 'running' | 'success' | 'error' | 'skipped', message: string): string;
|
|
43
|
+
declare function formatSeparator(width?: number, char?: string): string;
|
|
44
|
+
declare function formatEmptyMessage(message: string): string;
|
|
45
|
+
declare function formatCount(count: number, singular: string, plural?: string): string;
|
|
46
|
+
|
|
47
|
+
declare function resolveUserPath(input: string): string;
|
|
48
|
+
declare function resolveConfigDir(env?: NodeJS.ProcessEnv, homedir?: () => string): string;
|
|
49
|
+
declare function resolveHomeDir(): string | undefined;
|
|
50
|
+
declare function shortenHomePath(input: string): string;
|
|
51
|
+
declare function shortenHomeInString(input: string): string;
|
|
52
|
+
declare function displayPath(input: string): string;
|
|
53
|
+
declare function displayString(input: string): string;
|
|
54
|
+
declare function formatTerminalLink(label: string, url: string, opts?: {
|
|
55
|
+
fallback?: string;
|
|
56
|
+
force?: boolean;
|
|
57
|
+
}): string;
|
|
58
|
+
|
|
59
|
+
declare function resolveEffectiveHomeDir(env?: NodeJS.ProcessEnv, homedir?: () => string): string | undefined;
|
|
60
|
+
declare function resolveRequiredHomeDir(env?: NodeJS.ProcessEnv, homedir?: () => string): string;
|
|
61
|
+
declare function expandHomePrefix(input: string, opts?: {
|
|
62
|
+
home?: string;
|
|
63
|
+
env?: NodeJS.ProcessEnv;
|
|
64
|
+
homedir?: () => string;
|
|
65
|
+
}): string;
|
|
66
|
+
|
|
67
|
+
declare const DOCS_ROOT = "https://docs.openclaw.ai";
|
|
68
|
+
declare function formatDocsLink(path: string, label?: string, opts?: {
|
|
69
|
+
fallback?: string;
|
|
70
|
+
force?: boolean;
|
|
71
|
+
}): string;
|
|
72
|
+
declare function formatDocsRootLink(label?: string): string;
|
|
73
|
+
|
|
74
|
+
declare function emitJsonOk(data: unknown, meta?: Record<string, unknown>): void;
|
|
75
|
+
declare function emitJsonError(code: string, message: string): void;
|
|
76
|
+
|
|
77
|
+
declare function wrapNoteMessage(message: string, options?: {
|
|
78
|
+
maxWidth?: number;
|
|
79
|
+
columns?: number;
|
|
80
|
+
}): string;
|
|
81
|
+
declare function note(message: string, title?: string): void;
|
|
82
|
+
|
|
83
|
+
declare function registerActiveProgressLine(stream: NodeJS.WriteStream): void;
|
|
84
|
+
declare function clearActiveProgressLine(): void;
|
|
85
|
+
declare function unregisterActiveProgressLine(stream?: NodeJS.WriteStream): void;
|
|
86
|
+
|
|
87
|
+
declare function selectStyled<T>(params: Parameters<typeof select<T>>[0]): Promise<symbol | T>;
|
|
88
|
+
|
|
89
|
+
declare const stylePromptMessage: (message: string) => string;
|
|
90
|
+
declare const stylePromptTitle: (title?: string) => string | undefined;
|
|
91
|
+
declare const stylePromptHint: (hint?: string) => string | undefined;
|
|
92
|
+
|
|
93
|
+
type RestoreTerminalStateOptions = {
|
|
94
|
+
resumeStdin?: boolean;
|
|
95
|
+
resumeStdinIfPaused?: boolean;
|
|
96
|
+
};
|
|
97
|
+
declare function restoreTerminalState(reason?: string, options?: RestoreTerminalStateOptions): void;
|
|
98
|
+
|
|
99
|
+
type SafeStreamWriterOptions = {
|
|
100
|
+
beforeWrite?: () => void;
|
|
101
|
+
onBrokenPipe?: (err: NodeJS.ErrnoException, stream: NodeJS.WriteStream) => void;
|
|
102
|
+
};
|
|
103
|
+
type SafeStreamWriter = {
|
|
104
|
+
write: (stream: NodeJS.WriteStream, text: string) => boolean;
|
|
105
|
+
writeLine: (stream: NodeJS.WriteStream, text: string) => boolean;
|
|
106
|
+
reset: () => void;
|
|
107
|
+
isClosed: () => boolean;
|
|
108
|
+
};
|
|
109
|
+
declare function createSafeStreamWriter(options?: SafeStreamWriterOptions): SafeStreamWriter;
|
|
110
|
+
|
|
111
|
+
type Align = 'left' | 'right' | 'center';
|
|
112
|
+
type TableColumn = {
|
|
113
|
+
key: string;
|
|
114
|
+
header: string;
|
|
115
|
+
align?: Align;
|
|
116
|
+
minWidth?: number;
|
|
117
|
+
maxWidth?: number;
|
|
118
|
+
flex?: boolean;
|
|
119
|
+
};
|
|
120
|
+
type RenderTableOptions = {
|
|
121
|
+
columns: TableColumn[];
|
|
122
|
+
rows: Array<Record<string, string>>;
|
|
123
|
+
width?: number;
|
|
124
|
+
padding?: number;
|
|
125
|
+
border?: 'unicode' | 'ascii' | 'none';
|
|
126
|
+
};
|
|
127
|
+
declare function renderTable(opts: RenderTableOptions): string;
|
|
128
|
+
|
|
129
|
+
declare function styleHealthChannelLine(line: string, rich: boolean): string;
|
|
130
|
+
|
|
131
|
+
export { DOCS_ROOT, LOBSTER_PALETTE, type RenderTableOptions, type SafeStreamWriter, type SafeStreamWriterOptions, type TableColumn, clearActiveProgressLine, colorize, createSafeStreamWriter, displayPath, displayString, emitJsonError, emitJsonOk, expandHomePrefix, formatCommandExample, formatCount, formatDocsLink, formatDocsRootLink, formatEmptyMessage, formatError, formatHeading, formatInfo, formatKeyValue, formatListItem, formatSeparator, formatStatus, formatSuccess, formatTerminalLink, formatWarning, isRich, note, registerActiveProgressLine, renderTable, resolveConfigDir, resolveEffectiveHomeDir, resolveHomeDir, resolveRequiredHomeDir, resolveUserPath, restoreTerminalState, selectStyled, shortenHomeInString, shortenHomePath, stripAnsi, styleHealthChannelLine, stylePromptHint, stylePromptMessage, stylePromptTitle, theme, unregisterActiveProgressLine, visibleWidth, wrapNoteMessage };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as chalk from 'chalk';
|
|
2
|
+
import { select } from '@clack/prompts';
|
|
3
|
+
|
|
4
|
+
declare function stripAnsi(input: string): string;
|
|
5
|
+
declare function visibleWidth(input: string): number;
|
|
6
|
+
|
|
7
|
+
declare const LOBSTER_PALETTE: {
|
|
8
|
+
readonly accent: "#FF5A2D";
|
|
9
|
+
readonly accentBright: "#FF7A3D";
|
|
10
|
+
readonly accentDim: "#D14A22";
|
|
11
|
+
readonly info: "#FF8A5B";
|
|
12
|
+
readonly success: "#2FBF71";
|
|
13
|
+
readonly warn: "#FFB020";
|
|
14
|
+
readonly error: "#E23D2D";
|
|
15
|
+
readonly muted: "#8B7F77";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
declare const theme: {
|
|
19
|
+
readonly accent: chalk.ChalkInstance;
|
|
20
|
+
readonly accentBright: chalk.ChalkInstance;
|
|
21
|
+
readonly accentDim: chalk.ChalkInstance;
|
|
22
|
+
readonly info: chalk.ChalkInstance;
|
|
23
|
+
readonly success: chalk.ChalkInstance;
|
|
24
|
+
readonly warn: chalk.ChalkInstance;
|
|
25
|
+
readonly error: chalk.ChalkInstance;
|
|
26
|
+
readonly muted: chalk.ChalkInstance;
|
|
27
|
+
readonly heading: chalk.ChalkInstance;
|
|
28
|
+
readonly command: chalk.ChalkInstance;
|
|
29
|
+
readonly option: chalk.ChalkInstance;
|
|
30
|
+
};
|
|
31
|
+
declare const isRich: () => boolean;
|
|
32
|
+
declare const colorize: (rich: boolean, color: (value: string) => string, value: string) => string;
|
|
33
|
+
|
|
34
|
+
declare function formatSuccess(message: string): string;
|
|
35
|
+
declare function formatError(message: string): string;
|
|
36
|
+
declare function formatWarning(message: string): string;
|
|
37
|
+
declare function formatInfo(message: string): string;
|
|
38
|
+
declare function formatHeading(title: string): string;
|
|
39
|
+
declare function formatKeyValue(key: string, value: string, keyWidth?: number): string;
|
|
40
|
+
declare function formatCommandExample(command: string, description?: string): string;
|
|
41
|
+
declare function formatListItem(icon: string, text: string, description?: string): string;
|
|
42
|
+
declare function formatStatus(status: 'pending' | 'running' | 'success' | 'error' | 'skipped', message: string): string;
|
|
43
|
+
declare function formatSeparator(width?: number, char?: string): string;
|
|
44
|
+
declare function formatEmptyMessage(message: string): string;
|
|
45
|
+
declare function formatCount(count: number, singular: string, plural?: string): string;
|
|
46
|
+
|
|
47
|
+
declare function resolveUserPath(input: string): string;
|
|
48
|
+
declare function resolveConfigDir(env?: NodeJS.ProcessEnv, homedir?: () => string): string;
|
|
49
|
+
declare function resolveHomeDir(): string | undefined;
|
|
50
|
+
declare function shortenHomePath(input: string): string;
|
|
51
|
+
declare function shortenHomeInString(input: string): string;
|
|
52
|
+
declare function displayPath(input: string): string;
|
|
53
|
+
declare function displayString(input: string): string;
|
|
54
|
+
declare function formatTerminalLink(label: string, url: string, opts?: {
|
|
55
|
+
fallback?: string;
|
|
56
|
+
force?: boolean;
|
|
57
|
+
}): string;
|
|
58
|
+
|
|
59
|
+
declare function resolveEffectiveHomeDir(env?: NodeJS.ProcessEnv, homedir?: () => string): string | undefined;
|
|
60
|
+
declare function resolveRequiredHomeDir(env?: NodeJS.ProcessEnv, homedir?: () => string): string;
|
|
61
|
+
declare function expandHomePrefix(input: string, opts?: {
|
|
62
|
+
home?: string;
|
|
63
|
+
env?: NodeJS.ProcessEnv;
|
|
64
|
+
homedir?: () => string;
|
|
65
|
+
}): string;
|
|
66
|
+
|
|
67
|
+
declare const DOCS_ROOT = "https://docs.openclaw.ai";
|
|
68
|
+
declare function formatDocsLink(path: string, label?: string, opts?: {
|
|
69
|
+
fallback?: string;
|
|
70
|
+
force?: boolean;
|
|
71
|
+
}): string;
|
|
72
|
+
declare function formatDocsRootLink(label?: string): string;
|
|
73
|
+
|
|
74
|
+
declare function emitJsonOk(data: unknown, meta?: Record<string, unknown>): void;
|
|
75
|
+
declare function emitJsonError(code: string, message: string): void;
|
|
76
|
+
|
|
77
|
+
declare function wrapNoteMessage(message: string, options?: {
|
|
78
|
+
maxWidth?: number;
|
|
79
|
+
columns?: number;
|
|
80
|
+
}): string;
|
|
81
|
+
declare function note(message: string, title?: string): void;
|
|
82
|
+
|
|
83
|
+
declare function registerActiveProgressLine(stream: NodeJS.WriteStream): void;
|
|
84
|
+
declare function clearActiveProgressLine(): void;
|
|
85
|
+
declare function unregisterActiveProgressLine(stream?: NodeJS.WriteStream): void;
|
|
86
|
+
|
|
87
|
+
declare function selectStyled<T>(params: Parameters<typeof select<T>>[0]): Promise<symbol | T>;
|
|
88
|
+
|
|
89
|
+
declare const stylePromptMessage: (message: string) => string;
|
|
90
|
+
declare const stylePromptTitle: (title?: string) => string | undefined;
|
|
91
|
+
declare const stylePromptHint: (hint?: string) => string | undefined;
|
|
92
|
+
|
|
93
|
+
type RestoreTerminalStateOptions = {
|
|
94
|
+
resumeStdin?: boolean;
|
|
95
|
+
resumeStdinIfPaused?: boolean;
|
|
96
|
+
};
|
|
97
|
+
declare function restoreTerminalState(reason?: string, options?: RestoreTerminalStateOptions): void;
|
|
98
|
+
|
|
99
|
+
type SafeStreamWriterOptions = {
|
|
100
|
+
beforeWrite?: () => void;
|
|
101
|
+
onBrokenPipe?: (err: NodeJS.ErrnoException, stream: NodeJS.WriteStream) => void;
|
|
102
|
+
};
|
|
103
|
+
type SafeStreamWriter = {
|
|
104
|
+
write: (stream: NodeJS.WriteStream, text: string) => boolean;
|
|
105
|
+
writeLine: (stream: NodeJS.WriteStream, text: string) => boolean;
|
|
106
|
+
reset: () => void;
|
|
107
|
+
isClosed: () => boolean;
|
|
108
|
+
};
|
|
109
|
+
declare function createSafeStreamWriter(options?: SafeStreamWriterOptions): SafeStreamWriter;
|
|
110
|
+
|
|
111
|
+
type Align = 'left' | 'right' | 'center';
|
|
112
|
+
type TableColumn = {
|
|
113
|
+
key: string;
|
|
114
|
+
header: string;
|
|
115
|
+
align?: Align;
|
|
116
|
+
minWidth?: number;
|
|
117
|
+
maxWidth?: number;
|
|
118
|
+
flex?: boolean;
|
|
119
|
+
};
|
|
120
|
+
type RenderTableOptions = {
|
|
121
|
+
columns: TableColumn[];
|
|
122
|
+
rows: Array<Record<string, string>>;
|
|
123
|
+
width?: number;
|
|
124
|
+
padding?: number;
|
|
125
|
+
border?: 'unicode' | 'ascii' | 'none';
|
|
126
|
+
};
|
|
127
|
+
declare function renderTable(opts: RenderTableOptions): string;
|
|
128
|
+
|
|
129
|
+
declare function styleHealthChannelLine(line: string, rich: boolean): string;
|
|
130
|
+
|
|
131
|
+
export { DOCS_ROOT, LOBSTER_PALETTE, type RenderTableOptions, type SafeStreamWriter, type SafeStreamWriterOptions, type TableColumn, clearActiveProgressLine, colorize, createSafeStreamWriter, displayPath, displayString, emitJsonError, emitJsonOk, expandHomePrefix, formatCommandExample, formatCount, formatDocsLink, formatDocsRootLink, formatEmptyMessage, formatError, formatHeading, formatInfo, formatKeyValue, formatListItem, formatSeparator, formatStatus, formatSuccess, formatTerminalLink, formatWarning, isRich, note, registerActiveProgressLine, renderTable, resolveConfigDir, resolveEffectiveHomeDir, resolveHomeDir, resolveRequiredHomeDir, resolveUserPath, restoreTerminalState, selectStyled, shortenHomeInString, shortenHomePath, stripAnsi, styleHealthChannelLine, stylePromptHint, stylePromptMessage, stylePromptTitle, theme, unregisterActiveProgressLine, visibleWidth, wrapNoteMessage };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import be,{Chalk}from'chalk';import Ae from'fs';import J from'os';import q from'path';import {Filesystem}from'@easbot/utils';import {note,select}from'@clack/prompts';var pe="\\x1b\\[[0-9;]*m",he="\\x1b\\]8;;.*?\\x1b\\\\|\\x1b\\]8;;\\x1b\\\\",ge=new RegExp(pe,"g"),xe=new RegExp(he,"g");function te(e){return !e||e.length===0?"":e.replace(xe,"").replace(ge,"")}function R(e){return !e||e.length===0?0:Array.from(te(e)).length}var y={accent:"#FF5A2D",accentBright:"#FF7A3D",accentDim:"#D14A22",info:"#FF8A5B",success:"#2FBF71",warn:"#FFB020",error:"#E23D2D",muted:"#8B7F77"};var Ee=typeof process.env.FORCE_COLOR=="string"&&process.env.FORCE_COLOR.trim().length>0&&process.env.FORCE_COLOR.trim()!=="0",U=process.env.NO_COLOR&&!Ee?new Chalk({level:0}):be,L=e=>U.hex(e),m={accent:L(y.accent),accentBright:L(y.accentBright),accentDim:L(y.accentDim),info:L(y.info),success:L(y.success),warn:L(y.warn),error:L(y.error),muted:L(y.muted),heading:U.bold.hex(y.accent),command:L(y.accentBright),option:L(y.warn)},S=()=>U.level>0,E=(e,r,n)=>e?r(n):n;function ke(e){let r=S();return E(r,m.success,"\u2713")+` ${e}`}function ve(e){let r=S();return E(r,m.error,"\u2717")+` ${e}`}function $e(e){let r=S();return E(r,m.warn,"\u26A0")+` ${e}`}function We(e){let r=S();return E(r,m.info,"\u2139")+` ${e}`}function we(e){let r=S();return E(r,m.heading,e)}function Te(e,r,n=15){let i=S(),o=e.padEnd(n);return `${E(i,m.muted,o)} ${r}`}function ye(e,r){let n=S(),i=E(n,m.command,e);if(!r)return ` ${i}`;let o=E(n,m.muted,`# ${r}`);return ` ${i} ${o}`}function Pe(e,r,n){let i=S(),o=E(i,m.info,e),l=E(i,m.command,r),d=n?` ${E(i,m.muted,n)}`:"";return `${o} ${l}${d}`}function Oe(e,r){let n=S(),i,o;switch(e){case "pending":i="\u25CB",o=m.muted;break;case "running":i="\u25CF",o=m.info;break;case "success":i="\u2713",o=m.success;break;case "error":i="\u2717",o=m.error;break;case "skipped":i="\u25CB",o=m.muted;break}return E(n,o,i)+` ${r}`}function Re(e=80,r="\u2500"){let n=S();return E(n,m.muted,r.repeat(e))}function Le(e){let r=S();return E(r,m.muted,e)}function Ce(e,r,n){let i=S(),o=e===1?r:n||`${r}s`;return `${E(i,m.accent,String(e))} ${o}`}function C(e){let r=e?.trim();return r?Filesystem.toUnixPath(r):void 0}function D(e=process.env,r=J.homedir){let n=Ne(e,r);return n?Filesystem.resolve(n):void 0}function Ne(e,r){let n=C(e.EASBOT_HOME);if(n){if(n==="~"||n.startsWith("~/")||n.startsWith("~\\")){let l=C(e.HOME)??C(e.USERPROFILE)??ne(r);return l?n.replace(/^~(?=$|[\\/])/,l):void 0}return n}let i=C(e.HOME);if(i)return i;let o=C(e.USERPROFILE);return o||ne(r)}function ne(e){try{return C(e())}catch{return}}function B(e=process.env,r=J.homedir){return D(e,r)??Filesystem.resolve(process.cwd())}function K(e,r){if(!e.startsWith("~"))return C(e);let n=process.env.EASBOT_TEST_HOME?.trim();return n||(n=C(r?.home)??D(r?.env??process.env,r?.homedir??J.homedir)),n?e.replace(/^~(?=$|[\\/])/,n):C(e)}function oe(e){if(!e)return "";let r=e.trim();if(!r)return r;if(r.startsWith("~")){let n=K(r,{home:B(process.env,J.homedir),env:process.env,homedir:J.homedir});return q.resolve(n)}return q.resolve(r)}function Ie(e=process.env,r=J.homedir){let n=e.OPENCLAW_STATE_DIR?.trim()||e.CLAWDBOT_STATE_DIR?.trim();if(n)return oe(n);let i=q.join(B(e,r),".openclaw");try{if(Ae.existsSync(i))return i}catch{}return i}function ie(){return D(process.env,J.homedir)}function se(){let e=ie();return e?process.env.EASBOT_HOME?.trim()?{home:e,prefix:"$EASBOT_HOME"}:{home:e,prefix:"~"}:void 0}function ce(e){if(!e)return e;let r=se();if(!r)return e;let{home:n,prefix:i}=r;return e===n?i:e.startsWith(`${n}/`)||e.startsWith(`${n}\\`)?`${i}${e.slice(n.length)}`:e}function ae(e){if(!e)return e;let r=se();return r?e.split(r.home).join(r.prefix):e}function _e(e){return ce(e)}function G(e){return ae(e)}function j(e,r,n){let o=e.replaceAll("\x1B",""),l=r.replaceAll("\x1B","");return (n?.force===true?true:n?.force===false?false:!!process.stdout.isTTY)?`\x1B]8;;${l}\x07${o}\x1B]8;;\x07`:n?.fallback??`${o} (${l})`}var F="https://docs.openclaw.ai";function He(e,r,n){let i=e.trim(),o=i.startsWith("http")?i:`${F}${i.startsWith("/")?i:`/${i}`}`;return j(r??o,o,{fallback:n?.fallback??o,force:n?.force})}function Me(e){return j(e??F,F,{fallback:F})}function De(e,r){let n={ok:true,data:e};r!==void 0&&(n.meta=r),console.log(JSON.stringify(n,null,2));}function Fe(e,r){console.log(JSON.stringify({ok:false,error:{code:e,message:r}},null,2));}var X=e=>S()?m.accent(e):e,Z=e=>e&&S()?m.heading(e):e,Q=e=>e&&S()?m.muted(e):e;var Je=/^(https?:\/\/|file:\/\/)/i,je=/^[a-zA-Z]:[\\/]/,ze=/^[a-zA-Z0-9._-]+$/;function fe(e,r){if(r<=0)return [e];let n=Array.from(e),i=[];for(let o=0;o<n.length;o+=r)i.push(n.slice(o,o+r).join(""));return i.length>0?i:[e]}function ue(e){return e?Je.test(e)||e.startsWith("/")||e.startsWith("~/")||e.startsWith("./")||e.startsWith("../")||je.test(e)||e.startsWith("\\\\")||e.includes("/")||e.includes("\\")?true:e.includes("_")&&ze.test(e):false}function Ue(e,r){if(e.trim().length===0)return [e];if(r<=0)return [e];let n=e.match(/^(\s*)([-*\u2022]\s+)?(.*)$/),i=n?.[1]??"",o=n?.[2]??"",l=n?.[3]??"",d=`${i}${o}`,g=`${i}${o?" ".repeat(o.length):""}`,W=Math.max(10,r-R(d)),w=Math.max(10,r-R(g)),I=l.split(/\s+/).filter(Boolean),P=[],p="",x=d,k=W;for(let b of I){if(!p){if(R(b)>k){if(ue(b)){p=b;continue}let T=fe(b,k),f=T.shift()??"";P.push(x+f),x=g,k=w;for(let u of T)P.push(x+u);continue}p=b;continue}let N=`${p} ${b}`;if(R(N)<=k){p=N;continue}if(P.push(x+p),x=g,k=w,R(b)>k){if(ue(b)){p=b;continue}let T=fe(b,k),f=T.shift()??"";P.push(x+f);for(let u of T)P.push(x+u);p="";continue}p=b;}return (p||I.length===0)&&P.push(x+p),P}function le(e,r={}){let n=r.columns??process.stdout.columns??80,i=r.maxWidth??Math.max(40,Math.min(88,n-10));return e.split(`
|
|
2
|
+
`).flatMap(o=>Ue(o,i)).join(`
|
|
3
|
+
`)}function Ve(e,r){note(le(e),Z(r));}var H=null;function Ye(e){e.isTTY&&(H=e);}function ee(){H?.isTTY&&H.write("\r\x1B[2K");}function Ke(e){H&&(e&&H!==e||(H=null));}function Ge(e){return select({...e,message:X(e.message),options:e.options.map(r=>r.hint===void 0?r:{...r,hint:Q(r.hint)})})}var Xe="\x1B[0m\x1B[?25h\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?2004l";function z(e,r,n){let i=n?` (${n})`:"",o=`[terminal] restore ${e} failed${i}: ${String(r)}`;try{process.stderr.write(`${o}
|
|
4
|
+
`);}catch(l){console.error(`[terminal] restore reporting failed${i}: ${String(l)}`);}}function Ze(e,r={}){let n=r.resumeStdinIfPaused??r.resumeStdin??false;try{ee();}catch(o){z("progress line",o,e);}let i=process.stdin;if(i.isTTY&&typeof i.setRawMode=="function"){try{i.setRawMode(!1);}catch(o){z("raw mode",o,e);}if(n&&typeof i.isPaused=="function"&&i.isPaused())try{i.resume();}catch(o){z("stdin resume",o,e);}}if(process.stdout.isTTY)try{process.stdout.write(Xe);}catch(o){z("stdout reset",o,e);}}function Qe(e){let r=e?.code;return r==="EPIPE"||r==="EIO"}function er(e={}){let r=false,n=false,i=(g,W)=>{n||(n=true,e.onBrokenPipe?.(g,W));},o=(g,W)=>{if(!Qe(g))throw g;return r=true,i(g,W),false},l=(g,W)=>{if(r)return false;try{e.beforeWrite?.();}catch(w){return o(w,process.stderr)}try{return g.write(W),!r}catch(w){return o(w,g)}};return {write:l,writeLine:(g,W)=>l(g,`${W}
|
|
5
|
+
`),reset:()=>{r=false,n=false;},isClosed:()=>r}}function M(e,r){return !Number.isFinite(r)||r<=0?"":e.repeat(r)}function rr(e,r,n){let i=R(e);if(i>=r)return e;let o=r-i;if(n==="right")return `${M(" ",o)}${e}`;if(n==="center"){let l=Math.floor(o/2),d=o-l;return `${M(" ",l)}${e}${M(" ",d)}`}return `${e}${M(" ",o)}`}function tr(e,r){if(!e||e.length===0)return [""];if(!Number.isFinite(r)||r<=0)return [e];let n="\x1B",i=[];for(let t=0;t<e.length;){if(e[t]===n){if(e[t+1]==="["){let a=t+2;for(;a<e.length;){let h=e[a];if(h==="m")break;if(h&&h>="0"&&h<="9"){a+=1;continue}if(h===";"){a+=1;continue}break}if(e[a]==="m"){i.push({kind:"ansi",value:e.slice(t,a+1)}),t=a+1;continue}}if(e[t+1]==="]"&&e.slice(t+2,t+5)==="8;;"){let a=e.indexOf(`${n}\\`,t+5);if(a>=0){i.push({kind:"ansi",value:e.slice(t,a+2)}),t=a+2;continue}}}let s=e.codePointAt(t);if(!s)break;let c=String.fromCodePoint(s);i.push({kind:"char",value:c}),t+=c.length;}let o=i.findIndex(t=>t.kind==="char");if(o<0)return [e];let l=-1;for(let t=i.length-1;t>=0;t-=1){let s=i[t];if(s&&s.kind==="char"){l=t;break}}let d=i.slice(0,o).filter(t=>t.kind==="ansi").map(t=>t.value).join(""),g=i.slice(l+1).filter(t=>t.kind==="ansi").map(t=>t.value).join(""),W=i.slice(o,l+1),w=[],I=t=>t===" "||t===" "||t==="/"||t==="-"||t==="_"||t===".",P=t=>t===" "||t===" ",p=false,x=[],k=0,b=null,N=t=>(t??x).map(s=>s.value).join(""),T=t=>t.reduce((s,c)=>s+(c.kind==="char"?1:0),0),f=t=>{let s=t.replace(/\s+$/,"");s.trim().length!==0&&w.push(s);},u=t=>{if(x.length===0)return;if(t==null||t<=0){f(N()),x.length=0,k=0,b=null;return}let s=x.slice(0,t),c=x.slice(t);for(f(N(s));c.length>0;){let a=c[0];if(a?.kind!=="char"||!P(a.value))break;c.shift();}x.length=0,x.push(...c),k=T(x),b=null;};for(let t of W){if(t.kind==="ansi"){x.push(t);continue}let s=t.value;if(!(p&&(p=false,s===`
|
|
6
|
+
`))){if(s===`
|
|
7
|
+
`||s==="\r"){u(x.length),s==="\r"&&(p=true);continue}k+1>r&&k>0&&u(b),x.push(t),k+=1,I(s)&&(b=x.length);}}return u(x.length),w.length===0?[""]:!d&&!g?w:w.map(t=>t&&`${d}${t}${g}`)}function nr(e){if(e!=null&&!(!Number.isFinite(e)||e<=0))return Math.floor(e)}function or(e){let r=e.rows.map(f=>{let u={};for(let[t,s]of Object.entries(f))u[t]=G(s);return u}),n=e.border??"unicode";if(n==="none"){let f=e.columns;return `${[f.map(s=>s.header).join(" | "),...r.map(s=>f.map(c=>s[c.key]??"").join(" | "))].join(`
|
|
8
|
+
`)}
|
|
9
|
+
`}let i=Math.max(0,e.padding??1),o=e.columns,l=o.map(f=>{let u=R(f.header),t=Math.max(0,...r.map(s=>R(s[f.key]??"")));return {headerW:u,cellW:t}}),d=o.map((f,u)=>{let t=l[u];if(!t)throw new Error(`Internal error: metrics[${u}] is undefined`);let s=Math.max(t.headerW,t.cellW)+i*2,c=f.maxWidth?Math.min(s,f.maxWidth):s;return Math.max(f.minWidth??3,c)}),g=nr(e.width),W=o.length+1,w=d.reduce((f,u)=>f+u,0)+W,I=o.map((f,u)=>{let t=l[u];if(!t)throw new Error(`Internal error: metrics[${u}] is undefined`);return Math.max(f.minWidth??3,t.headerW+i*2,3)}),P=o.map((f,u)=>{let t=l[u];if(!t)throw new Error(`Internal error: metrics[${u}] is undefined`);return Math.max(t.headerW+i*2,3)});if(g&&w>g){let f=w-g,u=o.map((c,a)=>{let h=d[a];if(h===void 0)throw new Error(`Internal error: widths[${a}] is undefined`);return {i:a,w:h}}).filter(({i:c})=>!!o[c]?.flex).toSorted((c,a)=>a.w-c.w).map(c=>c.i),t=o.map((c,a)=>{let h=d[a];if(h===void 0)throw new Error(`Internal error: widths[${a}] is undefined`);return {i:a,w:h}}).filter(({i:c})=>!o[c]?.flex).toSorted((c,a)=>a.w-c.w).map(c=>c.i),s=(c,a)=>{for(;f>0;){let h=false;for(let O of c){let A=d[O],_=a[O];if(!(A===void 0||_===void 0)&&!(A<=_)&&(d[O]=A-1,f-=1,h=true,f<=0))break}if(!h)break}};s(u,I),s(u,P),s(t,I),s(t,P);}if(g){let f=o.length+1,u=d.reduce((s,c)=>s+c,0)+f,t=g-u;if(t>0){let s=o.map((c,a)=>({c,i:a})).filter(({c})=>!!c.flex).map(({i:c})=>c);if(s.length>0){let c=o.map(a=>typeof a.maxWidth=="number"&&a.maxWidth>0?Math.floor(a.maxWidth):Number.POSITIVE_INFINITY);for(;t>0;){let a=false;for(let h of s){let O=d[h],A=c[h];if(!(O===void 0||A===void 0)&&!(O>=A)&&(d[h]=O+1,t-=1,a=true,t<=0))break}if(!a)break}}}}let p=n==="ascii"?{tl:"+",tr:"+",bl:"+",br:"+",h:"-",v:"|",t:"+",ml:"+",m:"+",mr:"+",b:"+"}:{tl:"\u250C",tr:"\u2510",bl:"\u2514",br:"\u2518",h:"\u2500",v:"\u2502",t:"\u252C",ml:"\u251C",m:"\u253C",mr:"\u2524",b:"\u2534"},x=(f,u,t)=>`${f}${d.map(s=>{if(s===void 0)throw new Error("Internal error: width is undefined");return M(p.h,s)}).join(u)}${t}`,k=f=>{let u=d[f];if(u===void 0)throw new Error(`Internal error: widths[${f}] is undefined`);return Math.max(1,u-i*2)},b=M(" ",i),N=(f,u=false)=>{let s=o.map(h=>u?h.header:f[h.key]??"").map((h,O)=>tr(h,k(O))),c=Math.max(...s.map(h=>h.length)),a=[];for(let h=0;h<c;h+=1){let O=s.map((A,_)=>{let me=A[h]??"",re=o[_];if(!re)throw new Error(`Internal error: columns[${_}] is undefined`);let de=rr(me,k(_),re.align??"left");return `${b}${de}${b}`});a.push(`${p.v}${O.join(p.v)}${p.v}`);}return a},T=[];T.push(x(p.tl,p.t,p.tr)),T.push(...N({},true)),T.push(x(p.ml,p.m,p.mr));for(let f of r)T.push(...N(f,false));return T.push(x(p.bl,p.b,p.br)),`${T.join(`
|
|
10
|
+
`)}
|
|
11
|
+
`}function ir(e,r){if(!r)return e;let n=e.indexOf(":");if(n===-1)return e;let i=e.slice(0,n+1),o=e.slice(n+1).trimStart(),l=o.toLowerCase(),d=(g,W)=>`${i} ${W(o.slice(0,g.length))}${o.slice(g.length)}`;return l.startsWith("failed")?d("failed",m.error):l.startsWith("ok")?d("ok",m.success):l.startsWith("linked")?d("linked",m.success):l.startsWith("configured")?d("configured",m.success):l.startsWith("not linked")?d("not linked",m.warn):l.startsWith("not configured")?d("not configured",m.muted):l.startsWith("unknown")?d("unknown",m.warn):e}export{F as DOCS_ROOT,y as LOBSTER_PALETTE,ee as clearActiveProgressLine,E as colorize,er as createSafeStreamWriter,_e as displayPath,G as displayString,Fe as emitJsonError,De as emitJsonOk,K as expandHomePrefix,ye as formatCommandExample,Ce as formatCount,He as formatDocsLink,Me as formatDocsRootLink,Le as formatEmptyMessage,ve as formatError,we as formatHeading,We as formatInfo,Te as formatKeyValue,Pe as formatListItem,Re as formatSeparator,Oe as formatStatus,ke as formatSuccess,j as formatTerminalLink,$e as formatWarning,S as isRich,Ve as note,Ye as registerActiveProgressLine,or as renderTable,Ie as resolveConfigDir,D as resolveEffectiveHomeDir,ie as resolveHomeDir,B as resolveRequiredHomeDir,oe as resolveUserPath,Ze as restoreTerminalState,Ge as selectStyled,ae as shortenHomeInString,ce as shortenHomePath,te as stripAnsi,ir as styleHealthChannelLine,Q as stylePromptHint,X as stylePromptMessage,Z as stylePromptTitle,m as theme,Ke as unregisterActiveProgressLine,R as visibleWidth,le as wrapNoteMessage};
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@easbot/terminal",
|
|
3
|
+
"version": "0.3.11",
|
|
4
|
+
"description": "Terminal interaction toolkit for the EASBOT ecosystem — themed colors, ANSI utilities, OSC-8 hyperlinks, command formatting, table rendering, styled prompts, safe stream writes, and terminal-state recovery",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.mjs",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"easbot",
|
|
19
|
+
"terminal",
|
|
20
|
+
"cli",
|
|
21
|
+
"tui",
|
|
22
|
+
"ansi",
|
|
23
|
+
"color-theme",
|
|
24
|
+
"table",
|
|
25
|
+
"prompts",
|
|
26
|
+
"hyperlink",
|
|
27
|
+
"progress",
|
|
28
|
+
"typescript"
|
|
29
|
+
],
|
|
30
|
+
"author": "houjallen",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/houjallen/easbot.git",
|
|
35
|
+
"directory": "packages/terminal"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/houjallen/easbot/tree/main/packages/terminal#readme",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/houjallen/easbot/issues"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"README.md",
|
|
44
|
+
"README.en.md",
|
|
45
|
+
"LICENSE"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@clack/prompts": "^1.7.0",
|
|
49
|
+
"chalk": "^6.0.0",
|
|
50
|
+
"picocolors": "^1.1.1",
|
|
51
|
+
"@easbot/types": "0.3.11",
|
|
52
|
+
"@easbot/utils": "0.3.11"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^25.6.2",
|
|
56
|
+
"tsup": "^8.5.1",
|
|
57
|
+
"typescript": "^6.0.3",
|
|
58
|
+
"vitest": "^4.1.5"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=22.22.3"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"dev": "tsup --watch --env.NODE_ENV development",
|
|
68
|
+
"build": "tsup --env.NODE_ENV production",
|
|
69
|
+
"test": "vitest",
|
|
70
|
+
"test:run": "vitest run",
|
|
71
|
+
"test:coverage": "vitest run --coverage",
|
|
72
|
+
"lint": "biome check .",
|
|
73
|
+
"lint:fix": "biome check --write .",
|
|
74
|
+
"lint:report": "biome check --reporter=summary .",
|
|
75
|
+
"format": "biome format .",
|
|
76
|
+
"format:fix": "biome format --write .",
|
|
77
|
+
"type-check": "tsc --noEmit",
|
|
78
|
+
"clean": "npx rimraf dist node_modules",
|
|
79
|
+
"publish:npm": "bash scripts/publish.sh",
|
|
80
|
+
"publish:npm:win": "powershell -ExecutionPolicy Bypass -File scripts/publish.ps1"
|
|
81
|
+
}
|
|
82
|
+
}
|