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.
Files changed (131) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +3 -3
  3. package/bin/codex-managed-hook.mjs +80 -0
  4. package/dist/agents/baseagent.js +4 -0
  5. package/dist/agents/claude-runner.js +158 -44
  6. package/dist/agents/codex-app-server-client.js +153 -23
  7. package/dist/agents/codex-runner.js +331 -43
  8. package/dist/agents/ecagent-runner.js +53 -6
  9. package/dist/agents/gemini-runner.js +1 -0
  10. package/dist/agents/request-identity.js +55 -0
  11. package/dist/aun/aid/client.js +11 -21
  12. package/dist/aun/aid/managed-operation.js +107 -0
  13. package/dist/aun/msg/group.js +81 -12
  14. package/dist/aun/msg/managed-operation.js +804 -0
  15. package/dist/aun/msg/mention-schema.js +20 -0
  16. package/dist/aun/msg/p2p.js +7 -0
  17. package/dist/aun/outbox.js +28 -31
  18. package/dist/channels/aun.js +418 -235
  19. package/dist/channels/daemon.js +13 -0
  20. package/dist/cli/agent-command.js +72 -17
  21. package/dist/cli/agent.js +249 -22
  22. package/dist/cli/aun-commands.js +270 -69
  23. package/dist/cli/bench.js +12 -3
  24. package/dist/cli/daemon-commands.js +52 -33
  25. package/dist/cli/fs-command.js +60 -0
  26. package/dist/cli/handoff-command.js +3 -0
  27. package/dist/cli/index.js +106 -63
  28. package/dist/cli/init-cancel.js +208 -0
  29. package/dist/cli/init-channel.js +343 -195
  30. package/dist/cli/init.js +21 -9
  31. package/dist/cli/managed-command-guard.js +41 -0
  32. package/dist/cli/model.js +11 -4
  33. package/dist/cli/queue-command.js +11 -1
  34. package/dist/cli/response.js +71 -0
  35. package/dist/cli/restart-monitor.js +4 -18
  36. package/dist/cli/trigger-command.js +4 -0
  37. package/dist/config/builtin-roles.js +5 -0
  38. package/dist/config/contact-book-store.js +11 -3
  39. package/dist/config/contact-request-service.js +161 -15
  40. package/dist/config/gateway-config.js +26 -10
  41. package/dist/config/mention-mode.js +8 -24
  42. package/dist/core/agent-reload-coordinator.js +53 -0
  43. package/dist/core/auth/agent-delegation.js +0 -1
  44. package/dist/core/auth/operation-authorizer.js +62 -154
  45. package/dist/core/auth/operation-catalog.js +264 -22
  46. package/dist/core/bootstrap-messages.js +68 -0
  47. package/dist/core/bootstrap-service.js +144 -10
  48. package/dist/core/capability/providers/claude-capability-provider.js +9 -31
  49. package/dist/core/capability/providers/codex-capability-provider.js +4 -15
  50. package/dist/core/capability/skill-discovery.js +55 -0
  51. package/dist/core/channel-loader.js +24 -7
  52. package/dist/core/command/agent-control.js +14 -11
  53. package/dist/core/command/cli-intent-parser.js +9 -0
  54. package/dist/core/command/command-handler.js +467 -6
  55. package/dist/core/command/connect-menu.js +15 -0
  56. package/dist/core/command/group-menu.js +4 -4
  57. package/dist/core/command/menu-catalog.js +366 -0
  58. package/dist/core/command/menu-handler.js +213 -110
  59. package/dist/core/command/menu-protocol.js +71 -0
  60. package/dist/core/command/slash-gate.js +5 -2
  61. package/dist/core/command/slash-handler.js +45 -25
  62. package/dist/core/daemon-file-cache.js +2 -1
  63. package/dist/core/data-migration.js +9 -2
  64. package/dist/core/evolagent-registry.js +125 -35
  65. package/dist/core/evolagent.js +12 -5
  66. package/dist/core/handoff/dispatcher.js +5 -2
  67. package/dist/core/handoff/runtime.js +145 -21
  68. package/dist/core/handoff/store.js +24 -0
  69. package/dist/core/handoff/types.js +1 -0
  70. package/dist/core/inference/text-inference.js +38 -4
  71. package/dist/core/message/logical-queue-bridge.js +1 -0
  72. package/dist/core/message/mention-schema.js +126 -0
  73. package/dist/core/message/message-bridge.js +237 -79
  74. package/dist/core/message/message-log.js +23 -0
  75. package/dist/core/message/message-queue.js +20 -4
  76. package/dist/core/message/response-engine.js +54 -36
  77. package/dist/core/model/model-catalog.js +143 -24
  78. package/dist/core/model/model-diagnostics.js +28 -10
  79. package/dist/core/permission/ec-command-parser.js +156 -10
  80. package/dist/core/permission/index.js +1 -0
  81. package/dist/core/permission/readonly-shell-query.js +532 -0
  82. package/dist/core/permission/sandbox-runtime.js +22 -1
  83. package/dist/core/permission/shell-environment.js +46 -0
  84. package/dist/core/permission/tool-policy.js +292 -111
  85. package/dist/core/protected-paths.js +17 -9
  86. package/dist/core/runner-reload-transaction.js +57 -0
  87. package/dist/core/session/session-manager.js +21 -2
  88. package/dist/eck/manifest-engine.js +39 -13
  89. package/dist/index.js +644 -180
  90. package/dist/ipc.js +246 -19
  91. package/dist/response-system/coordinator.js +6 -4
  92. package/dist/response-system/engines/v1/proactive-flow.js +26 -8
  93. package/dist/response-system/modes/single-session/index.js +14 -1
  94. package/dist/response-system/selector.js +6 -5
  95. package/dist/trigger/anomaly-store.js +4 -0
  96. package/dist/trigger/script-executor.js +1 -0
  97. package/dist/utils/aid-bind.js +3 -8
  98. package/dist/utils/codex-app-server-registry.js +90 -0
  99. package/dist/utils/codex-cli.js +5 -1
  100. package/dist/utils/cross-platform.js +15 -0
  101. package/dist/utils/log-writer.js +6 -10
  102. package/dist/utils/logger.js +5 -5
  103. package/dist/utils/npm-ops.js +5 -12
  104. package/dist/utils/tool-summary.js +11 -0
  105. package/kits/docs/channels/aun.md +1 -1
  106. package/kits/docs/context-assembly.md +2 -2
  107. package/kits/docs/evolcore/agent.md +11 -6
  108. package/kits/docs/evolcore/aid.md +14 -0
  109. package/kits/docs/evolcore/msg.md +13 -0
  110. package/kits/docs/prompt-loading-architecture.md +1 -2
  111. package/kits/docs/venues/group.md +1 -1
  112. package/kits/eck_manifest.json +0 -12
  113. package/kits/rules/01-overview.md +9 -0
  114. package/kits/rules/04-relation.md +1 -1
  115. package/kits/rules/05-venue.md +2 -2
  116. package/kits/schemas/agent-config.schema.3.json +1 -1
  117. package/kits/schemas/agent-config.schema.4.json +1 -1
  118. package/kits/schemas/relation-config.schema.2.json +1 -1
  119. package/kits/schemas/role-config.schema.1.json +1 -1
  120. package/kits/schemas/single-session.schema.1.json +3 -3
  121. package/kits/schemas/single-session.schema.2.json +3 -3
  122. package/kits/templates/roles/admin.json +27 -0
  123. package/kits/templates/roles/member.json +44 -0
  124. package/kits/templates/roles/visitor.json +40 -2
  125. package/kits/templates/system-fragments/bootstrap.md +12 -6
  126. package/kits/templates/system-fragments/channel.md +6 -0
  127. package/kits/templates/system-fragments/session.md +2 -0
  128. package/package.json +3 -2
  129. package/skills/eclink/SKILL.md +15 -3
  130. package/skills/eclink/agents/openai.yaml +3 -3
  131. 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
- if (wantsHelp(args.slice(1))) {
102
- console.log(`用法: ec init [渠道] [选项]
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 绑定(daemon 或指定 agent)
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
- const initialized = await cmdInit({
175
- nonInteractive,
176
- baseagent: getArgValue(args, '--baseagent'),
177
- force: args.includes('--force'),
178
- owner: getArgValue(args, '--owner'),
179
- projectpath: getArgValue(args, '--projectpath'),
180
- ecweb: args.includes('--ecweb') ? true : undefined,
181
- format: getArgValue(args, '--format'),
182
- autoStart: wantsAutoStart ? true : wantsNoAutoStart ? false : undefined,
183
- });
184
- if (!initialized)
185
- process.exitCode = 1;
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
- await cmdStatus();
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
+ }