@x-otto/shared 0.1.0-alpha.6 → 0.1.0-alpha.7

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
@@ -2,7 +2,7 @@
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
- `@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 11 domain-specific error classes. It also exposes a browser-safe entry point (`@x-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
 
@@ -17,7 +17,7 @@ import { createLogger } from '@x-otto/shared'
17
17
  import { CodedError, NetworkError } from '@x-otto/shared'
18
18
  import { TypedEventEmitter } from '@x-otto/shared'
19
19
  import { parseJsonc, safeStringify } from '@x-otto/shared'
20
- import { request, stream } 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
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
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";
3
2
  import { normalizePath } from "@x-otto/env";
4
3
  import { ProxyAgent } from "undici";
@@ -71,8 +70,46 @@ declare function invariant(condition: unknown, message?: string | number): asser
71
70
  type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P] };
72
71
  type DeepReadonly<T> = { readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P] };
73
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
74
86
  //#region src/logger.d.ts
75
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
+ }
76
113
  interface LoggerOptions {
77
114
  level: LogLevel;
78
115
  destination: string;
@@ -97,6 +134,13 @@ declare function setLogLevel(level: LogLevel): void;
97
134
  */
98
135
  declare function isTransportInitialized(): boolean;
99
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;
100
144
  //#endregion
101
145
  //#region src/fs-extra.d.ts
102
146
  /**
@@ -178,6 +222,8 @@ declare function getProjectDir(): string;
178
222
  declare function getHomeDir(): string;
179
223
  declare function getThirdPartyToolDir(): string;
180
224
  declare function getThirdPartyToolBinaryDir(toolName: string, version?: string): string;
225
+ //#endregion
226
+ //#region src/log-paths.d.ts
181
227
  declare function createTempLoggerPath(): string;
182
228
  /**
183
229
  * RFC-093 T4:bash 溢出日志的项目本地路径(`.otto/tool-results/bash/`)。
@@ -225,7 +271,90 @@ declare function safeStringify(value: unknown, indent?: number): string;
225
271
  */
226
272
  declare function matchesGlobPattern(pattern: string, input: string): boolean;
227
273
  //#endregion
228
- //#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
+ }
229
358
  /**
230
359
  * 设置进程级默认 HTTP 代理。传空字符串或 falsy 值清除。
231
360
  * CLI 层在 settings.load() 后及 settings.change 事件中调用。
@@ -243,12 +372,12 @@ declare function createDispatcher(proxyUrl?: string): ProxyAgent | undefined;
243
372
  * 安装/卸载**进程级全局**代理 dispatcher(RFC-288 D0b)。
244
373
  *
245
374
  * 与 `createDispatcher` 的分工:后者产出 per-request/per-client 的 dispatcher,只作用于
246
- * 显式消费它的调用方(`request`/`stream`/Anthropic SDK);本函数改写 undici 的**全局**
375
+ * 显式消费它的调用方(`stream`/Anthropic SDK);本函数改写 undici 的**全局**
247
376
  * dispatcher,使进程内**所有** `fetch()` 自动经代理——包括各包直接裸调 `fetch` 的路径
248
377
  * (OAuth token 交换、远端持久化、install/upgrade 检查、service 登录等)。
249
378
  *
250
- * 背景(RFC-288 §背景·第零类):此前代理只对 `@x-otto/shared` 的 `request()`/`stream()`
251
- * 生效,全仓仅 1 个 import;Anthropic 走官方 SDK 未传 dispatcher,Node 22 内置 fetch 又
379
+ * 背景(RFC-288 §背景·第零类):此前代理只对 `@x-otto/shared` 的 `stream()` 生效,
380
+ * 全仓仅 1 个 import;Anthropic 走官方 SDK 未传 dispatcher,Node 22 内置 fetch 又
252
381
  * 不读 `HTTP_PROXY` 环境变量——导致「面板显示已启用但 Claude 请求直连」的静默失败。
253
382
  * 选择全局兜底而非逐个调用点接线,正是因为后者的漏接线成本已被这次事故证明。
254
383
  *
@@ -259,6 +388,16 @@ declare function createDispatcher(proxyUrl?: string): ProxyAgent | undefined;
259
388
  * 与 `createDispatcher` 的降级语义一致——启动期配置错误不应让进程崩溃。
260
389
  */
261
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
262
401
  /** `probeProxy` 的失败归类——决定提示语该不该归咎代理(RFC-288 D4)。 */
263
402
  type ProxyProbeErrorKind = /** 连不上代理本身(代理地址/端口错、代理软件没开)。 */'proxy-unreachable' /** 代理通了,但目标站点不可达(DNS 失败、目标宕机)——**不归咎代理**。 */ | 'target-unreachable' /** 超时,无法区分是代理慢还是目标慢。 */ | 'timeout' /** 其它未分类错误。 */ | 'unknown';
264
403
  interface ProxyProbeResult {
@@ -293,55 +432,6 @@ interface ProxyProbeResult {
293
432
  * @param timeoutMs 超时(默认 5s)。
294
433
  */
295
434
  declare function probeProxy(targetUrl: string, timeoutMs?: number): Promise<ProxyProbeResult>;
296
- interface HttpRequestOptions {
297
- url: string;
298
- method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
299
- headers?: Record<string, string>;
300
- body?: string | Record<string, unknown>;
301
- timeout?: number;
302
- /**
303
- * SSE 流的**空闲**超时(毫秒,仅 `stream()`):每收到一个 chunk 就重置;连续 `idleTimeout` 内无任何
304
- * 字节到达即 abort。区别于 `timeout`(整请求一次性墙钟上限)——空闲超时专治「连接活着但 provider
305
- * 静默不再吐字节」的挂死(健康流远比此频繁吐 token,不受影响)。默认 120s。
306
- *
307
- * RFC-361:本值只作用于**首个 SSE 事件产出之后**;首事件之前用 `firstTokenIdleTimeout`。
308
- */
309
- idleTimeout?: number;
310
- /**
311
- * RFC-361:**首个 SSE 事件产出之前**的空闲超时(毫秒,仅 `stream()`)。默认
312
- * `max(idleTimeout, 900_000)`。
313
- *
314
- * 为什么要分段:reasoning 模型在 max/xhigh 档下 thinking 阶段可持续数分钟零可见输出,
315
- * 这是**正常行为**;而"已经开始吐字却突然静默两分钟"才是真卡死。二者故障模式完全不同,
316
- * 用同一个阈值必然顾此失彼——调大则真卡死检测被延迟,调小则误杀长思考。
317
- * 真机实证:Terminal-Bench-2 评测中 4 个长推理任务因此被误杀(占 otto 侧失败 100%)。
318
- *
319
- * 注意重置判据是**原始 TCP chunk 到达**而非 SSE 事件产出,故服务端 keepalive 注释亦可
320
- * 续命——这是有意行为(说明连接活着),不影响"首事件是否已产出"的阶段判定。
321
- */
322
- firstTokenIdleTimeout?: number;
323
- signal?: AbortSignal;
324
- proxy?: string;
325
- /**
326
- * 成功响应回调(仅 stream(),仅 HTTP 200 时触发):在 SSE 迭代开始前同步调用,携带响应 Headers。
327
- * 用于读取成功路径的 rate-limit/usage 头(如 anthropic-ratelimit-unified-*, x-ratelimit-* 等),
328
- * 不必等到错误路径才挖 header(此时 response 对象仍在作用域内)。可选,不传=零开销。
329
- */
330
- onResponse?: (headers: Headers) => void;
331
- }
332
- interface HttpResponse {
333
- status: number;
334
- headers: Headers;
335
- body: string;
336
- ok: boolean;
337
- }
338
- interface SseEvent {
339
- event?: string;
340
- data: string;
341
- id?: string;
342
- }
343
- declare function request(options: HttpRequestOptions): Promise<HttpResponse>;
344
- declare function stream(options: HttpRequestOptions): AsyncIterable<SseEvent>;
345
435
  //#endregion
346
436
  //#region src/header-utils.d.ts
347
437
  /**
@@ -353,6 +443,15 @@ declare function stream(options: HttpRequestOptions): AsyncIterable<SseEvent>;
353
443
  */
354
444
  declare function headerGet(headers: unknown, key: string): string | undefined;
355
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
356
455
  //#region src/command.d.ts
357
456
  /**
358
457
  * 危险 shell 命令检测。
@@ -431,6 +530,20 @@ declare function generateChallenge(verifier: string): string;
431
530
  declare function createPkcePair(): PkcePair;
432
531
  declare function generateState(): string;
433
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
434
547
  //#region src/oauth/loopback-server.d.ts
435
548
  interface LoopbackServerOptions {
436
549
  ports?: number[];
@@ -438,6 +551,7 @@ interface LoopbackServerOptions {
438
551
  expectedState?: string;
439
552
  host?: string;
440
553
  signal?: AbortSignal;
554
+ pageRenderer?: PageRenderer;
441
555
  }
442
556
  interface LoopbackCallback {
443
557
  code: string;
@@ -510,16 +624,34 @@ declare function probeImageDimensions(base64: string, mime: string): {
510
624
  //#endregion
511
625
  //#region src/intranet-probe.d.ts
512
626
  /**
513
- * 探测 `OTTO_INTRANET_PROBE_URLS` 候选地址中是否有任意一个可达(HEAD 请求,命中即停)。
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 握手,命中即停)。
514
646
  *
515
- * 未配置探测地址(公网源码缺省场景)时不发起任何网络请求,直接返回 `false`——避免公网
516
- * 用户启动时产生不必要的网络往返或超时等待。
647
+ * 候选 = `OTTO_INTRANET_PROBE_URLS`(env 注入)∪ `extraUrls`(消费方传入的目标资源
648
+ * 自身地址,如 provider baseUrl)。两者皆空时不发起任何探测,直接返回 `false`——
649
+ * 避免公网用户启动时产生不必要的网络往返或超时等待。
517
650
  *
518
- * 结果按 `CACHE_TTL_MS` 缓存于进程内存——两处消费方(Setup Wizard 显示判据 + 模型可用性
519
- * 闸)短时间内重复调用不会重复发起探测请求。
651
+ * `probe` 为测试注入点(缺省真实 TCP 握手),生产调用方不传。
520
652
  */
521
- declare function isIntranetReachable(force?: boolean): Promise<boolean>;
522
- /** 测试专用:清空进程内缓存,避免用例间相互污染。 */
653
+ declare function isIntranetReachable(force?: boolean, extraUrls?: readonly string[], probe?: TcpConnectProbe): Promise<boolean>;
654
+ /** 测试专用:清空进程内 per-host 缓存,避免用例间相互污染。 */
523
655
  declare function resetIntranetReachabilityCache(): void;
524
656
  //#endregion
525
657
  //#region src/safe-dispose.d.ts
@@ -720,5 +852,5 @@ declare function releaseFileLockSync(dir: string, key: string, options?: Pick<Fi
720
852
  */
721
853
  declare function waitForFileLockReleaseSync(dir: string, key: string, options?: Pick<FileLockOptions, 'timeoutMs' | 'prefix'>): boolean;
722
854
  //#endregion
723
- 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, 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 SafeDisposeOptions, 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, isIntranetReachable, 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, resetIntranetReachabilityCache, safeDispose, safeDisposeOf, safeStringify, scoreSubsequence, setDefaultProxy, setLogLevel, splitFrontmatter, startLoopbackServer, stream, sweepStaleProjectOverflowLogs, sweepStaleTempLogs, toTruncationMeta, traceStartupPhase, traceStartupPhaseSync, truncateHead, truncateLine, truncateTail, waitForFileLockRelease, waitForFileLockReleaseSync };
855
+ 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, 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 };
724
856
  //# 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,resolveIntranetProbeUrls as l}from"@x-otto/env";import{PassThrough as u}from"node:stream";import{chmod as d,mkdir as f,open as p,readFile as m,readdir as h,stat as g,unlink as _,utimes as ee}from"node:fs/promises";import{lookup as te}from"mime-types";import{join as v,resolve as ne}from"node:path";import{tmpdir as re}from"node:os";import{createHash as ie,randomBytes as ae,randomUUID as y}from"node:crypto";import{parse as oe,printParseErrorCode as se}from"jsonc-parser";import ce from"safe-stable-stringify";import{createParser as le}from"eventsource-parser";import{EnvHttpProxyAgent as ue,ProxyAgent as de,getGlobalDispatcher as fe,setGlobalDispatcher as pe}from"undici";import{createServer as me}from"node:http";import{spawn as he}from"node:child_process";import{chmodSync as ge,closeSync as _e,mkdirSync as ve,openSync as ye,readFileSync as be,statSync as b,unlinkSync as xe,writeSync as Se}from"node:fs";var x=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}},S=class extends x{},Ce=class extends x{},C=class extends x{},we=class extends x{},Te=class extends x{};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 w(e,t){if(typeof e==`function`)w(e(),t);else if(!e)throw new De(t)}const Oe=new Set([`trace`,`debug`,`info`,`warn`,`error`,`fatal`]);function T(e){return Oe.has(e)?e:`info`}const ke=(()=>{try{return n(import.meta.url)}catch{return}})();function Ae(){if(!ke)return!1;try{return ke.resolve(`pino-pretty`),!0}catch{return!1}}function je(e){return e===`stdout`?1:e===`stderr`?2:e}function Me(){return process.env.OTTO_LOG_STDERR===`0`}function Ne(){return process.env.OTTO_LOG_FILE||`${process.env.HOME||process.env.USERPROFILE||`.`}/.otto/logs/otto.log`}function Pe(e,t){let n=Ae(),r=Me(),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:Ne(),mkdir:!0},level:t}]:[i];let a={target:`pino/file`,options:{destination:je(e),mkdir:!0},level:t};return r?[a]:[a,i]}const E=new u,Fe=new t;E.on(`data`,e=>{try{Fe.publish({log:[JSON.parse(e.toString())]})}catch(t){console.warn(`Failed to parse log chunk for listener:`,e.toString(),t)}});function Ie(e){return Fe.subscribe(`log`,e)}let D=null,O=T(a);const Le=new Set;function Re(e){if(Oe.has(e)){O=e,D&&(D.level=e);for(let t of Le)t.level=e}}function ze(){return k!==null}let k=null;function Be(e,t){return globalThis.__miniv8_wasm===void 0?{write(n){k??=r.transport({targets:Pe(e,t)}),k.write(n)}}:{write:e=>{E.write(e)}}}function Ve(e,t){return D??=r({level:e},r.multistream([{level:e,stream:E},{level:e,stream:Be(t,e)}])),O=T(D.level),D}function He(e,t){Ve(t?.level??T(a),t?.destination??i),w(D,`Root logger is not initialized`);let n=D.child({name:e},{level:t?.level??O});return Le.add(n),n}const A=10*1024*1024;async function Ue(e){return te(e)||`application/octet-stream`}async function We(e){await f(e,{recursive:!0})}async function Ge(e){try{return await g(e),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function Ke(e){return(await g(e)).isDirectory()}async function qe(e){return(await g(e)).isFile()}async function Je(e,t=A){let n=await g(e);if(n.size<=t)return{content:await m(e,`utf-8`),truncated:!1,totalBytes:n.size};let r=await p(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 Ye(e){let{content:t,...n}=e;return n}function Xe(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:`${(e/(1024*1024)).toFixed(1)}MB`}function j(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 Ze(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 fe}from"undici";import{createServer as pe}from"node:http";import{spawn as me}from"node:child_process";import{connect as he}from"node:net";import{chmodSync as ge,closeSync as _e,mkdirSync as ve,openSync as ye,readFileSync as be,statSync as xe,unlinkSync as Se,writeSync as Ce}from"node:fs";var y=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}},b=class extends y{},we=class extends y{},x=class extends y{},Te=class extends y{},Ee=class extends y{};const{setPrototypeOf:De=function(e,t){return e.__proto__=t,e}}=Object;var Oe=class e extends Error{framesToPop=1;name=`Invariant Violation`;constructor(t=`Invariant Violation`){super(typeof t==`number`?`Invariant Violation: ${t}`:t),De(this,e.prototype)}};function S(e,t){if(typeof e==`function`)S(e(),t);else if(!e)throw new Oe(t)}var ke=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}},Ae=class extends ke{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 je=new Set([`trace`,`debug`,`info`,`warn`,`error`,`fatal`]);function C(e){return je.has(e)?e:`info`}const Me=(()=>{try{return e(import.meta.url)}catch{return}})();function Ne(){if(!Me)return!1;try{return Me.resolve(`pino-pretty`),!0}catch{return!1}}function Pe(e){return e===`stdout`?1:e===`stderr`?2:e}function Fe(){return process.env.OTTO_LOG_STDERR===`0`}function Ie(){return process.env.OTTO_LOG_FILE||`${process.env.HOME||process.env.USERPROFILE||`.`}/.otto/logs/otto.log`}function Le(e,t){let n=Ne(),r=Fe(),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:Ie(),mkdir:!0},level:t}]:[i];let a={target:`pino/file`,options:{destination:Pe(e),mkdir:!0},level:t};return r?[a]:[a,i]}const w=new c,Re=new Ae;w.on(`data`,e=>{try{Re.publish({log:[JSON.parse(e.toString())]})}catch(t){console.warn(`Failed to parse log chunk for listener:`,e.toString(),t)}});function ze(e){return Re.subscribe(`log`,e)}let T=null,E=C(r);const D=new Set;function Be(e){if(je.has(e)){E=e,T&&(T.level=e);for(let t of D)t.level=e}}function Ve(){return O!==null}let O=null;function He(e,n){return globalThis.__miniv8_wasm===void 0?{write(r){O??=t.transport({targets:Le(e,n)}),O.write(r)}}:{write:e=>{w.write(e)}}}function k(e,n){return T??=t({level:e},t.multistream([{level:e,stream:w},{level:e,stream:He(n,e)}])),E=C(T.level),T}function A(e,t){k(t?.level??C(r),t?.destination??n),S(T,`Root logger is not initialized`);let i=T.child({name:e},{level:t?.level??E});return D.add(i),i}const Ue={log(e,t,r){k(E,n),S(T,`Root logger is not initialized`),T[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){Be(e)}},We=10*1024*1024;async function Ge(e){return te(e)||`application/octet-stream`}async function Ke(e){await u(e,{recursive:!0})}async function qe(e){try{return await m(e),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function Je(e){return(await m(e)).isDirectory()}async function Ye(e){return(await m(e)).isFile()}async function Xe(e,t=We){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 Ze(e){let{content:t,...n}=e;return n}function Qe(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:`${(e/(1024*1024)).toFixed(1)}MB`}function j(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 $e(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 j({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 j({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 j({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`),j({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 Qe(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 j({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 j({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=$e(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`),j({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 $e(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 et(e,t){return e.length<=t?{text:e,wasTruncated:!1}:{text:`${e.slice(0,t)}... [truncated]`,wasTruncated:!0}}const M=`otto-coding-`,N=`.log`;function P(){return s}function tt(){return o}function nt(){return ne(tt(),`tools`)}function rt(e,t=``){return ne(nt(),...t?[e,t]:[e])}function it(){let e=y();return v(re(),`${M}${e}${N}`)}function at(){let e=y();return v(P(),`tool-results`,`bash`,`${M}${e}${N}`)}async function ot(e,t){return ct(re(),e,t)}async function st(e,t){return ct(v(P(),`tool-results`,`bash`),e,t)}async function ct(e,t,n){let r;try{r=await h(e)}catch{return 0}let i=r.filter(e=>e.startsWith(M)&&e.endsWith(N)),a=0;return await Promise.all(i.map(async r=>{let i=v(e,r);try{n-(await g(i)).mtimeMs>t&&(await _(i),a++)}catch{}})),a}function lt(e){let t=[],n=oe(e,t,{allowTrailingComma:!0,disallowComments:!1,allowEmptyContent:!1});if(t.length>0){let e=t[0],n=e?se(e.error):`UnknownError`;throw Error(`Invalid JSONC: ${n}`)}return n}function ut(e,t){return ce(e,void 0,t)??`null`}function dt(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 F;function ft(e){F=e||void 0}function pt(){return F}function I(e){let t=e||F;if(t)try{return new de({uri:t})}catch{return}}let L;function mt(e){if(L===void 0&&(L=fe()),!e){pe(L);return}try{pe(new ue({httpProxy:e,httpsProxy:e,noProxy:`localhost,127.0.0.1,[::1]`}))}catch{}}async function ht(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:gt(e),errorMessage:i}}finally{clearTimeout(i)}}function gt(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 _t(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=I(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 S(`HTTP request aborted or timed out`,{code:`NETWORK_TIMEOUT`,cause:e}):new S(`HTTP request failed`,{code:`NETWORK_ERROR`,cause:e instanceof Error?e:Error(String(e))})}finally{clearTimeout(l)}}const vt={kind:`idle`},yt={kind:`wallclock`};function bt(e,t){return e?t.idleTimeout:t.firstTokenIdleTimeout}function xt(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 St(e){let t=e.signal.reason;return typeof t==`object`&&t&&`kind`in t?t.kind:`external`}async function*Ct(e){let{url:t,method:n=`POST`,headers:r={},body:i,signal:a}=e,{timeout:o,idleTimeout:s,firstTokenIdleTimeout:c}=xt(e),l=new AbortController,u=a?AbortSignal.any([a,l.signal]):l.signal,d=setTimeout(()=>l.abort(yt),o),f,p=!1,m=()=>{f&&clearTimeout(f),f=setTimeout(()=>l.abort(vt),bt(p,{idleTimeout:s,firstTokenIdleTimeout:c}))},h;try{let a=I(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 S(`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 S(`Response body is empty`,{code:`BAD_RESPONSE`});h=s.body.getReader();let c=new TextDecoder,l=[],d=le({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 S?e:l.signal.aborted||e instanceof DOMException&&e.name===`AbortError`?new S(`Stream aborted or timed out`,{code:`TIMEOUT`,cause:e instanceof Error?e:Error(String(e)),abortKind:St(l)}):new S(`Stream request failed`,{code:`NETWORK_ERROR`,cause:e instanceof Error?e:Error(String(e))})}finally{clearTimeout(d),f&&clearTimeout(f),h&&h.cancel().catch(()=>{})}}function wt(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 Tt=[{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 Et(e){let t=e.trim();if(!t)return{safe:!0};for(let e of Tt)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 Dt(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 R={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 Ot(e){return R[e]}const kt=Object.fromEntries(Object.entries(R).reverse().map(([e,t])=>[t,e]));function At(e){return kt[e]??e}function jt(e){return e.map(e=>R[e]??e)}function z(e){return e.toString(`base64url`)}function B(){return z(ae(32))}function V(e){return z(ie(`sha256`).update(e).digest())}function Mt(){let e=B();return{verifier:e,challenge:V(e),method:`S256`}}function Nt(){return z(ae(16))}const Pt=[1455,1457];function Ft(){return`<span class="otto-wordmark">OTTO</span>`}function It(){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 Lt(){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 H(e){return e.replace(/[&<>"']/g,e=>({"&":`&amp;`,"<":`&lt;`,">":`&gt;`,'"':`&quot;`,"'":`&#39;`})[e]??e)}function Rt(e,t,n,r){let i=e===`success`?It():Lt();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>${H(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
- ${Ft()}
79
- <section class="card">
80
- <div class="status-icon">${i}</div>
81
- <p class="kicker">${e===`success`?`Authentication`:`Error`}</p>
82
- <h1 id="title">${H(t)}</h1>
83
- <p class="desc">${H(n)}</p>
84
- <div class="separator"></div>
85
- <p class="footer">${H(r)}</p>
86
- </section>
87
- </main>
88
- </body>
89
- </html>`}const zt=Rt(`success`,`Login complete`,`You can close this window and return to the terminal.`,`Otto Auth`);function U(e){return Rt(`error`,`Login failed`,e,`Otto Auth`)}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 Bt(e,t,n){return new Promise((r,i)=>{if(n.length===0){i(new C(`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 C(`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 Vt(e,t){e.value||(e.value=!0,t())}async function Ht(e={}){let{ports:t=Pt,callbackPath:n=`/auth/callback`,expectedState:r,host:i=`127.0.0.1`,signal:a}=e;if(a?.aborted)throw new C(`Login cancelled`,{code:`OAUTH_CANCELLED`});let o,s,c={value:!1},l=new Promise((e,t)=>{o=t=>Vt(c,()=>e(t)),s=e=>Vt(c,()=>t(e))});l.catch(()=>{});let u=0,d=()=>`http://${i}:${u}`,f=me((e,t)=>{let i;try{i=new URL(e.url??`/`,d())}catch{W(t,400,U(`Invalid request URL`)),s(new C(`Invalid callback URL`,{code:`OAUTH_INVALID_REQUEST`}));return}if(i.pathname!==n){W(t,404,U(`Not found`));return}let a=i.searchParams.get(`error`);if(a){let e=i.searchParams.get(`error_description`)??``;W(t,400,U(e||a)),s(new C(`Authorization failed: ${a}${e?`: ${e}`:``}`,{code:`OAUTH_AUTHORIZE_FAILED`}));return}let c=i.searchParams.get(`code`);if(!c){W(t,400,U(`Missing authorization code.`)),s(new C(`Callback missing authorization code.`,{code:`OAUTH_AUTHORIZE_FAILED`}));return}let l=i.searchParams.get(`state`)??void 0;if(r!==void 0&&l!==r){W(t,400,U(`State mismatch.`)),s(new C(`OAuth state mismatch (possible CSRF).`,{code:`OAUTH_STATE_MISMATCH`}));return}W(t,200,zt),o({code:c,state:l})});u=await Bt(f,i,t);let p=!1,m=()=>new Promise(e=>{if(p){e();return}p=!0,s(new C(`Login cancelled (server closed)`,{code:`OAUTH_CANCELLED`})),f.close(()=>e()),f.closeAllConnections?.()});return a&&a.addEventListener(`abort`,()=>{s(new C(`Login cancelled`,{code:`OAUTH_CANCELLED`})),m()},{once:!0}),{port:u,redirectUri:`${d()}${n}`,waitForCallback:()=>l,close:m}}function Ut(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 he(n[0],[...n[1]],{detached:!0,stdio:`ignore`}).unref(),!0}catch{return!1}}const Wt=/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;function Gt(e){let t=Wt.exec(e);return t?{fields:Kt(t[1]??``),body:(t[2]??``).trim()}:null}function Kt(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 qt(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 Jt=8e3,Yt=5*1024*1024,Xt=Buffer.from([137,80,78,71,13,10,26,10]),Zt=Buffer.from(`GIF8`,`ascii`);function Qt(e,t){if(!(!e||e.length===0)&&!(e.length>7340032))try{return $t(Buffer.from(e.slice(0,128),`base64`),e,t)}catch{return}}function $t(e,t,n){let r=n.toLowerCase();if(r.includes(`png`))return en(e);if(r.includes(`jpeg`)||r.includes(`jpg`))return tn(Buffer.from(t,`base64`));if(r.includes(`gif`))return nn(e);if(r.includes(`webp`))return rn(e);if(e.length>=8&&e.subarray(0,8).equals(Xt))return en(e);if(e.length>=2&&e.readUInt16BE(0)===65496)return tn(Buffer.from(t,`base64`));if(e.length>=4&&e.subarray(0,4).equals(Zt))return nn(e);if(e.length>=12&&e.readUInt32BE(0)===1380533830&&e.readUInt32BE(8)===1464156752)return rn(e)}function en(e){if(!(e.length<24)&&e.subarray(0,8).equals(Xt))try{return e.toString(`ascii`,12,16)===`IHDR`?{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}:void 0}catch{return}}function tn(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 nn(e){if(!(e.length<10)&&e.subarray(0,4).equals(Zt))try{return{width:e.readUInt16LE(6),height:e.readUInt16LE(8)}}catch{return}}function rn(e){if(!(e.length<16)&&!(e.readUInt32BE(0)!==1380533830||e.readUInt32BE(8)!==1464156752))try{let t=e.toString(`ascii`,12,16);return t===`VP8X`?an(e):t===`VP8 `?on(e):t===`VP8L`?sn(e):void 0}catch{return}}function an(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 on(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 sn(e){if(e.length<25||e[20]!==47)return;let t=e.readUInt32LE(21);return{width:(t&16383)+1,height:(t>>14&16383)+1}}let G;async function cn(e=!1){let t=Date.now();if(!e&&G&&t-G.at<3e5)return G.reachable;let n=l();if(n.length===0)return G={reachable:!1,at:t},!1;for(let e of n){let n=new AbortController,r=setTimeout(()=>n.abort(),2e3);try{if(await fetch(e,{method:`HEAD`,signal:n.signal}))return G={reachable:!0,at:t},!0}catch{}finally{clearTimeout(r)}}return G={reachable:!1,at:t},!1}function ln(){G=void 0}async function un(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 dn(e,t={}){let n=e?.dispose;return typeof n==`function`?un(()=>n.call(e),t):!0}const K=process.env.OTTO_STARTUP_TRACE===`1`,fn=K?performance.now():0;function pn(e){if(!K)return;let t=performance.now()-fn;process.stderr.write(`[startup] ${e}: ${t.toFixed(1)}ms\n`)}async function mn(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 hn(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 gn(){return K}const _n=/[/\-_\s.]/;function vn(e){return e!==void 0&&_n.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:vn(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 yn(e,t){return q(e,t)?.score??-1}function bn(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 xn(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=xn(a,t.text,t.aliases);if(!e)continue;let n=q(a,t.text);o.push({item:t,score:e.score,highlightRanges:bn(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 Sn(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 Cn(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=He(`@x-otto/shared:file-lock`),wn=15e3;function Tn(e){return Math.floor(e/3)}const X=new Map,Z=new Map;function Q(e,t,n=`file-lock`){return v(e,`.${n}-${t}.lock`)}function En(e,t){Dn(e);let n=setInterval(()=>{let t=new Date;ee(e,t,t).catch(()=>{})},Tn(t));n.unref?.(),X.set(e,n)}function Dn(e){let t=X.get(e);t&&(clearInterval(t),X.delete(e))}async function On(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=async(n=!1)=>{try{let e=await p(i,`wx`),t=y();await e.writeFile(t),await e.close(),Z.set(i,t);try{await d(i,384)}catch{}return En(i,r),!0}catch(r){if(r.code===`EEXIST`)return!1;if(r.code===`ENOENT`&&!n){try{await f(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 g(i);if(Date.now()-e.mtimeMs>r)return await _(i).catch(()=>{}),await a()}catch{return await a()}return!1}async function kn(e,t,n={}){let r=Q(e,t,n.prefix);Dn(r);let i=Z.get(r);if(Z.delete(r),i!==void 0)try{if(await m(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 _(r).catch(()=>{})}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 g(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=ye(i,`wx`)}catch(r){let i=r.code;if(i===`EEXIST`)return!1;if(i===`ENOENT`&&!n){try{ve(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{_e(r)}$.set(i,o);try{ge(i,384)}catch{}return!0};if(a())return!0;try{let e=b(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(be(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{b(i)}catch{return!0}Atomics.wait(o,0,0,100)}return!1}export{Jt as API_IMAGE_DIMENSION_LIMIT_PX,we as AgentError,R as CLAUDE_TO_OTTO_TOOL,x as CodedError,wn as DEFAULT_FILE_LOCK_TIMEOUT_MS,t as EventBus,De as InvariantError,A as LARGE_FILE_BYTE_THRESHOLD,Te as McpError,S as NetworkError,C as OAuthError,Yt as PROVIDER_IMAGE_INLINE_MAX_BYTES,Ce as ProviderError,e as TypedEventEmitter,On as acquireFileLock,jn as acquireFileLockSync,Ie as attachLogListener,Dt as buildAllowedEnv,Et as checkShellCommand,I as createDispatcher,He as createLogger,Mt as createPkcePair,at as createProjectOverflowLogPath,it as createTempLoggerPath,Ge as exists,Xe as formatBytes,V as generateChallenge,Nt as generateState,B as generateVerifier,pt as getDefaultProxy,tt as getHomeDir,P as getProjectDir,rt as getThirdPartyToolBinaryDir,nt as getThirdPartyToolDir,wt as headerGet,mt as installGlobalProxyDispatcher,w as invariant,Ke as isDirectory,qe as isFile,cn as isIntranetReachable,gn as isStartupTraceEnabled,ze as isTransportInitialized,Q as lockPath,jt as mapClaudeAgentToolNames,Ot as mapClaudeToolName,pn as markStartupPhase,q as matchSubsequence,dt as matchesGlobPattern,Ue as mime,We as mkdirp,c as normalizePath,Ut as openBrowser,At as ottoToClaudeToolName,Kt as parseFrontmatterFields,qt as parseFrontmatterList,lt as parseJsonc,bn as positionsToHighlightRanges,Qt as probeImageDimensions,ht as probeProxy,J as rankCandidates,Cn as rankLabeledItems,Sn as rankNamedItems,Je as readFileCapped,kn as releaseFileLock,Mn as releaseFileLockSync,_t as request,ln as resetIntranetReachabilityCache,un as safeDispose,dn as safeDisposeOf,ut as safeStringify,yn as scoreSubsequence,ft as setDefaultProxy,Re as setLogLevel,Gt as splitFrontmatter,Ht as startLoopbackServer,Ct as stream,st as sweepStaleProjectOverflowLogs,ot as sweepStaleTempLogs,Ye as toTruncationMeta,mn as traceStartupPhase,hn as traceStartupPhaseSync,Ze as truncateHead,et as truncateLine,Qe as truncateTail,An as waitForFileLockRelease,Nn 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 et(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 j({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 j({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=tt(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`),j({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 tt(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 nt(e,t){return e.length<=t?{text:e,wasTruncated:!1}:{text:`${e.slice(0,t)}... [truncated]`,wasTruncated:!0}}function M(){return a}function rt(){return i}function it(){return _(rt(),`tools`)}function at(e,t=``){return _(it(),...t?[e,t]:[e])}const N=`otto-coding-`,P=`.log`;function ot(){let e=v();return g(ne(),`${N}${e}${P}`)}function st(){let e=v();return g(M(),`tool-results`,`bash`,`${N}${e}${P}`)}async function ct(e,t){return ut(ne(),e,t)}async function lt(e,t){return ut(g(M(),`tool-results`,`bash`),e,t)}async function ut(e,t,n){let r;try{r=await p(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=g(e,r);try{n-(await m(i)).mtimeMs>t&&(await h(i),a++)}catch{}})),a}function dt(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 ft(e,t){return se(e,void 0,t)??`null`}function pt(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 F;function mt(e){F=e||void 0}function I(){return F}function L(e){let t=e||F;if(t)try{return new ue({uri:t})}catch{return}}let R;function z(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{}}const ht={getUrl(){return I()},installGlobal(){z(F)},uninstallGlobal(){z(void 0)}},gt={kind:`idle`},_t={kind:`wallclock`};function vt(e,t){return e?t.idleTimeout:t.firstTokenIdleTimeout}function yt(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 bt(e){let t=e.signal.reason;return typeof t==`object`&&t&&`kind`in t?t.kind:`external`}async function*xt(e){let{url:t,method:n=`POST`,headers:r={},body:i,signal:a}=e,{timeout:o,idleTimeout:s,firstTokenIdleTimeout:c}=yt(e),l=new AbortController,u=a?AbortSignal.any([a,l.signal]):l.signal,d=setTimeout(()=>l.abort(_t),o),f,p=!1,m=()=>{f&&clearTimeout(f),f=setTimeout(()=>l.abort(gt),vt(p,{idleTimeout:s,firstTokenIdleTimeout:c}))},h;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: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 b(`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 b(`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 b?e:l.signal.aborted||e instanceof DOMException&&e.name===`AbortError`?new b(`Stream aborted or timed out`,{code:`TIMEOUT`,cause:e instanceof Error?e:Error(String(e)),abortKind:bt(l)}):new b(`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 St(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:Ct(e),errorMessage:i}}finally{clearTimeout(i)}}function Ct(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 wt(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 Tt=[{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 Et(e){let t=e.trim();if(!t)return{safe:!0};for(let e of Tt)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 Dt(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 B={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 Ot(e){return B[e]}const kt=Object.fromEntries(Object.entries(B).reverse().map(([e,t])=>[t,e]));function At(e){return kt[e]??e}function jt(e){return e.map(e=>B[e]??e)}function V(e){return e.toString(`base64url`)}function Mt(){return V(ie(32))}function H(e){return V(re(`sha256`).update(e).digest())}function Nt(){let e=Mt();return{verifier:e,challenge:H(e),method:`S256`}}function Pt(){return V(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 x(`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 x(`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 x(`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=pe((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 x(`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 x(`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 x(`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 x(`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 x(`Login cancelled (server closed)`,{code:`OAUTH_CANCELLED`})),p.close(()=>e()),p.closeAllConnections?.()});return a&&a.addEventListener(`abort`,()=>{c(new x(`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 me(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=he({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=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 An(e,t,n={}){let r=n.timeoutMs??15e3,i=Q(e,t,n.prefix),a=(n=!1)=>{let r;try{r=ye(i,`wx`)}catch(r){let i=r.code;if(i===`EEXIST`)return!1;if(i===`ENOENT`&&!n){try{ve(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{Ce(r,o)}finally{_e(r)}$.set(i,o);try{ge(i,384)}catch{}return!0};if(a())return!0;try{let e=xe(i);if(Date.now()-e.mtimeMs>r){try{Se(i)}catch{}return a()}}catch{return a()}return!1}function jn(e,t,n={}){let r=Q(e,t,n.prefix),i=$.get(r);if($.delete(r),i!==void 0)try{if(be(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{Se(r)}catch{}}function Mn(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{xe(i)}catch{return!0}Atomics.wait(o,0,0,100)}return!1}export{Gt as API_IMAGE_DIMENSION_LIMIT_PX,Te as AgentError,B as CLAUDE_TO_OTTO_TOOL,y as CodedError,Cn as DEFAULT_FILE_LOCK_TIMEOUT_MS,Ae as EventBus,Oe as InvariantError,We as LARGE_FILE_BYTE_THRESHOLD,Ee as McpError,b as NetworkError,x as OAuthError,Kt as PROVIDER_IMAGE_INLINE_MAX_BYTES,we as ProviderError,ke as TypedEventEmitter,Dn as acquireFileLock,An as acquireFileLockSync,ze as attachLogListener,Dt as buildAllowedEnv,Et as checkShellCommand,L as createDispatcher,A as createLogger,Nt as createPkcePair,st as createProjectOverflowLogPath,ot as createTempLoggerPath,Ue as defaultLoggerSink,ht as defaultProxyConfig,qe as exists,Qe as formatBytes,H as generateChallenge,Pt as generateState,Mt as generateVerifier,I as getDefaultProxy,rt as getHomeDir,M as getProjectDir,at as getThirdPartyToolBinaryDir,it as getThirdPartyToolDir,wt as headerGet,z as installGlobalProxyDispatcher,S as invariant,Je as isDirectory,Ye as isFile,sn as isIntranetReachable,hn as isStartupTraceEnabled,Ve as isTransportInitialized,on as isUrlReachable,Q as lockPath,jt as mapClaudeAgentToolNames,Ot as mapClaudeToolName,fn as markStartupPhase,q as matchSubsequence,pt as matchesGlobPattern,Ge as mime,Ke as mkdirp,o as normalizePath,Bt as openBrowser,At as ottoToClaudeToolName,Ut as parseFrontmatterFields,Wt as parseFrontmatterList,dt as parseJsonc,yn as positionsToHighlightRanges,Yt as probeImageDimensions,St as probeProxy,J as rankCandidates,Sn as rankLabeledItems,xn as rankNamedItems,Xe as readFileCapped,On as releaseFileLock,jn as releaseFileLockSync,cn as resetIntranetReachabilityCache,ln as safeDispose,un as safeDisposeOf,ft as safeStringify,vn as scoreSubsequence,mt as setDefaultProxy,Be as setLogLevel,Ht as splitFrontmatter,zt as startLoopbackServer,xt as stream,lt as sweepStaleProjectOverflowLogs,ct as sweepStaleTempLogs,Ze as toTruncationMeta,pn as traceStartupPhase,mn as traceStartupPhaseSync,$e as truncateHead,nt as truncateLine,et as truncateTail,kn as waitForFileLockRelease,Mn as waitForFileLockReleaseSync};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-otto/shared",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.7",
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": {
@@ -31,13 +27,13 @@
31
27
  "safe-stable-stringify": "2.5.0",
32
28
  "undici": "8.7.0",
33
29
  "uuid": "13.0.0",
34
- "@x-otto/env": "0.1.0-alpha.6"
30
+ "@x-otto/env": "0.1.0-alpha.7"
35
31
  },
36
32
  "devDependencies": {
37
33
  "@types/mime-types": "3.0.1"
38
34
  },
39
35
  "scripts": {
40
- "build": "tsdown && rm -f dist/*.map && sed -i '' '\\|//# sourceMappingURL=|d' dist/*.js dist/browser/index.js 2>/dev/null; true",
36
+ "build": "tsdown && rm -f dist/*.map && sed -i '' '\\|//# sourceMappingURL=|d' dist/*.js 2>/dev/null; true",
41
37
  "typecheck:project": "tsc -p tsconfig.json --noEmit",
42
38
  "typecheck:file": "tsc src/index.ts --noEmit --target ES2024 --module ESNext --moduleResolution bundler --esModuleInterop --skipLibCheck",
43
39
  "typecheck": "tsc --noEmit",
@@ -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