evolcore 0.0.10 → 0.0.12
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 +44 -0
- package/README.md +3 -3
- package/bin/codex-managed-hook.mjs +80 -0
- package/dist/agents/baseagent.js +4 -0
- package/dist/agents/claude-runner.js +158 -44
- package/dist/agents/codex-app-server-client.js +153 -23
- package/dist/agents/codex-runner.js +331 -43
- package/dist/agents/ecagent-runner.js +53 -6
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/agents/request-identity.js +55 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/msg/group.js +81 -12
- package/dist/aun/msg/managed-operation.js +804 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/aun/outbox.js +28 -31
- package/dist/channels/aun.js +418 -235
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +72 -17
- package/dist/cli/agent.js +249 -22
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/daemon-commands.js +52 -33
- package/dist/cli/fs-command.js +60 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +106 -63
- package/dist/cli/init-cancel.js +208 -0
- package/dist/cli/init-channel.js +343 -195
- package/dist/cli/init.js +21 -9
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +11 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +4 -18
- package/dist/cli/trigger-command.js +4 -0
- package/dist/config/builtin-roles.js +5 -0
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/gateway-config.js +26 -10
- package/dist/config/mention-mode.js +8 -24
- package/dist/core/agent-reload-coordinator.js +53 -0
- package/dist/core/auth/agent-delegation.js +0 -1
- package/dist/core/auth/operation-authorizer.js +62 -154
- package/dist/core/auth/operation-catalog.js +264 -22
- package/dist/core/bootstrap-messages.js +68 -0
- package/dist/core/bootstrap-service.js +144 -10
- package/dist/core/capability/providers/claude-capability-provider.js +9 -31
- package/dist/core/capability/providers/codex-capability-provider.js +4 -15
- package/dist/core/capability/skill-discovery.js +55 -0
- package/dist/core/channel-loader.js +24 -7
- package/dist/core/command/agent-control.js +14 -11
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +467 -6
- package/dist/core/command/connect-menu.js +15 -0
- package/dist/core/command/group-menu.js +4 -4
- package/dist/core/command/menu-catalog.js +366 -0
- package/dist/core/command/menu-handler.js +213 -110
- package/dist/core/command/menu-protocol.js +71 -0
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +45 -25
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/evolagent-registry.js +125 -35
- package/dist/core/evolagent.js +12 -5
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +145 -21
- package/dist/core/handoff/store.js +24 -0
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +38 -4
- package/dist/core/message/logical-queue-bridge.js +1 -0
- package/dist/core/message/mention-schema.js +126 -0
- package/dist/core/message/message-bridge.js +237 -79
- package/dist/core/message/message-log.js +23 -0
- package/dist/core/message/message-queue.js +20 -4
- package/dist/core/message/response-engine.js +54 -36
- package/dist/core/model/model-catalog.js +143 -24
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/ec-command-parser.js +156 -10
- package/dist/core/permission/index.js +1 -0
- package/dist/core/permission/readonly-shell-query.js +532 -0
- package/dist/core/permission/sandbox-runtime.js +22 -1
- package/dist/core/permission/shell-environment.js +46 -0
- package/dist/core/permission/tool-policy.js +292 -111
- package/dist/core/protected-paths.js +17 -9
- package/dist/core/runner-reload-transaction.js +57 -0
- package/dist/core/session/session-manager.js +21 -2
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +644 -180
- package/dist/ipc.js +246 -19
- package/dist/response-system/coordinator.js +6 -4
- package/dist/response-system/engines/v1/proactive-flow.js +26 -8
- package/dist/response-system/modes/single-session/index.js +14 -1
- package/dist/response-system/selector.js +6 -5
- package/dist/trigger/anomaly-store.js +4 -0
- package/dist/trigger/script-executor.js +1 -0
- package/dist/utils/aid-bind.js +3 -8
- package/dist/utils/codex-app-server-registry.js +90 -0
- package/dist/utils/codex-cli.js +5 -1
- package/dist/utils/cross-platform.js +15 -0
- package/dist/utils/log-writer.js +6 -10
- package/dist/utils/logger.js +5 -5
- package/dist/utils/npm-ops.js +5 -12
- package/dist/utils/tool-summary.js +11 -0
- package/kits/docs/channels/aun.md +1 -1
- package/kits/docs/context-assembly.md +2 -2
- package/kits/docs/evolcore/agent.md +11 -6
- package/kits/docs/evolcore/aid.md +14 -0
- package/kits/docs/evolcore/msg.md +13 -0
- package/kits/docs/prompt-loading-architecture.md +1 -2
- package/kits/docs/venues/group.md +1 -1
- package/kits/eck_manifest.json +0 -12
- package/kits/rules/01-overview.md +9 -0
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/agent-config.schema.3.json +1 -1
- package/kits/schemas/agent-config.schema.4.json +1 -1
- package/kits/schemas/relation-config.schema.2.json +1 -1
- package/kits/schemas/role-config.schema.1.json +1 -1
- package/kits/schemas/single-session.schema.1.json +3 -3
- package/kits/schemas/single-session.schema.2.json +3 -3
- package/kits/templates/roles/admin.json +27 -0
- package/kits/templates/roles/member.json +44 -0
- package/kits/templates/roles/visitor.json +40 -2
- package/kits/templates/system-fragments/bootstrap.md +12 -6
- package/kits/templates/system-fragments/channel.md +6 -0
- package/kits/templates/system-fragments/session.md +2 -0
- package/package.json +3 -2
- package/skills/eclink/SKILL.md +15 -3
- package/skills/eclink/agents/openai.yaml +3 -3
- package/dist/core/command/evol-menu-version-gate.js +0 -39
|
@@ -203,6 +203,7 @@ Commands:
|
|
|
203
203
|
}
|
|
204
204
|
const result = await ipcQuery(resolvePaths().socket, {
|
|
205
205
|
type: 'handoff-status', sessionId: process.env.EVOLCORE_SESSION_ID, handoffId,
|
|
206
|
+
delegationToken: process.env[AGENT_DELEGATION_TOKEN_ENV],
|
|
206
207
|
}, 5000);
|
|
207
208
|
if (!result) {
|
|
208
209
|
console.error('✗ daemon 暂时不可用');
|
|
@@ -234,6 +235,7 @@ Commands:
|
|
|
234
235
|
state: parsed.state,
|
|
235
236
|
filterSessionId: parsed.sessionId,
|
|
236
237
|
limit: parsed.limit,
|
|
238
|
+
delegationToken: process.env[AGENT_DELEGATION_TOKEN_ENV],
|
|
237
239
|
}, 5000);
|
|
238
240
|
if (!result) {
|
|
239
241
|
console.error('✗ daemon 暂时不可用');
|
|
@@ -276,6 +278,7 @@ Commands:
|
|
|
276
278
|
agent: parsed.agent,
|
|
277
279
|
handoffId: parsed.handoffId,
|
|
278
280
|
limit: parsed.limit,
|
|
281
|
+
delegationToken: process.env[AGENT_DELEGATION_TOKEN_ENV],
|
|
279
282
|
}, 5000);
|
|
280
283
|
if (!result) {
|
|
281
284
|
console.error('✗ daemon 暂时不可用');
|
package/dist/cli/index.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
process.env.AUN_LOG_INI_DISABLE = '1';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import { getPackageRoot } from '../paths.js';
|
|
6
|
+
import { getPackageRoot, resolvePaths } from '../paths.js';
|
|
7
|
+
import { ipcQuery } from '../ipc.js';
|
|
8
|
+
import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
|
|
7
9
|
import { cmdInit } from './init.js';
|
|
8
10
|
import { cmdInitWechat, cmdInitFeishu, cmdInitDingtalk, cmdInitQQBot, cmdInitWecom, cmdInitAun } from './init-channel.js';
|
|
9
11
|
import { isHelpFlag, wantsHelp, getArgValue } from './help.js';
|
|
@@ -15,6 +17,8 @@ import { cmdCtl } from './ctl-command.js';
|
|
|
15
17
|
import { cmdQueue } from './queue-command.js';
|
|
16
18
|
import { cmdStart, cmdStop, cmdRestart, cmdStatus, cmdLogs, cmdWatchCommand, cmdMv, cmdDiagnose } from './daemon-commands.js';
|
|
17
19
|
import { logCliCommand } from './command-log.js';
|
|
20
|
+
import { isInitCancelledError, printInitCancelHint } from './init-cancel.js';
|
|
21
|
+
import { assertManagedTopLevelCommandAllowed, ManagedCommandNotAllowedError } from './managed-command-guard.js';
|
|
18
22
|
const MAIN_HELP = `用法: ec <command> [选项]
|
|
19
23
|
|
|
20
24
|
服务:
|
|
@@ -86,6 +90,16 @@ process.on('warning', (w) => { if (w.name === 'ExperimentalWarning')
|
|
|
86
90
|
return; process.stderr.write((w.stack ?? String(w)) + '\n'); });
|
|
87
91
|
export async function main(args) {
|
|
88
92
|
const cmd = args[0] || 'start';
|
|
93
|
+
try {
|
|
94
|
+
assertManagedTopLevelCommandAllowed(cmd, args);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (!(error instanceof ManagedCommandNotAllowedError))
|
|
98
|
+
throw error;
|
|
99
|
+
console.error(`❌ [${error.code}] ${error.message}`);
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
89
103
|
logCliCommand(cmd, args);
|
|
90
104
|
if (cmd === '--version' || cmd === '-v' || cmd === '-V') {
|
|
91
105
|
const pkg = JSON.parse(fs.readFileSync(path.join(getPackageRoot(), 'package.json'), 'utf-8'));
|
|
@@ -97,9 +111,10 @@ export async function main(args) {
|
|
|
97
111
|
return;
|
|
98
112
|
}
|
|
99
113
|
switch (cmd) {
|
|
100
|
-
case 'init':
|
|
101
|
-
|
|
102
|
-
|
|
114
|
+
case 'init': {
|
|
115
|
+
try {
|
|
116
|
+
if (wantsHelp(args.slice(1))) {
|
|
117
|
+
console.log(`用法: ec init [渠道] [选项]
|
|
103
118
|
|
|
104
119
|
仅初始化 defaults.json:
|
|
105
120
|
ec init 交互式(写完 defaults.json 后嵌套 agent new)
|
|
@@ -119,72 +134,85 @@ export async function main(args) {
|
|
|
119
134
|
--force 已有不同 owner 时强制覆盖
|
|
120
135
|
|
|
121
136
|
配置渠道(先 ec agent new 创建 agent):
|
|
122
|
-
ec init aun AUN owner
|
|
137
|
+
ec init aun AUN owner 追加绑定(daemon 或指定 agent)
|
|
123
138
|
ec init feishu 飞书扫码登录
|
|
124
139
|
ec init wechat 微信扫码登录
|
|
125
140
|
ec init dingtalk 钉钉扫码登录
|
|
126
141
|
ec init qqbot QQ 机器人扫码绑定
|
|
127
142
|
ec init wecom 企业微信 AI Bot 扫码创建/手工配置`);
|
|
128
|
-
}
|
|
129
|
-
else if (args[1] === 'aun') {
|
|
130
|
-
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
131
|
-
suppressSdkLogs();
|
|
132
|
-
await cmdInitAun();
|
|
133
|
-
}
|
|
134
|
-
else if (args[1] === 'wechat') {
|
|
135
|
-
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
136
|
-
suppressSdkLogs();
|
|
137
|
-
await cmdInitWechat();
|
|
138
|
-
}
|
|
139
|
-
else if (args[1] === 'feishu') {
|
|
140
|
-
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
141
|
-
suppressSdkLogs();
|
|
142
|
-
await cmdInitFeishu();
|
|
143
|
-
}
|
|
144
|
-
else if (args[1] === 'dingtalk') {
|
|
145
|
-
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
146
|
-
suppressSdkLogs();
|
|
147
|
-
await cmdInitDingtalk();
|
|
148
|
-
}
|
|
149
|
-
else if (args[1] === 'qqbot') {
|
|
150
|
-
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
151
|
-
suppressSdkLogs();
|
|
152
|
-
await cmdInitQQBot();
|
|
153
|
-
}
|
|
154
|
-
else if (args[1] === 'wecom') {
|
|
155
|
-
await cmdInitWecom();
|
|
156
|
-
}
|
|
157
|
-
else if (args[1] && !args[1].startsWith('-')) {
|
|
158
|
-
const supported = ['feishu', 'wechat', 'aun', 'dingtalk', 'qqbot', 'wecom'];
|
|
159
|
-
console.error(`❌ 不支持的渠道: ${args[1]}`);
|
|
160
|
-
console.error(` 支持的渠道: ${supported.join(', ')}`);
|
|
161
|
-
process.exit(1);
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
165
|
-
suppressSdkLogs();
|
|
166
|
-
const nonInteractive = args.includes('--non-interactive');
|
|
167
|
-
const wantsAutoStart = args.includes('--auto-start');
|
|
168
|
-
const wantsNoAutoStart = args.includes('--no-auto-start');
|
|
169
|
-
if (wantsAutoStart && wantsNoAutoStart) {
|
|
170
|
-
console.error('❌ --auto-start 与 --no-auto-start 不能同时使用');
|
|
171
|
-
process.exitCode = 1;
|
|
172
|
-
break;
|
|
173
143
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
144
|
+
else {
|
|
145
|
+
const hasChannelTarget = Boolean(args[1] && !args[1].startsWith('-'));
|
|
146
|
+
if (hasChannelTarget)
|
|
147
|
+
printInitCancelHint();
|
|
148
|
+
if (args[1] === 'aun') {
|
|
149
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
150
|
+
suppressSdkLogs();
|
|
151
|
+
await cmdInitAun();
|
|
152
|
+
}
|
|
153
|
+
else if (args[1] === 'wechat') {
|
|
154
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
155
|
+
suppressSdkLogs();
|
|
156
|
+
await cmdInitWechat();
|
|
157
|
+
}
|
|
158
|
+
else if (args[1] === 'feishu') {
|
|
159
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
160
|
+
suppressSdkLogs();
|
|
161
|
+
await cmdInitFeishu();
|
|
162
|
+
}
|
|
163
|
+
else if (args[1] === 'dingtalk') {
|
|
164
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
165
|
+
suppressSdkLogs();
|
|
166
|
+
await cmdInitDingtalk();
|
|
167
|
+
}
|
|
168
|
+
else if (args[1] === 'qqbot') {
|
|
169
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
170
|
+
suppressSdkLogs();
|
|
171
|
+
await cmdInitQQBot();
|
|
172
|
+
}
|
|
173
|
+
else if (args[1] === 'wecom') {
|
|
174
|
+
await cmdInitWecom();
|
|
175
|
+
}
|
|
176
|
+
else if (args[1] && !args[1].startsWith('-')) {
|
|
177
|
+
const supported = ['feishu', 'wechat', 'aun', 'dingtalk', 'qqbot', 'wecom'];
|
|
178
|
+
console.error(`❌ 不支持的渠道: ${args[1]}`);
|
|
179
|
+
console.error(` 支持的渠道: ${supported.join(', ')}`);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
184
|
+
suppressSdkLogs();
|
|
185
|
+
const nonInteractive = args.includes('--non-interactive');
|
|
186
|
+
const wantsAutoStart = args.includes('--auto-start');
|
|
187
|
+
const wantsNoAutoStart = args.includes('--no-auto-start');
|
|
188
|
+
if (wantsAutoStart && wantsNoAutoStart) {
|
|
189
|
+
console.error('❌ --auto-start 与 --no-auto-start 不能同时使用');
|
|
190
|
+
process.exitCode = 1;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
const initialized = await cmdInit({
|
|
194
|
+
nonInteractive,
|
|
195
|
+
baseagent: getArgValue(args, '--baseagent'),
|
|
196
|
+
force: args.includes('--force'),
|
|
197
|
+
owner: getArgValue(args, '--owner'),
|
|
198
|
+
projectpath: getArgValue(args, '--projectpath'),
|
|
199
|
+
ecweb: args.includes('--ecweb') ? true : undefined,
|
|
200
|
+
format: getArgValue(args, '--format'),
|
|
201
|
+
autoStart: wantsAutoStart ? true : wantsNoAutoStart ? false : undefined,
|
|
202
|
+
});
|
|
203
|
+
if (!initialized && process.exitCode == null)
|
|
204
|
+
process.exitCode = 1;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
if (!isInitCancelledError(error))
|
|
210
|
+
throw error;
|
|
211
|
+
console.log('\n已退出初始化');
|
|
212
|
+
process.exitCode = 130;
|
|
186
213
|
}
|
|
187
214
|
break;
|
|
215
|
+
}
|
|
188
216
|
case 'start':
|
|
189
217
|
if (wantsHelp(args.slice(1))) {
|
|
190
218
|
printLifecycleHelp('start');
|
|
@@ -214,7 +242,22 @@ export async function main(args) {
|
|
|
214
242
|
printLifecycleHelp('status');
|
|
215
243
|
break;
|
|
216
244
|
}
|
|
217
|
-
|
|
245
|
+
if (process.env.EVOLCORE_SESSION_ID) {
|
|
246
|
+
const sessionId = process.env.EVOLCORE_SESSION_ID;
|
|
247
|
+
const delegationToken = process.env[AGENT_DELEGATION_TOKEN_ENV];
|
|
248
|
+
const result = await ipcQuery(resolvePaths().socket, {
|
|
249
|
+
type: 'status.auth', sessionId, delegationToken,
|
|
250
|
+
});
|
|
251
|
+
if (!result?.ok) {
|
|
252
|
+
console.error(`❌ ${result?.error || 'status is not allowed in this task'}`);
|
|
253
|
+
process.exitCode = 1;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
await cmdStatus();
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
await cmdStatus();
|
|
260
|
+
}
|
|
218
261
|
break;
|
|
219
262
|
case 'logs':
|
|
220
263
|
if (wantsHelp(args.slice(1))) {
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { setupRawKeyListener } from './raw-key-input.js';
|
|
2
|
+
const ESC = '\u001b';
|
|
3
|
+
const ANSI_SEQUENCE_TIMEOUT_MS = 40;
|
|
4
|
+
export const INIT_DOUBLE_ESCAPE_WINDOW_MS = 500;
|
|
5
|
+
export class InitCancelledError extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super('EvolCore init cancelled');
|
|
8
|
+
this.name = 'InitCancelledError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function isInitCancelledError(error) {
|
|
12
|
+
return error instanceof InitCancelledError || (error instanceof Error && error.name === 'InitCancelledError');
|
|
13
|
+
}
|
|
14
|
+
export function printInitCancelHint() {
|
|
15
|
+
if (process.stdin.isTTY) {
|
|
16
|
+
console.log('提示:任意步骤连续按两次 Esc 退出(Ctrl+C 也可退出)\n');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function isAnsiFinalByte(char) {
|
|
20
|
+
const code = char.charCodeAt(0);
|
|
21
|
+
return code >= 0x40 && code <= 0x7e;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Detect two standalone Escape presses without treating arrow/function-key
|
|
25
|
+
* escape sequences as Escape presses.
|
|
26
|
+
*/
|
|
27
|
+
export function createDoubleEscapeDetector(onDoubleEscape, windowMs = INIT_DOUBLE_ESCAPE_WINDOW_MS) {
|
|
28
|
+
let pendingBareEscape = false;
|
|
29
|
+
let pendingTimer;
|
|
30
|
+
let lastBareEscapeAt = 0;
|
|
31
|
+
let ansiSequence = '';
|
|
32
|
+
let disposed = false;
|
|
33
|
+
let triggered = false;
|
|
34
|
+
const clearPending = () => {
|
|
35
|
+
if (pendingTimer)
|
|
36
|
+
clearTimeout(pendingTimer);
|
|
37
|
+
pendingTimer = undefined;
|
|
38
|
+
pendingBareEscape = false;
|
|
39
|
+
};
|
|
40
|
+
const registerBareEscape = () => {
|
|
41
|
+
if (disposed || triggered)
|
|
42
|
+
return;
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
if (lastBareEscapeAt > 0 && now - lastBareEscapeAt <= windowMs) {
|
|
45
|
+
lastBareEscapeAt = 0;
|
|
46
|
+
triggered = true;
|
|
47
|
+
onDoubleEscape();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
lastBareEscapeAt = now;
|
|
51
|
+
};
|
|
52
|
+
const scheduleBareEscape = () => {
|
|
53
|
+
clearPending();
|
|
54
|
+
pendingBareEscape = true;
|
|
55
|
+
pendingTimer = setTimeout(() => {
|
|
56
|
+
pendingTimer = undefined;
|
|
57
|
+
pendingBareEscape = false;
|
|
58
|
+
registerBareEscape();
|
|
59
|
+
}, ANSI_SEQUENCE_TIMEOUT_MS);
|
|
60
|
+
};
|
|
61
|
+
const handle = (chunk) => {
|
|
62
|
+
if (disposed)
|
|
63
|
+
return;
|
|
64
|
+
const text = String(chunk);
|
|
65
|
+
for (let index = 0; index < text.length; index++) {
|
|
66
|
+
const char = text[index];
|
|
67
|
+
if (ansiSequence) {
|
|
68
|
+
lastBareEscapeAt = 0;
|
|
69
|
+
ansiSequence += char;
|
|
70
|
+
if (ansiSequence.length === 2 && ansiSequence[1] !== '[' && ansiSequence[1] !== 'O') {
|
|
71
|
+
ansiSequence = '';
|
|
72
|
+
}
|
|
73
|
+
else if (ansiSequence.length >= 2 && isAnsiFinalByte(char)) {
|
|
74
|
+
ansiSequence = '';
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (pendingBareEscape) {
|
|
79
|
+
clearPending();
|
|
80
|
+
if (char === ESC) {
|
|
81
|
+
registerBareEscape();
|
|
82
|
+
registerBareEscape();
|
|
83
|
+
}
|
|
84
|
+
else if (char === '[' || char === 'O') {
|
|
85
|
+
lastBareEscapeAt = 0;
|
|
86
|
+
ansiSequence = `${ESC}${char}`;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
lastBareEscapeAt = 0;
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (char !== ESC) {
|
|
94
|
+
lastBareEscapeAt = 0;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const next = text[index + 1];
|
|
98
|
+
if (next === ESC) {
|
|
99
|
+
registerBareEscape();
|
|
100
|
+
registerBareEscape();
|
|
101
|
+
index++;
|
|
102
|
+
}
|
|
103
|
+
else if (next === '[' || next === 'O') {
|
|
104
|
+
lastBareEscapeAt = 0;
|
|
105
|
+
ansiSequence = `${ESC}${next}`;
|
|
106
|
+
index++;
|
|
107
|
+
}
|
|
108
|
+
else if (next !== undefined) {
|
|
109
|
+
lastBareEscapeAt = 0;
|
|
110
|
+
index++;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
scheduleBareEscape();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
handle,
|
|
119
|
+
dispose: () => {
|
|
120
|
+
disposed = true;
|
|
121
|
+
clearPending();
|
|
122
|
+
ansiSequence = '';
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export function setupDoubleEscapeListener(onDoubleEscape, input = process.stdin) {
|
|
127
|
+
if (!input || typeof input.on !== 'function' || typeof input.removeListener !== 'function')
|
|
128
|
+
return () => { };
|
|
129
|
+
const detector = createDoubleEscapeDetector(onDoubleEscape);
|
|
130
|
+
const handler = (chunk) => detector.handle(chunk);
|
|
131
|
+
input.on('data', handler);
|
|
132
|
+
return () => {
|
|
133
|
+
input.removeListener('data', handler);
|
|
134
|
+
detector.dispose();
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
export function setupInitRawKeyListener(onKey, onDoubleEscape, input = process.stdin) {
|
|
138
|
+
const detector = createDoubleEscapeDetector(onDoubleEscape);
|
|
139
|
+
const cleanupRaw = setupRawKeyListener(key => {
|
|
140
|
+
detector.handle(key);
|
|
141
|
+
onKey(key);
|
|
142
|
+
}, input);
|
|
143
|
+
return () => {
|
|
144
|
+
cleanupRaw();
|
|
145
|
+
detector.dispose();
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** readline.question with the shared double-Escape cancellation behavior. */
|
|
149
|
+
export function askInit(rl, question) {
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
let settled = false;
|
|
152
|
+
const input = rl.input;
|
|
153
|
+
let cleanupInput = () => { };
|
|
154
|
+
const cleanup = () => {
|
|
155
|
+
cleanupInput();
|
|
156
|
+
rl.removeListener('SIGINT', cancel);
|
|
157
|
+
};
|
|
158
|
+
const cancel = () => {
|
|
159
|
+
if (settled)
|
|
160
|
+
return;
|
|
161
|
+
settled = true;
|
|
162
|
+
cleanup();
|
|
163
|
+
try {
|
|
164
|
+
rl.close();
|
|
165
|
+
}
|
|
166
|
+
catch { /* ignore */ }
|
|
167
|
+
try {
|
|
168
|
+
input?.pause?.();
|
|
169
|
+
}
|
|
170
|
+
catch { /* ignore */ }
|
|
171
|
+
reject(new InitCancelledError());
|
|
172
|
+
};
|
|
173
|
+
cleanupInput = setupDoubleEscapeListener(cancel, input);
|
|
174
|
+
rl.once('SIGINT', cancel);
|
|
175
|
+
try {
|
|
176
|
+
rl.question(question, answer => {
|
|
177
|
+
if (settled)
|
|
178
|
+
return;
|
|
179
|
+
settled = true;
|
|
180
|
+
cleanup();
|
|
181
|
+
resolve(answer);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (!settled) {
|
|
186
|
+
settled = true;
|
|
187
|
+
cleanup();
|
|
188
|
+
reject(error);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
export function waitForInitDelay(ms, signal) {
|
|
194
|
+
if (signal?.aborted)
|
|
195
|
+
return Promise.reject(new InitCancelledError());
|
|
196
|
+
return new Promise((resolve, reject) => {
|
|
197
|
+
const onAbort = () => {
|
|
198
|
+
clearTimeout(timer);
|
|
199
|
+
signal?.removeEventListener('abort', onAbort);
|
|
200
|
+
reject(new InitCancelledError());
|
|
201
|
+
};
|
|
202
|
+
const timer = setTimeout(() => {
|
|
203
|
+
signal?.removeEventListener('abort', onAbort);
|
|
204
|
+
resolve();
|
|
205
|
+
}, ms);
|
|
206
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
207
|
+
});
|
|
208
|
+
}
|