@x-otto/shared 0.1.0-alpha.0 → 0.1.0-alpha.10

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/README.md CHANGED
@@ -1,23 +1,23 @@
1
- # @otto/shared
1
+ # @x-otto/shared
2
2
 
3
3
  > General infrastructure toolkit: error system, structured logging, event emitters, HTTP/SSE client, JSONC, file system utilities, OAuth primitives, fuzzy search, text truncation, and more.
4
4
 
5
- `@otto/shared` provides the shared utilities consumed by most packages in the monorepo. It depends on `@otto/env` and adds pino for structured logging, undici for HTTP, and a comprehensive error hierarchy with 11 domain-specific error classes. It also exposes a browser-safe entry point (`@otto/shared/browser`) with event primitives only.
5
+ `@x-otto/shared` provides the shared utilities consumed by most packages in the monorepo. It depends on `@x-otto/env` and adds pino for structured logging, undici for HTTP, and a comprehensive error hierarchy with 5 domain-specific error classes.
6
6
 
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- pnpm add @otto/shared
10
+ pnpm add @x-otto/shared
11
11
  ```
12
12
 
13
13
  ## Usage
14
14
 
15
15
  ```ts
16
- import { createLogger } from '@otto/shared'
17
- import { CodedError, NetworkError } from '@otto/shared'
18
- import { TypedEventEmitter } from '@otto/shared'
19
- import { parseJsonc, safeStringify } from '@otto/shared'
20
- import { request, stream } from '@otto/shared'
16
+ import { createLogger } from '@x-otto/shared'
17
+ import { CodedError, NetworkError } from '@x-otto/shared'
18
+ import { TypedEventEmitter } from '@x-otto/shared'
19
+ import { parseJsonc, safeStringify } from '@x-otto/shared'
20
+ import { stream } from '@x-otto/shared'
21
21
 
22
22
  // Logging
23
23
  const log = createLogger('my-module')
@@ -31,8 +31,7 @@ const bus = new TypedEventEmitter<{ data: (payload: string) => void }>()
31
31
  bus.on('data', (msg) => console.log(msg))
32
32
 
33
33
  // HTTP + SSE
34
- const resp = await request('https://api.example.com/data')
35
- for await (const event of stream('https://api.example.com/stream')) {
34
+ for await (const event of stream({ url: 'https://api.example.com/stream' })) {
36
35
  console.log(event)
37
36
  }
38
37
  ```
@@ -41,7 +40,7 @@ for await (const event of stream('https://api.example.com/stream')) {
41
40
 
42
41
  ### Error System
43
42
  - `CodedError` — base class with `code` and optional `cause` chain
44
- - 11 domain error classes: `ConfigError`, `NetworkError`, `ProviderError`, `OAuthError`, `StreamError`, `AgentError`, `ToolError`, `HookError`, `SessionError`, `ExtensionError`, `McpError`
43
+ - 5 domain error classes: `NetworkError`, `ProviderError`, `OAuthError`, `AgentError`, `McpError`
45
44
 
46
45
  ### Logging
47
46
  - `createLogger(name)` — create named child logger (pino-based, lazy-init root)
@@ -54,12 +53,10 @@ for await (const event of stream('https://api.example.com/stream')) {
54
53
  - `EventBus<T>` — extended emitter with `publish()` and wildcard `*` subscription
55
54
 
56
55
  ### Resource Disposal
57
- - `DisposableStack` / `AsyncDisposableStack` — LIFO resource cleanup (implements `Symbol.dispose`)
58
- - `createDisposable()` / `createAsyncDisposable()` — factory helpers
56
+ - `safeDispose(fn, options?)` / `safeDisposeOf(apis, options?)` — isolated disposal runner (RFC-370): plugin-provided `dispose()` callbacks that throw are contained, with structured log context (`{ pluginId, api }`)
59
57
 
60
58
  ### HTTP & SSE
61
- - `request(options)` — Fetch API wrapper with timeout and proxy support
62
- - `stream(url, options)` — SSE stream reader returning `AsyncIterable<SseEvent>`
59
+ - `stream(options)` — SSE stream reader returning `AsyncIterable<SseEvent>`
63
60
  - `setDefaultProxy()` / `getDefaultProxy()` / `createDispatcher()` — proxy configuration
64
61
  - `probeProxy(url)` — proxy connectivity check
65
62
 
@@ -90,7 +87,7 @@ for await (const event of stream('https://api.example.com/stream')) {
90
87
  - `rankCandidates(query, items)` / `rankNamedItems(query, items)` — ranked search
91
88
 
92
89
  ### Other
93
- - `invariant(condition, message)` — type-narrowing assertion (merged from `@otto/invariant`, RFC-157 M3a)
90
+ - `invariant(condition, message)` — type-narrowing assertion (merged from `@x-otto/invariant`)
94
91
  - `splitFrontmatter(text)` / `parseFrontmatterFields(text)` — frontmatter parsing
95
92
  - `probeImageDimensions(data)` — image dimension extraction
96
93
  - `checkShellCommand(cmd)` / `buildAllowedEnv()` — command validation
@@ -99,7 +96,7 @@ for await (const event of stream('https://api.example.com/stream')) {
99
96
 
100
97
  ## Dependencies
101
98
 
102
- - Internal: `@otto/env`
99
+ - Internal: `@x-otto/env`
103
100
  - External: `pino`, `pino-pretty`, `eventsource-parser`, `jsonc-parser`, `mime-types`, `safe-stable-stringify`, `uuid`, `undici`
104
101
 
105
102
  ## Related
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { i as TypedEventEmitter, n as EventListener, r as Events, t as EventBus } from "./event-bus-inioRzYz.js";
2
1
  import pino, { Logger } from "pino";
2
+ import { normalizePath } from "@x-otto/env";
3
3
  import { ProxyAgent } from "undici";
4
4
 
5
5
  //#region src/error.d.ts
@@ -28,6 +28,20 @@ declare class CodedError extends Error {
28
28
  * `packages/coding/src/plugin/module-wiring.ts`)在包装点补充。
29
29
  */
30
30
  readonly pluginSource?: string;
31
+ /**
32
+ * RFC-361 D4:SSE 流被 abort 时的**触发源**——`'idle'`(空闲超时)/ `'wallclock'`
33
+ * (整请求墙钟超时)/ `'external'`(调用方传入的 signal 主动中止)。
34
+ *
35
+ * 存在的理由:三个 abort 源产生完全相同的 AbortError,错误消息也统一是
36
+ * "Stream aborted or timed out"。上层此前只能按**消息文本**分类,而
37
+ * `stream-errors.ts` 的 `TRANSIENT_NETWORK_PATTERNS` 含 `/timed? ?out/i`,于是
38
+ * idle 超时被误判为网络断连、走了「固定 30s × 5 次」的断连重试节奏——每次必然
39
+ * 复现同样超时(真机实测 12 个任务累计空耗 231 分钟)。有了本字段,重试层可以
40
+ * 按**语义**而非文本分类。
41
+ *
42
+ * 仅由 `@x-otto/shared` 的 `stream()` 设置;其他错误路径为 `undefined`。
43
+ */
44
+ readonly abortKind?: 'idle' | 'wallclock' | 'external';
31
45
  constructor(message: string, options: {
32
46
  code: string;
33
47
  cause?: Error;
@@ -35,6 +49,7 @@ declare class CodedError extends Error {
35
49
  headers?: unknown;
36
50
  errorType?: string;
37
51
  pluginSource?: string;
52
+ abortKind?: 'idle' | 'wallclock' | 'external';
38
53
  });
39
54
  }
40
55
  declare class NetworkError extends CodedError {}
@@ -55,8 +70,46 @@ declare function invariant(condition: unknown, message?: string | number): asser
55
70
  type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P] };
56
71
  type DeepReadonly<T> = { readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P] };
57
72
  //#endregion
73
+ //#region src/event-emitter.d.ts
74
+ type EventListener = (...args: never[]) => void;
75
+ type Events = Record<string, EventListener>;
76
+ declare class TypedEventEmitter<T extends Record<keyof T, EventListener>> {
77
+ private readonly listeners;
78
+ on<K extends keyof T>(event: K, listener: T[K]): () => void;
79
+ off<K extends keyof T>(event: K, listener: T[K]): void;
80
+ emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>): void;
81
+ once<K extends keyof T>(event: K, listener: T[K]): () => void;
82
+ removeAllListeners<K extends keyof T>(event?: K): void;
83
+ listenerCount<K extends keyof T>(event: K): number;
84
+ }
85
+ //#endregion
58
86
  //#region src/logger.d.ts
59
87
  type LogLevel = pino.Level;
88
+ /**
89
+ * 日志输出端接口(RFC-407 M2-1,seam 化设计先行)。
90
+ *
91
+ * **为什么需要这个接口**:shared 的 logger 当前硬绑定 pino(transport/child/multistream),
92
+ * 插件经 plugin SDK 的 `ctx.logger`(`sdk.ts:188`)拿到的是**字符串消息接口**
93
+ * (`info/warn/error/debug`),与 `createLogger` 返回的 `pino.Logger` 不对等——
94
+ * 两者之间没有接口边界,无法桥接。LoggerSink 定义了这个边界:
95
+ * 宿主侧 `createLogger` 返回的 logger 内部走 sink 注册表;
96
+ * 将来插件需要宿主 logger 实例时,经 ctx 注入 LoggerSink(而非 pino.Logger)。
97
+ *
98
+ * **M2 范围(R5:接口先行,不建注册表)**:本接口仅定义 + 默认实现包装现有 pino 行为。
99
+ * 不注册为插件贡献点(RFC-105 R8:0 个真实消费者不注册)。
100
+ * 等真实插件诉求出现(如插件需要结构化日志而非字符串消息)再注册为贡献点。
101
+ *
102
+ * **不改 `createLogger` 签名**——消费方返回类型仍为 `pino.Logger`,LoggerSink 是
103
+ * shared 内部的接口边界,插件侧无感。
104
+ */
105
+ interface LoggerSink {
106
+ /** 写一条结构化日志。 */
107
+ log(level: LogLevel, msg: string, fields?: Record<string, unknown>): void;
108
+ /** 创建命名子 sink(对应 pino child logger)。 */
109
+ child(name: string): LoggerSink;
110
+ /** 动态调整级别。 */
111
+ setLevel(level: LogLevel): void;
112
+ }
60
113
  interface LoggerOptions {
61
114
  level: LogLevel;
62
115
  destination: string;
@@ -81,12 +134,19 @@ declare function setLogLevel(level: LogLevel): void;
81
134
  */
82
135
  declare function isTransportInitialized(): boolean;
83
136
  declare function createLogger(name: string, options?: LoggerOptions): pino.Logger;
137
+ /**
138
+ * 默认 LoggerSink 实现——包装 pino logger(RFC-407 M2-1)。
139
+ *
140
+ * 这是 pino 之上的接口边界,不是 pino 的替代。宿主侧 createLogger 仍返回 pino.Logger;
141
+ * 本实现供将来 ctx 注入使用(插件拿到 LoggerSink 而非 pino 实例)。
142
+ */
143
+ declare const defaultLoggerSink: LoggerSink;
84
144
  //#endregion
85
145
  //#region src/fs-extra.d.ts
86
146
  /**
87
147
  * 大文件字节阈值单一权威定义(RFC-189 D2,grill 已确认 10MB):文件"用户主动打开任意
88
148
  * 文件系统路径查看"场景(`/open` 命令、`@file` sigil 预览)的读取层与渲染层
89
- * (`@otto/tui file-content-views.ts` 的候选视图熔断)共用同一个数字,避免两处各自
149
+ * (`@x-otto/tui file-content-views.ts` 的候选视图熔断)共用同一个数字,避免两处各自
90
150
  * 定义产生不一致(如读取层限 10MB、渲染层限 8MB)。
91
151
  */
92
152
  declare const LARGE_FILE_BYTE_THRESHOLD: number;
@@ -157,26 +217,13 @@ declare function truncateLine(line: string, maxChars: number): {
157
217
  wasTruncated: boolean;
158
218
  };
159
219
  //#endregion
160
- //#region ../env/dist/index.d.ts
161
- declare function normalizePath(path: string): string;
162
- /**
163
- * 从 `startDir` 向上查找最近的 `pnpm-workspace.yaml`(monorepo root 标志文件),返回其所在目录。
164
- * 找不到(如在非本仓的用户项目里运行)返回 `undefined`。
165
- *
166
- * RFC-130:单一真源实现——原先在 `@otto/coding`(`install-plugin.ts`,供 CLI `--repo` 消费)与
167
- * `@otto/plugin`(`discovery.ts`,供仓库级插件发现消费)各自维护一份逐字相同的实现(copy-paste),
168
- * 无法互相 import(`@otto/plugin` 是 `@otto/coding` 的依赖,反向 import 会成环)。收敛到零依赖的
169
- * `@otto/env`(两者共同的底层依赖)消除维护漂移风险(终局审核 finding,2026-07-09)。
170
- *
171
- * 终止条件用 `dirname(dir) === dir`(到达文件系统根)而非固定层数上限——后者在深层嵌套路径下
172
- * 会误截断(旧实现的 `i < 20` 上限已移除,审核 finding:该上限是冗余约束)。
173
- */
174
- //#endregion
175
220
  //#region src/path.d.ts
176
221
  declare function getProjectDir(): string;
177
222
  declare function getHomeDir(): string;
178
223
  declare function getThirdPartyToolDir(): string;
179
224
  declare function getThirdPartyToolBinaryDir(toolName: string, version?: string): string;
225
+ //#endregion
226
+ //#region src/log-paths.d.ts
180
227
  declare function createTempLoggerPath(): string;
181
228
  /**
182
229
  * RFC-093 T4:bash 溢出日志的项目本地路径(`.otto/tool-results/bash/`)。
@@ -206,7 +253,7 @@ declare function safeStringify(value: unknown, indent?: number): string;
206
253
  /**
207
254
  * glob-match.ts —— 轻量 glob 匹配(RFC-187 D6)。
208
255
  *
209
- * 用于文件名/相对路径匹配场景(非路径 specifier 语义,与 `@otto/setting`
256
+ * 用于文件名/相对路径匹配场景(非路径 specifier 语义,与 `@x-otto/setting`
210
257
  * `globToRegexSource` 的 `~` 展开/绝对路径锚定正交,各自独立实现——本函数不做
211
258
  * `~` 展开,返回 boolean 而非 regex 字符串,且默认大小写不敏感)。
212
259
  *
@@ -217,14 +264,97 @@ declare function safeStringify(value: unknown, indent?: number): string;
217
264
  * 已知边界(RFC-187 R8):`**` 是简单字符串替换为 `.*`,与相邻字面斜杠之间
218
265
  * 不做"零或多层目录"的特殊语义——形如 "**" + 斜杠 + "*.proto" 的模式不匹配无前导
219
266
  * 目录的 "c.proto",形如 "a" + 斜杠 + "**" + 斜杠 + "b" 的模式不匹配 "a/b"("**"
220
- * 不能吸收相邻的字面斜杠)。这是与 `@otto/setting` `globToRegexSource` 同款的简单
267
+ * 不能吸收相邻的字面斜杠)。这是与 `@x-otto/setting` `globToRegexSource` 同款的简单
221
268
  * 转换限制,非 bug。插件作者/matcher 声明方需要"零或多层"语义时应显式声明不带
222
269
  * 前导/尾随双星号斜杠的等价模式(如同时声明 `*.proto` 覆盖顶层),而非依赖 `**`
223
270
  * 自动吸收空目录段。
224
271
  */
225
272
  declare function matchesGlobPattern(pattern: string, input: string): boolean;
226
273
  //#endregion
227
- //#region src/http.d.ts
274
+ //#region src/http-client.d.ts
275
+ /**
276
+ * HTTP 传输层接口(RFC-407 M2-3,seam 化设计先行,仅定义不接线)。
277
+ *
278
+ * **为什么需要这个接口**:stream() 当前是唯一传输实现,硬绑定 undici + eventsource-parser。
279
+ * 将来 retry/重定向策略插件需要替换传输层,但当前 0 个真实消费者。本接口仅为"留缝"——
280
+ * 定义形状,不接线,不注册为贡献点(RFC-105 R8:0 消费者不注册)。
281
+ *
282
+ * **M2 范围**:仅定义接口。默认实现就是现有 stream() 函数本身(不包装、不代理——
283
+ * 避免为 0 消费者引入一层薄封装的过度设计)。
284
+ */
285
+ interface HttpTransport {
286
+ /** 发起 SSE 流请求,返回事件异步迭代器。 */
287
+ stream(options: HttpRequestOptions): AsyncIterable<SseEvent>;
288
+ }
289
+ interface HttpRequestOptions {
290
+ url: string;
291
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
292
+ headers?: Record<string, string>;
293
+ body?: string | Record<string, unknown>;
294
+ timeout?: number;
295
+ /**
296
+ * SSE 流的**空闲**超时(毫秒,仅 `stream()`):每收到一个 chunk 就重置;连续 `idleTimeout` 内无任何
297
+ * 字节到达即 abort。区别于 `timeout`(整请求一次性墙钟上限)——空闲超时专治「连接活着但 provider
298
+ * 静默不再吐字节」的挂死(健康流远比此频繁吐 token,不受影响)。默认 120s。
299
+ *
300
+ * RFC-361:本值只作用于**首个 SSE 事件产出之后**;首事件之前用 `firstTokenIdleTimeout`。
301
+ */
302
+ idleTimeout?: number;
303
+ /**
304
+ * RFC-361:**首个 SSE 事件产出之前**的空闲超时(毫秒,仅 `stream()`)。默认
305
+ * `max(idleTimeout, 900_000)`。
306
+ *
307
+ * 为什么要分段:reasoning 模型在 max/xhigh 档下 thinking 阶段可持续数分钟零可见输出,
308
+ * 这是**正常行为**;而"已经开始吐字却突然静默两分钟"才是真卡死。二者故障模式完全不同,
309
+ * 用同一个阈值必然顾此失彼——调大则真卡死检测被延迟,调小则误杀长思考。
310
+ * 真机实证:Terminal-Bench-2 评测中 4 个长推理任务因此被误杀(占 otto 侧失败 100%)。
311
+ *
312
+ * 注意重置判据是**原始 TCP chunk 到达**而非 SSE 事件产出,故服务端 keepalive 注释亦可
313
+ * 续命——这是有意行为(说明连接活着),不影响"首事件是否已产出"的阶段判定。
314
+ */
315
+ firstTokenIdleTimeout?: number;
316
+ signal?: AbortSignal;
317
+ proxy?: string;
318
+ /**
319
+ * 成功响应回调(仅 stream(),仅 HTTP 200 时触发):在 SSE 迭代开始前同步调用,携带响应 Headers。
320
+ * 用于读取成功路径的 rate-limit/usage 头(如 anthropic-ratelimit-unified-*, x-ratelimit-* 等),
321
+ * 不必等到错误路径才挖 header(此时 response 对象仍在作用域内)。可选,不传=零开销。
322
+ */
323
+ onResponse?: (headers: Headers) => void;
324
+ }
325
+ interface HttpResponse {
326
+ status: number;
327
+ headers: Headers;
328
+ body: string;
329
+ ok: boolean;
330
+ }
331
+ interface SseEvent {
332
+ event?: string;
333
+ data: string;
334
+ id?: string;
335
+ }
336
+ declare function stream(options: HttpRequestOptions): AsyncIterable<SseEvent>;
337
+ //#endregion
338
+ //#region src/proxy.d.ts
339
+ /**
340
+ * 代理配置接口(RFC-407 M2-2,seam 化设计先行)。
341
+ *
342
+ * **为什么需要这个接口**:`defaultProxyUrl` 是模块级 `let` 变量,RFC-365 已实证插件内联
343
+ * 副本持有独立实例,宿主 `setDefaultProxy()` 不传播。本接口把代理配置从模块单例提升为
344
+ * 装配对象——进程启动时显式装配,插件经 ctx 读取(不再依赖模块单例 + 全局槽的"双保险")。
345
+ *
346
+ * **M2 范围(R5:接口先行,不建注册表)**:本接口仅定义 + 默认实现包装现有行为。
347
+ * `setDefaultProxy`/`getDefaultProxy`/`installGlobalProxyDispatcher` 保留为兼容函数
348
+ * (内部委托装配对象),消费方无感。不注册为插件贡献点(RFC-105 R8)。
349
+ */
350
+ interface ProxyConfig {
351
+ /** 获取当前代理 URL。 */
352
+ getUrl(): string | undefined;
353
+ /** 安装进程级全局代理 dispatcher。传 falsy 卸载。 */
354
+ installGlobal(): void;
355
+ /** 卸载全局代理,还原到启动快照。 */
356
+ uninstallGlobal(): void;
357
+ }
228
358
  /**
229
359
  * 设置进程级默认 HTTP 代理。传空字符串或 falsy 值清除。
230
360
  * CLI 层在 settings.load() 后及 settings.change 事件中调用。
@@ -242,12 +372,12 @@ declare function createDispatcher(proxyUrl?: string): ProxyAgent | undefined;
242
372
  * 安装/卸载**进程级全局**代理 dispatcher(RFC-288 D0b)。
243
373
  *
244
374
  * 与 `createDispatcher` 的分工:后者产出 per-request/per-client 的 dispatcher,只作用于
245
- * 显式消费它的调用方(`request`/`stream`/Anthropic SDK);本函数改写 undici 的**全局**
375
+ * 显式消费它的调用方(`stream`/Anthropic SDK);本函数改写 undici 的**全局**
246
376
  * dispatcher,使进程内**所有** `fetch()` 自动经代理——包括各包直接裸调 `fetch` 的路径
247
377
  * (OAuth token 交换、远端持久化、install/upgrade 检查、service 登录等)。
248
378
  *
249
- * 背景(RFC-288 §背景·第零类):此前代理只对 `@otto/shared` 的 `request()`/`stream()`
250
- * 生效,全仓仅 1 个 import;Anthropic 走官方 SDK 未传 dispatcher,Node 22 内置 fetch 又
379
+ * 背景(RFC-288 §背景·第零类):此前代理只对 `@x-otto/shared` 的 `stream()` 生效,
380
+ * 全仓仅 1 个 import;Anthropic 走官方 SDK 未传 dispatcher,Node 22 内置 fetch 又
251
381
  * 不读 `HTTP_PROXY` 环境变量——导致「面板显示已启用但 Claude 请求直连」的静默失败。
252
382
  * 选择全局兜底而非逐个调用点接线,正是因为后者的漏接线成本已被这次事故证明。
253
383
  *
@@ -258,6 +388,16 @@ declare function createDispatcher(proxyUrl?: string): ProxyAgent | undefined;
258
388
  * 与 `createDispatcher` 的降级语义一致——启动期配置错误不应让进程崩溃。
259
389
  */
260
390
  declare function installGlobalProxyDispatcher(url?: string): void;
391
+ /**
392
+ * 默认 ProxyConfig 实现——包装现有模块单例行为(RFC-407 M2-2)。
393
+ *
394
+ * 这是接口边界,不是新实现。`setDefaultProxy`/`getDefaultProxy`/`installGlobalProxyDispatcher`
395
+ * 保留为兼容函数(内部委托本装配对象的同名方法),消费方无感。
396
+ * 将来装配对象可改为显式注入(进程启动时构造),插件经 ctx 读取 ProxyConfig。
397
+ */
398
+ declare const defaultProxyConfig: ProxyConfig;
399
+ //#endregion
400
+ //#region src/probe.d.ts
261
401
  /** `probeProxy` 的失败归类——决定提示语该不该归咎代理(RFC-288 D4)。 */
262
402
  type ProxyProbeErrorKind = /** 连不上代理本身(代理地址/端口错、代理软件没开)。 */'proxy-unreachable' /** 代理通了,但目标站点不可达(DNS 失败、目标宕机)——**不归咎代理**。 */ | 'target-unreachable' /** 超时,无法区分是代理慢还是目标慢。 */ | 'timeout' /** 其它未分类错误。 */ | 'unknown';
263
403
  interface ProxyProbeResult {
@@ -292,40 +432,6 @@ interface ProxyProbeResult {
292
432
  * @param timeoutMs 超时(默认 5s)。
293
433
  */
294
434
  declare function probeProxy(targetUrl: string, timeoutMs?: number): Promise<ProxyProbeResult>;
295
- interface HttpRequestOptions {
296
- url: string;
297
- method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
298
- headers?: Record<string, string>;
299
- body?: string | Record<string, unknown>;
300
- timeout?: number;
301
- /**
302
- * SSE 流的**空闲**超时(毫秒,仅 `stream()`):每收到一个 chunk 就重置;连续 `idleTimeout` 内无任何
303
- * 字节到达即 abort。区别于 `timeout`(整请求一次性墙钟上限)——空闲超时专治「连接活着但 provider
304
- * 静默不再吐字节」的挂死(健康流远比此频繁吐 token,不受影响)。默认 120s。
305
- */
306
- idleTimeout?: number;
307
- signal?: AbortSignal;
308
- proxy?: string;
309
- /**
310
- * 成功响应回调(仅 stream(),仅 HTTP 200 时触发):在 SSE 迭代开始前同步调用,携带响应 Headers。
311
- * 用于读取成功路径的 rate-limit/usage 头(如 anthropic-ratelimit-unified-*, x-ratelimit-* 等),
312
- * 不必等到错误路径才挖 header(此时 response 对象仍在作用域内)。可选,不传=零开销。
313
- */
314
- onResponse?: (headers: Headers) => void;
315
- }
316
- interface HttpResponse {
317
- status: number;
318
- headers: Headers;
319
- body: string;
320
- ok: boolean;
321
- }
322
- interface SseEvent {
323
- event?: string;
324
- data: string;
325
- id?: string;
326
- }
327
- declare function request(options: HttpRequestOptions): Promise<HttpResponse>;
328
- declare function stream(options: HttpRequestOptions): AsyncIterable<SseEvent>;
329
435
  //#endregion
330
436
  //#region src/header-utils.d.ts
331
437
  /**
@@ -337,6 +443,15 @@ declare function stream(options: HttpRequestOptions): AsyncIterable<SseEvent>;
337
443
  */
338
444
  declare function headerGet(headers: unknown, key: string): string | undefined;
339
445
  //#endregion
446
+ //#region src/event-bus.d.ts
447
+ type UnSubscribe = () => void;
448
+ type SubscribeEvent = Events;
449
+ declare class EventBus<T extends SubscribeEvent = SubscribeEvent> extends TypedEventEmitter<T> {
450
+ publish(event: Partial<{ [K in keyof T]: Parameters<T[K]> }>): void;
451
+ subscribe<K extends keyof T>(type: K, callback: T[K], once?: boolean): UnSubscribe;
452
+ subscribeAll(callback: (...args: unknown[]) => void): UnSubscribe;
453
+ }
454
+ //#endregion
340
455
  //#region src/command.d.ts
341
456
  /**
342
457
  * 危险 shell 命令检测。
@@ -415,6 +530,20 @@ declare function generateChallenge(verifier: string): string;
415
530
  declare function createPkcePair(): PkcePair;
416
531
  declare function generateState(): string;
417
532
  //#endregion
533
+ //#region src/oauth/page-renderer.d.ts
534
+ /**
535
+ * OAuth 回调页渲染器接口(RFC-407 M1-3)。
536
+ *
537
+ * shared 只提供极简默认实现(纯文本页面)。品牌视觉版由 UI 层或插件提供,
538
+ * 经 LoopbackServerOptions.pageRenderer 注入。
539
+ */
540
+ interface PageRenderer {
541
+ /** 渲染成功页 HTML */
542
+ renderSuccess(title: string, message: string, footer: string): string;
543
+ /** 渲染错误页 HTML */
544
+ renderError(title: string, message: string, footer: string): string;
545
+ }
546
+ //#endregion
418
547
  //#region src/oauth/loopback-server.d.ts
419
548
  interface LoopbackServerOptions {
420
549
  ports?: number[];
@@ -422,6 +551,7 @@ interface LoopbackServerOptions {
422
551
  expectedState?: string;
423
552
  host?: string;
424
553
  signal?: AbortSignal;
554
+ pageRenderer?: PageRenderer;
425
555
  }
426
556
  interface LoopbackCallback {
427
557
  code: string;
@@ -466,12 +596,12 @@ declare const API_IMAGE_DIMENSION_LIMIT_PX = 8000;
466
596
  * 底层约束是 provider(Anthropic)对单图输入的字节上限。此前四处各自硬编码 5MB 并靠
467
597
  * 注释「与 XX 对齐 / 变更时同步更新 XX」手动维护,是漂移反模式。收敛到本处(provider
468
598
  * 图片约束常量的既有归属地,与上面的维度上限同域)。四处消费方:
469
- * - `@otto/tools` fs/read.ts(读取本地图片文件的字节上限)
470
- * - `@otto/mcp` mcp-tool-adapter.ts(MCP 工具产出图片的字节上限)
471
- * - `@otto/tui` input/clipboard-image.ts(剪贴板图片的字节上限)
472
- * - `@otto/agent` tool-result-clamp.ts(RFC-151 D2:image 块内联上限,超限走 spill)
599
+ * - `@x-otto/tools` fs/read.ts(读取本地图片文件的字节上限)
600
+ * - `@x-otto/mcp` mcp-tool-adapter.ts(MCP 工具产出图片的字节上限)
601
+ * - `@x-otto/tui` input/clipboard-image.ts(剪贴板图片的字节上限)
602
+ * - `@x-otto/agent` tool-result-clamp.ts(RFC-151 D2:image 块内联上限,超限走 spill)
473
603
  *
474
- * **不做 env 覆盖**(刻意):`@otto/agent` 是确定性纯引擎(RFC-192,零 env 读取以保证
604
+ * **不做 env 覆盖**(刻意):`@x-otto/agent` 是确定性纯引擎(RFC-192,零 env 读取以保证
475
605
  * 重放一致性),若本常量经 env 求值,agent 消费的值会随环境漂移、破坏 record/replay 不
476
606
  * 变式。故保持字面量;未来若需可调,应经注入传入 agent,而非改本常量为 env 读取。
477
607
  *
@@ -492,6 +622,64 @@ declare function probeImageDimensions(base64: string, mime: string): {
492
622
  height: number;
493
623
  } | undefined;
494
624
  //#endregion
625
+ //#region src/intranet-probe.d.ts
626
+ /**
627
+ * TCP 握手探测原语(可注入,测试用 mock 替代真实 socket)。
628
+ * host + 端口 + 超时 → 握手成功 = true。
629
+ */
630
+ type TcpConnectProbe = (host: string, port: number, timeoutMs: number) => Promise<boolean>;
631
+ interface UrlProbeOptions {
632
+ force?: boolean;
633
+ timeoutMs?: number;
634
+ /** 注入探测原语(测试);缺省真实 TCP 握手。 */
635
+ connect?: TcpConnectProbe;
636
+ }
637
+ /**
638
+ * 探测单个 URL 指向的主机是否可达(TCP 握手,"ping 通"语义,见文件头)。
639
+ *
640
+ * URL 解析出 host + 端口(http 缺省 80 / https 缺省 443 / 显式端口优先);非 http(s)
641
+ * scheme 或非法 URL 无法探测,返回 false 且不发起连接。结果按 host:port 缓存。
642
+ */
643
+ declare function isUrlReachable(url: string, opts?: UrlProbeOptions): Promise<boolean>;
644
+ /**
645
+ * 探测候选地址中是否有任意一个可达(TCP 握手,命中即停)。
646
+ *
647
+ * 候选 = `OTTO_INTRANET_PROBE_URLS`(env 注入)∪ `extraUrls`(消费方传入的目标资源
648
+ * 自身地址,如 provider baseUrl)。两者皆空时不发起任何探测,直接返回 `false`——
649
+ * 避免公网用户启动时产生不必要的网络往返或超时等待。
650
+ *
651
+ * `probe` 为测试注入点(缺省真实 TCP 握手),生产调用方不传。
652
+ */
653
+ declare function isIntranetReachable(force?: boolean, extraUrls?: readonly string[], probe?: TcpConnectProbe): Promise<boolean>;
654
+ /** 测试专用:清空进程内 per-host 缓存,避免用例间相互污染。 */
655
+ declare function resetIntranetReachabilityCache(): void;
656
+ //#endregion
657
+ //#region src/safe-dispose.d.ts
658
+ /** 清理回调可能的形态:同步、异步、或压根没提供。 */
659
+ type DisposeFn = () => void | Promise<void>;
660
+ interface SafeDisposeOptions {
661
+ /** 日志端口(省略则失败静默——仅供无 logger 语境的纯函数测试使用)。 */
662
+ logger?: Pick<Logger, 'warn'>;
663
+ /** 结构化日志上下文(如 `{ pluginId, api }`),便于定位是谁的 dispose 炸了。 */
664
+ context?: Record<string, unknown>;
665
+ /** 日志文案前缀,缺省 'dispose failed'。 */
666
+ label?: string;
667
+ }
668
+ /**
669
+ * 隔离执行一个清理回调。**永不抛出**,返回 `true` 表示清理成功、`false` 表示失败(已记录)。
670
+ *
671
+ * `fn` 为 `undefined` 时视为"无需清理",返回 `true`(成功语义——没有要清的东西不算失败)。
672
+ */
673
+ declare function safeDispose(fn: DisposeFn | undefined, options?: SafeDisposeOptions): Promise<boolean>;
674
+ /**
675
+ * `safeDispose` 的鸭子类型版:对象上有可调用的 `dispose` 才执行,否则视为无需清理。
676
+ *
677
+ * 存在理由:注册表持有的是 `ProviderStream` 等**不保证实现 dispose** 的接口,调用点
678
+ * 此前都在内联做 `typeof (x as {dispose?}).dispose === 'function'` 的双重断言——
679
+ * 收口到这里同时消除该断言的重复与 `as` 扩散。
680
+ */
681
+ declare function safeDisposeOf(target: unknown, options?: SafeDisposeOptions): Promise<boolean>;
682
+ //#endregion
495
683
  //#region src/startup-trace.d.ts
496
684
  /**
497
685
  * startup-trace.ts —— 启动相位打点(RFC-150 D7/M0)。
@@ -602,7 +790,7 @@ declare function rankLabeledItems<T extends {
602
790
  declare const DEFAULT_FILE_LOCK_TIMEOUT_MS = 15000;
603
791
  /** 锁文件路径:`<dir>/.file-lock-<key>.lock`。调用方通过 dir/key 组合天然隔离不同锁域。
604
792
  *
605
- * RFC-303 D7:命名格式的调用方(`@otto/ai` 的 oauth-refresh-lock.ts)有滚动升级兼容
793
+ * RFC-303 D7:命名格式的调用方(`@x-otto/ai` 的 oauth-refresh-lock.ts)有滚动升级兼容
606
794
  * 硬约束——历史锁文件名是 `.auth-refresh-<provider>.lock`,若本函数改变格式会导致
607
795
  * 新旧进程各锁各的文件、互斥失效。故格式做成可参数化:`prefix` 缺省 `file-lock`
608
796
  * (新调用方用默认值),历史调用方显式传入 `auth-refresh` 保持字节级不变。
@@ -613,7 +801,7 @@ interface FileLockOptions {
613
801
  timeoutMs?: number;
614
802
  /**
615
803
  * 锁文件名前缀(`.{prefix}-{key}.lock`)。缺省 `'file-lock'`。
616
- * 历史调用方(如 `@otto/ai` 的 oauth-refresh-lock.ts)需显式传入其历史前缀
804
+ * 历史调用方(如 `@x-otto/ai` 的 oauth-refresh-lock.ts)需显式传入其历史前缀
617
805
  * (`'auth-refresh'`)以保持锁文件名字节级不变——改变格式会破坏滚动升级期
618
806
  * 新旧进程的互斥(各锁各的文件,等于没锁)。
619
807
  */
@@ -645,6 +833,12 @@ declare function acquireFileLock(dir: string, key: string, options?: FileLockOpt
645
833
  * (会误删偷锁者的新锁),仅记录诊断日志。
646
834
  */
647
835
  declare function releaseFileLock(dir: string, key: string, options?: Pick<FileLockOptions, 'prefix'>): Promise<void>;
836
+ /**
837
+ * B1(评审建议优化):当前进程是否仍持有指定锁(token 实核,非本地缓存)。
838
+ * 供"挂起恢复后覆盖写"防护:进程被 SIGSTOP/GC stall 超过陈旧阈值 → 锁被另一进程
839
+ * stale-takeover 偷走 → 恢复后本函数返回 false,调用方应放弃覆盖写、走读盘恢复。
840
+ */
841
+ declare function isFileLockHeldByMe(dir: string, key: string, options?: Pick<FileLockOptions, 'prefix'>): Promise<boolean>;
648
842
  /**
649
843
  * 等待另一进程持有的锁释放,超时返回 false(调用方应走自己的兜底路径,如重读磁盘或
650
844
  * 降级为无锁执行)。超时阈值应与陈旧锁清理阈值相等,保证两者协调一致。
@@ -664,5 +858,5 @@ declare function releaseFileLockSync(dir: string, key: string, options?: Pick<Fi
664
858
  */
665
859
  declare function waitForFileLockReleaseSync(dir: string, key: string, options?: Pick<FileLockOptions, 'timeoutMs' | 'prefix'>): boolean;
666
860
  //#endregion
667
- export { API_IMAGE_DIMENSION_LIMIT_PX, AgentError, CLAUDE_TO_OTTO_TOOL, CodedError, type CommandCheckResult, DEFAULT_FILE_LOCK_TIMEOUT_MS, type DeepPartial, type DeepReadonly, EventBus, type EventListener, type Events, type FileLockOptions, type FuzzySearchOptions, type HighlightRange, type HttpRequestOptions, type HttpResponse, InvariantError, LARGE_FILE_BYTE_THRESHOLD, type LogLevel, type Logger, type LoggerOptions, type LoopbackCallback, type LoopbackServer, type LoopbackServerOptions, McpError, NetworkError, OAuthError, PROVIDER_IMAGE_INLINE_MAX_BYTES, type ParsedFrontmatter, type PkcePair, ProviderError, type ProxyProbeErrorKind, type ProxyProbeResult, type RankedMatch, type ReadCappedResult, type SearchableItem, type SseEvent, type SubsequenceResult, type TruncateOptions, type TruncatedResult, type TruncationMeta, TypedEventEmitter, acquireFileLock, acquireFileLockSync, attachLogListener, buildAllowedEnv, checkShellCommand, createDispatcher, createLogger, createPkcePair, createProjectOverflowLogPath, createTempLoggerPath, exists, formatBytes, generateChallenge, generateState, generateVerifier, getDefaultProxy, getHomeDir, getProjectDir, getThirdPartyToolBinaryDir, getThirdPartyToolDir, headerGet, installGlobalProxyDispatcher, invariant, isDirectory, isFile, isStartupTraceEnabled, isTransportInitialized, lockPath, mapClaudeAgentToolNames, mapClaudeToolName, markStartupPhase, matchSubsequence, matchesGlobPattern, mime, mkdirp, normalizePath, openBrowser, ottoToClaudeToolName, parseFrontmatterFields, parseFrontmatterList, parseJsonc, positionsToHighlightRanges, probeImageDimensions, probeProxy, rankCandidates, rankLabeledItems, rankNamedItems, readFileCapped, releaseFileLock, releaseFileLockSync, request, safeStringify, scoreSubsequence, setDefaultProxy, setLogLevel, splitFrontmatter, startLoopbackServer, stream, sweepStaleProjectOverflowLogs, sweepStaleTempLogs, toTruncationMeta, traceStartupPhase, traceStartupPhaseSync, truncateHead, truncateLine, truncateTail, waitForFileLockRelease, waitForFileLockReleaseSync };
861
+ export { API_IMAGE_DIMENSION_LIMIT_PX, AgentError, CLAUDE_TO_OTTO_TOOL, CodedError, type CommandCheckResult, DEFAULT_FILE_LOCK_TIMEOUT_MS, type DeepPartial, type DeepReadonly, type DisposeFn, EventBus, type EventListener, type Events, type FileLockOptions, type FuzzySearchOptions, type HighlightRange, type HttpRequestOptions, type HttpResponse, type HttpTransport, InvariantError, LARGE_FILE_BYTE_THRESHOLD, type LogLevel, type Logger, type LoggerOptions, type LoggerSink, type LoopbackCallback, type LoopbackServer, type LoopbackServerOptions, McpError, NetworkError, OAuthError, PROVIDER_IMAGE_INLINE_MAX_BYTES, type PageRenderer, type ParsedFrontmatter, type PkcePair, ProviderError, type ProxyConfig, type ProxyProbeErrorKind, type ProxyProbeResult, type RankedMatch, type ReadCappedResult, type SafeDisposeOptions, type SearchableItem, type SseEvent, type SubsequenceResult, type TcpConnectProbe, type TruncateOptions, type TruncatedResult, type TruncationMeta, TypedEventEmitter, acquireFileLock, acquireFileLockSync, attachLogListener, buildAllowedEnv, checkShellCommand, createDispatcher, createLogger, createPkcePair, createProjectOverflowLogPath, createTempLoggerPath, defaultLoggerSink, defaultProxyConfig, exists, formatBytes, generateChallenge, generateState, generateVerifier, getDefaultProxy, getHomeDir, getProjectDir, getThirdPartyToolBinaryDir, getThirdPartyToolDir, headerGet, installGlobalProxyDispatcher, invariant, isDirectory, isFile, isFileLockHeldByMe, isIntranetReachable, isStartupTraceEnabled, isTransportInitialized, isUrlReachable, lockPath, mapClaudeAgentToolNames, mapClaudeToolName, markStartupPhase, matchSubsequence, matchesGlobPattern, mime, mkdirp, normalizePath, openBrowser, ottoToClaudeToolName, parseFrontmatterFields, parseFrontmatterList, parseJsonc, positionsToHighlightRanges, probeImageDimensions, probeProxy, rankCandidates, rankLabeledItems, rankNamedItems, readFileCapped, releaseFileLock, releaseFileLockSync, resetIntranetReachabilityCache, safeDispose, safeDisposeOf, safeStringify, scoreSubsequence, setDefaultProxy, setLogLevel, splitFrontmatter, startLoopbackServer, stream, sweepStaleProjectOverflowLogs, sweepStaleTempLogs, toTruncationMeta, traceStartupPhase, traceStartupPhaseSync, truncateHead, truncateLine, truncateTail, waitForFileLockRelease, waitForFileLockReleaseSync };
668
862
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,90 +1,7 @@
1
- import{n as e,t}from"./event-bus-CABu09lC.js";import{createRequire as n}from"node:module";import r from"pino";import{LOG_FILE as i,LOG_LEVEL as a,OTTO_HOME as o,OTTO_PROJECT_DIR as s,normalizePath as c}from"@otto/env";import{PassThrough as l}from"node:stream";import{chmod as u,mkdir as d,open as f,readFile as p,readdir as m,stat as h,unlink as g,utimes as ee}from"node:fs/promises";import{lookup as te}from"mime-types";import{join as _,resolve as ne}from"node:path";import{tmpdir as v}from"node:os";import{createHash as re,randomBytes as ie,randomUUID as y}from"node:crypto";import{parse as ae,printParseErrorCode as oe}from"jsonc-parser";import se from"safe-stable-stringify";import{createParser as ce}from"eventsource-parser";import{EnvHttpProxyAgent as le,ProxyAgent as ue,getGlobalDispatcher as de,setGlobalDispatcher as fe}from"undici";import{createServer as pe}from"node:http";import{spawn as me}from"node:child_process";import{chmodSync as he,closeSync as ge,mkdirSync as _e,openSync as ve,readFileSync as ye,statSync as be,unlinkSync as xe,writeSync as Se}from"node:fs";var b=class extends Error{code;cause;status;headers;errorType;pluginSource;constructor(e,t){super(e,{cause:t.cause}),this.name=new.target.name,this.code=t.code,this.cause=t.cause,this.status=t.status,this.headers=t.headers,this.errorType=t.errorType,this.pluginSource=t.pluginSource}},x=class extends b{},Ce=class extends b{},S=class extends b{},we=class extends b{},Te=class extends b{};const{setPrototypeOf:Ee=function(e,t){return e.__proto__=t,e}}=Object;var C=class e extends Error{framesToPop=1;name=`Invariant Violation`;constructor(t=`Invariant Violation`){super(typeof t==`number`?`Invariant Violation: ${t}`:t),Ee(this,e.prototype)}};function w(e,t){if(typeof e==`function`)w(e(),t);else if(!e)throw new C(t)}const De=new Set([`trace`,`debug`,`info`,`warn`,`error`,`fatal`]);function T(e){return De.has(e)?e:`info`}const Oe=(()=>{try{return n(import.meta.url)}catch{return}})();function ke(){if(!Oe)return!1;try{return Oe.resolve(`pino-pretty`),!0}catch{return!1}}function Ae(e){return e===`stdout`?1:e===`stderr`?2:e}function je(){return process.env.OTTO_LOG_STDERR===`0`}function Me(){return process.env.OTTO_LOG_FILE||`${process.env.HOME||process.env.USERPROFILE||`.`}/.otto/logs/otto.log`}function Ne(e,t){let n=ke(),r=je(),i={target:n?`pino-pretty`:`pino/file`,options:n?{colorize:!0,destination:2}:{destination:2},level:t};if(!e||e===`stdout`||e===`stderr`)return r?[{target:`pino/file`,options:{destination:Me(),mkdir:!0},level:t}]:[i];let a={target:`pino/file`,options:{destination:Ae(e),mkdir:!0},level:t};return r?[a]:[a,i]}const E=new l,D=new t;E.on(`data`,e=>{try{D.publish({log:[JSON.parse(e.toString())]})}catch(t){console.warn(`Failed to parse log chunk for listener:`,e.toString(),t)}});function Pe(e){return D.subscribe(`log`,e)}let O=null,k=T(a);const Fe=new Set;function Ie(e){if(De.has(e)){k=e,O&&(O.level=e);for(let t of Fe)t.level=e}}function Le(){return A!==null}let A=null;function Re(e,t){return{write(n){A??=r.transport({targets:Ne(e,t)}),A.write(n)}}}function ze(e,t){return O??=r({level:e},r.multistream([{level:e,stream:E},{level:e,stream:Re(t,e)}])),k=T(O.level),O}function Be(e,t){ze(t?.level??T(a),t?.destination??i),w(O,`Root logger is not initialized`);let n=O.child({name:e},{level:t?.level??k});return Fe.add(n),n}const j=10*1024*1024;async function Ve(e){return te(e)||`application/octet-stream`}async function He(e){await d(e,{recursive:!0})}async function Ue(e){try{return await h(e),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function We(e){return(await h(e)).isDirectory()}async function Ge(e){return(await h(e)).isFile()}async function Ke(e,t=j){let n=await h(e);if(n.size<=t)return{content:await p(e,`utf-8`),truncated:!1,totalBytes:n.size};let r=await f(e,`r`);try{let e=Buffer.alloc(t),{bytesRead:i}=await r.read(e,0,t,0);return{content:e.subarray(0,i).toString(`utf-8`),truncated:!0,totalBytes:n.size}}finally{await r.close()}}function qe(e){let{content:t,...n}=e;return n}function Je(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:`${(e/(1024*1024)).toFixed(1)}MB`}function M(e){let t=e.outputBytes??Buffer.byteLength(e.content,`utf-8`),n=e.outputLines??e.content.split(`
2
- `).length;return{content:e.content,truncated:e.truncated,truncatedBy:e.truncatedBy,totalLines:e.totalLines,totalBytes:e.totalBytes,outputLines:n,outputBytes:t,lastLinePartial:e.lastLinePartial,firstLineExceedsLimit:e.firstLineExceedsLimit,options:e.options}}function Ye(e,t){let{maxLines:n,maxBytes:r}=t,i=Buffer.byteLength(e,`utf-8`),a=e.split(`
1
+ import{createRequire as e}from"node:module";import t from"pino";import{LOG_FILE as n,LOG_LEVEL as r,OTTO_HOME as i,OTTO_PROJECT_DIR as a,normalizePath as o,resolveIntranetProbeUrls as s}from"@x-otto/env";import{PassThrough as c}from"node:stream";import{chmod as l,mkdir as u,open as d,readFile as f,readdir as p,stat as m,unlink as h,utimes as ee}from"node:fs/promises";import{lookup as te}from"mime-types";import{join as g,resolve as _}from"node:path";import{tmpdir as ne}from"node:os";import{createHash as re,randomBytes as ie,randomUUID as v}from"node:crypto";import{parse as ae,printParseErrorCode as oe}from"jsonc-parser";import se from"safe-stable-stringify";import{createParser as ce}from"eventsource-parser";import{EnvHttpProxyAgent as le,ProxyAgent as ue,getGlobalDispatcher as de,setGlobalDispatcher as y}from"undici";import{createServer as fe}from"node:http";import{spawn as pe}from"node:child_process";import{connect as me}from"node:net";import{chmodSync as he,closeSync as ge,mkdirSync as _e,openSync as ve,readFileSync as ye,statSync as be,unlinkSync as xe,writeSync as Se}from"node:fs";var b=class extends Error{code;cause;status;headers;errorType;pluginSource;abortKind;constructor(e,t){super(e,{cause:t.cause}),this.name=new.target.name,this.code=t.code,this.cause=t.cause,this.status=t.status,this.headers=t.headers,this.errorType=t.errorType,this.pluginSource=t.pluginSource,this.abortKind=t.abortKind}},x=class extends b{},Ce=class extends b{},S=class extends b{},we=class extends b{},Te=class extends b{};const{setPrototypeOf:Ee=function(e,t){return e.__proto__=t,e}}=Object;var De=class e extends Error{framesToPop=1;name=`Invariant Violation`;constructor(t=`Invariant Violation`){super(typeof t==`number`?`Invariant Violation: ${t}`:t),Ee(this,e.prototype)}};function C(e,t){if(typeof e==`function`)C(e(),t);else if(!e)throw new De(t)}var Oe=class{listeners={};on(e,t){let n=this.listeners[e];return n||(n=new Set,this.listeners[e]=n),n.add(t),()=>this.off(e,t)}off(e,t){this.listeners[e]?.delete(t)}emit(e,...t){let n=this.listeners[e];if(n)for(let e of[...n])e(...t)}once(e,t){let n=((...r)=>{this.off(e,n),t(...r)});return this.on(e,n)}removeAllListeners(e){if(e!==void 0)delete this.listeners[e];else for(let e of Object.keys(this.listeners))delete this.listeners[e]}listenerCount(e){return this.listeners[e]?.size??0}},ke=class extends Oe{publish(e){let t=(e,...t)=>this.emit(e,...t);for(let[n,r]of Object.entries(e)){let e=r??[];t(n,...e),t(`*`,n,...e)}}subscribe(e,t,n=!1){return n?this.once(e,t):this.on(e,t)}subscribeAll(e){return this.subscribe(`*`,e)}};const Ae=new Set([`trace`,`debug`,`info`,`warn`,`error`,`fatal`]);function w(e){return Ae.has(e)?e:`info`}const je=(()=>{try{return e(import.meta.url)}catch{return}})();function Me(){if(!je)return!1;try{return je.resolve(`pino-pretty`),!0}catch{return!1}}function Ne(e){return e===`stdout`?1:e===`stderr`?2:e}function Pe(){return process.env.OTTO_LOG_STDERR===`0`}function Fe(){return process.env.OTTO_LOG_FILE||`${process.env.HOME||process.env.USERPROFILE||`.`}/.otto/logs/otto.log`}function Ie(e,t){let n=Me(),r=Pe(),i={target:n?`pino-pretty`:`pino/file`,options:n?{colorize:!0,destination:2}:{destination:2},level:t};if(!e||e===`stdout`||e===`stderr`)return r?[{target:`pino/file`,options:{destination:Fe(),mkdir:!0},level:t}]:[i];let a={target:`pino/file`,options:{destination:Ne(e),mkdir:!0},level:t};return r?[a]:[a,i]}const T=new c,Le=new ke;T.on(`data`,e=>{try{Le.publish({log:[JSON.parse(e.toString())]})}catch(t){console.warn(`Failed to parse log chunk for listener:`,e.toString(),t)}});function Re(e){return Le.subscribe(`log`,e)}let E=null,D=w(r);const ze=new Set;function O(e){if(Ae.has(e)){D=e,E&&(E.level=e);for(let t of ze)t.level=e}}function Be(){return k!==null}let k=null;function Ve(e,n){return globalThis.__miniv8_wasm===void 0?{write(r){k??=t.transport({targets:Ie(e,n)}),k.write(r)}}:{write:e=>{T.write(e)}}}function He(e,n){return E??=t({level:e},t.multistream([{level:e,stream:T},{level:e,stream:Ve(n,e)}])),D=w(E.level),E}function A(e,t){He(t?.level??w(r),t?.destination??n),C(E,`Root logger is not initialized`);let i=E.child({name:e},{level:t?.level??D});return ze.add(i),i}const Ue={log(e,t,r){He(D,n),C(E,`Root logger is not initialized`),E[e](r??{},t)},child(e){let t=A(e);return{log(e,n,r){t[e](r??{},n)},child(e){let n=t.child({name:e});return{log(e,t,r){n[e](r??{},t)},child(e){let n=t.child({name:e});return{log(e,t,r){n[e](r??{},t)},child:()=>{throw Error(`LoggerSink.child depth limit`)},setLevel(e){n.level=e}}},setLevel(e){n.level=e}}},setLevel(e){t.level=e}}},setLevel(e){O(e)}},j=10*1024*1024;async function We(e){return te(e)||`application/octet-stream`}async function Ge(e){await u(e,{recursive:!0})}async function Ke(e){try{return await m(e),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function qe(e){return(await m(e)).isDirectory()}async function Je(e){return(await m(e)).isFile()}async function Ye(e,t=j){let n=await m(e);if(n.size<=t)return{content:await f(e,`utf-8`),truncated:!1,totalBytes:n.size};let r=await d(e,`r`);try{let e=Buffer.alloc(t),{bytesRead:i}=await r.read(e,0,t,0);return{content:e.subarray(0,i).toString(`utf-8`),truncated:!0,totalBytes:n.size}}finally{await r.close()}}function Xe(e){let{content:t,...n}=e;return n}function Ze(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:`${(e/(1024*1024)).toFixed(1)}MB`}function M(e){let t=e.outputBytes??Buffer.byteLength(e.content,`utf-8`),n=e.outputLines??e.content.split(`
2
+ `).length;return{content:e.content,truncated:e.truncated,truncatedBy:e.truncatedBy,totalLines:e.totalLines,totalBytes:e.totalBytes,outputLines:n,outputBytes:t,lastLinePartial:e.lastLinePartial,firstLineExceedsLimit:e.firstLineExceedsLimit,options:e.options}}function Qe(e,t){let{maxLines:n,maxBytes:r}=t,i=Buffer.byteLength(e,`utf-8`),a=e.split(`
3
3
  `),o=a.length;if(a.length<=n&&i<=r)return M({content:e,truncated:!1,truncatedBy:`lines`,totalLines:o,totalBytes:i,lastLinePartial:!1,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:o,outputBytes:i});if(n===0)return M({content:``,truncated:!0,truncatedBy:`lines`,totalLines:o,totalBytes:i,lastLinePartial:!1,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:0,outputBytes:0});if(Buffer.byteLength(a[0],`utf-8`)>r)return M({content:``,truncated:!0,truncatedBy:`bytes`,totalLines:o,totalBytes:i,lastLinePartial:!1,firstLineExceedsLimit:!0,options:{maxLines:n,maxBytes:r},outputLines:0,outputBytes:0});let s=[],c=0,l=`lines`;for(let e=0;e<a.length&&e<n;e++){let t=a[e],n=Buffer.byteLength(t,`utf-8`)+(s.length>0?1:0);if(c+n>r){l=`bytes`;break}s.push(t),c+=n}return s.length>=n&&c<=r&&(l=`lines`),M({content:s.join(`
4
- `),truncated:!0,truncatedBy:l,totalLines:o,totalBytes:i,lastLinePartial:!1,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:s.length,outputBytes:c})}function Xe(e,t){let{maxLines:n,maxBytes:r}=t,i=Buffer.byteLength(e,`utf-8`),a=e.split(`
5
- `),o=a.length;if(o<=n&&i<=r)return M({content:e,truncated:!1,truncatedBy:`lines`,totalLines:o,totalBytes:i,lastLinePartial:!1,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:o,outputBytes:i});if(n===0)return M({content:``,truncated:!0,truncatedBy:`lines`,totalLines:o,totalBytes:i,lastLinePartial:!1,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:0,outputBytes:0});let s=[],c=0,l=`lines`,u=!1;for(let e=a.length-1;e>=0&&s.length<n;e--){let t=a[e],n=Buffer.byteLength(t,`utf-8`)+(s.length>0?1:0);if(c+n>r){if(l=`bytes`,s.length===0){let e=Ze(t,r);e.length>0&&(s.unshift(e),c=Buffer.byteLength(e,`utf-8`),u=!0)}break}s.unshift(t),c+=n}return s.length>=n&&c<=r&&(l=`lines`),M({content:s.join(`
6
- `),truncated:!0,truncatedBy:l,totalLines:o,totalBytes:i,lastLinePartial:u,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:s.length,outputBytes:c})}function Ze(e,t){let n=Buffer.from(e,`utf-8`);if(n.length<=t)return e;let r=n.length-t;for(;r<n.length;){let e=n[r];if(e===void 0||(e&192)!=128)break;r++}return n.subarray(r).toString(`utf-8`)}function Qe(e,t){return e.length<=t?{text:e,wasTruncated:!1}:{text:`${e.slice(0,t)}... [truncated]`,wasTruncated:!0}}const N=`otto-coding-`,P=`.log`;function F(){return s}function $e(){return o}function et(){return ne($e(),`tools`)}function tt(e,t=``){return ne(et(),...t?[e,t]:[e])}function nt(){let e=y();return _(v(),`${N}${e}${P}`)}function rt(){let e=y();return _(F(),`tool-results`,`bash`,`${N}${e}${P}`)}async function it(e,t){return ot(v(),e,t)}async function at(e,t){return ot(_(F(),`tool-results`,`bash`),e,t)}async function ot(e,t,n){let r;try{r=await m(e)}catch{return 0}let i=r.filter(e=>e.startsWith(N)&&e.endsWith(P)),a=0;return await Promise.all(i.map(async r=>{let i=_(e,r);try{n-(await h(i)).mtimeMs>t&&(await g(i),a++)}catch{}})),a}function st(e){let t=[],n=ae(e,t,{allowTrailingComma:!0,disallowComments:!1,allowEmptyContent:!1});if(t.length>0){let e=t[0],n=e?oe(e.error):`UnknownError`;throw Error(`Invalid JSONC: ${n}`)}return n}function ct(e,t){return se(e,void 0,t)??`null`}function lt(e,t){let n=``;for(let t=0;t<e.length;t++){let r=e[t];r===`*`?e[t+1]===`*`?(n+=`.*`,t++):n+=`[^/]*`:r===`?`?n+=`.`:n+=r.replace(/[.+^${}()|[\]\\]/g,`\\$&`)}return RegExp(`^${n}$`,`i`).test(t)}let I;function ut(e){I=e||void 0}function dt(){return I}function L(e){let t=e||I;if(t)try{return new ue({uri:t})}catch{return}}let R;function ft(e){if(R===void 0&&(R=de()),!e){fe(R);return}try{fe(new le({httpProxy:e,httpsProxy:e,noProxy:`localhost,127.0.0.1,[::1]`}))}catch{}}async function pt(e,t=5e3){let n=Date.now(),r=new AbortController,i=setTimeout(()=>r.abort(),t);try{let t=await fetch(e,{method:`GET`,signal:r.signal});return{ok:!0,latencyMs:Date.now()-n,status:t.status}}catch(e){let t=Date.now()-n;if(r.signal.aborted)return{ok:!1,latencyMs:t,errorKind:`timeout`,errorMessage:`timeout`};let i=e instanceof Error?e.message:String(e);return{ok:!1,latencyMs:t,errorKind:mt(e),errorMessage:i}}finally{clearTimeout(i)}}function mt(e){switch(e?.cause?.code??e?.code){case`ECONNREFUSED`:case`ECONNRESET`:case`EHOSTUNREACH`:case`ENETUNREACH`:return`proxy-unreachable`;case`ENOTFOUND`:case`EAI_AGAIN`:return`target-unreachable`;case`UND_ERR_CONNECT_TIMEOUT`:case`UND_ERR_HEADERS_TIMEOUT`:case`ETIMEDOUT`:return`timeout`;default:return`unknown`}}async function ht(e){let{url:t,method:n=`POST`,headers:r={},body:i,timeout:a=6e4,signal:o}=e,s=new AbortController,c=o?AbortSignal.any([o,s.signal]):s.signal,l=setTimeout(()=>s.abort(),a);try{let a=L(e.proxy),o={method:n,headers:{"content-type":`application/json`,...r},signal:c};i&&(o.body=typeof i==`string`?i:JSON.stringify(i));let s=await fetch(t,a?{...o,dispatcher:a}:o),l=await s.text();return{status:s.status,headers:s.headers,body:l,ok:s.ok}}catch(e){throw e instanceof DOMException&&e.name===`AbortError`?new x(`HTTP request aborted or timed out`,{code:`NETWORK_TIMEOUT`,cause:e}):new x(`HTTP request failed`,{code:`NETWORK_ERROR`,cause:e instanceof Error?e:Error(String(e))})}finally{clearTimeout(l)}}async function*gt(e){let{url:t,method:n=`POST`,headers:r={},body:i,timeout:a=3e5,idleTimeout:o=12e4,signal:s}=e,c=new AbortController,l=s?AbortSignal.any([s,c.signal]):c.signal,u=setTimeout(()=>c.abort(),a),d,f=()=>{d&&clearTimeout(d),d=setTimeout(()=>c.abort(),o)},p;try{let a=L(e.proxy),o={method:n,headers:{"content-type":`application/json`,accept:`text/event-stream`,...r},body:i?typeof i==`string`?i:JSON.stringify(i):void 0,signal:l},s=await fetch(t,a?{...o,dispatcher:a}:o);if(!s.ok){let e=await s.text(),t=`BAD_RESPONSE`;s.status===429?t=`RATE_LIMIT`:s.status===529?t=`OVERLOADED`:s.status>=500?t=`SERVER_ERROR`:s.status>=400&&(t=`REQUEST_ERROR`);let n;try{let t=JSON.parse(e)?.error;n=t?.code??t?.type}catch{}throw new x(`Http request failed ${s.status}: ${e}`,{code:t,status:s.status,headers:s.headers,errorType:n})}if(e.onResponse?.(s.headers),!s.body)throw new x(`Response body is empty`,{code:`BAD_RESPONSE`});p=s.body.getReader();let c=new TextDecoder,u=[],d=ce({onEvent(e){u.push({event:e.event||void 0,data:e.data,id:e.id||void 0})}});for(f();;){let{done:e,value:t}=await p.read();if(e)break;for(f(),d.feed(c.decode(t,{stream:!0}));u.length>0;){let e=u.shift();e&&(yield e)}}for(d.feed(c.decode());u.length>0;){let e=u.shift();e&&(yield e)}}catch(e){throw e instanceof x?e:e instanceof DOMException&&e.name===`AbortError`?new x(`Stream aborted or timed out`,{code:`TIMEOUT`,cause:e}):new x(`Stream request failed`,{code:`NETWORK_ERROR`,cause:e instanceof Error?e:Error(String(e))})}finally{clearTimeout(u),d&&clearTimeout(d),p&&p.cancel().catch(()=>{})}}function _t(e,t){if(e==null)return;if(typeof e.get==`function`)return e.get(t)??void 0;let n=e;for(let e of Object.keys(n))if(e.toLowerCase()===t){let t=n[e];return typeof t==`string`?t:void 0}}const vt=[{description:`递归强制删除`,substring:`rm -rf`},{description:`递归强制删除(长格式)`,substring:`rm -r -f`},{description:`递归强制删除(长格式 2)`,substring:`rm --recursive --force`},{description:`删除根目录`,substring:`rm -rf /`},{description:`权限提升`,substring:`sudo rm`},{description:`权限提升`,substring:`sudo `},{description:`chmod 777`,substring:`chmod 777`},{description:`chmod -R 777`,substring:`chmod -R 777`},{description:`chmod 777 递归`,substring:`chmod -R`},{description:`curl 管道 shell 执行`,regex:/curl\s+\S+\s*\|\s*(ba)?sh/},{description:`wget 管道 shell 执行`,regex:/wget\s+\S+\s*-\s*O\s*-\s*\|\s*(ba)?sh/},{description:`fork bomb`,regex:/:\s*\(\s*\)\s*\{\s*:\s*\|\s*:?\s*&\s*\}\s*;?\s*:/},{description:`覆盖关键系统文件`,regex:/>\s*\/(etc|boot|sys|proc|dev)\//},{description:`mkfs 格式化`,substring:`mkfs.`},{description:`dd 覆写磁盘`,regex:/dd\s+if=.*of=\/dev\//}];function yt(e){let t=e.trim();if(!t)return{safe:!0};for(let e of vt)if(e.substring!==void 0&&t.includes(e.substring)||e.regex!==void 0&&e.regex.test(t))return{safe:!1,reason:e.description};return{safe:!0}}function bt(e){let t=new Map,n=[`LC_`,`OTTO_`,`npm_`],r=new Set([`PATH`,`HOME`,`USER`,`SHELL`,`TMPDIR`,`TEMP`,`TMP`,`LANG`,`NODE_PATH`,`NODE_ENV`,`TERM`,`COLORTERM`,`TERM_PROGRAM`,`SSH_AUTH_SOCK`,`DISPLAY`,`WAYLAND_DISPLAY`,`XDG_RUNTIME_DIR`,`XDG_SESSION_TYPE`]);for(let[e,i]of Object.entries(process.env)){if(!i)continue;if(r.has(e)){t.set(e,i);continue}let a=e.toLowerCase();a.includes(`api_key`)||a.includes(`token`)||a.includes(`secret`)||a.includes(`password`)||a.includes(`passwd`)||a.includes(`credential`)||a.includes(`auth`)||n.some(t=>e.startsWith(t))&&t.set(e,i)}if(e)for(let[n,r]of Object.entries(e))r===void 0?t.delete(n):t.set(n,r);return Object.fromEntries(t)}const z={Bash:`bash`,Read:`read`,Write:`write`,Edit:`edit`,MultiEdit:`edit`,Grep:`grep`,Glob:`find`,WebFetch:`web_fetch`,WebSearch:`web_search`,Task:`task_delegate`,TodoWrite:`write_todos`};function xt(e){return z[e]}const St=Object.fromEntries(Object.entries(z).reverse().map(([e,t])=>[t,e]));function Ct(e){return St[e]??e}function wt(e){return e.map(e=>z[e]??e)}function B(e){return e.toString(`base64url`)}function V(){return B(ie(32))}function H(e){return B(re(`sha256`).update(e).digest())}function Tt(){let e=V();return{verifier:e,challenge:H(e),method:`S256`}}function Et(){return B(ie(16))}const Dt=[1455,1457];function Ot(){return`<span class="otto-wordmark">OTTO</span>`}function kt(){return`<svg viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
7
- <circle cx="26" cy="26" r="24" fill="var(--gray-3)" stroke="var(--gray-5)" stroke-width="2"/>
8
- <path d="M15.5 27l7 6.5 14-15" stroke="var(--gray-12)" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/>
9
- </svg>`}function At(){return`<svg viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
10
- <circle cx="26" cy="26" r="24" fill="var(--gray-3)" stroke="var(--gray-5)" stroke-width="2"/>
11
- <path d="M19 19l14 14M33 19l-14 14" stroke="var(--gray-9)" stroke-width="2.8" stroke-linecap="round"/>
12
- </svg>`}function U(e){return e.replace(/[&<>"']/g,e=>({"&":`&amp;`,"<":`&lt;`,">":`&gt;`,'"':`&quot;`,"'":`&#39;`})[e]??e)}function jt(e,t,n,r){let i=e===`success`?kt():At();return`<!doctype html>
13
- <html lang="en">
14
- <head>
15
- <meta charset="utf-8">
16
- <meta name="viewport" content="width=device-width,initial-scale=1">
17
- <title>${U(t)}</title>
18
- <style>
19
- :root {
20
- --gray-1:#FFFFFF;--gray-2:#F5F6FA;--gray-3:#EEF0F9;
21
- --gray-5:#DBDDEB;--gray-7:#AEB2C8;--gray-9:#7A7EA0;
22
- --gray-11:#565A78;--gray-12:#12152B;
23
- --radius-xl:24px;
24
- --shadow-card:0 1px 2px rgba(18,21,43,.04),0 8px 24px rgba(18,21,43,.06);
25
- --hairline:.75px;
26
- --pixel:"Silkscreen","Press Start 2P","Pixelify Sans","SF Mono",monospace;
27
- }
28
-
29
- @media (prefers-color-scheme:dark) {
30
- :root:not([data-theme=light]) {
31
- --gray-1:#191F39;--gray-2:#111529;--gray-3:rgba(255,255,255,.08);
32
- --gray-5:rgba(255,255,255,.11);--gray-7:rgba(255,255,255,.18);
33
- --gray-9:rgba(255,255,255,.34);--gray-11:#AAB2CC;--gray-12:#F4F6FF;
34
- --shadow-card:0 1px 2px rgba(0,0,0,.14),0 8px 24px rgba(0,0,0,.22);
35
- }
36
- }
37
-
38
- *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
39
- body{
40
- min-height:100dvh;display:grid;place-items:center;padding:32px;
41
- background:var(--gray-2);-webkit-font-smoothing:antialiased;
42
- }
43
-
44
- .page{width:min(440px,100%);text-align:center}
45
-
46
- .otto-wordmark{font-family:var(--pixel);font-size:28px;font-weight:700;letter-spacing:.04em;color:var(--gray-12);margin-bottom:28px}
47
-
48
- .card{
49
- padding:36px 40px;border-radius:var(--radius-xl);
50
- border:var(--hairline) solid var(--gray-3);background:var(--gray-1);
51
- box-shadow:var(--shadow-card);
52
- }
53
-
54
- .status-icon{display:inline-block;margin-bottom:22px}
55
- .status-icon svg{width:52px;height:52px}
56
-
57
- .card *{font-family:var(--pixel)}
58
- .kicker{
59
- font-size:11px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
60
- color:var(--gray-7);margin-bottom:10px;
61
- }
62
- h1{font-size:22px;font-weight:700;line-height:1.3;letter-spacing:.02em;color:var(--gray-12)}
63
- .desc{
64
- margin-top:14px;font-size:13px;line-height:1.7;color:var(--gray-11);
65
- max-width:300px;margin-left:auto;margin-right:auto;
66
- }
67
- .separator{
68
- width:60px;height:2px;margin:24px auto 20px;background:var(--gray-3)
69
- }
70
- .footer{
71
- font-size:11px;color:var(--gray-7);font-weight:600;letter-spacing:.06em;
72
- text-transform:uppercase;
73
- }
74
- </style>
75
- </head>
76
- <body>
77
- <main class="page" aria-labelledby="title">
78
- ${Ot()}
79
- <section class="card">
80
- <div class="status-icon">${i}</div>
81
- <p class="kicker">${e===`success`?`Authentication`:`Error`}</p>
82
- <h1 id="title">${U(t)}</h1>
83
- <p class="desc">${U(n)}</p>
84
- <div class="separator"></div>
85
- <p class="footer">${U(r)}</p>
86
- </section>
87
- </main>
88
- </body>
89
- </html>`}const Mt=jt(`success`,`Login complete`,`You can close this window and return to the terminal.`,`Otto Auth`);function W(e){return jt(`error`,`Login failed`,e,`Otto Auth`)}function G(e,t,n){let r=Buffer.from(n,`utf-8`);e.writeHead(t,{"Content-Type":`text/html; charset=utf-8`,"Content-Length":String(r.length),Connection:`close`}),e.end(r)}function Nt(e,t,n){return new Promise((r,i)=>{if(n.length===0){i(new S(`No loopback port specified.`,{code:`OAUTH_LOOPBACK_PORT_UNAVAILABLE`}));return}let a=0,o=()=>{e.removeListener(`error`,l),e.removeListener(`listening`,c)},s=()=>{let r=n[a++];e.listen(r,t)},c=()=>{o();let t=e.address();r(typeof t==`object`&&t?t.port:n[a-1])},l=e=>{if(e.code===`EADDRINUSE`||e.code===`EACCES`){if(a<n.length){s();return}o(),i(new S(`No available loopback port among: ${n.join(`, `)}`,{code:`OAUTH_LOOPBACK_PORT_UNAVAILABLE`}));return}o(),i(e)};e.on(`error`,l),e.on(`listening`,c),s()})}function Pt(e,t){e.value||(e.value=!0,t())}async function Ft(e={}){let{ports:t=Dt,callbackPath:n=`/auth/callback`,expectedState:r,host:i=`127.0.0.1`,signal:a}=e;if(a?.aborted)throw new S(`Login cancelled`,{code:`OAUTH_CANCELLED`});let o,s,c={value:!1},l=new Promise((e,t)=>{o=t=>Pt(c,()=>e(t)),s=e=>Pt(c,()=>t(e))});l.catch(()=>{});let u=0,d=()=>`http://${i}:${u}`,f=pe((e,t)=>{let i;try{i=new URL(e.url??`/`,d())}catch{G(t,400,W(`Invalid request URL`)),s(new S(`Invalid callback URL`,{code:`OAUTH_INVALID_REQUEST`}));return}if(i.pathname!==n){G(t,404,W(`Not found`));return}let a=i.searchParams.get(`error`);if(a){let e=i.searchParams.get(`error_description`)??``;G(t,400,W(e||a)),s(new S(`Authorization failed: ${a}${e?`: ${e}`:``}`,{code:`OAUTH_AUTHORIZE_FAILED`}));return}let c=i.searchParams.get(`code`);if(!c){G(t,400,W(`Missing authorization code.`)),s(new S(`Callback missing authorization code.`,{code:`OAUTH_AUTHORIZE_FAILED`}));return}let l=i.searchParams.get(`state`)??void 0;if(r!==void 0&&l!==r){G(t,400,W(`State mismatch.`)),s(new S(`OAuth state mismatch (possible CSRF).`,{code:`OAUTH_STATE_MISMATCH`}));return}G(t,200,Mt),o({code:c,state:l})});u=await Nt(f,i,t);let p=!1,m=()=>new Promise(e=>{if(p){e();return}p=!0,s(new S(`Login cancelled (server closed)`,{code:`OAUTH_CANCELLED`})),f.close(()=>e()),f.closeAllConnections?.()});return a&&a.addEventListener(`abort`,()=>{s(new S(`Login cancelled`,{code:`OAUTH_CANCELLED`})),m()},{once:!0}),{port:u,redirectUri:`${d()}${n}`,waitForCallback:()=>l,close:m}}function It(e){try{new URL(e)}catch{return!1}try{let t=process.platform,n=t===`darwin`?[`open`,[e]]:t===`win32`?[`cmd`,[`/c`,`start`,``,e]]:[`xdg-open`,[e]];return me(n[0],[...n[1]],{detached:!0,stdio:`ignore`}).unref(),!0}catch{return!1}}const Lt=/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;function Rt(e){let t=Lt.exec(e);return t?{fields:zt(t[1]??``),body:(t[2]??``).trim()}:null}function zt(e){let t=new Map;for(let n of e.split(`
90
- `)){let e=n.indexOf(`:`);if(e<=0)continue;let r=n.slice(0,e).trim().toLowerCase().replaceAll(`_`,``),i=n.slice(e+1).trim().replace(/^["']|["']$/g,``);r&&i&&t.set(r,i)}return t}function Bt(e){if(!e)return;let t=(e.startsWith(`[`)&&e.endsWith(`]`)?e.slice(1,-1):e).split(`,`).map(e=>e.trim().replace(/^["']|["']$/g,``)).filter(e=>e!==``);return t.length>0?t:void 0}const Vt=8e3,Ht=5*1024*1024,Ut=Buffer.from([137,80,78,71,13,10,26,10]),Wt=Buffer.from(`GIF8`,`ascii`);function Gt(e,t){if(!(!e||e.length===0)&&!(e.length>7340032))try{return Kt(Buffer.from(e.slice(0,128),`base64`),e,t)}catch{return}}function Kt(e,t,n){let r=n.toLowerCase();if(r.includes(`png`))return qt(e);if(r.includes(`jpeg`)||r.includes(`jpg`))return Jt(Buffer.from(t,`base64`));if(r.includes(`gif`))return Yt(e);if(r.includes(`webp`))return Xt(e);if(e.length>=8&&e.subarray(0,8).equals(Ut))return qt(e);if(e.length>=2&&e.readUInt16BE(0)===65496)return Jt(Buffer.from(t,`base64`));if(e.length>=4&&e.subarray(0,4).equals(Wt))return Yt(e);if(e.length>=12&&e.readUInt32BE(0)===1380533830&&e.readUInt32BE(8)===1464156752)return Xt(e)}function qt(e){if(!(e.length<24)&&e.subarray(0,8).equals(Ut))try{return e.toString(`ascii`,12,16)===`IHDR`?{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}:void 0}catch{return}}function Jt(e){if(!(e.length<2))try{let t=2,n=0;for(;t<e.length-1;){if(n++,n>1e5)return;let r=e.readUInt16BE(t);if(r===65497||(r&65280)!=65280)return;let i=r&255;if(i>=192&&i<=195||i>=197&&i<=199||i>=201&&i<=203||i>=205&&i<=207){if(t+9>e.length||e.readUInt16BE(t+2)<8)return;let n=e.readUInt16BE(t+5);return{width:e.readUInt16BE(t+7),height:n}}if(i===216||i===217){t+=2;continue}if(t+4>e.length)return;let a=e.readUInt16BE(t+2);if(a<2)return;t+=2+a}return}catch{return}}function Yt(e){if(!(e.length<10)&&e.subarray(0,4).equals(Wt))try{return{width:e.readUInt16LE(6),height:e.readUInt16LE(8)}}catch{return}}function Xt(e){if(!(e.length<16)&&!(e.readUInt32BE(0)!==1380533830||e.readUInt32BE(8)!==1464156752))try{let t=e.toString(`ascii`,12,16);return t===`VP8X`?Zt(e):t===`VP8 `?Qt(e):t===`VP8L`?$t(e):void 0}catch{return}}function Zt(e){if(!(e.length<30))return{width:(e[24]|e[25]<<8|e[26]<<16)+1,height:(e[27]|e[28]<<8|e[29]<<16)+1}}function Qt(e){if(e.length<30)return;let t=e.readUInt16LE(26)&16383,n=e.readUInt16LE(28)&16383;if(!(t===0||n===0))return{width:t,height:n}}function $t(e){if(e.length<25||e[20]!==47)return;let t=e.readUInt32LE(21);return{width:(t&16383)+1,height:(t>>14&16383)+1}}const K=process.env.OTTO_STARTUP_TRACE===`1`,en=K?performance.now():0;function tn(e){if(!K)return;let t=performance.now()-en;process.stderr.write(`[startup] ${e}: ${t.toFixed(1)}ms\n`)}async function nn(e,t){if(!K)return t();let n=performance.now();try{return await t()}finally{let t=performance.now()-n;process.stderr.write(`[startup] ${e}: ${t.toFixed(1)}ms\n`)}}function rn(e,t){if(!K)return t();let n=performance.now();try{return t()}finally{let t=performance.now()-n;process.stderr.write(`[startup] ${e}: ${t.toFixed(1)}ms\n`)}}function an(){return K}const on=/[/\-_\s.]/;function sn(e){return e!==void 0&&on.test(e)}function q(e,t){if(e.length===0)return{score:0,positions:[]};if(t.length===0)return null;let n=e.toLowerCase(),r=t.toLowerCase(),i=0,a=0,o=[],s=-2;for(let e=0;e<r.length&&a<n.length;e++)r[e]===n[a]&&(s===e-1?i+=24:(i+=16,s>=0&&(i-=(e-s-1)*3)),e===0?i+=12:sn(r[e-1])&&(i+=8),o.push(e),s=e,a++);return a<n.length?null:(i-=Math.max(0,r.length-n.length)*.5,{score:i,positions:o})}function cn(e,t){return q(e,t)?.score??-1}function ln(e){if(e.length===0)return[];let t=[],n=e[0],r=n+1;for(let i=1;i<e.length;i++){let a=e[i];a===r?r++:(t.push({start:n,end:r}),n=a,r=a+1)}return t.push({start:n,end:r}),t}function un(e,t,n){let r=q(e,t);for(let t of n??[]){let n=q(e,t);n&&(!r||n.score>r.score)&&(r=n)}return r}function J(e,t,n={}){let r=n.maxResults??50,i=n.matchEmpty??!0,a=t.trim();if(a===``)return i?e.slice(0,r).map(e=>({item:e,score:0,highlightRanges:[]})):[];let o=[];for(let t of e){let e=un(a,t.text,t.aliases);if(!e)continue;let n=q(a,t.text);o.push({item:t,score:e.score,highlightRanges:ln(n?.positions??[])})}return o.sort((e,t)=>t.score-e.score||(e.item.sortKey??e.item.text).localeCompare(t.item.sortKey??t.item.text)),o.slice(0,r)}function dn(e,t,n){return J(e.map(e=>({text:e.name,aliases:e.description?[e.description]:void 0,payload:e,sortKey:e.sortKey})),t,n).map(e=>e.item.payload)}function fn(e,t,n){return J(e.map(e=>({text:e.searchText??e.label,aliases:[e.group,e.detail,e.value].filter(e=>!!e),payload:e})),t,{maxResults:n?.maxResults??e.length,matchEmpty:n?.matchEmpty}).map(e=>e.item.payload)}const Y=Be(`@otto/shared:file-lock`),pn=15e3;function mn(e){return Math.floor(e/3)}const X=new Map,Z=new Map;function Q(e,t,n=`file-lock`){return _(e,`.${n}-${t}.lock`)}function hn(e,t){gn(e);let n=setInterval(()=>{let t=new Date;ee(e,t,t).catch(()=>{})},mn(t));n.unref?.(),X.set(e,n)}function gn(e){let t=X.get(e);t&&(clearInterval(t),X.delete(e))}async function _n(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=async(n=!1)=>{try{let e=await f(i,`wx`),t=y();await e.writeFile(t),await e.close(),Z.set(i,t);try{await u(i,384)}catch{}return hn(i,r),!0}catch(r){if(r.code===`EEXIST`)return!1;if(r.code===`ENOENT`&&!n){try{await d(e,{recursive:!0})}catch(e){return Y.warn(`Failed to create lock directory for ${t}: ${e.message}`),!1}return await a(!0)}return Y.warn(`Failed to acquire file lock for ${t}: ${r.message}`),!1}};if(await a())return!0;try{let e=await h(i);if(Date.now()-e.mtimeMs>r)return await g(i).catch(()=>{}),await a()}catch{return await a()}return!1}async function vn(e,t,n={}){let r=Q(e,t,n.prefix);gn(r);let i=Z.get(r);if(Z.delete(r),i!==void 0)try{if(await p(r,`utf-8`)!==i){Y.warn(`File lock ownership drifted for ${t} — another process stole this lock (likely stale-lock takeover during a heartbeat gap). Skipping unlink to avoid deleting the new owner's lock.`);return}}catch{}await g(r).catch(()=>{})}async function yn(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=Date.now()+r;for(;Date.now()<a;){try{await h(i)}catch{return!0}await new Promise(e=>setTimeout(e,100))}return!1}const $=new Map;function bn(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=(n=!1)=>{let r;try{r=ve(i,`wx`)}catch(r){let i=r.code;if(i===`EEXIST`)return!1;if(i===`ENOENT`&&!n){try{_e(e,{recursive:!0})}catch(e){return Y.warn(`Failed to create lock directory for ${t}: ${e.message}`),!1}return a(!0)}return Y.warn(`Failed to acquire file lock for ${t}: ${r.message}`),!1}let o=y();try{Se(r,o)}finally{ge(r)}$.set(i,o);try{he(i,384)}catch{}return!0};if(a())return!0;try{let e=be(i);if(Date.now()-e.mtimeMs>r){try{xe(i)}catch{}return a()}}catch{return a()}return!1}function xn(e,t,n={}){let r=Q(e,t,n.prefix),i=$.get(r);if($.delete(r),i!==void 0)try{if(ye(r,`utf-8`)!==i){Y.warn(`File lock ownership drifted for ${t} — another process stole this lock. Skipping unlink to avoid deleting the new owner's lock.`);return}}catch{}try{xe(r)}catch{}}function Sn(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=Date.now()+r,o=new Int32Array(new SharedArrayBuffer(4));for(;Date.now()<a;){try{be(i)}catch{return!0}Atomics.wait(o,0,0,100)}return!1}export{Vt as API_IMAGE_DIMENSION_LIMIT_PX,we as AgentError,z as CLAUDE_TO_OTTO_TOOL,b as CodedError,pn as DEFAULT_FILE_LOCK_TIMEOUT_MS,t as EventBus,C as InvariantError,j as LARGE_FILE_BYTE_THRESHOLD,Te as McpError,x as NetworkError,S as OAuthError,Ht as PROVIDER_IMAGE_INLINE_MAX_BYTES,Ce as ProviderError,e as TypedEventEmitter,_n as acquireFileLock,bn as acquireFileLockSync,Pe as attachLogListener,bt as buildAllowedEnv,yt as checkShellCommand,L as createDispatcher,Be as createLogger,Tt as createPkcePair,rt as createProjectOverflowLogPath,nt as createTempLoggerPath,Ue as exists,Je as formatBytes,H as generateChallenge,Et as generateState,V as generateVerifier,dt as getDefaultProxy,$e as getHomeDir,F as getProjectDir,tt as getThirdPartyToolBinaryDir,et as getThirdPartyToolDir,_t as headerGet,ft as installGlobalProxyDispatcher,w as invariant,We as isDirectory,Ge as isFile,an as isStartupTraceEnabled,Le as isTransportInitialized,Q as lockPath,wt as mapClaudeAgentToolNames,xt as mapClaudeToolName,tn as markStartupPhase,q as matchSubsequence,lt as matchesGlobPattern,Ve as mime,He as mkdirp,c as normalizePath,It as openBrowser,Ct as ottoToClaudeToolName,zt as parseFrontmatterFields,Bt as parseFrontmatterList,st as parseJsonc,ln as positionsToHighlightRanges,Gt as probeImageDimensions,pt as probeProxy,J as rankCandidates,fn as rankLabeledItems,dn as rankNamedItems,Ke as readFileCapped,vn as releaseFileLock,xn as releaseFileLockSync,ht as request,ct as safeStringify,cn as scoreSubsequence,ut as setDefaultProxy,Ie as setLogLevel,Rt as splitFrontmatter,Ft as startLoopbackServer,gt as stream,at as sweepStaleProjectOverflowLogs,it as sweepStaleTempLogs,qe as toTruncationMeta,nn as traceStartupPhase,rn as traceStartupPhaseSync,Ye as truncateHead,Qe as truncateLine,Xe as truncateTail,yn as waitForFileLockRelease,Sn as waitForFileLockReleaseSync};
4
+ `),truncated:!0,truncatedBy:l,totalLines:o,totalBytes:i,lastLinePartial:!1,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:s.length,outputBytes:c})}function $e(e,t){let{maxLines:n,maxBytes:r}=t,i=Buffer.byteLength(e,`utf-8`),a=e.split(`
5
+ `),o=a.length;if(o<=n&&i<=r)return M({content:e,truncated:!1,truncatedBy:`lines`,totalLines:o,totalBytes:i,lastLinePartial:!1,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:o,outputBytes:i});if(n===0)return M({content:``,truncated:!0,truncatedBy:`lines`,totalLines:o,totalBytes:i,lastLinePartial:!1,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:0,outputBytes:0});let s=[],c=0,l=`lines`,u=!1;for(let e=a.length-1;e>=0&&s.length<n;e--){let t=a[e],n=Buffer.byteLength(t,`utf-8`)+(s.length>0?1:0);if(c+n>r){if(l=`bytes`,s.length===0){let e=et(t,r);e.length>0&&(s.unshift(e),c=Buffer.byteLength(e,`utf-8`),u=!0)}break}s.unshift(t),c+=n}return s.length>=n&&c<=r&&(l=`lines`),M({content:s.join(`
6
+ `),truncated:!0,truncatedBy:l,totalLines:o,totalBytes:i,lastLinePartial:u,firstLineExceedsLimit:!1,options:{maxLines:n,maxBytes:r},outputLines:s.length,outputBytes:c})}function et(e,t){let n=Buffer.from(e,`utf-8`);if(n.length<=t)return e;let r=n.length-t;for(;r<n.length;){let e=n[r];if(e===void 0||(e&192)!=128)break;r++}return n.subarray(r).toString(`utf-8`)}function tt(e,t){return e.length<=t?{text:e,wasTruncated:!1}:{text:`${e.slice(0,t)}... [truncated]`,wasTruncated:!0}}function N(){return a}function nt(){return i}function rt(){return _(nt(),`tools`)}function it(e,t=``){return _(rt(),...t?[e,t]:[e])}const P=`otto-coding-`,F=`.log`;function at(){let e=v();return g(ne(),`${P}${e}${F}`)}function ot(){let e=v();return g(N(),`tool-results`,`bash`,`${P}${e}${F}`)}async function st(e,t){return lt(ne(),e,t)}async function ct(e,t){return lt(g(N(),`tool-results`,`bash`),e,t)}async function lt(e,t,n){let r;try{r=await p(e)}catch{return 0}let i=r.filter(e=>e.startsWith(P)&&e.endsWith(F)),a=0;return await Promise.all(i.map(async r=>{let i=g(e,r);try{n-(await m(i)).mtimeMs>t&&(await h(i),a++)}catch{}})),a}function ut(e){let t=[],n=ae(e,t,{allowTrailingComma:!0,disallowComments:!1,allowEmptyContent:!1});if(t.length>0){let e=t[0],n=e?oe(e.error):`UnknownError`;throw Error(`Invalid JSONC: ${n}`)}return n}function dt(e,t){return se(e,void 0,t)??`null`}function ft(e,t){let n=``;for(let t=0;t<e.length;t++){let r=e[t];r===`*`?e[t+1]===`*`?(n+=`.*`,t++):n+=`[^/]*`:r===`?`?n+=`.`:n+=r.replace(/[.+^${}()|[\]\\]/g,`\\$&`)}return RegExp(`^${n}$`,`i`).test(t)}let I;function pt(e){I=e||void 0}function L(){return I}function R(e){let t=e||I;if(t)try{return new ue({uri:t})}catch{return}}let z;function B(e){if(z===void 0&&(z=de()),!e){y(z);return}try{y(new le({httpProxy:e,httpsProxy:e,noProxy:`localhost,127.0.0.1,[::1]`}))}catch{}}const mt={getUrl(){return L()},installGlobal(){B(I)},uninstallGlobal(){B(void 0)}},ht={kind:`idle`},gt={kind:`wallclock`};function _t(e,t){return e?t.idleTimeout:t.firstTokenIdleTimeout}function vt(e){let t=e.idleTimeout??12e4,n=e.firstTokenIdleTimeout??Math.max(t,9e5),r=e.timeout??3e5;return{timeout:Math.max(r,n+6e4),idleTimeout:t,firstTokenIdleTimeout:n}}function yt(e){let t=e.signal.reason;return typeof t==`object`&&t&&`kind`in t?t.kind:`external`}async function*bt(e){let{url:t,method:n=`POST`,headers:r={},body:i,signal:a}=e,{timeout:o,idleTimeout:s,firstTokenIdleTimeout:c}=vt(e),l=new AbortController,u=a?AbortSignal.any([a,l.signal]):l.signal,d=setTimeout(()=>l.abort(gt),o),f,p=!1,m=()=>{f&&clearTimeout(f),f=setTimeout(()=>l.abort(ht),_t(p,{idleTimeout:s,firstTokenIdleTimeout:c}))},h;try{let a=R(e.proxy),o={method:n,headers:{"content-type":`application/json`,accept:`text/event-stream`,...r},body:i?typeof i==`string`?i:JSON.stringify(i):void 0,signal:u},s=await fetch(t,a?{...o,dispatcher:a}:o);if(!s.ok){let e=await s.text(),t=`BAD_RESPONSE`;s.status===429?t=`RATE_LIMIT`:s.status===529?t=`OVERLOADED`:s.status>=500?t=`SERVER_ERROR`:s.status>=400&&(t=`REQUEST_ERROR`);let n;try{let t=JSON.parse(e)?.error;n=t?.code??t?.type}catch{}throw new x(`Http request failed ${s.status}: ${e}`,{code:t,status:s.status,headers:s.headers,errorType:n})}if(e.onResponse?.(s.headers),!s.body)throw new x(`Response body is empty`,{code:`BAD_RESPONSE`});h=s.body.getReader();let c=new TextDecoder,l=[],d=ce({onEvent(e){l.push({event:e.event||void 0,data:e.data,id:e.id||void 0})}});for(m();;){let{done:e,value:t}=await h.read();if(e)break;for(m(),d.feed(c.decode(t,{stream:!0}));l.length>0;){let e=l.shift();e&&(p||(p=!0,m()),yield e)}}for(d.feed(c.decode());l.length>0;){let e=l.shift();e&&(p||(p=!0,m()),yield e)}}catch(e){throw e instanceof x?e:l.signal.aborted||e instanceof DOMException&&e.name===`AbortError`?new x(`Stream aborted or timed out`,{code:`TIMEOUT`,cause:e instanceof Error?e:Error(String(e)),abortKind:yt(l)}):new x(`Stream request failed`,{code:`NETWORK_ERROR`,cause:e instanceof Error?e:Error(String(e))})}finally{clearTimeout(d),f&&clearTimeout(f),h&&h.cancel().catch(()=>{})}}async function xt(e,t=5e3){let n=Date.now(),r=new AbortController,i=setTimeout(()=>r.abort(),t);try{let t=await fetch(e,{method:`GET`,signal:r.signal});return{ok:!0,latencyMs:Date.now()-n,status:t.status}}catch(e){let t=Date.now()-n;if(r.signal.aborted)return{ok:!1,latencyMs:t,errorKind:`timeout`,errorMessage:`timeout`};let i=e instanceof Error?e.message:String(e);return{ok:!1,latencyMs:t,errorKind:St(e),errorMessage:i}}finally{clearTimeout(i)}}function St(e){switch(e?.cause?.code??e?.code){case`ECONNREFUSED`:case`ECONNRESET`:case`EHOSTUNREACH`:case`ENETUNREACH`:return`proxy-unreachable`;case`ENOTFOUND`:case`EAI_AGAIN`:return`target-unreachable`;case`UND_ERR_CONNECT_TIMEOUT`:case`UND_ERR_HEADERS_TIMEOUT`:case`ETIMEDOUT`:return`timeout`;default:return`unknown`}}function Ct(e,t){if(e==null)return;if(typeof e.get==`function`)return e.get(t)??void 0;let n=e;for(let e of Object.keys(n))if(e.toLowerCase()===t){let t=n[e];return typeof t==`string`?t:void 0}}const wt=[{description:`递归强制删除`,substring:`rm -rf`},{description:`递归强制删除(长格式)`,substring:`rm -r -f`},{description:`递归强制删除(长格式 2)`,substring:`rm --recursive --force`},{description:`删除根目录`,substring:`rm -rf /`},{description:`权限提升`,substring:`sudo rm`},{description:`权限提升`,substring:`sudo `},{description:`chmod 777`,substring:`chmod 777`},{description:`chmod -R 777`,substring:`chmod -R 777`},{description:`chmod 777 递归`,substring:`chmod -R`},{description:`curl 管道 shell 执行`,regex:/curl\s+\S+\s*\|\s*(ba)?sh/},{description:`wget 管道 shell 执行`,regex:/wget\s+\S+\s*-\s*O\s*-\s*\|\s*(ba)?sh/},{description:`fork bomb`,regex:/:\s*\(\s*\)\s*\{\s*:\s*\|\s*:?\s*&\s*\}\s*;?\s*:/},{description:`覆盖关键系统文件`,regex:/>\s*\/(etc|boot|sys|proc|dev)\//},{description:`mkfs 格式化`,substring:`mkfs.`},{description:`dd 覆写磁盘`,regex:/dd\s+if=.*of=\/dev\//}];function Tt(e){let t=e.trim();if(!t)return{safe:!0};for(let e of wt)if(e.substring!==void 0&&t.includes(e.substring)||e.regex!==void 0&&e.regex.test(t))return{safe:!1,reason:e.description};return{safe:!0}}function Et(e){let t=new Map,n=[`LC_`,`OTTO_`,`npm_`],r=new Set([`PATH`,`HOME`,`USER`,`SHELL`,`TMPDIR`,`TEMP`,`TMP`,`LANG`,`NODE_PATH`,`NODE_ENV`,`TERM`,`COLORTERM`,`TERM_PROGRAM`,`SSH_AUTH_SOCK`,`DISPLAY`,`WAYLAND_DISPLAY`,`XDG_RUNTIME_DIR`,`XDG_SESSION_TYPE`]);for(let[e,i]of Object.entries(process.env)){if(!i)continue;if(r.has(e)){t.set(e,i);continue}let a=e.toLowerCase();a.includes(`api_key`)||a.includes(`token`)||a.includes(`secret`)||a.includes(`password`)||a.includes(`passwd`)||a.includes(`credential`)||a.includes(`auth`)||n.some(t=>e.startsWith(t))&&t.set(e,i)}if(e)for(let[n,r]of Object.entries(e))r===void 0?t.delete(n):t.set(n,r);return Object.fromEntries(t)}const V={Bash:`bash`,Read:`read`,Write:`write`,Edit:`edit`,MultiEdit:`edit`,Grep:`grep`,Glob:`find`,WebFetch:`web_fetch`,WebSearch:`web_search`,Task:`task_delegate`,TodoWrite:`write_todos`};function Dt(e){return V[e]}const Ot=Object.fromEntries(Object.entries(V).reverse().map(([e,t])=>[t,e]));function kt(e){return Ot[e]??e}function At(e){return e.map(e=>V[e]??e)}function H(e){return e.toString(`base64url`)}function jt(){return H(ie(32))}function Mt(e){return H(re(`sha256`).update(e).digest())}function Nt(){let e=jt();return{verifier:e,challenge:Mt(e),method:`S256`}}function Pt(){return H(ie(16))}const Ft={renderSuccess(e,t,n){return`<!DOCTYPE html><html><body><h1>${U(e)}</h1><p>${U(t)}</p><p>${U(n)}</p></body></html>`},renderError(e,t,n){return`<!DOCTYPE html><html><body><h1>Error</h1><p>${U(e)}</p><p>${U(t)}</p><p>${U(n)}</p></body></html>`}};function U(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}const It=[1455,1457];function W(e,t,n){let r=Buffer.from(n,`utf-8`);e.writeHead(t,{"Content-Type":`text/html; charset=utf-8`,"Content-Length":String(r.length),Connection:`close`}),e.end(r)}function Lt(e,t,n){return new Promise((r,i)=>{if(n.length===0){i(new S(`No loopback port specified.`,{code:`OAUTH_LOOPBACK_PORT_UNAVAILABLE`}));return}let a=0,o=()=>{e.removeListener(`error`,l),e.removeListener(`listening`,c)},s=()=>{let r=n[a++];e.listen(r,t)},c=()=>{o();let t=e.address();r(typeof t==`object`&&t?t.port:n[a-1])},l=e=>{if(e.code===`EADDRINUSE`||e.code===`EACCES`){if(a<n.length){s();return}o(),i(new S(`No available loopback port among: ${n.join(`, `)}`,{code:`OAUTH_LOOPBACK_PORT_UNAVAILABLE`}));return}o(),i(e)};e.on(`error`,l),e.on(`listening`,c),s()})}function Rt(e,t){e.value||(e.value=!0,t())}async function zt(e={}){let{ports:t=It,callbackPath:n=`/auth/callback`,expectedState:r,host:i=`127.0.0.1`,signal:a,pageRenderer:o=Ft}=e;if(a?.aborted)throw new S(`Login cancelled`,{code:`OAUTH_CANCELLED`});let s,c,l={value:!1},u=new Promise((e,t)=>{s=t=>Rt(l,()=>e(t)),c=e=>Rt(l,()=>t(e))});u.catch(()=>{});let d=0,f=()=>`http://${i}:${d}`,p=fe((e,t)=>{let i;try{i=new URL(e.url??`/`,f())}catch{W(t,400,o.renderError(`Login failed`,`Invalid request URL`,`Otto Auth`)),c(new S(`Invalid callback URL`,{code:`OAUTH_INVALID_REQUEST`}));return}if(i.pathname!==n){W(t,404,o.renderError(`Login failed`,`Not found`,`Otto Auth`));return}let a=i.searchParams.get(`error`);if(a){let e=i.searchParams.get(`error_description`)??``;W(t,400,o.renderError(`Login failed`,e||a,`Otto Auth`)),c(new S(`Authorization failed: ${a}${e?`: ${e}`:``}`,{code:`OAUTH_AUTHORIZE_FAILED`}));return}let l=i.searchParams.get(`code`);if(!l){W(t,400,o.renderError(`Login failed`,`Missing authorization code.`,`Otto Auth`)),c(new S(`Callback missing authorization code.`,{code:`OAUTH_AUTHORIZE_FAILED`}));return}let u=i.searchParams.get(`state`)??void 0;if(r!==void 0&&u!==r){W(t,400,o.renderError(`Login failed`,`State mismatch.`,`Otto Auth`)),c(new S(`OAuth state mismatch (possible CSRF).`,{code:`OAUTH_STATE_MISMATCH`}));return}W(t,200,o.renderSuccess(`Login complete`,`You can close this window and return to the terminal.`,`Otto Auth`)),s({code:l,state:u})});d=await Lt(p,i,t);let m=!1,h=()=>new Promise(e=>{if(m){e();return}m=!0,c(new S(`Login cancelled (server closed)`,{code:`OAUTH_CANCELLED`})),p.close(()=>e()),p.closeAllConnections?.()});return a&&a.addEventListener(`abort`,()=>{c(new S(`Login cancelled`,{code:`OAUTH_CANCELLED`})),h()},{once:!0}),{port:d,redirectUri:`${f()}${n}`,waitForCallback:()=>u,close:h}}function Bt(e){try{new URL(e)}catch{return!1}try{let t=process.platform,n=t===`darwin`?[`open`,[e]]:t===`win32`?[`cmd`,[`/c`,`start`,``,e]]:[`xdg-open`,[e]];return pe(n[0],[...n[1]],{detached:!0,stdio:`ignore`}).unref(),!0}catch{return!1}}const Vt=/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;function Ht(e){let t=Vt.exec(e);return t?{fields:Ut(t[1]??``),body:(t[2]??``).trim()}:null}function Ut(e){let t=new Map;for(let n of e.split(`
7
+ `)){let e=n.indexOf(`:`);if(e<=0)continue;let r=n.slice(0,e).trim().toLowerCase().replaceAll(`_`,``),i=n.slice(e+1).trim().replace(/^["']|["']$/g,``);r&&i&&t.set(r,i)}return t}function Wt(e){if(!e)return;let t=(e.startsWith(`[`)&&e.endsWith(`]`)?e.slice(1,-1):e).split(`,`).map(e=>e.trim().replace(/^["']|["']$/g,``)).filter(e=>e!==``);return t.length>0?t:void 0}const Gt=8e3,Kt=5*1024*1024,qt=Buffer.from([137,80,78,71,13,10,26,10]),Jt=Buffer.from(`GIF8`,`ascii`);function Yt(e,t){if(!(!e||e.length===0)&&!(e.length>7340032))try{return Xt(Buffer.from(e.slice(0,128),`base64`),e,t)}catch{return}}function Xt(e,t,n){let r=n.toLowerCase();if(r.includes(`png`))return Zt(e);if(r.includes(`jpeg`)||r.includes(`jpg`))return Qt(Buffer.from(t,`base64`));if(r.includes(`gif`))return $t(e);if(r.includes(`webp`))return en(e);if(e.length>=8&&e.subarray(0,8).equals(qt))return Zt(e);if(e.length>=2&&e.readUInt16BE(0)===65496)return Qt(Buffer.from(t,`base64`));if(e.length>=4&&e.subarray(0,4).equals(Jt))return $t(e);if(e.length>=12&&e.readUInt32BE(0)===1380533830&&e.readUInt32BE(8)===1464156752)return en(e)}function Zt(e){if(!(e.length<24)&&e.subarray(0,8).equals(qt))try{return e.toString(`ascii`,12,16)===`IHDR`?{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}:void 0}catch{return}}function Qt(e){if(!(e.length<2))try{let t=2,n=0;for(;t<e.length-1;){if(n++,n>1e5)return;let r=e.readUInt16BE(t);if(r===65497||(r&65280)!=65280)return;let i=r&255;if(i>=192&&i<=195||i>=197&&i<=199||i>=201&&i<=203||i>=205&&i<=207){if(t+9>e.length||e.readUInt16BE(t+2)<8)return;let n=e.readUInt16BE(t+5);return{width:e.readUInt16BE(t+7),height:n}}if(i===216||i===217){t+=2;continue}if(t+4>e.length)return;let a=e.readUInt16BE(t+2);if(a<2)return;t+=2+a}return}catch{return}}function $t(e){if(!(e.length<10)&&e.subarray(0,4).equals(Jt))try{return{width:e.readUInt16LE(6),height:e.readUInt16LE(8)}}catch{return}}function en(e){if(!(e.length<16)&&!(e.readUInt32BE(0)!==1380533830||e.readUInt32BE(8)!==1464156752))try{let t=e.toString(`ascii`,12,16);return t===`VP8X`?tn(e):t===`VP8 `?nn(e):t===`VP8L`?rn(e):void 0}catch{return}}function tn(e){if(!(e.length<30))return{width:(e[24]|e[25]<<8|e[26]<<16)+1,height:(e[27]|e[28]<<8|e[29]<<16)+1}}function nn(e){if(e.length<30)return;let t=e.readUInt16LE(26)&16383,n=e.readUInt16LE(28)&16383;if(!(t===0||n===0))return{width:t,height:n}}function rn(e){if(e.length<25||e[20]!==47)return;let t=e.readUInt32LE(21);return{width:(t&16383)+1,height:(t>>14&16383)+1}}function an(e,t,n){return new Promise(r=>{let i=me({host:e,port:t}),a=!1,o=e=>{a||(a=!0,i.destroy(),r(e))};setTimeout(()=>o(!1),n).unref?.(),i.setTimeout(n),i.once(`connect`,()=>o(!0)),i.once(`error`,()=>o(!1)),i.once(`timeout`,()=>o(!1))})}const G=new Map;async function on(e,t={}){let n,r;try{let t=new URL(e);if(t.protocol!==`http:`&&t.protocol!==`https:`)return!1;n=t.hostname,r=t.port?Number(t.port):t.protocol===`https:`?443:80}catch{return!1}let i=`${n}:${r}`,a=Date.now();if(!t.force){let e=G.get(i);if(e&&a-e.at<3e5)return e.reachable}let o=await(t.connect??an)(n,r,t.timeoutMs??2e3);return G.set(i,{reachable:o,at:Date.now()}),o}async function sn(e=!1,t=[],n){let r=[...s(),...t];if(r.length===0)return!1;for(let t of r)if(await on(t,{force:e,...n?{connect:n}:{}}))return!0;return!1}function cn(){G.clear()}async function ln(e,t={}){if(e===void 0)return!0;try{return await e(),!0}catch(e){return t.logger?.warn({...t.context,err:e instanceof Error?e.message:String(e)},t.label??`dispose failed`),!1}}async function un(e,t={}){let n=e?.dispose;return typeof n==`function`?ln(()=>n.call(e),t):!0}const K=process.env.OTTO_STARTUP_TRACE===`1`,dn=K?performance.now():0;function fn(e){if(!K)return;let t=performance.now()-dn;process.stderr.write(`[startup] ${e}: ${t.toFixed(1)}ms\n`)}async function pn(e,t){if(!K)return t();let n=performance.now();try{return await t()}finally{let t=performance.now()-n;process.stderr.write(`[startup] ${e}: ${t.toFixed(1)}ms\n`)}}function mn(e,t){if(!K)return t();let n=performance.now();try{return t()}finally{let t=performance.now()-n;process.stderr.write(`[startup] ${e}: ${t.toFixed(1)}ms\n`)}}function hn(){return K}const gn=/[/\-_\s.]/;function _n(e){return e!==void 0&&gn.test(e)}function q(e,t){if(e.length===0)return{score:0,positions:[]};if(t.length===0)return null;let n=e.toLowerCase(),r=t.toLowerCase(),i=0,a=0,o=[],s=-2;for(let e=0;e<r.length&&a<n.length;e++)r[e]===n[a]&&(s===e-1?i+=24:(i+=16,s>=0&&(i-=(e-s-1)*3)),e===0?i+=12:_n(r[e-1])&&(i+=8),o.push(e),s=e,a++);return a<n.length?null:(i-=Math.max(0,r.length-n.length)*.5,{score:i,positions:o})}function vn(e,t){return q(e,t)?.score??-1}function yn(e){if(e.length===0)return[];let t=[],n=e[0],r=n+1;for(let i=1;i<e.length;i++){let a=e[i];a===r?r++:(t.push({start:n,end:r}),n=a,r=a+1)}return t.push({start:n,end:r}),t}function bn(e,t,n){let r=q(e,t);for(let t of n??[]){let n=q(e,t);n&&(!r||n.score>r.score)&&(r=n)}return r}function J(e,t,n={}){let r=n.maxResults??50,i=n.matchEmpty??!0,a=t.trim();if(a===``)return i?e.slice(0,r).map(e=>({item:e,score:0,highlightRanges:[]})):[];let o=[];for(let t of e){let e=bn(a,t.text,t.aliases);if(!e)continue;let n=q(a,t.text);o.push({item:t,score:e.score,highlightRanges:yn(n?.positions??[])})}return o.sort((e,t)=>t.score-e.score||(e.item.sortKey??e.item.text).localeCompare(t.item.sortKey??t.item.text)),o.slice(0,r)}function xn(e,t,n){return J(e.map(e=>({text:e.name,aliases:e.description?[e.description]:void 0,payload:e,sortKey:e.sortKey})),t,n).map(e=>e.item.payload)}function Sn(e,t,n){return J(e.map(e=>({text:e.searchText??e.label,aliases:[e.group,e.detail,e.value].filter(e=>!!e),payload:e})),t,{maxResults:n?.maxResults??e.length,matchEmpty:n?.matchEmpty}).map(e=>e.item.payload)}const Y=A(`@x-otto/shared:file-lock`),Cn=15e3;function wn(e){return Math.floor(e/3)}const X=new Map,Z=new Map;function Q(e,t,n=`file-lock`){return g(e,`.${n}-${t}.lock`)}function Tn(e,t){En(e);let n=setInterval(()=>{let t=new Date;ee(e,t,t).catch(()=>{})},wn(t));n.unref?.(),X.set(e,n)}function En(e){let t=X.get(e);t&&(clearInterval(t),X.delete(e))}async function Dn(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=async(n=!1)=>{try{let e=await d(i,`wx`),t=v();await e.writeFile(t),await e.close(),Z.set(i,t);try{await l(i,384)}catch{}return Tn(i,r),!0}catch(r){if(r.code===`EEXIST`)return!1;if(r.code===`ENOENT`&&!n){try{await u(e,{recursive:!0})}catch(e){return Y.warn(`Failed to create lock directory for ${t}: ${e.message}`),!1}return await a(!0)}return Y.warn(`Failed to acquire file lock for ${t}: ${r.message}`),!1}};if(await a())return!0;try{let e=await m(i);if(Date.now()-e.mtimeMs>r)return await h(i).catch(()=>{}),await a()}catch{return await a()}return!1}async function On(e,t,n={}){let r=Q(e,t,n.prefix);En(r);let i=Z.get(r);if(Z.delete(r),i!==void 0)try{if(await f(r,`utf-8`)!==i){Y.warn(`File lock ownership drifted for ${t} — another process stole this lock (likely stale-lock takeover during a heartbeat gap). Skipping unlink to avoid deleting the new owner's lock.`);return}}catch{}await h(r).catch(()=>{})}async function kn(e,t,n={}){let r=Q(e,t,n.prefix),i=Z.get(r);if(i===void 0)return!1;try{return await f(r,`utf-8`)===i}catch{return!1}}async function An(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=Date.now()+r;for(;Date.now()<a;){try{await m(i)}catch{return!0}await new Promise(e=>setTimeout(e,100))}return!1}const $=new Map;function jn(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=(n=!1)=>{let r;try{r=ve(i,`wx`)}catch(r){let i=r.code;if(i===`EEXIST`)return!1;if(i===`ENOENT`&&!n){try{_e(e,{recursive:!0})}catch(e){return Y.warn(`Failed to create lock directory for ${t}: ${e.message}`),!1}return a(!0)}return Y.warn(`Failed to acquire file lock for ${t}: ${r.message}`),!1}let o=v();try{Se(r,o)}finally{ge(r)}$.set(i,o);try{he(i,384)}catch{}return!0};if(a())return!0;try{let e=be(i);if(Date.now()-e.mtimeMs>r){try{xe(i)}catch{}return a()}}catch{return a()}return!1}function Mn(e,t,n={}){let r=Q(e,t,n.prefix),i=$.get(r);if($.delete(r),i!==void 0)try{if(ye(r,`utf-8`)!==i){Y.warn(`File lock ownership drifted for ${t} — another process stole this lock. Skipping unlink to avoid deleting the new owner's lock.`);return}}catch{}try{xe(r)}catch{}}function Nn(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=Date.now()+r,o=new Int32Array(new SharedArrayBuffer(4));for(;Date.now()<a;){try{be(i)}catch{return!0}Atomics.wait(o,0,0,100)}return!1}export{Gt as API_IMAGE_DIMENSION_LIMIT_PX,we as AgentError,V as CLAUDE_TO_OTTO_TOOL,b as CodedError,Cn as DEFAULT_FILE_LOCK_TIMEOUT_MS,ke as EventBus,De as InvariantError,j as LARGE_FILE_BYTE_THRESHOLD,Te as McpError,x as NetworkError,S as OAuthError,Kt as PROVIDER_IMAGE_INLINE_MAX_BYTES,Ce as ProviderError,Oe as TypedEventEmitter,Dn as acquireFileLock,jn as acquireFileLockSync,Re as attachLogListener,Et as buildAllowedEnv,Tt as checkShellCommand,R as createDispatcher,A as createLogger,Nt as createPkcePair,ot as createProjectOverflowLogPath,at as createTempLoggerPath,Ue as defaultLoggerSink,mt as defaultProxyConfig,Ke as exists,Ze as formatBytes,Mt as generateChallenge,Pt as generateState,jt as generateVerifier,L as getDefaultProxy,nt as getHomeDir,N as getProjectDir,it as getThirdPartyToolBinaryDir,rt as getThirdPartyToolDir,Ct as headerGet,B as installGlobalProxyDispatcher,C as invariant,qe as isDirectory,Je as isFile,kn as isFileLockHeldByMe,sn as isIntranetReachable,hn as isStartupTraceEnabled,Be as isTransportInitialized,on as isUrlReachable,Q as lockPath,At as mapClaudeAgentToolNames,Dt as mapClaudeToolName,fn as markStartupPhase,q as matchSubsequence,ft as matchesGlobPattern,We as mime,Ge as mkdirp,o as normalizePath,Bt as openBrowser,kt as ottoToClaudeToolName,Ut as parseFrontmatterFields,Wt as parseFrontmatterList,ut as parseJsonc,yn as positionsToHighlightRanges,Yt as probeImageDimensions,xt as probeProxy,J as rankCandidates,Sn as rankLabeledItems,xn as rankNamedItems,Ye as readFileCapped,On as releaseFileLock,Mn as releaseFileLockSync,cn as resetIntranetReachabilityCache,ln as safeDispose,un as safeDisposeOf,dt as safeStringify,vn as scoreSubsequence,pt as setDefaultProxy,O as setLogLevel,Ht as splitFrontmatter,zt as startLoopbackServer,bt as stream,ct as sweepStaleProjectOverflowLogs,st as sweepStaleTempLogs,Xe as toTruncationMeta,pn as traceStartupPhase,mn as traceStartupPhaseSync,Qe as truncateHead,tt as truncateLine,$e as truncateTail,An as waitForFileLockRelease,Nn as waitForFileLockReleaseSync};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-otto/shared",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.10",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -11,10 +11,6 @@
11
11
  ".": {
12
12
  "types": "./dist/index.d.ts",
13
13
  "import": "./dist/index.js"
14
- },
15
- "./browser": {
16
- "types": "./dist/browser/index.d.ts",
17
- "import": "./dist/browser/index.js"
18
14
  }
19
15
  },
20
16
  "publishConfig": {
@@ -22,15 +18,7 @@
22
18
  "registry": "https://registry.npmjs.org",
23
19
  "tag": "alpha"
24
20
  },
25
- "scripts": {
26
- "build": "tsdown && rm -f dist/*.map && sed -i '' '\\|//# sourceMappingURL=|d' dist/*.js dist/browser/index.js 2>/dev/null; true",
27
- "typecheck:project": "tsc -p tsconfig.json --noEmit",
28
- "typecheck:file": "tsc src/index.ts --noEmit --target ES2024 --module ESNext --moduleResolution bundler --esModuleInterop --skipLibCheck",
29
- "typecheck": "tsc --noEmit",
30
- "clean": "rm -rf dist"
31
- },
32
21
  "dependencies": {
33
- "@x-otto/env": "workspace:*",
34
22
  "eventsource-parser": "3.0.6",
35
23
  "jsonc-parser": "3.3.1",
36
24
  "mime-types": "3.0.2",
@@ -38,9 +26,17 @@
38
26
  "pino-pretty": "13.1.3",
39
27
  "safe-stable-stringify": "2.5.0",
40
28
  "undici": "8.7.0",
41
- "uuid": "13.0.0"
29
+ "uuid": "13.0.0",
30
+ "@x-otto/env": "0.1.0-alpha.10"
42
31
  },
43
32
  "devDependencies": {
44
33
  "@types/mime-types": "3.0.1"
34
+ },
35
+ "scripts": {
36
+ "build": "tsdown && rm -f dist/*.map && sed -i '' '\\|//# sourceMappingURL=|d' dist/*.js 2>/dev/null; true",
37
+ "typecheck:project": "tsc -p tsconfig.json --noEmit",
38
+ "typecheck:file": "tsc src/index.ts --noEmit --target ES2024 --module ESNext --moduleResolution bundler --esModuleInterop --skipLibCheck",
39
+ "typecheck": "tsc --noEmit",
40
+ "clean": "rm -rf dist"
45
41
  }
46
- }
42
+ }
@@ -1,2 +0,0 @@
1
- import { i as TypedEventEmitter, r as Events, t as EventBus } from "../event-bus-inioRzYz.js";
2
- export { EventBus, type Events, TypedEventEmitter };
@@ -1 +0,0 @@
1
- import{n as e,t}from"../event-bus-CABu09lC.js";export{t as EventBus,e as TypedEventEmitter};
@@ -1 +0,0 @@
1
- var e=class{listeners={};on(e,t){let n=this.listeners[e];return n||(n=new Set,this.listeners[e]=n),n.add(t),()=>this.off(e,t)}off(e,t){this.listeners[e]?.delete(t)}emit(e,...t){let n=this.listeners[e];if(n)for(let e of[...n])e(...t)}once(e,t){let n=((...r)=>{this.off(e,n),t(...r)});return this.on(e,n)}removeAllListeners(e){if(e!==void 0)delete this.listeners[e];else for(let e of Object.keys(this.listeners))delete this.listeners[e]}listenerCount(e){return this.listeners[e]?.size??0}},t=class extends e{publish(e){let t=(e,...t)=>this.emit(e,...t);for(let[n,r]of Object.entries(e)){let e=r??[];t(n,...e),t(`*`,n,...e)}}subscribe(e,t,n=!1){return n?this.once(e,t):this.on(e,t)}subscribeAll(e){return this.subscribe(`*`,e)}};export{e as n,t};
@@ -1,24 +0,0 @@
1
- //#region src/event-emitter.d.ts
2
- type EventListener = (...args: never[]) => void;
3
- type Events = Record<string, EventListener>;
4
- declare class TypedEventEmitter<T extends Record<keyof T, EventListener>> {
5
- private readonly listeners;
6
- on<K extends keyof T>(event: K, listener: T[K]): () => void;
7
- off<K extends keyof T>(event: K, listener: T[K]): void;
8
- emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>): void;
9
- once<K extends keyof T>(event: K, listener: T[K]): () => void;
10
- removeAllListeners<K extends keyof T>(event?: K): void;
11
- listenerCount<K extends keyof T>(event: K): number;
12
- }
13
- //#endregion
14
- //#region src/event-bus.d.ts
15
- type UnSubscribe = () => void;
16
- type SubscribeEvent = Events;
17
- declare class EventBus<T extends SubscribeEvent = SubscribeEvent> extends TypedEventEmitter<T> {
18
- publish(event: Partial<{ [K in keyof T]: Parameters<T[K]> }>): void;
19
- subscribe<K extends keyof T>(type: K, callback: T[K], once?: boolean): UnSubscribe;
20
- subscribeAll(callback: (...args: unknown[]) => void): UnSubscribe;
21
- }
22
- //#endregion
23
- export { TypedEventEmitter as i, EventListener as n, Events as r, EventBus as t };
24
- //# sourceMappingURL=event-bus-inioRzYz.d.ts.map