@x-otto/plugin 0.1.0-alpha.4 → 0.1.0-alpha.6
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 +10 -5
- package/dist/index.d.ts +123 -3
- package/dist/index.js +2 -2
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -53,6 +53,8 @@ src/
|
|
|
53
53
|
discovery.ts # discoverPlugins three-tier directory scanning
|
|
54
54
|
contributions.ts # PluginContributions unified contribution vocabulary type
|
|
55
55
|
contribution-points.ts # contribution point registry (POINT constants)
|
|
56
|
+
points-extension.ts # per-domain contribution point constants
|
|
57
|
+
points-shell.ts # per-domain contribution point constants
|
|
56
58
|
contribution-resolver.ts # contribution point resolver
|
|
57
59
|
context-keys.ts # context key evaluation
|
|
58
60
|
contribution-dispatch.ts # contribution point dispatcher
|
|
@@ -60,7 +62,8 @@ src/
|
|
|
60
62
|
dependency-graph.ts # topoSortPlugins topological sort
|
|
61
63
|
engine-compat.ts # engine version compatibility gate
|
|
62
64
|
api-version.ts # PLUGIN_API_VERSION constant
|
|
63
|
-
|
|
65
|
+
capabilities.ts # PLUGIN_CAPABILITIES + HIGH_RISK_CAPABILITIES risk classification (single source)
|
|
66
|
+
plugin-trust.ts # trust model (executable surface scan + per-capability grant)
|
|
64
67
|
trust-store.ts # trust allowlist persistence
|
|
65
68
|
i18n-registry.ts # internationalization entry registry
|
|
66
69
|
input/ # sigil input system (builtin/file/registry)
|
|
@@ -78,13 +81,15 @@ tests/ # test files
|
|
|
78
81
|
|
|
79
82
|
## Trust Model
|
|
80
83
|
|
|
81
|
-
High-risk capability list (`HIGH_RISK_CAPABILITIES
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
High-risk capability list (`HIGH_RISK_CAPABILITIES`, derived in `capabilities.ts` from the per-capability risk
|
|
85
|
+
classification): provider / tools / tui.renderer / network / wire-protocol / a2ui.component / panel.backend /
|
|
86
|
+
mcp.server / input.resolver / a2ui.renderer / agent.dispatch / service / session.read / llm.complete / user.ask.
|
|
87
|
+
When an already-trusted plugin is upgraded and declares one of these capabilities for the first time, it must be
|
|
88
|
+
re-confirmed.
|
|
84
89
|
|
|
85
90
|
## Dependencies
|
|
86
91
|
|
|
87
|
-
- Internal: `@x-otto/env`, `@x-otto/interchange`, `@x-otto/provider`, `@x-otto/shared`
|
|
92
|
+
- Internal: `@x-otto/env`, `@x-otto/hook-contracts`, `@x-otto/interchange`, `@x-otto/provider`, `@x-otto/shared`
|
|
88
93
|
- External: `zod`, `semver`
|
|
89
94
|
|
|
90
95
|
## Related
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { TypedEventEmitter } from "@x-otto/shared";
|
|
|
3
3
|
import * as _$_x_otto_interchange0 from "@x-otto/interchange";
|
|
4
4
|
import { PluginInputRegistry, SigilEntry, SigilKind, SigilPrefix, SigilProvider, SigilSource, sigilOf } from "@x-otto/interchange";
|
|
5
5
|
import * as _$_x_otto_provider0 from "@x-otto/provider";
|
|
6
|
+
import { HookAbortSignal, HookPayloadMap, WaterfallTiming } from "@x-otto/hook-contracts";
|
|
6
7
|
|
|
7
8
|
//#region src/capabilities.d.ts
|
|
8
9
|
/**
|
|
@@ -1447,6 +1448,10 @@ declare const scriptsSchema: z.ZodObject<{
|
|
|
1447
1448
|
}, z.core.$catchall<z.ZodString>>;
|
|
1448
1449
|
type PluginScripts = z.infer<typeof scriptsSchema>;
|
|
1449
1450
|
declare const manifestSchema: z.ZodObject<{
|
|
1451
|
+
plane: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
|
|
1452
|
+
host: "host";
|
|
1453
|
+
preset: "preset";
|
|
1454
|
+
}>>>;
|
|
1450
1455
|
id: z.ZodString;
|
|
1451
1456
|
name: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
1452
1457
|
version: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
@@ -2011,6 +2016,13 @@ declare const manifestSchema: z.ZodObject<{
|
|
|
2011
2016
|
}> | undefined, Record<string, unknown>>>>>;
|
|
2012
2017
|
}, z.core.$strip>;
|
|
2013
2018
|
type PluginManifest = z.infer<typeof manifestSchema>;
|
|
2019
|
+
/** RFC-327 修订 D2(M2):manifest 显式 plane 声明('host' | 'preset')。 */
|
|
2020
|
+
type PluginManifestPlane = 'host' | 'preset';
|
|
2021
|
+
/**
|
|
2022
|
+
* RFC-327 修订 D2(M2):解析 manifest 的 plane 声明。缺省 = `'preset'`
|
|
2023
|
+
* (存量插件零行为变化——绝大多数插件贡献是会话/workspace 级,host 是刻意声明)。
|
|
2024
|
+
*/
|
|
2025
|
+
declare function resolveManifestPlane(manifest: PluginManifest): PluginManifestPlane;
|
|
2014
2026
|
/** 有效激活事件:字段缺失或空 → `['onStartup']`(存量插件零行为变化,RFC-105 D7)。 */
|
|
2015
2027
|
declare function resolveActivationEvents(manifest: PluginManifest): string[];
|
|
2016
2028
|
/**
|
|
@@ -2355,6 +2367,10 @@ type PreToolUseDecision = {
|
|
|
2355
2367
|
* RFC-129 D1 边界纪律:新增的 9 个字段全部是 observer-only——不开放任何可变换消息/参数的
|
|
2356
2368
|
* interceptor timing(`messages.transform`/`chat.params`/`system.prompt.transform`/
|
|
2357
2369
|
* `chat.message.after` 均不对插件开放,见 RFC-129 §1.2 非目标 + 放弃方案)。
|
|
2370
|
+
*
|
|
2371
|
+
* RFC-327 修订 D1(M1)显式修订:上述边界纪律对 `waterfall` 贡献面**不再成立**——
|
|
2372
|
+
* waterfall 是**新的贡献面**(不是 observer 改 interceptor),经受控白名单 timing
|
|
2373
|
+
* (`WaterfallTiming`)开放三个受控中间产物的改写点,见下方 `waterfall` 字段说明。
|
|
2358
2374
|
*/
|
|
2359
2375
|
interface PluginHooks {
|
|
2360
2376
|
preToolUse?: (ctx: ToolUseContext) => PreToolUseDecision | void | Promise<PreToolUseDecision | void>;
|
|
@@ -2424,6 +2440,13 @@ interface PluginHooks {
|
|
|
2424
2440
|
taskId: string;
|
|
2425
2441
|
error: string;
|
|
2426
2442
|
}) => void | Promise<void>;
|
|
2443
|
+
/**
|
|
2444
|
+
* 任务被取消时触发(`task.cancelled`,08-13 终局 review 观察项:纯 observer,
|
|
2445
|
+
* 对自迭代感知"子任务被取消"有真实价值——被取消的任务不该重复派发)。
|
|
2446
|
+
*/
|
|
2447
|
+
taskCancelled?: (ctx: {
|
|
2448
|
+
taskId: string;
|
|
2449
|
+
}) => void | Promise<void>;
|
|
2427
2450
|
/**
|
|
2428
2451
|
* 任务(子 agent task)建档时触发(`task.created`),先于 `task.started` 触发。
|
|
2429
2452
|
* 此时 agent 归属尚未确定(`TaskHookInfo` 无 subagent 字段),仅 taskId 可用。
|
|
@@ -2436,7 +2459,38 @@ interface PluginHooks {
|
|
|
2436
2459
|
taskId: string;
|
|
2437
2460
|
agent: string;
|
|
2438
2461
|
}) => void | Promise<void>;
|
|
2462
|
+
/**
|
|
2463
|
+
* RFC-327 修订 D1(M1):waterfall 贡献面——插件经受控白名单 timing 改写主循环中间产物。
|
|
2464
|
+
*
|
|
2465
|
+
* 语义对齐 HookRegistry 既有 interceptor(priority 序 → 共享可变 output → 后续可见前者
|
|
2466
|
+
* 改写),**外加** serial-with-early-bail:任一 handler 返回 `{ abort: true }` →
|
|
2467
|
+
* 终止该 timing 的整条 hook 链,output 保留已累积值(后续引擎 hook 不执行)。
|
|
2468
|
+
*
|
|
2469
|
+
* 安全边界(R1'/R10):
|
|
2470
|
+
* - 只作用于白名单 timing 的**受控中间产物**(`system.prompt.transform` /
|
|
2471
|
+
* `chat.params` / `messages.transform`)——不开放原始会话存储与工具执行参数
|
|
2472
|
+
* (messages.transform 会看到待发送消息列表,含历史派生内容——白名单设计内的
|
|
2473
|
+
* 能力,全程审计);
|
|
2474
|
+
* - 白名单编译期强制(`WaterfallTiming` 从 `HookPayloadMap` Pick),装载器运行时校验兜底,
|
|
2475
|
+
* 白名单外 timing 一律拒绝注册(fail-closed);
|
|
2476
|
+
* - `system.prompt.transform` 默认只开放 `systemTail`(volatile,cache 断点后,逐轮可变)
|
|
2477
|
+
* + 预算闸(4KB 顶)——`systemPrompt`(stable,进 prompt cache)**第一版不开放**,
|
|
2478
|
+
* 尝试写入被装载器拒绝(fail-closed,写入会永久污染后续所有轮次的 prompt cache 前缀);
|
|
2479
|
+
* - 全程审计(pluginId + timing + 变更摘要),走既有 capability/trust/隔离 RPC 三重门,
|
|
2480
|
+
* 不另建旁路。
|
|
2481
|
+
*
|
|
2482
|
+
* 能力面跃迁声明:从「插件只能 deny/ask 升级限制」到「插件能改写受控中间产物」是质变,
|
|
2483
|
+
* 本字段是新贡献面(非 observer 改 interceptor),配套 D1a 分权与安全门见 RFC-327 修订 §3。
|
|
2484
|
+
*/
|
|
2485
|
+
waterfall?: PluginWaterfallHooks;
|
|
2439
2486
|
}
|
|
2487
|
+
/**
|
|
2488
|
+
* RFC-327 修订 D1(M1):waterfall handler 逐 timing 签名——input/output 与引擎
|
|
2489
|
+
* `HookPayloadMap` 对齐(编译期逐 timing 精确类型,无双重断言),返回值支持 abort 哨兵。
|
|
2490
|
+
*/
|
|
2491
|
+
type PluginWaterfallHandler<T extends WaterfallTiming> = (input: HookPayloadMap[T]['input'], output: HookPayloadMap[T]['output']) => void | HookAbortSignal | Promise<void | HookAbortSignal>;
|
|
2492
|
+
/** RFC-327 修订 D1(M1):waterfall 贡献面的逐 timing 映射(mapped type 保持 per-key 类型精确)。 */
|
|
2493
|
+
type PluginWaterfallHooks = { [T in WaterfallTiming]?: PluginWaterfallHandler<T> };
|
|
2440
2494
|
/** 装载时注入工厂的上下文。 */
|
|
2441
2495
|
interface PluginContext {
|
|
2442
2496
|
/** 插件 id(= manifest.id = source 标签)。 */
|
|
@@ -2475,9 +2529,18 @@ interface PluginContext {
|
|
|
2475
2529
|
* 未注入 `wireProtocolRegistry` 时(如轻量测试装配)恒返回 `undefined`。
|
|
2476
2530
|
*/
|
|
2477
2531
|
getWireProtocol: (wireApi: string) => _$_x_otto_provider0.WireProtocolRegistration | undefined;
|
|
2532
|
+
/**
|
|
2533
|
+
* 解析该插件自身 provider 的裸凭据 key(RFC-148 M3,`resolveProviderAuth` 的姊妹方法):
|
|
2534
|
+
* 多数代码式 provider 工厂(如 GitHub Copilot 自定义头场景)只需要裸 key,不需要
|
|
2535
|
+
* `mode`/`beta` 等完整凭据上下文。跨插件隔离:仅放行该插件 manifest 声明的
|
|
2536
|
+
* `codeProviderIds` 白名单或自身 `<pluginId>:` 前缀命名空间,其余一律返回 `null`
|
|
2537
|
+
* (不触达 AuthStore,见 `plugin-module-manager.ts` 装载器实现)。
|
|
2538
|
+
* 未注入 `providerRegistry` 或 api 不属于本插件时恒返回 `null`。
|
|
2539
|
+
*/
|
|
2540
|
+
resolveProviderCredential: (api: string) => Promise<string | null>;
|
|
2478
2541
|
/**
|
|
2479
2542
|
* 解析该插件自身 provider 的完整凭据(RFC-148 M3,`resolveProviderCredential` 的姊妹方法):
|
|
2480
|
-
*
|
|
2543
|
+
* 前者只给裸 key(多数代码式 provider 够用,如 GitHub Copilot),本方法额外给 `mode`/`beta`
|
|
2481
2544
|
* ——`anthropic-messages` 协议实现需要区分 api_key 与 oauth 模式(决定是否启用 Claude Code
|
|
2482
2545
|
* 客户端伪装)。跨插件隔离规则与 `resolveProviderCredential` 一致(同一 owned-namespace
|
|
2483
2546
|
* 白名单校验,装载器实现层面共享同一份判定逻辑,见 `plugin-module-manager.ts`)。
|
|
@@ -2579,6 +2642,12 @@ interface PluginTool {
|
|
|
2579
2642
|
data?: unknown;
|
|
2580
2643
|
error?: unknown;
|
|
2581
2644
|
};
|
|
2645
|
+
/**
|
|
2646
|
+
* 模型侧序列化契约(与 @x-otto/interchange 的 AgentTool.parameters 对齐):
|
|
2647
|
+
* provider 序列化时无此方法则模型收到空 {} schema、参数完全不可见。
|
|
2648
|
+
* 手写 schema 必须与 safeParse 成对提供(2026-08-13 终局复核回归教训)。
|
|
2649
|
+
*/
|
|
2650
|
+
toJSONSchema?: () => unknown;
|
|
2582
2651
|
};
|
|
2583
2652
|
/**
|
|
2584
2653
|
* 执行体。第二参数 `ctx` 为 RFC-303 M4 可选上下文(pluginId/workspaceDir/logger/
|
|
@@ -2605,7 +2674,7 @@ interface PluginTool {
|
|
|
2605
2674
|
type MonitorEvent = {
|
|
2606
2675
|
type: 'turn.completed';
|
|
2607
2676
|
sessionId: string;
|
|
2608
|
-
|
|
2677
|
+
tokenUsage: {
|
|
2609
2678
|
input: number;
|
|
2610
2679
|
output: number;
|
|
2611
2680
|
};
|
|
@@ -2651,7 +2720,32 @@ type MonitorEvent = {
|
|
|
2651
2720
|
type: 'tool.unregistered';
|
|
2652
2721
|
toolName: string;
|
|
2653
2722
|
source?: string;
|
|
2723
|
+
}
|
|
2724
|
+
/**
|
|
2725
|
+
* 插件装载失败(自迭代闭环负反馈面,08-13 终局 review B1):模型生成/用户安装的
|
|
2726
|
+
* 插件在装载链路任一环节失败时派发。与 `pluginHealthTracker`(人看面板)不同,
|
|
2727
|
+
* 本事件进 monitor 事件面——运行中的插件(如 skill-inductor)与模型可感知
|
|
2728
|
+
* "我生成的插件坏了",是自迭代"失败可学习"的结构化通道。observer-only。
|
|
2729
|
+
*/
|
|
2730
|
+
| {
|
|
2731
|
+
type: 'plugin.load-failed';
|
|
2732
|
+
pluginId: string;
|
|
2733
|
+
reason: PluginLoadFailureReason;
|
|
2734
|
+
message: string;
|
|
2735
|
+
}
|
|
2736
|
+
/**
|
|
2737
|
+
* 能力缺口上报(自迭代闭环认知输入面,08-13 终局 review B2):capability_gap 工具
|
|
2738
|
+
* 每次上报经宿主桥接派发。缺口记录本身是跨会话聚合 store(CapabilityGapReportStore),
|
|
2739
|
+
* 本事件是同一事实的事件面投影——运行中的 service 插件无需轮询即可感知"本工作区
|
|
2740
|
+
* 出现了新能力缺口"并主动响应。observer-only。
|
|
2741
|
+
*/
|
|
2742
|
+
| {
|
|
2743
|
+
type: 'capability_gap.reported';
|
|
2744
|
+
dedupKey: string;
|
|
2745
|
+
searchTerms: string[];
|
|
2654
2746
|
};
|
|
2747
|
+
/** 插件装载失败原因分类(`plugin.load-failed` 事件 reason 字段)。 */
|
|
2748
|
+
type PluginLoadFailureReason = /** 依赖缺失/环(topology 层剔除,RFC-132 D5)。 */'dependency-missing' /** 依赖环(topology 层剔除,RFC-132 D5)。 */ | 'dependency-cycle' /** 引擎版本门不兼容(RFC-148 M0)。 */ | 'version-incompatible' /** plugin.ts 编译/加载/工厂执行失败(module-loader 层)。 */ | 'module-error' /** project/repository 插件未通过布尔信任门(RFC-124 C1,08-13 终局复核 B1 扩展)。 */ | 'not-trusted' /** manifest 声明高危能力但未显式授予(pendingCapabilities 门,RFC-140 D3,08-13 终局复核 B1 扩展)。 */ | 'capability-not-granted' /** 声明式 provider/oauth 轴装载拒绝(能力未声明/id 冲突/保留 id 等,08-13 终局复核 B1 扩展)。 */ | 'provider-rejected';
|
|
2655
2749
|
/**
|
|
2656
2750
|
* 插件后台观察者(RFC-129 D6,capability: `monitor`)。`onEvent` 抛错被装载器 100% 隔离
|
|
2657
2751
|
* (try/catch 吞掉 + 记录日志,绝不 rethrow、绝不阻塞引擎主流程或影响其他插件/hook,R5)——
|
|
@@ -3155,6 +3249,20 @@ declare function trustPath(storePath: string, key: string, label: string): boole
|
|
|
3155
3249
|
* 调用方(`extension-plugin.ts` / `misc.ts` 的撤销入口)应捕获并如实告知用户。
|
|
3156
3250
|
*/
|
|
3157
3251
|
declare function untrustPath(storePath: string, key: string, label: string): boolean;
|
|
3252
|
+
/**
|
|
3253
|
+
* RFC-327 修订 R6'(M2):版本感知的授予——授予高危能力的同时记录授权时刻的插件
|
|
3254
|
+
* manifest 版本快照。语义同 `grantCapabilities`,额外写 `versions[key] = version`。
|
|
3255
|
+
*
|
|
3256
|
+
* 幂等语义:同 key 同版本重复调用 → 能力已含 + 版本已一致 → 返回 false(无变更)。
|
|
3257
|
+
* 版本变更后再次授予 → 覆盖版本快照并合并能力(重新评估的落点)。
|
|
3258
|
+
*/
|
|
3259
|
+
declare function grantCapabilitiesWithVersion(storePath: string, key: string, caps: readonly string[], version: string, label: string): boolean;
|
|
3260
|
+
/**
|
|
3261
|
+
* RFC-327 修订 R6'(M2):版本匹配的能力读取——仅当记录的授权版本 === 当前版本时
|
|
3262
|
+
* 返回已授予能力;版本不符(或旧文件无版本快照)返回 `[]`(fail-closed)——
|
|
3263
|
+
* 插件更新后需重新评估白名单 timing,trust 不跨版本自动存续。
|
|
3264
|
+
*/
|
|
3265
|
+
declare function grantedCapabilitiesForVersion(storePath: string, key: string, currentVersion: string): string[];
|
|
3158
3266
|
//#endregion
|
|
3159
3267
|
//#region src/plugin-trust.d.ts
|
|
3160
3268
|
/** 信任白名单落盘路径(用户级)。`OTTO_PLUGIN_TRUST_PATH` 可覆盖(测试隔离)。调用时求值。 */
|
|
@@ -3179,6 +3287,18 @@ declare function grantedPluginCapabilities(pluginDir: string, storePath?: string
|
|
|
3179
3287
|
declare function readHighRiskCapabilities(sourceDir: string): string[];
|
|
3180
3288
|
/** 显式授予插件一组高危能力(幂等,持久化;用户逐项/整体确认后调用)。 */
|
|
3181
3289
|
declare function grantPluginCapabilities(pluginDir: string, caps: readonly string[], storePath?: string): void;
|
|
3290
|
+
/**
|
|
3291
|
+
* RFC-327 修订 R6'(M2):版本感知的授予——同 grantPluginCapabilities,额外记录
|
|
3292
|
+
* 授权时刻的 manifest 版本快照。插件更新后调用
|
|
3293
|
+
* `grantedPluginCapabilitiesForVersion` 读取时版本不匹配 → 空集(fail-closed,
|
|
3294
|
+
* trust 不跨版本自动存续,需重新评估白名单)。
|
|
3295
|
+
*/
|
|
3296
|
+
declare function grantPluginCapabilitiesWithVersion(pluginDir: string, caps: readonly string[], version: string, storePath?: string): void;
|
|
3297
|
+
/**
|
|
3298
|
+
* RFC-327 修订 R6'(M2):版本匹配的能力读取——仅当授权版本 === 当前 manifest 版本时
|
|
3299
|
+
* 返回已授予能力;版本变更(或旧文件无版本快照)→ `[]`(需重新评估)。
|
|
3300
|
+
*/
|
|
3301
|
+
declare function grantedPluginCapabilitiesForVersion(pluginDir: string, version: string, storePath?: string): string[];
|
|
3182
3302
|
/** 插件目录受信任门面探测结果(可执行面 ① + 内容面 ② + 高危能力声明 ③)。 */
|
|
3183
3303
|
interface PluginExecutableSurface {
|
|
3184
3304
|
mcpJson: boolean;
|
|
@@ -3299,5 +3419,5 @@ declare class PluginI18nRegistry extends TypedEventEmitter<PluginI18nRegistryEve
|
|
|
3299
3419
|
t(fullKey: string, locale?: string): string;
|
|
3300
3420
|
}
|
|
3301
3421
|
//#endregion
|
|
3302
|
-
export { type ActionContribution, type AgentContribution, type CommandContribution, type CommandRef, type ContextKeyProvider, type ContextMap, type ContextSourceContribution, type ContributionPoint, type DiscoverPluginsOptions, type DiscoveredPlugin, type DispatchCaps, EXTENSION_POINTS, type EngineCompatResult, type FeedbackIssueInput, type FeedbackIssueResult, type FeedbackProvider, type FileProviderOptions, HIGH_RISK_CAPABILITIES, type HostAskInput, ID_RE, MAX_I18N_ENTRIES_PER_PLUGIN, MAX_I18N_FILE_BYTES, MAX_I18N_LOCALE_FILES_PER_PLUGIN, MAX_I18N_TRANSLATION_BYTES, MAX_THEME_PRESETS_PER_PLUGIN, type McpContribution, type McpTransport, type MenuContribution, type MonitorEvent, type OAuthContribution, PLUGIN_API_VERSION, PLUGIN_CAPABILITIES, PLUGIN_MANIFEST_FILENAME, POINT, type PerfMetricContribution, type PluginA2uiComponentEntry, type PluginA2uiRendererEntry, type PluginCapability, type PluginCliContribution, type PluginContext, type PluginContributes, type PluginContributions, type PluginExecutableSurface, type PluginFactory, type PluginFileViewerEntry, type PluginHooks, type PluginHostCapabilities, type PluginI18nContribution, type PluginI18nEntry, PluginI18nRegistry, type PluginInputRegistry, type PluginManifest, type PluginModelEntry, type PluginModule, type PluginMonitor, type PluginPanelEntry, type PluginProviderEntry, type PluginProviderFactory, type PluginRendererEntry, type PluginScheduleSubscription, type PluginScopedStorageShape, type PluginScripts, type PluginService, type PluginServiceContext, type PluginServiceNotification, type PluginThemePresetColors, type PluginThemePresetContribution, type PluginTool, type PluginToolContext, type PluginTrustVerdict, type PluginWireProtocolRegistration, type PreToolUseDecision, type PulseSurveyEntry, type PulseSurveyRating, type PulseSurveyResolvedConfig, type ResolvedAction, SHELL_POINTS, type ScheduleTickNotification, type SessionIdleNotification, type SigilEntry, type SigilEntryContribution, type SigilKind, type SigilPrefix, type SigilProvider, type SigilSource, type SkillContribution, type StatusItemContribution, type StatusWidgetContribution, type ToolUseContext, type TopoSortResult, type TranscriptMessage, createBuiltinHashtags, createFileProvider, createPluginInputRegistry, definePlugin, detectPluginExecutableSurface, discoverPlugins, dispatch, evaluateContextKey, evaluateContextKeys, evaluatePluginTrust, evaluateWhen, expandPath, filterEngineCompatible, grantPluginCapabilities, grantedPluginCapabilities, isEngineCompatible, isPathTrusted, isPluginTrusted, mergeContributions, parsePluginManifest, pluginTrustStorePath, readHighRiskCapabilities, resolveActions, resolveActivationEvents, sigilOf, topoSortPlugins, trustPath, trustPlugin, untrustPath, untrustPlugin };
|
|
3422
|
+
export { type ActionContribution, type AgentContribution, type CommandContribution, type CommandRef, type ContextKeyProvider, type ContextMap, type ContextSourceContribution, type ContributionPoint, type DiscoverPluginsOptions, type DiscoveredPlugin, type DispatchCaps, EXTENSION_POINTS, type EngineCompatResult, type FeedbackIssueInput, type FeedbackIssueResult, type FeedbackProvider, type FileProviderOptions, HIGH_RISK_CAPABILITIES, type HostAskInput, ID_RE, MAX_I18N_ENTRIES_PER_PLUGIN, MAX_I18N_FILE_BYTES, MAX_I18N_LOCALE_FILES_PER_PLUGIN, MAX_I18N_TRANSLATION_BYTES, MAX_THEME_PRESETS_PER_PLUGIN, type McpContribution, type McpTransport, type MenuContribution, type MonitorEvent, type OAuthContribution, PLUGIN_API_VERSION, PLUGIN_CAPABILITIES, PLUGIN_MANIFEST_FILENAME, POINT, type PerfMetricContribution, type PluginA2uiComponentEntry, type PluginA2uiRendererEntry, type PluginCapability, type PluginCliContribution, type PluginContext, type PluginContributes, type PluginContributions, type PluginExecutableSurface, type PluginFactory, type PluginFileViewerEntry, type PluginHooks, type PluginHostCapabilities, type PluginI18nContribution, type PluginI18nEntry, PluginI18nRegistry, type PluginInputRegistry, type PluginLoadFailureReason, type PluginManifest, type PluginManifestPlane, type PluginModelEntry, type PluginModule, type PluginMonitor, type PluginPanelEntry, type PluginProviderEntry, type PluginProviderFactory, type PluginRendererEntry, type PluginScheduleSubscription, type PluginScopedStorageShape, type PluginScripts, type PluginService, type PluginServiceContext, type PluginServiceNotification, type PluginThemePresetColors, type PluginThemePresetContribution, type PluginTool, type PluginToolContext, type PluginTrustVerdict, type PluginWaterfallHandler, type PluginWaterfallHooks, type PluginWireProtocolRegistration, type PreToolUseDecision, type PulseSurveyEntry, type PulseSurveyRating, type PulseSurveyResolvedConfig, type ResolvedAction, SHELL_POINTS, type ScheduleTickNotification, type SessionIdleNotification, type SigilEntry, type SigilEntryContribution, type SigilKind, type SigilPrefix, type SigilProvider, type SigilSource, type SkillContribution, type StatusItemContribution, type StatusWidgetContribution, type ToolUseContext, type TopoSortResult, type TranscriptMessage, createBuiltinHashtags, createFileProvider, createPluginInputRegistry, definePlugin, detectPluginExecutableSurface, discoverPlugins, dispatch, evaluateContextKey, evaluateContextKeys, evaluatePluginTrust, evaluateWhen, expandPath, filterEngineCompatible, grantCapabilitiesWithVersion, grantPluginCapabilities, grantPluginCapabilitiesWithVersion, grantedCapabilitiesForVersion, grantedPluginCapabilities, grantedPluginCapabilitiesForVersion, isEngineCompatible, isPathTrusted, isPluginTrusted, mergeContributions, parsePluginManifest, pluginTrustStorePath, readHighRiskCapabilities, resolveActions, resolveActivationEvents, resolveManifestPlane, sigilOf, topoSortPlugins, trustPath, trustPlugin, untrustPath, untrustPlugin };
|
|
3303
3423
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{z as e}from"zod";import{TypedEventEmitter as t,acquireFileLockSync as n,createLogger as r,releaseFileLockSync as i,waitForFileLockReleaseSync as a}from"@x-otto/shared";import{closeSync as o,existsSync as s,fsyncSync as c,mkdirSync as l,openSync as u,readFileSync as d,readSync as f,readdirSync as p,renameSync as ee,statSync as te,unlinkSync as ne,writeFileSync as re}from"node:fs";import{basename as m,dirname as ie,join as h,relative as ae,resolve as g}from"node:path";import{OTTO_HOME as _,findWorkspaceRoot as oe,isShadowModeEnabled as se,resolveConfigLayers as ce}from"@x-otto/env";import{satisfies as le,validRange as ue}from"semver";import{homedir as v}from"node:os";import{sigilOf as y}from"@x-otto/interchange";import{readdir as de}from"node:fs/promises";import{randomUUID as fe}from"node:crypto";const pe=[`provider`,`tools`,`hooks`,`tui.renderer`,`network`,`context`,`monitor`,`wire-protocol`,`a2ui.component`,`panel.backend`,`mcp.server`,`feedback`,`input.resolver`,`a2ui.renderer`,`theme`,`agent.dispatch`,`service`,`storage`,`session.read`,`llm.complete`,`user.notify`,`user.ask`],b={provider:!0,tools:!0,hooks:!1,"tui.renderer":!0,network:!0,context:!1,monitor:!1,"wire-protocol":!0,"a2ui.component":!0,"panel.backend":!0,"mcp.server":!0,feedback:!1,"input.resolver":!0,"a2ui.renderer":!0,theme:!1,"agent.dispatch":!0,service:!0,storage:!1,"session.read":!0,"llm.complete":!0,"user.notify":!1,"user.ask":!0},x=Object.freeze(Object.keys(b).filter(e=>b[e])),S=r(`@x-otto/coding:plugin-manifest`),C=`otto-plugin.json`,w=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,T=e=>e.optional().catch(void 0),E=e.string().refine(e=>e.trim().length>0,`must be non-empty`),me=e.object({id:E,title:E,entry:E.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:T(E)}),he=e.object({role:T(E),toolName:T(E),contentType:T(E)}).refine(e=>e.role!=null||e.toolName!=null||e.contentType!=null,{message:`matcher must declare at least one of role/toolName/contentType`}),ge=e.object({id:E,matcher:he,entry:E.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:T(E)}),_e=e.object({id:E,matcher:he,entry:E.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:T(E)}),ve=e.object({extensions:T(e.array(E)),filenames:T(e.array(E)),glob:T(e.array(E))}).refine(e=>(e.extensions?.length??0)+(e.filenames?.length??0)+(e.glob?.length??0)>0,{message:`matcher must declare at least one of extensions/filenames/glob`}),ye=D(e.object({id:E,label:E,matcher:ve,entry:E.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:T(E),order:T(e.number())}),`fileViewers`),be=D(e.object({type:E,entry:E.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:T(E)}),`a2uiComponents`),xe=D(e.object({id:E,entry:E.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:T(E),order:T(e.number()),tickMs:T(e.number())}),`statusWidgets`),Se=D(ge,`renderers`),Ce=D(me,`panels`),we=e.discriminatedUnion(`type`,[e.object({type:e.literal(`builtin`),id:E}),e.object({type:e.literal(`mcp`),server:E,tool:E,params:T(e.record(e.string(),e.unknown()))}),e.object({type:e.literal(`open`),target:E}),e.object({type:e.literal(`command`),name:E,args:T(E)}),e.object({type:e.literal(`script`),name:E})]),Te=e.object({id:E,title:E,command:we,color:T(e.enum([`accent`,`amber`,`red`,`green`,`gray`]))}),Ee=e.object({action:E,point:E,when:T(E),order:T(e.number())}),De=e.object({key:E,groups:e.array(e.array(E)),map:e.object({none:E,partial:E,all:E})});function D(t,n){return e.array(e.unknown()).transform(e=>{let r=e.map(e=>t.safeParse(e)),i=r.filter(e=>e.success).map(e=>e.data),a=r.length-i.length;if(a>0&&n){let e=r.filter(e=>!e.success).map(e=>e.success?``:e.error.issues.map(e=>e.message).join(`; `));S.warn({label:n,dropped:a,total:r.length,issues:e},`contributes.${n}: ${a}/${r.length} 条 entry 校验失败,已静默丢弃(fail-soft)`)}return i.length>0?i:void 0})}const Oe=e.object({input:e.number().min(0),output:e.number().min(0),cacheRead:e.number().min(0),cacheWrite:e.number().min(0)}),ke=e.object({maxImagesPerRequest:e.number().int().positive(),maxDimensionPxIfOverLimit:T(e.number().int().positive())}),Ae=e.enum([`planning`,`knowledge`,`coding`,`reasoning`,`vision`,`speed`,`long-context`]),je=e.enum([`low`,`medium`,`high`,`xhigh`,`max`]),Me=e.enum([`enabled`,`adaptive`]),Ne=e.object({id:E,contextWindow:e.number().int().positive(),maxOutput:e.number().int().positive(),cost:T(Oe),strengths:T(e.array(Ae).min(1)),reasoning:T(e.boolean()),input:T(e.array(e.enum([`text`,`image`])).min(1)),thinkingLevels:T(e.array(je).min(1)),thinkingMode:T(Me)}),Pe=e.enum([`openai-completions`,`openai-responses`,`anthropic-messages`]),Fe=e.object({label:E,value:E.refine(e=>!e.startsWith(`!`)&&!e.startsWith(`#`),`value must not start with '!' or '#' (mode-prefix collision)`),kind:e.enum([`resource`,`hashtag`]),description:T(E),resolverId:T(E)}),Ie=e.object({id:E,dataSource:e.object({contextKey:E}),point:T(E),when:T(E),order:T(e.number())}),Le=e.union([E,e.object({file:E.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`)})]),Re=e.object({id:E,priority:T(e.number()),content:Le}),ze=e.object({id:E,translations:e.record(e.string(),e.string())}),Be=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/,O=e=>!Be.test(e),Ve=/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/,k=t=>e.string().max(16).refine(e=>Ve.test(e),`${t} must be a #RRGGBB or #RGB hex color`),He=e.object({brand:k(`brand`),brandShimmer:k(`brandShimmer`),accent:k(`accent`),accentDim:k(`accentDim`),accentBright:k(`accentBright`),text:T(k(`text`)),inactive:k(`inactive`),secondary:k(`secondary`),subtle:k(`subtle`),replyPrefix:k(`replyPrefix`),success:k(`success`),error:k(`error`),warning:k(`warning`),special:k(`special`),suggestion:k(`suggestion`),permission:k(`permission`),codeText:k(`codeText`),diffAdd:k(`diffAdd`),diffRemove:k(`diffRemove`),diffAddBg:k(`diffAddBg`),diffRemoveBg:k(`diffRemoveBg`),panelBorder:k(`panelBorder`),overlayBg:k(`overlayBg`),tagBg:k(`tagBg`),toolText:k(`toolText`),focusBorder:k(`focusBorder`),selection:k(`selection`)}),A=e.enum(`brand.brandShimmer.accent.accentDim.accentBright.text.inactive.secondary.subtle.replyPrefix.success.error.warning.special.suggestion.permission.codeText.diffAdd.diffRemove.diffAddBg.diffRemoveBg.panelBorder.overlayBg.tagBg.toolText.focusBorder.selection`.split(`.`)),Ue=e.strictObject({heading1:T(A),heading2:T(A),headingWeak:T(A),listMarker:T(A),listMarkerMuted:T(A),quoteBar:T(A),quoteText:T(A),link:T(A),inlineCode:T(A),codeFence:T(A),tableHeader:T(A),tableDivider:T(A),listIndent:T(e.union([e.literal(2),e.literal(3)])),listMarkers:T(e.tuple([e.string().min(1).max(4),e.string().min(1).max(4),e.string().min(1).max(4),e.string().min(1).max(4)])),codeBlockDivider:T(e.enum([`hr`,`none`]))}),We=e.object({localId:E.max(64).refine(e=>w.test(e),`localId must be kebab-case`),label:E.max(64).refine(O,`label must not contain control characters`),appearance:e.enum([`dark`,`light`]),colors:He,markdown:T(Ue),meta:T(e.object({author:T(E.max(128).refine(O,`author must not contain control characters`)),description:T(E.max(256).refine(O,`description must not contain control characters`)),version:T(E.max(32).refine(O,`version must not contain control characters`))}))}),Ge=10,Ke=500,qe=2*1024,Je=20,Ye=64*1024,Xe=e.object({id:E,label:E,toolRef:E}),Ze=e.object({header:E.refine(e=>/^[a-zA-Z0-9][a-zA-Z0-9\-_]*$/.test(e),`header name must be alphanumeric with hyphens/underscores only`),source:e.object({kind:e.literal(`jwt-claim`),claim:E,namespace:T(E),tokenSources:T(e.array(e.enum([`id_token`,`access_token`])))})}),j=e.object({imageConstraints:T(ke),supportsTools:T(e.boolean())}),M=e.object({url:E,responseFormat:e.literal(`openai-list`),authScheme:T(e.enum([`bearer`,`x-api-key`,`auto`])),betaHeaderName:T(E),modelIdExclude:T(D(e.string(),`modelIdExclude`))}),N=e.object({store:T(e.boolean()),sendMaxOutputTokens:T(e.boolean())}),Qe=e.object({id:E,name:T(E),catalogOnly:T(e.boolean()),baseUrl:T(E),wireApi:T(Pe),envKey:T(E),oauthRef:T(E),headers:T(e.record(e.string(),e.string())),models:T(D(Ne,`models`)),tokenDerivedHeaders:T(D(Ze,`tokenDerivedHeaders`)),modelsEndpoint:T(M),responseBodyPolicy:T(N),allowAuthHeaderOverride:T(e.boolean()),preserveProviderId:T(e.boolean()),requiresIntranet:T(e.boolean())}).extend(j.shape).superRefine((t,n)=>{if(t.catalogOnly===!0){t.wireApi&&n.addIssue({code:e.ZodIssueCode.custom,message:`catalogOnly entries must not declare wireApi; use providerFactories for custom protocols`,path:[`wireApi`]}),t.baseUrl&&n.addIssue({code:e.ZodIssueCode.custom,message:`catalogOnly entries must not declare baseUrl; use providerFactories for custom protocols`,path:[`baseUrl`]});return}t.baseUrl||n.addIssue({code:e.ZodIssueCode.custom,message:`baseUrl is required unless catalogOnly is true`,path:[`baseUrl`]}),t.wireApi||n.addIssue({code:e.ZodIssueCode.custom,message:`wireApi is required unless catalogOnly is true`,path:[`wireApi`]})}),$e=e.object({name:E.refine(e=>/^[a-z][a-z0-9-]*$/.test(e),`must be lowercase kebab-case`),bin:T(E)}),et=e.discriminatedUnion(`kind`,[e.object({kind:e.literal(`pkce`),id:E,name:T(E),clientId:E,authorizeUrl:E,tokenUrl:E,redirectUri:E,scope:E,loopbackPorts:T(e.array(e.number().int().positive())),assumesLoopbackAlways:T(e.boolean()),extraAuthParams:T(e.record(e.string(),e.string())),tokenExchangeEncoding:T(e.enum([`json`,`form`])),subscriptionScoped:T(e.boolean()),oauthBeta:T(E)}),e.object({kind:e.literal(`device-flow`),id:E,name:T(E),clientId:E,deviceCodeUrlTemplate:E,tokenUrlTemplate:E,refreshUrlTemplate:T(E),scope:E,allowCustomDomain:T(e.boolean()),userAgent:T(E)})]),tt=e.object({skills:T(e.boolean()),agents:T(e.boolean()),commands:T(e.boolean()),mcp:T(e.boolean()),cli:T($e),panels:T(Ce),actions:T(D(Te,`actions`)),menus:T(D(Ee,`menus`)),contextKeys:T(D(De,`contextKeys`)),providers:T(D(Qe,`providers`)),oauth:T(D(et,`oauth`)),statusItems:T(D(Ie,`statusItems`)),perfMetrics:T(D(Xe,`perfMetrics`)),renderers:T(Se),fileViewers:T(ye),statusWidgets:T(xe),a2uiComponents:T(be),contextSources:T(D(Re,`contextSources`)),i18n:T(D(ze,`i18n`)),themePresets:T(D(We,`themePresets`)),i18nDir:T(E.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`)),inputEntries:T(E),sigilEntries:T(D(Fe,`sigilEntries`)),a2uiRenderers:T(D(_e,`a2uiRenderers`))}),nt=e.enum(pe),rt=e.array(e.unknown()).transform(e=>{let t=e.map(e=>nt.safeParse(e)).filter(e=>e.success).map(e=>e.data);return t.length>0?[...new Set(t)]:void 0}),it=/^(onStartup|onCommand:[^\s]+|onView:[^\s]+|onProvider:[^\s]+)$/,at=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&it.test(e));return t.length>0?[...new Set(t)]:void 0}),ot=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&w.test(e));return t.length>0?[...new Set(t)]:void 0}),st=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&e.trim().length>0);return t.length>0?[...new Set(t)]:void 0}),ct=M.extend({headers:T(e.record(e.string(),e.string()))}),lt=e.record(e.string(),e.unknown()).transform(e=>{let t={};for(let[n,r]of Object.entries(e)){let e=ct.safeParse(r);e.success&&(t[n]=e.data)}return Object.keys(t).length>0?t:void 0}),ut=e.object({baseUrl:E,models:D(Ne,`codeProviderModels`),responseBodyPolicy:T(N)}).extend(j.shape),dt=e.record(e.string(),e.unknown()).transform(e=>{let t={};for(let[n,r]of Object.entries(e)){let e=ut.safeParse(r);e.success&&e.data.models&&e.data.models.length>0&&(t[n]=e.data)}return Object.keys(t).length>0?t:void 0}),ft=e.object({postinstall:T(E),setup:T(E)}).catchall(E),pt=e.object({otto:T(E)}),mt=e.object({id:e.string().regex(w),name:T(e.string()),version:T(e.string()),description:T(e.string()),engines:T(pt),contributes:T(tt),scripts:T(ft),capabilities:T(rt),activationEvents:T(at),dependsOn:T(ot),codeProviderIds:T(st),codeProviderModelsEndpoints:T(lt),codeProviderModels:T(dt)});function ht(e){return e.activationEvents?.length?e.activationEvents:[`onStartup`]}function P(e,t){let n;try{n=JSON.parse(e)}catch(e){return S.warn({sourcePath:t,err:String(e)},`plugin manifest invalid JSON, skipped`),null}let r=mt.safeParse(n);return r.success?r.data:(S.warn({sourcePath:t,issues:r.error.issues.map(e=>e.path.join(`.`)||`<root>`)},`plugin manifest missing/invalid "id" (kebab-case required) or not an object, skipped`),null)}const F=r(`@x-otto/coding:plugin-discovery`);function I(e){try{return te(e).isDirectory()}catch{return!1}}const gt=[{id:`otto-plugin-manager`,dir:`<builtin:otto-plugin-manager>`,scope:`builtin`,manifest:{id:`otto-plugin-manager`,name:`Plugin Manager`,description:`otto 插件生命周期管理(create/build/dev/install)—— 随 otto 内置分发,恒定信任、恒定启用。`,version:void 0,contributes:void 0,scripts:void 0,capabilities:void 0,activationEvents:void 0,dependsOn:void 0}},{id:`otto-tui`,dir:`<builtin:otto-tui>`,scope:`builtin`,manifest:{id:`otto-tui`,name:`TUI`,description:`otto 终端交互界面本体(渲染/输入/面板/状态栏)—— 随 otto 内置分发,恒定信任、恒定启用。`,version:void 0,contributes:void 0,scripts:void 0,capabilities:void 0,activationEvents:void 0,dependsOn:void 0}}];function _t(e){let{cwd:t,homedir:n}=e,r=e.disabled?new Set(e.disabled):null,i=ce({cwd:t,homedir:n,claudeCompat:!1,workspaceRoot:oe(t)}).filter(e=>e.kind===`otto`).map(e=>({dir:h(e.dir,`plugins`),scope:e.scope}));for(let t of e.bundledDirs??[])s(t)&&i.unshift({dir:t,scope:`bundled`});let a=new Map;for(let e of gt)a.set(e.id,e);for(let{dir:e,scope:t}of i){if(!s(e)||!I(e))continue;let n;try{n=p(e).sort()}catch(t){F.warn({root:e,err:String(t)},`failed to read plugins root, skipped`);continue}for(let i of n){let n=h(e,i);if(!I(n))continue;let o=h(n,C);if(!s(o))continue;let c;try{c=d(o,`utf-8`)}catch(e){F.warn({manifestPath:o,err:String(e)},`failed to read plugin manifest, skipped`);continue}let l=P(c,o);l&&(r?.has(l.id)||a.has(l.id)||a.set(l.id,{id:l.id,dir:n,manifest:l,scope:t}))}}return[...a.values()].sort((e,t)=>e.id.localeCompare(t.id))}function L(e){return e.manifest.dependsOn??[]}function vt(e){let t=new Map,n=new Map,r=e.filter(e=>e.scope===`builtin`),i=e.filter(e=>e.scope!==`builtin`),a=new Set(r.map(e=>e.id)),o=new Map;for(let e of i)o.set(e.id,e);let s=e=>o.has(e)||a.has(e),c=!0;for(;c;){c=!1;for(let e of o.values()){let t=L(e).filter(e=>!s(e));t.length>0&&(n.set(e.id,[...new Set(t)]),o.delete(e.id),c=!0)}}let l=new Map,u=new Map;for(let e of o.values()){let t=L(e).filter(e=>o.has(e));l.set(e.id,t.length);for(let n of t){let t=u.get(n);t?t.push(e.id):u.set(n,[e.id])}}let d=[...o.values()].filter(e=>l.get(e.id)===0).map(e=>e.id).sort((e,t)=>e.localeCompare(t)),f=[];for(;d.length>0;){let e=d.shift();f.push(e);let t=[];for(let n of u.get(e)??[]){let e=(l.get(n)??0)-1;l.set(n,e),e===0&&t.push(n)}t.length>0&&(d.push(...t),d.sort((e,t)=>e.localeCompare(t)))}let p=new Set(f);for(let e of o.values())p.has(e.id)||t.set(e.id,yt(e.id,o));return{ordered:[...r,...f.map(e=>o.get(e))],cyclic:t,missingDeps:n}}function yt(e,t){let n=[],r=new Set,i=e;for(;i&&!r.has(i);){n.push(i),r.add(i);let e=t.get(i);if(!e)break;i=L(e).filter(e=>t.has(e)).sort((e,t)=>e.localeCompare(t))[0]}return i===e&&n.push(e),n}const bt=1;function xt(){return`1.0.0`}function R(e){if(e.scope===`builtin`)return!0;let t=e.manifest.engines?.otto;return t?ue(t)?le(xt(),t):!1:!0}function St(e){let t=[],n=new Map;for(let r of e)R(r)?t.push(r):n.set(r.id,r.manifest.engines?.otto??`(invalid range)`);return{compatible:t,incompatible:n}}function Ct(e,t,n){if(!e?.length)return t?[...t]:void 0;if(!t?.length)return[...e];if(!n)return[...e,...t];let r=new Map(t.map(e=>[n(e),e])),i=new Set,a=[];for(let t of e){let e=n(t);i.add(e),a.push(r.has(e)?r.get(e):t)}for(let e of t)i.has(n(e))||a.push(e);return a}const z={commands:e=>e.name,agents:e=>e.name,skills:e=>e.name,mcp:e=>e.name,panels:e=>e.id,actions:e=>e.id,menus:null,contextKeys:e=>e.key,statusItems:e=>e.id,renderers:e=>e.id,contextSources:e=>e.id,a2uiComponents:e=>e.type,statusWidgets:e=>e.id,fileViewers:e=>e.id};function wt(...e){let t=e.filter(e=>e!=null);return t.length===0?{}:t.reduce((e,t)=>{let n={};for(let r of Object.keys(z)){let i=z[r],a=Ct(e[r],t[r],i);a&&(n[r]=a)}return n},{})}const B={extensionDetailActions:`extension/detail/actions`},V={statuslineItem:`shell/statusline/item`,menuItem:`shell/menu/item`},Tt={...B,...V};function H(e,t){if(!e)return!0;let n=e.indexOf(`:`);if(n===-1){let n=t[e];return n===!0||typeof n==`string`&&n.length>0}let r=e.slice(0,n),i=e.slice(n+1),a=t[r];return a!=null&&String(a)===i}function Et(e,t,n,r){let i=new Map((n??[]).map(e=>[e.id,e]));return(t??[]).filter(t=>t.point===e&&H(t.when,r)).map(e=>({menu:e,action:i.get(e.action)})).filter(e=>e.action!=null).sort((e,t)=>(e.menu.order??0)-(t.menu.order??0)).map(({action:e})=>({id:e.id,title:e.title,color:e.color,command:e.command}))}function U(e){let t=e.replace(/\$\{OTTO_HOME\}/g,_);return t===`~`?v():t.startsWith(`~/`)?v()+t.slice(1):t}function W(e,t=s){let n=e.groups.length;if(n===0)return e.map.none;let r=e.groups.filter(e=>e.some(e=>t(U(e)))).length;return r===0?e.map.none:r===n?e.map.all:e.map.partial}function Dt(e,t){let n={};for(let r of e??[])n[r.key]=W(r,t);return n}var G=class extends Error{constructor(e){super(`dispatch: 缺少 capability「${e}」——宿主未注入`),this.name=`MissingCapabilityError`}};async function Ot(e,t){switch(e.type){case`builtin`:if(!t.builtin)throw new G(`builtin`);await t.builtin(e.id);return;case`mcp`:if(!t.callMcp)throw new G(`mcp`);await t.callMcp(e.server,e.tool,e.params);return;case`open`:if(!t.open)throw new G(`open`);await t.open(e.target);return;case`command`:if(!t.runCommand)throw new G(`command`);await t.runCommand(e.name,e.args);return;case`script`:if(!t.runScript)throw new G(`script`);await t.runScript(e.name);return;default:throw new G(e.type)}}function kt(e){return e}function K(e){return`${e.kind}\u0000${e.label}`}function At(e,t){if(t===``)return 0;let n=e.label.toLowerCase();return n.startsWith(t)?2:e.description&&e.description.toLowerCase().includes(t)?1:n.includes(t)?0:-1}function jt(e){return e.startsWith(`!`)||e.startsWith(`#`)}function Mt(){let e=new Map,t=new Map;function n(t){for(let n of t){if(jt(n.value))throw Error(`[plugin-input] sigil value 不得以 '!' 或 '#' 开头(会被误判为 bash/memory 模式):${JSON.stringify(n.value)}`);let t=K(n);e.delete(t),e.set(t,n)}}function r(t){let n=[...e.values()];return t?n.filter(e=>y(e.kind)===t):n}function i(t,n,r=50){let i=t.toLowerCase(),a=[],o=0;for(let t of e.values()){let e=o++;if(n&&y(t.kind)!==n)continue;let r=At(t,i);r<0||a.push({entry:t,score:r,order:e})}return a.sort((e,t)=>t.score-e.score||e.order-t.order),a.length<=r?a.map(e=>e.entry):a.slice(0,r).map(e=>e.entry)}function a(e,n){let r=t.get(e);return r||(r=new Set,t.set(e,r)),r.add(n),()=>{r?.delete(n)}}async function o(e,n,r=50){let a=i(e,n,r),o=n?t.get(n):void 0;if(!o||o.size===0)return a;let s=[],c=await Promise.allSettled([...o].map(t=>Promise.resolve().then(()=>t(e,n))));for(let e of c)e.status===`fulfilled`&&s.push(...e.value.filter(e=>!jt(e.value)));let l=new Set(a.map(e=>K(e))),u=[...a];for(let e of s){let t=K(e);l.has(t)||(l.add(t),u.push(e))}return u.length<=r?u:u.slice(0,r)}function s(t){for(let n of e.values())if(n.label===t)return n}function c(t){let n=!1;for(let[r,i]of e)i.label===t&&(e.delete(r),n=!0);return n}function l(t){for(let[n,r]of e)r.source===t&&e.delete(n)}return{register:n,registerProvider:a,search:i,searchAsync:o,getAll:r,findByLabel:s,unregister:c,unregisterBySource:l}}function Nt(){return[{label:`Code Review`,description:`请求代码审查`,value:`Please review the following code for correctness, style, security, and performance. Provide specific, actionable feedback.`,kind:`hashtag`,source:`builtin`},{label:`Write Tests`,description:`为代码编写单元测试`,value:`Please write comprehensive unit tests for the following code. Cover normal cases, edge cases, error cases, and ensure high coverage.`,kind:`hashtag`,source:`builtin`},{label:`Explain Code`,description:`解释代码逻辑`,value:`Please explain the following code in detail. Cover the overall architecture, key algorithms, data flow, and any notable design decisions.`,kind:`hashtag`,source:`builtin`}]}const Pt=r(`@x-otto/plugin:file-provider`),Ft=new Set([`node_modules`,`.git`,`dist`,`build`,`out`,`coverage`,`.next`,`.nuxt`,`.turbo`,`.nx`,`.cache`,`.otto`,`.venv`,`__pycache__`]);async function It(e,t,n){let r=[];async function i(a,o){if(r.length>=t)return;let s;try{s=await de(a,{withFileTypes:!0})}catch(e){o&&n?.(e);return}for(let n of s){if(r.length>=t)return;if(n.isDirectory()){if(Ft.has(n.name)||n.name.startsWith(`.`))continue;await i(h(a,n.name),!1)}else n.isFile()&&r.push(ae(e,h(a,n.name)))}}return await i(e,!0),r}function Lt(e,t){if(t===``)return 0;let n=e.toLowerCase();return m(n).startsWith(t)?3:n.startsWith(t)?2:n.includes(t)?1:-1}function Rt(e,t,n,r){let i;try{i=u(h(e,t),`r`);let a=Buffer.allocUnsafe(8192),o=f(i,a,0,a.length,0),s=a.toString(`utf-8`,0,o).split(`
|
|
1
|
+
import{z as e}from"zod";import{TypedEventEmitter as t,acquireFileLockSync as n,createLogger as r,releaseFileLockSync as i,waitForFileLockReleaseSync as a}from"@x-otto/shared";import{closeSync as o,existsSync as s,fsyncSync as c,mkdirSync as l,openSync as u,readFileSync as d,readSync as f,readdirSync as p,renameSync as ee,statSync as te,unlinkSync as ne,writeFileSync as re}from"node:fs";import{basename as ie,dirname as ae,join as m,relative as oe,resolve as h}from"node:path";import{OTTO_HOME as g,findWorkspaceRoot as se,isShadowModeEnabled as ce,resolveConfigLayers as le}from"@x-otto/env";import{satisfies as ue,validRange as de}from"semver";import{homedir as fe}from"node:os";import{sigilOf as _}from"@x-otto/interchange";import{readdir as pe}from"node:fs/promises";import{randomUUID as me}from"node:crypto";const he=[`provider`,`tools`,`hooks`,`tui.renderer`,`network`,`context`,`monitor`,`wire-protocol`,`a2ui.component`,`panel.backend`,`mcp.server`,`feedback`,`input.resolver`,`a2ui.renderer`,`theme`,`agent.dispatch`,`service`,`storage`,`session.read`,`llm.complete`,`user.notify`,`user.ask`],ge={provider:!0,tools:!0,hooks:!1,"tui.renderer":!0,network:!0,context:!1,monitor:!1,"wire-protocol":!0,"a2ui.component":!0,"panel.backend":!0,"mcp.server":!0,feedback:!1,"input.resolver":!0,"a2ui.renderer":!0,theme:!1,"agent.dispatch":!0,service:!0,storage:!1,"session.read":!0,"llm.complete":!0,"user.notify":!1,"user.ask":!0},v=Object.freeze(Object.keys(ge).filter(e=>ge[e])),y=r(`@x-otto/coding:plugin-manifest`),b=`otto-plugin.json`,x=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,S=e=>e.optional().catch(void 0),C=e.string().refine(e=>e.trim().length>0,`must be non-empty`),_e=e.object({id:C,title:C,entry:C.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:S(C)}),w=e.object({role:S(C),toolName:S(C),contentType:S(C)}).refine(e=>e.role!=null||e.toolName!=null||e.contentType!=null,{message:`matcher must declare at least one of role/toolName/contentType`}),ve=e.object({id:C,matcher:w,entry:C.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:S(C)}),ye=e.object({id:C,matcher:w,entry:C.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:S(C)}),be=e.object({extensions:S(e.array(C)),filenames:S(e.array(C)),glob:S(e.array(C))}).refine(e=>(e.extensions?.length??0)+(e.filenames?.length??0)+(e.glob?.length??0)>0,{message:`matcher must declare at least one of extensions/filenames/glob`}),xe=T(e.object({id:C,label:C,matcher:be,entry:C.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:S(C),order:S(e.number())}),`fileViewers`),Se=T(e.object({type:C,entry:C.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:S(C)}),`a2uiComponents`),Ce=T(e.object({id:C,entry:C.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:S(C),order:S(e.number()),tickMs:S(e.number())}),`statusWidgets`),we=T(ve,`renderers`),Te=T(_e,`panels`),Ee=e.discriminatedUnion(`type`,[e.object({type:e.literal(`builtin`),id:C}),e.object({type:e.literal(`mcp`),server:C,tool:C,params:S(e.record(e.string(),e.unknown()))}),e.object({type:e.literal(`open`),target:C}),e.object({type:e.literal(`command`),name:C,args:S(C)}),e.object({type:e.literal(`script`),name:C})]),De=e.object({id:C,title:C,command:Ee,color:S(e.enum([`accent`,`amber`,`red`,`green`,`gray`]))}),Oe=e.object({action:C,point:C,when:S(C),order:S(e.number())}),ke=e.object({key:C,groups:e.array(e.array(C)),map:e.object({none:C,partial:C,all:C})});function T(t,n){return e.array(e.unknown()).transform(e=>{let r=e.map(e=>t.safeParse(e)),i=r.filter(e=>e.success).map(e=>e.data),a=r.length-i.length;if(a>0&&n){let e=r.filter(e=>!e.success).map(e=>e.success?``:e.error.issues.map(e=>e.message).join(`; `));y.warn({label:n,dropped:a,total:r.length,issues:e},`contributes.${n}: ${a}/${r.length} 条 entry 校验失败,已静默丢弃(fail-soft)`)}return i.length>0?i:void 0})}const Ae=e.object({input:e.number().min(0),output:e.number().min(0),cacheRead:e.number().min(0),cacheWrite:e.number().min(0)}),je=e.object({maxImagesPerRequest:e.number().int().positive(),maxDimensionPxIfOverLimit:S(e.number().int().positive())}),Me=e.enum([`planning`,`knowledge`,`coding`,`reasoning`,`vision`,`speed`,`long-context`]),Ne=e.enum([`low`,`medium`,`high`,`xhigh`,`max`]),Pe=e.enum([`enabled`,`adaptive`]),E=e.object({id:C,contextWindow:e.number().int().positive(),maxOutput:e.number().int().positive(),cost:S(Ae),strengths:S(e.array(Me).min(1)),reasoning:S(e.boolean()),input:S(e.array(e.enum([`text`,`image`])).min(1)),thinkingLevels:S(e.array(Ne).min(1)),thinkingMode:S(Pe)}),Fe=e.enum([`openai-completions`,`openai-responses`,`anthropic-messages`]),Ie=e.object({label:C,value:C.refine(e=>!e.startsWith(`!`)&&!e.startsWith(`#`),`value must not start with '!' or '#' (mode-prefix collision)`),kind:e.enum([`resource`,`hashtag`]),description:S(C),resolverId:S(C)}),Le=e.object({id:C,dataSource:e.object({contextKey:C}),point:S(C),when:S(C),order:S(e.number())}),Re=e.union([C,e.object({file:C.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`)})]),ze=e.object({id:C,priority:S(e.number()),content:Re}),Be=e.object({id:C,translations:e.record(e.string(),e.string())}),Ve=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/,D=e=>!Ve.test(e),He=/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/,O=t=>e.string().max(16).refine(e=>He.test(e),`${t} must be a #RRGGBB or #RGB hex color`),Ue=e.object({brand:O(`brand`),brandShimmer:O(`brandShimmer`),accent:O(`accent`),accentDim:O(`accentDim`),accentBright:O(`accentBright`),text:S(O(`text`)),inactive:O(`inactive`),secondary:O(`secondary`),subtle:O(`subtle`),replyPrefix:O(`replyPrefix`),success:O(`success`),error:O(`error`),warning:O(`warning`),special:O(`special`),suggestion:O(`suggestion`),permission:O(`permission`),codeText:O(`codeText`),diffAdd:O(`diffAdd`),diffRemove:O(`diffRemove`),diffAddBg:O(`diffAddBg`),diffRemoveBg:O(`diffRemoveBg`),panelBorder:O(`panelBorder`),overlayBg:O(`overlayBg`),tagBg:O(`tagBg`),toolText:O(`toolText`),focusBorder:O(`focusBorder`),selection:O(`selection`)}),k=e.enum(`brand.brandShimmer.accent.accentDim.accentBright.text.inactive.secondary.subtle.replyPrefix.success.error.warning.special.suggestion.permission.codeText.diffAdd.diffRemove.diffAddBg.diffRemoveBg.panelBorder.overlayBg.tagBg.toolText.focusBorder.selection`.split(`.`)),We=e.strictObject({heading1:S(k),heading2:S(k),headingWeak:S(k),listMarker:S(k),listMarkerMuted:S(k),quoteBar:S(k),quoteText:S(k),link:S(k),inlineCode:S(k),codeFence:S(k),tableHeader:S(k),tableDivider:S(k),listIndent:S(e.union([e.literal(2),e.literal(3)])),listMarkers:S(e.tuple([e.string().min(1).max(4),e.string().min(1).max(4),e.string().min(1).max(4),e.string().min(1).max(4)])),codeBlockDivider:S(e.enum([`hr`,`none`]))}),Ge=e.object({localId:C.max(64).refine(e=>x.test(e),`localId must be kebab-case`),label:C.max(64).refine(D,`label must not contain control characters`),appearance:e.enum([`dark`,`light`]),colors:Ue,markdown:S(We),meta:S(e.object({author:S(C.max(128).refine(D,`author must not contain control characters`)),description:S(C.max(256).refine(D,`description must not contain control characters`)),version:S(C.max(32).refine(D,`version must not contain control characters`))}))}),Ke=10,qe=500,Je=2*1024,Ye=20,Xe=64*1024,Ze=e.object({id:C,label:C,toolRef:C}),Qe=e.object({header:C.refine(e=>/^[a-zA-Z0-9][a-zA-Z0-9\-_]*$/.test(e),`header name must be alphanumeric with hyphens/underscores only`),source:e.object({kind:e.literal(`jwt-claim`),claim:C,namespace:S(C),tokenSources:S(e.array(e.enum([`id_token`,`access_token`])))})}),A=e.object({imageConstraints:S(je),supportsTools:S(e.boolean())}),j=e.object({url:C,responseFormat:e.literal(`openai-list`),authScheme:S(e.enum([`bearer`,`x-api-key`,`auto`])),betaHeaderName:S(C),modelIdExclude:S(T(e.string(),`modelIdExclude`))}),M=e.object({store:S(e.boolean()),sendMaxOutputTokens:S(e.boolean())}),$e=e.object({id:C,name:S(C),catalogOnly:S(e.boolean()),baseUrl:S(C),wireApi:S(Fe),envKey:S(C),oauthRef:S(C),headers:S(e.record(e.string(),e.string())),models:S(T(E,`models`)),tokenDerivedHeaders:S(T(Qe,`tokenDerivedHeaders`)),modelsEndpoint:S(j),responseBodyPolicy:S(M),allowAuthHeaderOverride:S(e.boolean()),preserveProviderId:S(e.boolean()),requiresIntranet:S(e.boolean())}).extend(A.shape).superRefine((t,n)=>{if(t.catalogOnly===!0){t.wireApi&&n.addIssue({code:e.ZodIssueCode.custom,message:`catalogOnly entries must not declare wireApi; use providerFactories for custom protocols`,path:[`wireApi`]}),t.baseUrl&&n.addIssue({code:e.ZodIssueCode.custom,message:`catalogOnly entries must not declare baseUrl; use providerFactories for custom protocols`,path:[`baseUrl`]});return}t.baseUrl||n.addIssue({code:e.ZodIssueCode.custom,message:`baseUrl is required unless catalogOnly is true`,path:[`baseUrl`]}),t.wireApi||n.addIssue({code:e.ZodIssueCode.custom,message:`wireApi is required unless catalogOnly is true`,path:[`wireApi`]})}),et=e.object({name:C.refine(e=>/^[a-z][a-z0-9-]*$/.test(e),`must be lowercase kebab-case`),bin:S(C)}),tt=e.discriminatedUnion(`kind`,[e.object({kind:e.literal(`pkce`),id:C,name:S(C),clientId:C,authorizeUrl:C,tokenUrl:C,redirectUri:C,scope:C,loopbackPorts:S(e.array(e.number().int().positive())),assumesLoopbackAlways:S(e.boolean()),extraAuthParams:S(e.record(e.string(),e.string())),tokenExchangeEncoding:S(e.enum([`json`,`form`])),subscriptionScoped:S(e.boolean()),oauthBeta:S(C)}),e.object({kind:e.literal(`device-flow`),id:C,name:S(C),clientId:C,deviceCodeUrlTemplate:C,tokenUrlTemplate:C,refreshUrlTemplate:S(C),scope:C,allowCustomDomain:S(e.boolean()),userAgent:S(C)})]),nt=e.object({skills:S(e.boolean()),agents:S(e.boolean()),commands:S(e.boolean()),mcp:S(e.boolean()),cli:S(et),panels:S(Te),actions:S(T(De,`actions`)),menus:S(T(Oe,`menus`)),contextKeys:S(T(ke,`contextKeys`)),providers:S(T($e,`providers`)),oauth:S(T(tt,`oauth`)),statusItems:S(T(Le,`statusItems`)),perfMetrics:S(T(Ze,`perfMetrics`)),renderers:S(we),fileViewers:S(xe),statusWidgets:S(Ce),a2uiComponents:S(Se),contextSources:S(T(ze,`contextSources`)),i18n:S(T(Be,`i18n`)),themePresets:S(T(Ge,`themePresets`)),i18nDir:S(C.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`)),inputEntries:S(C),sigilEntries:S(T(Ie,`sigilEntries`)),a2uiRenderers:S(T(ye,`a2uiRenderers`))}),rt=e.enum(he),it=e.array(e.unknown()).transform(e=>{let t=e.map(e=>rt.safeParse(e)).filter(e=>e.success).map(e=>e.data);return t.length>0?[...new Set(t)]:void 0}),at=/^(onStartup|onCommand:[^\s]+|onView:[^\s]+|onProvider:[^\s]+)$/,ot=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&at.test(e));return t.length>0?[...new Set(t)]:void 0}),st=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&x.test(e));return t.length>0?[...new Set(t)]:void 0}),ct=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&e.trim().length>0);return t.length>0?[...new Set(t)]:void 0}),lt=j.extend({headers:S(e.record(e.string(),e.string()))}),ut=e.record(e.string(),e.unknown()).transform(e=>{let t={};for(let[n,r]of Object.entries(e)){let e=lt.safeParse(r);e.success&&(t[n]=e.data)}return Object.keys(t).length>0?t:void 0}),dt=e.object({baseUrl:C,models:T(E,`codeProviderModels`),responseBodyPolicy:S(M)}).extend(A.shape),ft=e.record(e.string(),e.unknown()).transform(e=>{let t={};for(let[n,r]of Object.entries(e)){let e=dt.safeParse(r);e.success&&e.data.models&&e.data.models.length>0&&(t[n]=e.data)}return Object.keys(t).length>0?t:void 0}),pt=e.object({postinstall:S(C),setup:S(C)}).catchall(C),mt=e.object({otto:S(C)}),ht=e.object({plane:S(e.enum([`host`,`preset`])),id:e.string().regex(x),name:S(e.string()),version:S(e.string()),description:S(e.string()),engines:S(mt),contributes:S(nt),scripts:S(pt),capabilities:S(it),activationEvents:S(ot),dependsOn:S(st),codeProviderIds:S(ct),codeProviderModelsEndpoints:S(ut),codeProviderModels:S(ft)});function gt(e){return e.plane??`preset`}function _t(e){return e.activationEvents?.length?e.activationEvents:[`onStartup`]}function N(e,t){let n;try{n=JSON.parse(e)}catch(e){return y.warn({sourcePath:t,err:String(e)},`plugin manifest invalid JSON, skipped`),null}let r=ht.safeParse(n);return r.success?r.data:(y.warn({sourcePath:t,issues:r.error.issues.map(e=>e.path.join(`.`)||`<root>`)},`plugin manifest missing/invalid "id" (kebab-case required) or not an object, skipped`),null)}const P=r(`@x-otto/coding:plugin-discovery`);function F(e){try{return te(e).isDirectory()}catch{return!1}}const vt=[{id:`otto-plugin-manager`,dir:`<builtin:otto-plugin-manager>`,scope:`builtin`,manifest:{id:`otto-plugin-manager`,name:`Plugin Manager`,description:`otto 插件生命周期管理(create/build/dev/install)—— 随 otto 内置分发,恒定信任、恒定启用。`,version:void 0,contributes:void 0,scripts:void 0,capabilities:void 0,activationEvents:void 0,dependsOn:void 0}},{id:`otto-tui`,dir:`<builtin:otto-tui>`,scope:`builtin`,manifest:{id:`otto-tui`,name:`TUI`,description:`otto 终端交互界面本体(渲染/输入/面板/状态栏)—— 随 otto 内置分发,恒定信任、恒定启用。`,version:void 0,contributes:void 0,scripts:void 0,capabilities:void 0,activationEvents:void 0,dependsOn:void 0}}];function yt(e){let{cwd:t,homedir:n}=e,r=e.disabled?new Set(e.disabled):null,i=le({cwd:t,homedir:n,claudeCompat:!1,workspaceRoot:se(t)}).filter(e=>e.kind===`otto`).map(e=>({dir:m(e.dir,`plugins`),scope:e.scope}));for(let t of e.bundledDirs??[])s(t)&&i.unshift({dir:t,scope:`bundled`});let a=new Map;for(let e of vt)a.set(e.id,e);for(let{dir:e,scope:t}of i){if(!s(e)||!F(e))continue;let n;try{n=p(e).sort()}catch(t){P.warn({root:e,err:String(t)},`failed to read plugins root, skipped`);continue}for(let i of n){let n=m(e,i);if(!F(n))continue;let o=m(n,b);if(!s(o))continue;let c;try{c=d(o,`utf-8`)}catch(e){P.warn({manifestPath:o,err:String(e)},`failed to read plugin manifest, skipped`);continue}let l=N(c,o);l&&(r?.has(l.id)||a.has(l.id)||a.set(l.id,{id:l.id,dir:n,manifest:l,scope:t}))}}return[...a.values()].sort((e,t)=>e.id.localeCompare(t.id))}function I(e){return e.manifest.dependsOn??[]}function bt(e){let t=new Map,n=new Map,r=e.filter(e=>e.scope===`builtin`),i=e.filter(e=>e.scope!==`builtin`),a=new Set(r.map(e=>e.id)),o=new Map;for(let e of i)o.set(e.id,e);let s=e=>o.has(e)||a.has(e),c=!0;for(;c;){c=!1;for(let e of o.values()){let t=I(e).filter(e=>!s(e));t.length>0&&(n.set(e.id,[...new Set(t)]),o.delete(e.id),c=!0)}}let l=new Map,u=new Map;for(let e of o.values()){let t=I(e).filter(e=>o.has(e));l.set(e.id,t.length);for(let n of t){let t=u.get(n);t?t.push(e.id):u.set(n,[e.id])}}let d=[...o.values()].filter(e=>l.get(e.id)===0).map(e=>e.id).sort((e,t)=>e.localeCompare(t)),f=[];for(;d.length>0;){let e=d.shift();f.push(e);let t=[];for(let n of u.get(e)??[]){let e=(l.get(n)??0)-1;l.set(n,e),e===0&&t.push(n)}t.length>0&&(d.push(...t),d.sort((e,t)=>e.localeCompare(t)))}let p=new Set(f);for(let e of o.values())p.has(e.id)||t.set(e.id,xt(e.id,o));return{ordered:[...r,...f.map(e=>o.get(e))],cyclic:t,missingDeps:n}}function xt(e,t){let n=[],r=new Set,i=e;for(;i&&!r.has(i);){n.push(i),r.add(i);let e=t.get(i);if(!e)break;i=I(e).filter(e=>t.has(e)).sort((e,t)=>e.localeCompare(t))[0]}return i===e&&n.push(e),n}const St=1;function Ct(){return`1.0.0`}function L(e){if(e.scope===`builtin`)return!0;let t=e.manifest.engines?.otto;return t?de(t)?ue(Ct(),t):!1:!0}function wt(e){let t=[],n=new Map;for(let r of e)L(r)?t.push(r):n.set(r.id,r.manifest.engines?.otto??`(invalid range)`);return{compatible:t,incompatible:n}}function Tt(e,t,n){if(!e?.length)return t?[...t]:void 0;if(!t?.length)return[...e];if(!n)return[...e,...t];let r=new Map(t.map(e=>[n(e),e])),i=new Set,a=[];for(let t of e){let e=n(t);i.add(e),a.push(r.has(e)?r.get(e):t)}for(let e of t)i.has(n(e))||a.push(e);return a}const R={commands:e=>e.name,agents:e=>e.name,skills:e=>e.name,mcp:e=>e.name,panels:e=>e.id,actions:e=>e.id,menus:null,contextKeys:e=>e.key,statusItems:e=>e.id,renderers:e=>e.id,contextSources:e=>e.id,a2uiComponents:e=>e.type,statusWidgets:e=>e.id,fileViewers:e=>e.id};function Et(...e){let t=e.filter(e=>e!=null);return t.length===0?{}:t.reduce((e,t)=>{let n={};for(let r of Object.keys(R)){let i=R[r],a=Tt(e[r],t[r],i);a&&(n[r]=a)}return n},{})}const z={extensionDetailActions:`extension/detail/actions`},B={statuslineItem:`shell/statusline/item`,menuItem:`shell/menu/item`},Dt={...z,...B};function V(e,t){if(!e)return!0;let n=e.indexOf(`:`);if(n===-1){let n=t[e];return n===!0||typeof n==`string`&&n.length>0}let r=e.slice(0,n),i=e.slice(n+1),a=t[r];return a!=null&&String(a)===i}function Ot(e,t,n,r){let i=new Map((n??[]).map(e=>[e.id,e]));return(t??[]).filter(t=>t.point===e&&V(t.when,r)).map(e=>({menu:e,action:i.get(e.action)})).filter(e=>e.action!=null).sort((e,t)=>(e.menu.order??0)-(t.menu.order??0)).map(({action:e})=>({id:e.id,title:e.title,color:e.color,command:e.command}))}function H(e){let t=e.replace(/\$\{OTTO_HOME\}/g,g);return t===`~`?fe():t.startsWith(`~/`)?fe()+t.slice(1):t}function kt(e,t=s){let n=e.groups.length;if(n===0)return e.map.none;let r=e.groups.filter(e=>e.some(e=>t(H(e)))).length;return r===0?e.map.none:r===n?e.map.all:e.map.partial}function At(e,t){let n={};for(let r of e??[])n[r.key]=kt(r,t);return n}var U=class extends Error{constructor(e){super(`dispatch: 缺少 capability「${e}」——宿主未注入`),this.name=`MissingCapabilityError`}};async function jt(e,t){switch(e.type){case`builtin`:if(!t.builtin)throw new U(`builtin`);await t.builtin(e.id);return;case`mcp`:if(!t.callMcp)throw new U(`mcp`);await t.callMcp(e.server,e.tool,e.params);return;case`open`:if(!t.open)throw new U(`open`);await t.open(e.target);return;case`command`:if(!t.runCommand)throw new U(`command`);await t.runCommand(e.name,e.args);return;case`script`:if(!t.runScript)throw new U(`script`);await t.runScript(e.name);return;default:throw new U(e.type)}}function Mt(e){return e}function W(e){return`${e.kind}\u0000${e.label}`}function Nt(e,t){if(t===``)return 0;let n=e.label.toLowerCase();return n.startsWith(t)?2:e.description&&e.description.toLowerCase().includes(t)?1:n.includes(t)?0:-1}function Pt(e){return e.startsWith(`!`)||e.startsWith(`#`)}function Ft(){let e=new Map,t=new Map;function n(t){for(let n of t){if(Pt(n.value))throw Error(`[plugin-input] sigil value 不得以 '!' 或 '#' 开头(会被误判为 bash/memory 模式):${JSON.stringify(n.value)}`);let t=W(n);e.delete(t),e.set(t,n)}}function r(t){let n=[...e.values()];return t?n.filter(e=>_(e.kind)===t):n}function i(t,n,r=50){let i=t.toLowerCase(),a=[],o=0;for(let t of e.values()){let e=o++;if(n&&_(t.kind)!==n)continue;let r=Nt(t,i);r<0||a.push({entry:t,score:r,order:e})}return a.sort((e,t)=>t.score-e.score||e.order-t.order),a.length<=r?a.map(e=>e.entry):a.slice(0,r).map(e=>e.entry)}function a(e,n){let r=t.get(e);return r||(r=new Set,t.set(e,r)),r.add(n),()=>{r?.delete(n)}}async function o(e,n,r=50){let a=i(e,n,r),o=n?t.get(n):void 0;if(!o||o.size===0)return a;let s=[],c=await Promise.allSettled([...o].map(t=>Promise.resolve().then(()=>t(e,n))));for(let e of c)e.status===`fulfilled`&&s.push(...e.value.filter(e=>!Pt(e.value)));let l=new Set(a.map(e=>W(e))),u=[...a];for(let e of s){let t=W(e);l.has(t)||(l.add(t),u.push(e))}return u.length<=r?u:u.slice(0,r)}function s(t){for(let n of e.values())if(n.label===t)return n}function c(t){let n=!1;for(let[r,i]of e)i.label===t&&(e.delete(r),n=!0);return n}function l(t){for(let[n,r]of e)r.source===t&&e.delete(n)}return{register:n,registerProvider:a,search:i,searchAsync:o,getAll:r,findByLabel:s,unregister:c,unregisterBySource:l}}function It(){return[{label:`Code Review`,description:`请求代码审查`,value:`Please review the following code for correctness, style, security, and performance. Provide specific, actionable feedback.`,kind:`hashtag`,source:`builtin`},{label:`Write Tests`,description:`为代码编写单元测试`,value:`Please write comprehensive unit tests for the following code. Cover normal cases, edge cases, error cases, and ensure high coverage.`,kind:`hashtag`,source:`builtin`},{label:`Explain Code`,description:`解释代码逻辑`,value:`Please explain the following code in detail. Cover the overall architecture, key algorithms, data flow, and any notable design decisions.`,kind:`hashtag`,source:`builtin`}]}const Lt=r(`@x-otto/plugin:file-provider`),Rt=new Set([`node_modules`,`.git`,`dist`,`build`,`out`,`coverage`,`.next`,`.nuxt`,`.turbo`,`.nx`,`.cache`,`.otto`,`.venv`,`__pycache__`]);async function zt(e,t,n){let r=[];async function i(a,o){if(r.length>=t)return;let s;try{s=await pe(a,{withFileTypes:!0})}catch(e){o&&n?.(e);return}for(let n of s){if(r.length>=t)return;if(n.isDirectory()){if(Rt.has(n.name)||n.name.startsWith(`.`))continue;await i(m(a,n.name),!1)}else n.isFile()&&r.push(oe(e,m(a,n.name)))}}return await i(e,!0),r}function Bt(e,t){if(t===``)return 0;let n=e.toLowerCase();return ie(n).startsWith(t)?3:n.startsWith(t)?2:n.includes(t)?1:-1}function Vt(e,t,n,r){let i;try{i=u(m(e,t),`r`);let a=Buffer.allocUnsafe(8192),o=f(i,a,0,a.length,0),s=a.toString(`utf-8`,0,o).split(`
|
|
2
2
|
`).slice(0,n).join(`
|
|
3
|
-
`);return s.length>r?`${s.slice(0,r-1)}…`:s}catch{return``}finally{if(i!==void 0)try{o(i)}catch{}}}function
|
|
3
|
+
`);return s.length>r?`${s.slice(0,r-1)}…`:s}catch{return``}finally{if(i!==void 0)try{o(i)}catch{}}}function Ht(e){let{cwd:t,max:n=20,walkMax:r=5e3,ttlMs:i=1e4,now:a=Date.now}=e,o=null,s=0,c=null,l=new Map;async function u(){return o&&a()-s<i?o:c||(c=zt(t,r,e=>{Lt.warn({cwd:t,err:String(e)},`@file provider workspace walk failed at root — @ panel will show no files`)}).then(e=>(o=e,s=a(),c=null,l=new Map,e)),c)}function d(e){let n=l.get(e);if(n!==void 0)return n;let r=Vt(t,e,20,500);return l.set(e,r),r}return async e=>{let t=await u(),r=e.toLowerCase(),i=[];for(let e of t){let t=Bt(e,r);if(!(t<0)&&(i.push({path:e,score:t}),r===``&&i.length>=n))break}return i.sort((e,t)=>t.score-e.score||e.path.length-t.path.length),i.slice(0,n).map(({path:e})=>({label:e,value:`[@file:${e.replace(/]/g,`\\]`)}]`,kind:`file`,source:`file`,preview:d(e)}))}}const Ut=r(`@x-otto/coding:trust-store`),Wt={"ui.renderer":`tui.renderer`};function Gt(e){return[...new Set(e.map(e=>Wt[e]??e))]}const G=2e3;function K(e,t,r){let o=ae(e),s=ie(e),c=n(o,s,{timeoutMs:G});if(c||a(o,s,{timeoutMs:G})&&(c=n(o,s,{timeoutMs:G})),!c&&r?.requireLock)throw Error(`Failed to acquire trust store lock within ${G}ms; revocation aborted to avoid silently losing it to a concurrent writer`);try{return t()}finally{c&&i(o,s)}}function q(e){try{let t=JSON.parse(d(e,`utf-8`)),n=t?.trusted,r={},i=t?.capabilities;if(i&&typeof i==`object`)for(let[e,t]of Object.entries(i))Array.isArray(t)&&(r[e]=Gt(t.filter(e=>typeof e==`string`)));let a={},o=t?.versions;if(o&&typeof o==`object`)for(let[e,t]of Object.entries(o))typeof t==`string`&&(a[h(e)]=t);return{trusted:Array.isArray(n)?n.filter(e=>typeof e==`string`):[],capabilities:r,versions:a}}catch{return{trusted:[],capabilities:{},versions:{}}}}function Kt(e,t){let n=`${e}.${me()}.tmp`,r;try{r=u(n,`w`),re(r,t),c(r),o(r),r=void 0,ee(n,e)}catch(e){if(r!==void 0)try{o(r)}catch{}try{ne(n)}catch{}throw e}}function J(e,t,n){if(!ce())try{l(ae(e),{recursive:!0});let n={trusted:[...new Set(t.trusted)]};t.capabilities&&Object.keys(t.capabilities).length>0&&(n.capabilities=t.capabilities),t.versions&&Object.keys(t.versions).length>0&&(n.versions=t.versions),Kt(e,JSON.stringify(n,null,2))}catch(t){throw Ut.warn({storePath:e,err:t},`Failed to persist ${n} store`),t}}function qt(e,t){return q(e).trusted.includes(h(t))}function Jt(e,t,n){return K(e,()=>{let r=h(t),i=q(e);return i.trusted.includes(r)?!1:(i.trusted.push(r),J(e,i,n),!0)})}function Yt(e,t,n){return K(e,()=>{let r=h(t),i=q(e),a=i.trusted.filter(e=>e!==r),o={...i.capabilities},s=r in o;delete o[r];let c={...i.versions},l=r in c;return delete c[r],a.length===i.trusted.length&&!s&&!l?!1:(J(e,{trusted:a,capabilities:o,versions:c},n),!0)},{requireLock:!0})}function Xt(e,t){return q(e).capabilities?.[h(t)]??[]}function Zt(e,t,n,r){return K(e,()=>{let i=h(t),a=q(e),o=new Set(a.capabilities?.[i]??[]),s=o.size;for(let e of n)o.add(e);return o.size===s?!1:(J(e,{trusted:a.trusted,capabilities:{...a.capabilities,[i]:[...o]}},r),!0)})}function Y(e,t,n,r,i){return K(e,()=>{let a=h(t),o=q(e),s=new Set(o.capabilities?.[a]??[]),c=s.size;for(let e of n)s.add(e);let l=o.versions?.[a]!==r;return s.size===c&&!l?!1:(J(e,{trusted:o.trusted,capabilities:{...o.capabilities,[a]:[...s]},versions:{...o.versions,[a]:r}},i),!0)})}function Qt(e,t,n){let r=h(t),i=q(e);return i.versions?.[r]===n?i.capabilities?.[r]??[]:[]}const X=r(`@x-otto/plugin:trust`);function Z(){return process.env.OTTO_PLUGIN_TRUST_PATH||m(g,`plugin-trust.json`)}function $t(e,t=Z()){return qt(t,e)}function en(e,t=Z()){Jt(t,e,`plugin-trust`)&&X.info({dir:h(e)},`Plugin trusted`)}function tn(e,t=Z()){Yt(t,e,`plugin-trust`)&&X.info({dir:h(e)},`Plugin trust revoked`)}function nn(e,t=Z()){return Xt(t,e)}function rn(e){try{let t=m(e,b);if(!s(t))return[];let n=N(d(t,`utf8`),t);return n?.capabilities?n.capabilities.filter(e=>v.includes(e)):[]}catch{return[]}}function an(e,t,n=Z()){Zt(n,e,t,`plugin-trust`)&&X.info({dir:h(e),caps:t},`Plugin capabilities granted`)}function on(e,t,n,r=Z()){Y(r,e,t,n,`plugin-trust`)&&X.info({dir:h(e),caps:t,version:n},`Plugin capabilities granted (version-bound)`)}function sn(e,t,n=Z()){return Qt(n,e,t)}function Q(e){try{return p(e).length>0}catch{return!1}}function cn(e,t){let n=h(e),r=s(m(n,`.mcp.json`)),i=Q(m(n,`commands`)),a=!!(t?.scripts?.postinstall||t?.scripts?.setup),o=!!(t?.contributes?.panels&&t.contributes.panels.length>0),c=[`plugin.ts`,`plugin.tsx`].some(e=>s(m(n,e))),l=Q(m(n,`skills`)),u=Q(m(n,`agents`)),d=!!t?.contributes?.cli,f=!!t?.capabilities?.some(e=>v.includes(e));return{mcpJson:r,commands:i,scripts:a,panels:o,pluginEntry:c,skills:l,agents:u,cli:d,highRiskCapabilities:f,any:r||i||a||o||c||l||u||d||f}}function ln(e,t=Z()){let n=cn(e.dir,e.manifest),r=$t(e.dir,t),i=(e.scope===`project`||e.scope===`repository`)&&n.any&&!r,a=new Set(e.manifest?.capabilities??[]),o=e.scope===`bundled`||e.scope===`builtin`?a:new Set(Xt(t,e.dir));return{surface:n,trusted:r,gated:i,pendingCapabilities:v.filter(e=>a.has(e)&&!o.has(e)),grantedCapabilities:v.filter(e=>a.has(e)&&o.has(e))}}function $(e){return e.split(/[-_]/)[0]??e}var un=class extends t{entries=new Map;currentLocale=`zh`;register(e){this.entries.clear();for(let t of e)this.entries.set(`${t.pluginId}:${t.id}`,t.translations)}get size(){return this.entries.size}getLocale(){return this.currentLocale}setLocale(e){e!==this.currentLocale&&(this.currentLocale=e,this.emit(`change`,e))}t(e,t=this.currentLocale){let n=this.entries.get(e);if(!n)return e;let r=n[t];if(r!==void 0)return r;let i=$(t);for(let[e,t]of Object.entries(n))if($(e)===i)return t;return n.en===void 0?Object.values(n)[0]??e:n.en}};export{z as EXTENSION_POINTS,v as HIGH_RISK_CAPABILITIES,x as ID_RE,qe as MAX_I18N_ENTRIES_PER_PLUGIN,Xe as MAX_I18N_FILE_BYTES,Ye as MAX_I18N_LOCALE_FILES_PER_PLUGIN,Je as MAX_I18N_TRANSLATION_BYTES,Ke as MAX_THEME_PRESETS_PER_PLUGIN,St as PLUGIN_API_VERSION,he as PLUGIN_CAPABILITIES,b as PLUGIN_MANIFEST_FILENAME,Dt as POINT,un as PluginI18nRegistry,B as SHELL_POINTS,It as createBuiltinHashtags,Ht as createFileProvider,Ft as createPluginInputRegistry,Mt as definePlugin,cn as detectPluginExecutableSurface,yt as discoverPlugins,jt as dispatch,kt as evaluateContextKey,At as evaluateContextKeys,ln as evaluatePluginTrust,V as evaluateWhen,H as expandPath,wt as filterEngineCompatible,Y as grantCapabilitiesWithVersion,an as grantPluginCapabilities,on as grantPluginCapabilitiesWithVersion,Qt as grantedCapabilitiesForVersion,nn as grantedPluginCapabilities,sn as grantedPluginCapabilitiesForVersion,L as isEngineCompatible,qt as isPathTrusted,$t as isPluginTrusted,Et as mergeContributions,N as parsePluginManifest,Z as pluginTrustStorePath,rn as readHighRiskCapabilities,Ot as resolveActions,_t as resolveActivationEvents,gt as resolveManifestPlane,_ as sigilOf,bt as topoSortPlugins,Jt as trustPath,en as trustPlugin,Yt as untrustPath,tn as untrustPlugin};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x-otto/plugin",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.6",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist",
|
|
6
6
|
"README.md"
|
|
@@ -22,10 +22,11 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"semver": "7.7.4",
|
|
24
24
|
"zod": "4.3.6",
|
|
25
|
-
"@x-otto/interchange": "0.1.0-alpha.3",
|
|
26
25
|
"@x-otto/env": "0.1.0-alpha.6",
|
|
26
|
+
"@x-otto/provider": "0.1.0-alpha.6",
|
|
27
|
+
"@x-otto/interchange": "0.1.0-alpha.5",
|
|
27
28
|
"@x-otto/shared": "0.1.0-alpha.6",
|
|
28
|
-
"@x-otto/
|
|
29
|
+
"@x-otto/hook-contracts": "0.0.1-alpha.3"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@types/semver": "7.7.1"
|