@x-otto/plugin 0.1.0-alpha.5 → 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 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
- plugin-trust.ts # trust model (HIGH_RISK_CAPABILITIES)
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`): provider / tools / tui.renderer / network / wire-protocol /
82
- a2ui.component / panel.backend / input.resolver / a2ui.renderer / agent.dispatch / service. When an already-trusted
83
- plugin is upgraded and declares one of these capabilities for the first time, it must be re-confirmed.
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
@@ -1448,6 +1448,10 @@ declare const scriptsSchema: z.ZodObject<{
1448
1448
  }, z.core.$catchall<z.ZodString>>;
1449
1449
  type PluginScripts = z.infer<typeof scriptsSchema>;
1450
1450
  declare const manifestSchema: z.ZodObject<{
1451
+ plane: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
1452
+ host: "host";
1453
+ preset: "preset";
1454
+ }>>>;
1451
1455
  id: z.ZodString;
1452
1456
  name: z.ZodCatch<z.ZodOptional<z.ZodString>>;
1453
1457
  version: z.ZodCatch<z.ZodOptional<z.ZodString>>;
@@ -2012,6 +2016,13 @@ declare const manifestSchema: z.ZodObject<{
2012
2016
  }> | undefined, Record<string, unknown>>>>>;
2013
2017
  }, z.core.$strip>;
2014
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;
2015
2026
  /** 有效激活事件:字段缺失或空 → `['onStartup']`(存量插件零行为变化,RFC-105 D7)。 */
2016
2027
  declare function resolveActivationEvents(manifest: PluginManifest): string[];
2017
2028
  /**
@@ -2429,6 +2440,13 @@ interface PluginHooks {
2429
2440
  taskId: string;
2430
2441
  error: string;
2431
2442
  }) => void | Promise<void>;
2443
+ /**
2444
+ * 任务被取消时触发(`task.cancelled`,08-13 终局 review 观察项:纯 observer,
2445
+ * 对自迭代感知"子任务被取消"有真实价值——被取消的任务不该重复派发)。
2446
+ */
2447
+ taskCancelled?: (ctx: {
2448
+ taskId: string;
2449
+ }) => void | Promise<void>;
2432
2450
  /**
2433
2451
  * 任务(子 agent task)建档时触发(`task.created`),先于 `task.started` 触发。
2434
2452
  * 此时 agent 归属尚未确定(`TaskHookInfo` 无 subagent 字段),仅 taskId 可用。
@@ -2450,7 +2468,9 @@ interface PluginHooks {
2450
2468
  *
2451
2469
  * 安全边界(R1'/R10):
2452
2470
  * - 只作用于白名单 timing 的**受控中间产物**(`system.prompt.transform` /
2453
- * `chat.params` / `messages.transform`)——不开放历史消息、用户输入、tool 参数;
2471
+ * `chat.params` / `messages.transform`)——不开放原始会话存储与工具执行参数
2472
+ * (messages.transform 会看到待发送消息列表,含历史派生内容——白名单设计内的
2473
+ * 能力,全程审计);
2454
2474
  * - 白名单编译期强制(`WaterfallTiming` 从 `HookPayloadMap` Pick),装载器运行时校验兜底,
2455
2475
  * 白名单外 timing 一律拒绝注册(fail-closed);
2456
2476
  * - `system.prompt.transform` 默认只开放 `systemTail`(volatile,cache 断点后,逐轮可变)
@@ -2509,9 +2529,18 @@ interface PluginContext {
2509
2529
  * 未注入 `wireProtocolRegistry` 时(如轻量测试装配)恒返回 `undefined`。
2510
2530
  */
2511
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>;
2512
2541
  /**
2513
2542
  * 解析该插件自身 provider 的完整凭据(RFC-148 M3,`resolveProviderCredential` 的姊妹方法):
2514
- * 后者只给裸 key(多数代码式 provider 够用,如 GitHub Copilot),本方法额外给 `mode`/`beta`
2543
+ * 前者只给裸 key(多数代码式 provider 够用,如 GitHub Copilot),本方法额外给 `mode`/`beta`
2515
2544
  * ——`anthropic-messages` 协议实现需要区分 api_key 与 oauth 模式(决定是否启用 Claude Code
2516
2545
  * 客户端伪装)。跨插件隔离规则与 `resolveProviderCredential` 一致(同一 owned-namespace
2517
2546
  * 白名单校验,装载器实现层面共享同一份判定逻辑,见 `plugin-module-manager.ts`)。
@@ -2645,7 +2674,7 @@ interface PluginTool {
2645
2674
  type MonitorEvent = {
2646
2675
  type: 'turn.completed';
2647
2676
  sessionId: string;
2648
- tokens: {
2677
+ tokenUsage: {
2649
2678
  input: number;
2650
2679
  output: number;
2651
2680
  };
@@ -2691,7 +2720,32 @@ type MonitorEvent = {
2691
2720
  type: 'tool.unregistered';
2692
2721
  toolName: string;
2693
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[];
2694
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';
2695
2749
  /**
2696
2750
  * 插件后台观察者(RFC-129 D6,capability: `monitor`)。`onEvent` 抛错被装载器 100% 隔离
2697
2751
  * (try/catch 吞掉 + 记录日志,绝不 rethrow、绝不阻塞引擎主流程或影响其他插件/hook,R5)——
@@ -3195,6 +3249,20 @@ declare function trustPath(storePath: string, key: string, label: string): boole
3195
3249
  * 调用方(`extension-plugin.ts` / `misc.ts` 的撤销入口)应捕获并如实告知用户。
3196
3250
  */
3197
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[];
3198
3266
  //#endregion
3199
3267
  //#region src/plugin-trust.d.ts
3200
3268
  /** 信任白名单落盘路径(用户级)。`OTTO_PLUGIN_TRUST_PATH` 可覆盖(测试隔离)。调用时求值。 */
@@ -3219,6 +3287,18 @@ declare function grantedPluginCapabilities(pluginDir: string, storePath?: string
3219
3287
  declare function readHighRiskCapabilities(sourceDir: string): string[];
3220
3288
  /** 显式授予插件一组高危能力(幂等,持久化;用户逐项/整体确认后调用)。 */
3221
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[];
3222
3302
  /** 插件目录受信任门面探测结果(可执行面 ① + 内容面 ② + 高危能力声明 ③)。 */
3223
3303
  interface PluginExecutableSurface {
3224
3304
  mcpJson: boolean;
@@ -3339,5 +3419,5 @@ declare class PluginI18nRegistry extends TypedEventEmitter<PluginI18nRegistryEve
3339
3419
  t(fullKey: string, locale?: string): string;
3340
3420
  }
3341
3421
  //#endregion
3342
- 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 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, 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 };
3343
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 zt(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=It(t,r,e=>{Pt.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=Rt(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=Lt(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 Bt=r(`@x-otto/coding:trust-store`),Vt={"ui.renderer":`tui.renderer`};function Ht(e){return[...new Set(e.map(e=>Vt[e]??e))]}const q=2e3;function J(e,t,r){let o=ie(e),s=m(e),c=n(o,s,{timeoutMs:q});if(c||a(o,s,{timeoutMs:q})&&(c=n(o,s,{timeoutMs:q})),!c&&r?.requireLock)throw Error(`Failed to acquire trust store lock within ${q}ms; revocation aborted to avoid silently losing it to a concurrent writer`);try{return t()}finally{c&&i(o,s)}}function Y(e){try{let t=JSON.parse(d(e,`utf-8`)),n=t?.trusted,r=t?.capabilities,i={};if(r&&typeof r==`object`)for(let[e,t]of Object.entries(r))Array.isArray(t)&&(i[e]=Ht(t.filter(e=>typeof e==`string`)));return{trusted:Array.isArray(n)?n.filter(e=>typeof e==`string`):[],capabilities:i}}catch{return{trusted:[],capabilities:{}}}}function Ut(e,t){let n=`${e}.${fe()}.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 X(e,t,n){if(!se())try{l(ie(e),{recursive:!0});let n={trusted:[...new Set(t.trusted)]};t.capabilities&&Object.keys(t.capabilities).length>0&&(n.capabilities=t.capabilities),Ut(e,JSON.stringify(n,null,2))}catch(t){throw Bt.warn({storePath:e,err:t},`Failed to persist ${n} store`),t}}function Wt(e,t){return Y(e).trusted.includes(g(t))}function Gt(e,t,n){return J(e,()=>{let r=g(t),i=Y(e);return i.trusted.includes(r)?!1:(i.trusted.push(r),X(e,i,n),!0)})}function Kt(e,t,n){return J(e,()=>{let r=g(t),i=Y(e),a=i.trusted.filter(e=>e!==r),o={...i.capabilities},s=r in o;return delete o[r],a.length===i.trusted.length&&!s?!1:(X(e,{trusted:a,capabilities:o},n),!0)},{requireLock:!0})}function qt(e,t){return Y(e).capabilities?.[g(t)]??[]}function Jt(e,t,n,r){return J(e,()=>{let i=g(t),a=Y(e),o=new Set(a.capabilities?.[i]??[]),s=o.size;for(let e of n)o.add(e);return o.size===s?!1:(X(e,{trusted:a.trusted,capabilities:{...a.capabilities,[i]:[...o]}},r),!0)})}const Z=r(`@x-otto/plugin:trust`);function Q(){return process.env.OTTO_PLUGIN_TRUST_PATH||h(_,`plugin-trust.json`)}function Yt(e,t=Q()){return Wt(t,e)}function Xt(e,t=Q()){Gt(t,e,`plugin-trust`)&&Z.info({dir:g(e)},`Plugin trusted`)}function Zt(e,t=Q()){Kt(t,e,`plugin-trust`)&&Z.info({dir:g(e)},`Plugin trust revoked`)}function Qt(e,t=Q()){return qt(t,e)}function $t(e){try{let t=h(e,C);if(!s(t))return[];let n=P(d(t,`utf8`),t);return n?.capabilities?n.capabilities.filter(e=>x.includes(e)):[]}catch{return[]}}function en(e,t,n=Q()){Jt(n,e,t,`plugin-trust`)&&Z.info({dir:g(e),caps:t},`Plugin capabilities granted`)}function $(e){try{return p(e).length>0}catch{return!1}}function tn(e,t){let n=g(e),r=s(h(n,`.mcp.json`)),i=$(h(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(h(n,e))),l=$(h(n,`skills`)),u=$(h(n,`agents`)),d=!!t?.contributes?.cli,f=!!t?.capabilities?.some(e=>x.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 nn(e,t=Q()){let n=tn(e.dir,e.manifest),r=Yt(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(qt(t,e.dir));return{surface:n,trusted:r,gated:i,pendingCapabilities:x.filter(e=>a.has(e)&&!o.has(e)),grantedCapabilities:x.filter(e=>a.has(e)&&o.has(e))}}function rn(e){return e.split(/[-_]/)[0]??e}var an=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=rn(t);for(let[e,t]of Object.entries(n))if(rn(e)===i)return t;return n.en===void 0?Object.values(n)[0]??e:n.en}};export{B as EXTENSION_POINTS,x as HIGH_RISK_CAPABILITIES,w as ID_RE,Ke as MAX_I18N_ENTRIES_PER_PLUGIN,Ye as MAX_I18N_FILE_BYTES,Je as MAX_I18N_LOCALE_FILES_PER_PLUGIN,qe as MAX_I18N_TRANSLATION_BYTES,Ge as MAX_THEME_PRESETS_PER_PLUGIN,bt as PLUGIN_API_VERSION,pe as PLUGIN_CAPABILITIES,C as PLUGIN_MANIFEST_FILENAME,Tt as POINT,an as PluginI18nRegistry,V as SHELL_POINTS,Nt as createBuiltinHashtags,zt as createFileProvider,Mt as createPluginInputRegistry,kt as definePlugin,tn as detectPluginExecutableSurface,_t as discoverPlugins,Ot as dispatch,W as evaluateContextKey,Dt as evaluateContextKeys,nn as evaluatePluginTrust,H as evaluateWhen,U as expandPath,St as filterEngineCompatible,en as grantPluginCapabilities,Qt as grantedPluginCapabilities,R as isEngineCompatible,Wt as isPathTrusted,Yt as isPluginTrusted,wt as mergeContributions,P as parsePluginManifest,Q as pluginTrustStorePath,$t as readHighRiskCapabilities,Et as resolveActions,ht as resolveActivationEvents,y as sigilOf,vt as topoSortPlugins,Gt as trustPath,Xt as trustPlugin,Kt as untrustPath,Zt as untrustPlugin};
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.5",
3
+ "version": "0.1.0-alpha.6",
4
4
  "files": [
5
5
  "dist",
6
6
  "README.md"
@@ -22,11 +22,11 @@
22
22
  "dependencies": {
23
23
  "semver": "7.7.4",
24
24
  "zod": "4.3.6",
25
- "@x-otto/hook-contracts": "0.0.1-alpha.2",
26
- "@x-otto/provider": "0.1.0-alpha.5",
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/interchange": "0.1.0-alpha.4",
29
- "@x-otto/env": "0.1.0-alpha.6"
29
+ "@x-otto/hook-contracts": "0.0.1-alpha.3"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/semver": "7.7.1"