@x-otto/plugin 0.1.0-alpha.7 → 0.1.0-alpha.9
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/dist/index.d.ts +62 -4
- package/dist/index.js +1 -1
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { HookAbortSignal, HookPayloadMap, WaterfallTiming } from "@x-otto/hook-c
|
|
|
22
22
|
* - 非高危 = 纯数据 / 用户显式触发 / 写面物理隔离 / 单向推送不进入模型上下文。
|
|
23
23
|
*/
|
|
24
24
|
/** 能力声明词表(RFC-105 D8)。详见各能力注释。 */
|
|
25
|
-
declare const PLUGIN_CAPABILITIES: readonly ["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", "config", "credentials", "cli.command", "tui.command", "events", "settings.read"];
|
|
25
|
+
declare const PLUGIN_CAPABILITIES: readonly ["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", "config", "credentials", "cli.command", "tui.command", "events", "settings.read", "schedule"];
|
|
26
26
|
type PluginCapability = (typeof PLUGIN_CAPABILITIES)[number];
|
|
27
27
|
/**
|
|
28
28
|
* 高危能力轴(RFC-105 D8):既有布尔信任**不**自动授予这些轴——已信任插件升级后首次
|
|
@@ -2182,7 +2182,7 @@ declare const manifestSchema: z.ZodObject<{
|
|
|
2182
2182
|
postinstall: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
2183
2183
|
setup: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
2184
2184
|
}, z.core.$catchall<z.ZodString>>>>;
|
|
2185
|
-
capabilities: z.ZodCatch<z.ZodOptional<z.ZodPipe<z.ZodArray<z.ZodUnknown>, z.ZodTransform<("config" | "events" | "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" | "credentials" | "cli.command" | "tui.command" | "settings.read")[] | undefined, unknown[]>>>>;
|
|
2185
|
+
capabilities: z.ZodCatch<z.ZodOptional<z.ZodPipe<z.ZodArray<z.ZodUnknown>, z.ZodTransform<("config" | "events" | "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" | "credentials" | "cli.command" | "tui.command" | "settings.read" | "schedule")[] | undefined, unknown[]>>>>;
|
|
2186
2186
|
activationEvents: z.ZodCatch<z.ZodOptional<z.ZodPipe<z.ZodArray<z.ZodUnknown>, z.ZodTransform<string[] | undefined, unknown[]>>>>;
|
|
2187
2187
|
dependsOn: z.ZodCatch<z.ZodOptional<z.ZodPipe<z.ZodArray<z.ZodUnknown>, z.ZodTransform<string[] | undefined, unknown[]>>>>;
|
|
2188
2188
|
codeProviderIds: z.ZodCatch<z.ZodOptional<z.ZodPipe<z.ZodArray<z.ZodUnknown>, z.ZodTransform<string[] | undefined, unknown[]>>>>;
|
|
@@ -3327,7 +3327,11 @@ interface PluginModule {
|
|
|
3327
3327
|
* 由宿主在命令执行时注入(装载时不持有 TUI 会话状态)。
|
|
3328
3328
|
*
|
|
3329
3329
|
* 一次性边界(继承 RFC-410 D5 纪律):handler 只做查询/交互式一次性命令,**不得**做
|
|
3330
|
-
* 生命周期管理/后台常驻/detached
|
|
3330
|
+
* 生命周期管理/后台常驻/detached 进程。
|
|
3331
|
+
*
|
|
3332
|
+
* **超时语义(RFC-440 §M2 D8)**:宿主以 60s 超时包装执行。超时后宿主**停止等待**并向用户
|
|
3333
|
+
* 提示,但**无法强杀** handler(进程内无独立线程)——挂起的 handler 会作为孤儿继续运行。
|
|
3334
|
+
* 因此"不常驻、按时返回"是插件作者必须自觉遵守的契约,而非宿主能强制的边界。
|
|
3331
3335
|
*/
|
|
3332
3336
|
slashCommands?: Record<string, PluginSlashCommandHandler>;
|
|
3333
3337
|
/** 激活钩子:装载完成、贡献注册后调用。 */
|
|
@@ -3420,7 +3424,10 @@ interface PluginSlashCommandContext {
|
|
|
3420
3424
|
}
|
|
3421
3425
|
/**
|
|
3422
3426
|
* RFC-440:TUI 斜杠命令 handler 签名。返回 void(TUI 内以 toast/面板呈现结果,
|
|
3423
|
-
*
|
|
3427
|
+
* 无进程退出码概念)。
|
|
3428
|
+
*
|
|
3429
|
+
* 宿主以 60s 超时包装调用:handler 挂起时宿主停止等待并 toast 提示,TUI 不被无限期阻塞;
|
|
3430
|
+
* 但进程内**无法强杀** handler,超时后它仍可能在后台运行(RFC-440 §M2 D8/R3)。
|
|
3424
3431
|
*/
|
|
3425
3432
|
type PluginSlashCommandHandler = (ctx: PluginSlashCommandContext) => Promise<void> | void;
|
|
3426
3433
|
/**
|
|
@@ -3472,6 +3479,57 @@ interface PluginHostCapabilities {
|
|
|
3472
3479
|
}) => Promise<{
|
|
3473
3480
|
jobId: string;
|
|
3474
3481
|
}>;
|
|
3482
|
+
/**
|
|
3483
|
+
* RFC-441 M2:操作宿主调度器(**扁平端口**——隔离路径 stub 是扁平方法名→函数 Proxy,
|
|
3484
|
+
* 嵌套 `schedule:{...}` 必然 TypeError,评审 P0-2)。需 manifest 声明
|
|
3485
|
+
* `capabilities: ["schedule"]`(高危,fail-closed);未授予时均为 `undefined`。
|
|
3486
|
+
*
|
|
3487
|
+
* **安全契约(宿主侧强制)**:
|
|
3488
|
+
* - origin 固定 `'model'`(工具场景——插件是工具载体,发起者仍是模型,评审 P1);
|
|
3489
|
+
* 插件不可自选 origin,不可伪装 user/main/plugin。
|
|
3490
|
+
* - cron/锁/持久化/terminate 咽喉点全在宿主侧(RFC-443 内核留主线程)。
|
|
3491
|
+
* - 数据形状完全对齐 ScheduleCapability(create 含 name/target echo,list 含
|
|
3492
|
+
* enabled/recurring/last*——工具结果文案依赖,不缩减,评审 P1)。
|
|
3493
|
+
*/
|
|
3494
|
+
scheduleCreate?: (input: {
|
|
3495
|
+
cron: string;
|
|
3496
|
+
prompt: string;
|
|
3497
|
+
recurring: boolean;
|
|
3498
|
+
name?: string;
|
|
3499
|
+
maxDurationMs?: number;
|
|
3500
|
+
target?: 'job' | 'session';
|
|
3501
|
+
modelId?: string;
|
|
3502
|
+
thinkingLevel?: 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
3503
|
+
eventTrigger?: {
|
|
3504
|
+
source: string;
|
|
3505
|
+
event: string;
|
|
3506
|
+
filter?: {
|
|
3507
|
+
sessionId?: string;
|
|
3508
|
+
};
|
|
3509
|
+
};
|
|
3510
|
+
}) => Promise<{
|
|
3511
|
+
taskId: string;
|
|
3512
|
+
nextFireAt: number;
|
|
3513
|
+
name: string;
|
|
3514
|
+
target?: 'job' | 'session';
|
|
3515
|
+
}>;
|
|
3516
|
+
scheduleList?: () => Promise<Array<{
|
|
3517
|
+
id: string;
|
|
3518
|
+
name: string;
|
|
3519
|
+
cronExpression: string;
|
|
3520
|
+
enabled: boolean;
|
|
3521
|
+
recurring: boolean;
|
|
3522
|
+
nextFireAt: number;
|
|
3523
|
+
lastFiredAt?: number;
|
|
3524
|
+
lastFireFailed?: boolean;
|
|
3525
|
+
lastFireError?: string;
|
|
3526
|
+
}>>;
|
|
3527
|
+
scheduleDelete?: (taskId: string) => Promise<boolean>;
|
|
3528
|
+
scheduleRun?: (taskId: string) => Promise<void>;
|
|
3529
|
+
scheduleCancel?: (taskId: string) => Promise<{
|
|
3530
|
+
ok: boolean;
|
|
3531
|
+
reason?: string;
|
|
3532
|
+
}>;
|
|
3475
3533
|
/**
|
|
3476
3534
|
* RFC-318 D2(M1):读取一段会话轨迹(**只读**)。需 manifest 声明
|
|
3477
3535
|
* `capabilities: ["session.read"]`(高危,fail-closed);未授予时本字段为 `undefined`。
|
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 oe,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 pe,sigilOf as _}from"@x-otto/interchange";import{readdir as me}from"node:fs/promises";import{randomUUID as he}from"node:crypto";const v=`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,config,credentials,cli.command,tui.command,events,settings.read`.split(`,`),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,config:!1,credentials:!0,"cli.command":!0,"tui.command":!0,events:!1,"settings.read":!1},y=Object.freeze(Object.keys(ge).filter(e=>ge[e])),b=r(`@x-otto/coding:plugin-manifest`),x=`otto-plugin.json`,S=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,C=e=>e.optional().catch(void 0),w=e.string().refine(e=>e.trim().length>0,`must be non-empty`),_e=e.object({id:w,title:w,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w)}),ve=e.object({role:C(w),toolName:C(w),contentType:C(w)}).refine(e=>e.role!=null||e.toolName!=null||e.contentType!=null,{message:`matcher must declare at least one of role/toolName/contentType`}),ye=e.object({id:w,matcher:ve,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w)}),be=e.object({id:w,matcher:ve,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w)}),xe=e.object({extensions:C(e.array(w)),filenames:C(e.array(w)),glob:C(e.array(w))}).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`}),Se=T(e.object({id:w,label:w,matcher:xe,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w),order:C(e.number())}),`fileViewers`),Ce=T(e.object({type:w,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w)}),`a2uiComponents`),we=T(e.object({id:w,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w),order:C(e.number()),tickMs:C(e.number())}),`statusWidgets`),Te=T(ye,`renderers`),Ee=T(_e,`panels`),De=e.discriminatedUnion(`type`,[e.object({type:e.literal(`builtin`),id:w}),e.object({type:e.literal(`mcp`),server:w,tool:w,params:C(e.record(e.string(),e.unknown()))}),e.object({type:e.literal(`open`),target:w}),e.object({type:e.literal(`command`),name:w,args:C(w)}),e.object({type:e.literal(`script`),name:w})]),Oe=e.object({id:w,title:w,command:De,color:C(e.enum([`accent`,`amber`,`red`,`green`,`gray`]))}),ke=e.object({action:w,point:w,when:C(w),order:C(e.number())}),Ae=e.object({key:w,groups:e.array(e.array(w)),map:e.object({none:w,partial:w,all:w})});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(`; `));b.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 je=e.object({input:e.number().min(0),output:e.number().min(0),cacheRead:e.number().min(0),cacheWrite:e.number().min(0)}),Me=e.object({maxImagesPerRequest:e.number().int().positive(),maxDimensionPxIfOverLimit:C(e.number().int().positive())}),Ne=e.enum([`planning`,`knowledge`,`coding`,`reasoning`,`vision`,`speed`,`long-context`]),Pe=e.enum([`low`,`medium`,`high`,`xhigh`,`max`]),Fe=e.enum([`enabled`,`adaptive`]),Ie=e.object({id:w,contextWindow:e.number().int().positive(),maxOutput:e.number().int().positive(),cost:C(je),strengths:C(e.array(Ne).min(1)),reasoning:C(e.boolean()),input:C(e.array(e.enum([`text`,`image`])).min(1)),thinkingLevels:C(e.array(Pe).min(1)),thinkingMode:C(Fe)}),Le=e.enum([`openai-completions`,`openai-responses`,`anthropic-messages`]),Re=e.object({label:w,value:w.refine(e=>!e.startsWith(`!`)&&!e.startsWith(`#`),`value must not start with '!' or '#' (mode-prefix collision)`),kind:e.enum([`resource`,`hashtag`]),description:C(w),resolverId:C(w)}),ze=e.object({id:w,dataSource:e.object({contextKey:w}),point:C(w),when:C(w),order:C(e.number())}),Be=e.union([w,e.object({file:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`)})]),Ve=e.object({id:w,priority:C(e.number()),content:Be}),He=e.object({id:w,translations:e.record(e.string(),e.string())}),Ue=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/,E=e=>!Ue.test(e),We=/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/,D=t=>e.string().max(16).refine(e=>We.test(e),`${t} must be a #RRGGBB or #RGB hex color`),Ge=e.object({brand:D(`brand`),brandShimmer:D(`brandShimmer`),accent:D(`accent`),accentDim:D(`accentDim`),accentBright:D(`accentBright`),text:C(D(`text`)),inactive:D(`inactive`),secondary:D(`secondary`),subtle:D(`subtle`),replyPrefix:D(`replyPrefix`),success:D(`success`),error:D(`error`),warning:D(`warning`),special:D(`special`),suggestion:D(`suggestion`),permission:D(`permission`),codeText:D(`codeText`),diffAdd:D(`diffAdd`),diffRemove:D(`diffRemove`),diffAddBg:D(`diffAddBg`),diffRemoveBg:D(`diffRemoveBg`),panelBorder:D(`panelBorder`),overlayBg:D(`overlayBg`),tagBg:D(`tagBg`),toolText:D(`toolText`),focusBorder:D(`focusBorder`),selection:D(`selection`)}),O=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(`.`)),Ke=e.strictObject({heading1:C(O),heading2:C(O),headingWeak:C(O),listMarker:C(O),listMarkerMuted:C(O),quoteBar:C(O),quoteText:C(O),link:C(O),inlineCode:C(O),codeFence:C(O),tableHeader:C(O),tableDivider:C(O),listIndent:C(e.union([e.literal(2),e.literal(3)])),listMarkers:C(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:C(e.enum([`hr`,`none`]))}),qe=e.object({localId:w.max(64).refine(e=>S.test(e),`localId must be kebab-case`),label:w.max(64).refine(E,`label must not contain control characters`),appearance:e.enum([`dark`,`light`]),colors:Ge,markdown:C(Ke),meta:C(e.object({author:C(w.max(128).refine(E,`author must not contain control characters`)),description:C(w.max(256).refine(E,`description must not contain control characters`)),version:C(w.max(32).refine(E,`version must not contain control characters`))}))}),Je=10,Ye=500,Xe=2*1024,Ze=20,Qe=64*1024,$e=32,et=4,tt=8*1024,nt=256,rt=e.object({id:w,label:w,toolRef:w}),it=e.object({header:w.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:w,namespace:C(w),tokenSources:C(e.array(e.enum([`id_token`,`access_token`])))})}),k=e.object({imageConstraints:C(Me),supportsTools:C(e.boolean())}),A=e.object({url:w,responseFormat:e.literal(`openai-list`),authScheme:C(e.enum([`bearer`,`x-api-key`,`auto`])),betaHeaderName:C(w),modelIdExclude:C(T(e.string(),`modelIdExclude`))}),j=e.object({store:C(e.boolean()),sendMaxOutputTokens:C(e.boolean())}),at=e.object({id:w,name:C(w),catalogOnly:C(e.boolean()),baseUrl:C(w),wireApi:C(Le),envKey:C(w),oauthRef:C(w),headers:C(e.record(e.string(),e.string())),models:C(T(Ie,`models`)),tokenDerivedHeaders:C(T(it,`tokenDerivedHeaders`)),modelsEndpoint:C(A),responseBodyPolicy:C(j),allowAuthHeaderOverride:C(e.boolean()),preserveProviderId:C(e.boolean()),requiresIntranet:C(e.boolean())}).extend(k.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`]})}),ot=e.object({name:w.refine(e=>/^[a-z][a-z0-9-]*$/.test(e),`must be lowercase kebab-case`),bin:C(w)}),st=e.object({name:w.refine(e=>/^[a-z][a-z0-9-]*$/.test(e),`must be lowercase kebab-case`),summary:w.max(200).refine(E,`must not contain control characters`),i18nKey:C(w)}),ct=e.object({name:w.refine(e=>/^[a-z][a-z0-9-]*$/.test(e),`must be lowercase kebab-case`),description:w.max(200).refine(E,`must not contain control characters`),group:C(w),i18nKey:C(w)}),lt=e.discriminatedUnion(`kind`,[e.object({kind:e.literal(`pkce`),id:w,name:C(w),clientId:w,authorizeUrl:w,tokenUrl:w,redirectUri:w,scope:w,loopbackPorts:C(e.array(e.number().int().positive())),assumesLoopbackAlways:C(e.boolean()),extraAuthParams:C(e.record(e.string(),e.string())),tokenExchangeEncoding:C(e.enum([`json`,`form`])),subscriptionScoped:C(e.boolean()),oauthBeta:C(w)}),e.object({kind:e.literal(`device-flow`),id:w,name:C(w),clientId:w,deviceCodeUrlTemplate:w,tokenUrlTemplate:w,refreshUrlTemplate:C(w),scope:w,allowCustomDomain:C(e.boolean()),userAgent:C(w)})]),ut=e.object({type:e.enum([`string`,`number`,`integer`,`boolean`,`array`,`object`]),title:C(w),description:C(w),default:e.unknown().optional(),enum:C(e.array(e.unknown())),minimum:C(e.number()),maximum:C(e.number()),pattern:C(w),items:e.unknown().optional(),properties:C(e.record(e.string(),e.unknown()))});function M(e,t){if(t>4||typeof e!=`object`||!e)return!1;let n=e;if(typeof n.description==`string`&&n.description.length>256)return!1;if(n.properties&&typeof n.properties==`object`){let e=n.properties;if(Object.keys(e).length>32)return!1;for(let n of Object.values(e))if(!M(n,t+1))return!1}return!(n.items&&typeof n.items==`object`&&!M(n.items,t+1))}const dt=e.unknown().transform(e=>{if(typeof e!=`object`||!e)return;let t=JSON.stringify(e).length;if(t>8192){b.warn({bytes:t,limit:tt},`config schema exceeds byte limit, dropped`);return}if(!M(e,0)){b.warn(`config schema exceeds depth/field/description limit, dropped`);return}let n=ut.safeParse(e);if(!n.success){b.warn({err:n.error.issues},`config schema invalid, dropped`);return}return n.data}),ft=e.object({id:w,label:C(w),entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),capabilities:C(e.object({atomic:C(e.boolean()),permSecure:C(e.boolean()),crossProcessLock:C(e.boolean()),watchable:C(e.boolean())}))}),pt=e.object({type:w.refine(e=>{let t=e.split(`.`);return t.length>=2&&t.every(e=>e.length>0)},`event type must be <pluginId>.<event> namespaced format`),description:C(w),payloadSchema:C(e.record(e.string(),e.unknown()))}),mt=e.object({skills:C(e.boolean()),agents:C(e.boolean()),commands:C(e.boolean()),mcp:C(e.boolean()),cli:C(ot),cliCommands:C(T(st,`cliCommands`)),slashCommands:C(T(ct,`slashCommands`)),panels:C(Ee),actions:C(T(Oe,`actions`)),menus:C(T(ke,`menus`)),contextKeys:C(T(Ae,`contextKeys`)),providers:C(T(at,`providers`)),oauth:C(T(lt,`oauth`)),statusItems:C(T(ze,`statusItems`)),perfMetrics:C(T(rt,`perfMetrics`)),renderers:C(Te),fileViewers:C(Se),statusWidgets:C(we),a2uiComponents:C(Ce),contextSources:C(T(Ve,`contextSources`)),i18n:C(T(He,`i18n`)),themePresets:C(T(qe,`themePresets`)),i18nDir:C(w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`)),inputEntries:C(w),sigilEntries:C(T(Re,`sigilEntries`)),a2uiRenderers:C(T(be,`a2uiRenderers`)),config:C(dt),credentialBackends:C(T(ft,`credentialBackends`)),events:C(T(pt,`events`))}),ht=e.enum(v),gt=e.array(e.unknown()).transform(e=>{let t=e.map(e=>ht.safeParse(e)).filter(e=>e.success).map(e=>e.data);return t.length>0?[...new Set(t)]:void 0}),_t=/^(onStartup|onCommand:[^\s]+|onView:[^\s]+|onProvider:[^\s]+)$/,vt=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&_t.test(e));return t.length>0?[...new Set(t)]:void 0}),yt=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&S.test(e));return t.length>0?[...new Set(t)]:void 0}),bt=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}),xt=A.extend({headers:C(e.record(e.string(),e.string()))}),St=e.record(e.string(),e.unknown()).transform(e=>{let t={};for(let[n,r]of Object.entries(e)){let e=xt.safeParse(r);e.success&&(t[n]=e.data)}return Object.keys(t).length>0?t:void 0}),Ct=e.object({baseUrl:w,models:T(Ie,`codeProviderModels`),responseBodyPolicy:C(j)}).extend(k.shape),wt=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&&e.data.models&&e.data.models.length>0&&(t[n]=e.data)}return Object.keys(t).length>0?t:void 0}),Tt=e.object({postinstall:C(w),setup:C(w)}).catchall(w),Et=e.object({otto:C(w)}),Dt=e.object({plane:C(e.enum([`host`,`preset`])),id:e.string().regex(S),name:C(e.string()),version:C(e.string()),description:C(e.string()),engines:C(Et),contributes:C(mt),scripts:C(Tt),capabilities:C(gt),activationEvents:C(vt),dependsOn:C(yt),codeProviderIds:C(bt),codeProviderModelsEndpoints:C(St),codeProviderModels:C(wt)});function Ot(e){return e.plane??`preset`}function kt(e){return e.activationEvents?.length?e.activationEvents:[`onStartup`]}function N(e,t){let n;try{n=JSON.parse(e)}catch(e){return b.warn({sourcePath:t,err:String(e)},`plugin manifest invalid JSON, skipped`),null}let r=Dt.safeParse(n);return r.success?r.data:(b.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 At=[{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 jt(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: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 At)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=h(e,i);if(!F(n))continue;let o=h(n,x);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 Mt(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,Nt(e.id,o));return{ordered:[...r,...f.map(e=>o.get(e))],cyclic:t,missingDeps:n}}function Nt(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 Pt=1;function Ft(){return`1.0.0`}function L(e){if(e.scope===`builtin`)return!0;let t=e.manifest.engines?.otto;return t?de(t)?ue(Ft(),t):!1:!0}function It(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 Lt(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,events:e=>e.type};function Rt(...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=Lt(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`},zt={...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 Bt(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,oe);return t===`~`?fe():t.startsWith(`~/`)?fe()+t.slice(1):t}function U(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 Vt(e,t){let n={};for(let r of e??[])n[r.key]=U(r,t);return n}var W=class extends Error{constructor(e){super(`dispatch: 缺少 capability「${e}」——宿主未注入`),this.name=`MissingCapabilityError`}};async function Ht(e,t){switch(e.type){case`builtin`:if(!t.builtin)throw new W(`builtin`);await t.builtin(e.id);return;case`mcp`:if(!t.callMcp)throw new W(`mcp`);await t.callMcp(e.server,e.tool,e.params);return;case`open`:if(!t.open)throw new W(`open`);await t.open(e.target);return;case`command`:if(!t.runCommand)throw new W(`command`);await t.runCommand(e.name,e.args);return;case`script`:if(!t.runScript)throw new W(`script`);await t.runScript(e.name);return;default:throw new W(e.type)}}function Ut(e){return e}function G(e){return`${e.kind}\u0000${e.label}`}function Wt(e,t){if(t===``)return 0;let n=e.label.toLowerCase();return n.startsWith(t)?3:e.description&&e.description.toLowerCase().includes(t)?2:n.includes(t)?1:e.kind.toLowerCase()===t?0:-1}function Gt(e){return e.startsWith(`!`)||e.startsWith(`#`)}function Kt(){let e=new Map,t=new Map;function n(t){for(let n of t){if(Gt(n.value))throw Error(`[plugin-input] sigil value 不得以 '!' 或 '#' 开头(会被误判为 bash/memory 模式):${JSON.stringify(n.value)}`);let t=G(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=Wt(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=>!Gt(e.value)));let l=new Set(a.map(e=>G(e))),u=[...a];for(let e of s){let t=G(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 qt(){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 Jt=r(`@x-otto/plugin:file-provider`),Yt=new Set([`node_modules`,`.git`,`dist`,`build`,`out`,`coverage`,`.next`,`.nuxt`,`.turbo`,`.nx`,`.cache`,`.otto`,`.venv`,`__pycache__`]);async function Xt(e,t,n){let r=[];async function i(a,o){if(r.length>=t)return;let s;try{s=await me(a,{withFileTypes:!0})}catch(e){o&&n?.(e);return}for(let n of s){if(r.length>=t)return;if(n.isDirectory()){if(Yt.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 Zt(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 Qt(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 m,dirname as ie,join as h,relative as ae,resolve as g}from"node:path";import{OTTO_HOME as oe,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 pe,sigilOf as _}from"@x-otto/interchange";import{readdir as me}from"node:fs/promises";import{randomUUID as he}from"node:crypto";const v=`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,config,credentials,cli.command,tui.command,events,settings.read,schedule`.split(`,`),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,config:!1,credentials:!0,"cli.command":!0,"tui.command":!0,events:!1,"settings.read":!1,schedule:!0},y=Object.freeze(Object.keys(ge).filter(e=>ge[e])),b=r(`@x-otto/coding:plugin-manifest`),x=`otto-plugin.json`,S=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,C=e=>e.optional().catch(void 0),w=e.string().refine(e=>e.trim().length>0,`must be non-empty`),_e=e.object({id:w,title:w,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w)}),ve=e.object({role:C(w),toolName:C(w),contentType:C(w)}).refine(e=>e.role!=null||e.toolName!=null||e.contentType!=null,{message:`matcher must declare at least one of role/toolName/contentType`}),ye=e.object({id:w,matcher:ve,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w)}),be=e.object({id:w,matcher:ve,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w)}),xe=e.object({extensions:C(e.array(w)),filenames:C(e.array(w)),glob:C(e.array(w))}).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`}),Se=T(e.object({id:w,label:w,matcher:xe,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w),order:C(e.number())}),`fileViewers`),Ce=T(e.object({type:w,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w)}),`a2uiComponents`),we=T(e.object({id:w,entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),export:C(w),order:C(e.number()),tickMs:C(e.number())}),`statusWidgets`),Te=T(ye,`renderers`),Ee=T(_e,`panels`),De=e.discriminatedUnion(`type`,[e.object({type:e.literal(`builtin`),id:w}),e.object({type:e.literal(`mcp`),server:w,tool:w,params:C(e.record(e.string(),e.unknown()))}),e.object({type:e.literal(`open`),target:w}),e.object({type:e.literal(`command`),name:w,args:C(w)}),e.object({type:e.literal(`script`),name:w})]),Oe=e.object({id:w,title:w,command:De,color:C(e.enum([`accent`,`amber`,`red`,`green`,`gray`]))}),ke=e.object({action:w,point:w,when:C(w),order:C(e.number())}),Ae=e.object({key:w,groups:e.array(e.array(w)),map:e.object({none:w,partial:w,all:w})});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(`; `));b.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 je=e.object({input:e.number().min(0),output:e.number().min(0),cacheRead:e.number().min(0),cacheWrite:e.number().min(0)}),Me=e.object({maxImagesPerRequest:e.number().int().positive(),maxDimensionPxIfOverLimit:C(e.number().int().positive())}),Ne=e.enum([`planning`,`knowledge`,`coding`,`reasoning`,`vision`,`speed`,`long-context`]),Pe=e.enum([`low`,`medium`,`high`,`xhigh`,`max`]),Fe=e.enum([`enabled`,`adaptive`]),Ie=e.object({id:w,contextWindow:e.number().int().positive(),maxOutput:e.number().int().positive(),cost:C(je),strengths:C(e.array(Ne).min(1)),reasoning:C(e.boolean()),input:C(e.array(e.enum([`text`,`image`])).min(1)),thinkingLevels:C(e.array(Pe).min(1)),thinkingMode:C(Fe)}),Le=e.enum([`openai-completions`,`openai-responses`,`anthropic-messages`]),Re=e.object({label:w,value:w.refine(e=>!e.startsWith(`!`)&&!e.startsWith(`#`),`value must not start with '!' or '#' (mode-prefix collision)`),kind:e.enum([`resource`,`hashtag`]),description:C(w),resolverId:C(w)}),ze=e.object({id:w,dataSource:e.object({contextKey:w}),point:C(w),when:C(w),order:C(e.number())}),Be=e.union([w,e.object({file:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`)})]),Ve=e.object({id:w,priority:C(e.number()),content:Be}),He=e.object({id:w,translations:e.record(e.string(),e.string())}),Ue=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/,E=e=>!Ue.test(e),We=/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/,D=t=>e.string().max(16).refine(e=>We.test(e),`${t} must be a #RRGGBB or #RGB hex color`),Ge=e.object({brand:D(`brand`),brandShimmer:D(`brandShimmer`),accent:D(`accent`),accentDim:D(`accentDim`),accentBright:D(`accentBright`),text:C(D(`text`)),inactive:D(`inactive`),secondary:D(`secondary`),subtle:D(`subtle`),replyPrefix:D(`replyPrefix`),success:D(`success`),error:D(`error`),warning:D(`warning`),special:D(`special`),suggestion:D(`suggestion`),permission:D(`permission`),codeText:D(`codeText`),diffAdd:D(`diffAdd`),diffRemove:D(`diffRemove`),diffAddBg:D(`diffAddBg`),diffRemoveBg:D(`diffRemoveBg`),panelBorder:D(`panelBorder`),overlayBg:D(`overlayBg`),tagBg:D(`tagBg`),toolText:D(`toolText`),focusBorder:D(`focusBorder`),selection:D(`selection`)}),O=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(`.`)),Ke=e.strictObject({heading1:C(O),heading2:C(O),headingWeak:C(O),listMarker:C(O),listMarkerMuted:C(O),quoteBar:C(O),quoteText:C(O),link:C(O),inlineCode:C(O),codeFence:C(O),tableHeader:C(O),tableDivider:C(O),listIndent:C(e.union([e.literal(2),e.literal(3)])),listMarkers:C(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:C(e.enum([`hr`,`none`]))}),qe=e.object({localId:w.max(64).refine(e=>S.test(e),`localId must be kebab-case`),label:w.max(64).refine(E,`label must not contain control characters`),appearance:e.enum([`dark`,`light`]),colors:Ge,markdown:C(Ke),meta:C(e.object({author:C(w.max(128).refine(E,`author must not contain control characters`)),description:C(w.max(256).refine(E,`description must not contain control characters`)),version:C(w.max(32).refine(E,`version must not contain control characters`))}))}),Je=10,Ye=500,Xe=2*1024,Ze=20,Qe=64*1024,$e=32,et=4,tt=8*1024,nt=256,rt=e.object({id:w,label:w,toolRef:w}),it=e.object({header:w.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:w,namespace:C(w),tokenSources:C(e.array(e.enum([`id_token`,`access_token`])))})}),k=e.object({imageConstraints:C(Me),supportsTools:C(e.boolean())}),A=e.object({url:w,responseFormat:e.literal(`openai-list`),authScheme:C(e.enum([`bearer`,`x-api-key`,`auto`])),betaHeaderName:C(w),modelIdExclude:C(T(e.string(),`modelIdExclude`))}),j=e.object({store:C(e.boolean()),sendMaxOutputTokens:C(e.boolean())}),at=e.object({id:w,name:C(w),catalogOnly:C(e.boolean()),baseUrl:C(w),wireApi:C(Le),envKey:C(w),oauthRef:C(w),headers:C(e.record(e.string(),e.string())),models:C(T(Ie,`models`)),tokenDerivedHeaders:C(T(it,`tokenDerivedHeaders`)),modelsEndpoint:C(A),responseBodyPolicy:C(j),allowAuthHeaderOverride:C(e.boolean()),preserveProviderId:C(e.boolean()),requiresIntranet:C(e.boolean())}).extend(k.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`]})}),ot=e.object({name:w.refine(e=>/^[a-z][a-z0-9-]*$/.test(e),`must be lowercase kebab-case`),bin:C(w)}),st=e.object({name:w.refine(e=>/^[a-z][a-z0-9-]*$/.test(e),`must be lowercase kebab-case`),summary:w.max(200).refine(E,`must not contain control characters`),i18nKey:C(w)}),ct=e.object({name:w.refine(e=>/^[a-z][a-z0-9-]*$/.test(e),`must be lowercase kebab-case`),description:w.max(200).refine(E,`must not contain control characters`),group:C(w),i18nKey:C(w)}),lt=e.discriminatedUnion(`kind`,[e.object({kind:e.literal(`pkce`),id:w,name:C(w),clientId:w,authorizeUrl:w,tokenUrl:w,redirectUri:w,scope:w,loopbackPorts:C(e.array(e.number().int().positive())),assumesLoopbackAlways:C(e.boolean()),extraAuthParams:C(e.record(e.string(),e.string())),tokenExchangeEncoding:C(e.enum([`json`,`form`])),subscriptionScoped:C(e.boolean()),oauthBeta:C(w)}),e.object({kind:e.literal(`device-flow`),id:w,name:C(w),clientId:w,deviceCodeUrlTemplate:w,tokenUrlTemplate:w,refreshUrlTemplate:C(w),scope:w,allowCustomDomain:C(e.boolean()),userAgent:C(w)})]),ut=e.object({type:e.enum([`string`,`number`,`integer`,`boolean`,`array`,`object`]),title:C(w),description:C(w),default:e.unknown().optional(),enum:C(e.array(e.unknown())),minimum:C(e.number()),maximum:C(e.number()),pattern:C(w),items:e.unknown().optional(),properties:C(e.record(e.string(),e.unknown()))});function M(e,t){if(t>4||typeof e!=`object`||!e)return!1;let n=e;if(typeof n.description==`string`&&n.description.length>256)return!1;if(n.properties&&typeof n.properties==`object`){let e=n.properties;if(Object.keys(e).length>32)return!1;for(let n of Object.values(e))if(!M(n,t+1))return!1}return!(n.items&&typeof n.items==`object`&&!M(n.items,t+1))}const dt=e.unknown().transform(e=>{if(typeof e!=`object`||!e)return;let t=JSON.stringify(e).length;if(t>8192){b.warn({bytes:t,limit:tt},`config schema exceeds byte limit, dropped`);return}if(!M(e,0)){b.warn(`config schema exceeds depth/field/description limit, dropped`);return}let n=ut.safeParse(e);if(!n.success){b.warn({err:n.error.issues},`config schema invalid, dropped`);return}return n.data}),ft=e.object({id:w,label:C(w),entry:w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`),capabilities:C(e.object({atomic:C(e.boolean()),permSecure:C(e.boolean()),crossProcessLock:C(e.boolean()),watchable:C(e.boolean())}))}),pt=e.object({type:w.refine(e=>{let t=e.split(`.`);return t.length>=2&&t.every(e=>e.length>0)},`event type must be <pluginId>.<event> namespaced format`),description:C(w),payloadSchema:C(e.record(e.string(),e.unknown()))}),mt=e.object({skills:C(e.boolean()),agents:C(e.boolean()),commands:C(e.boolean()),mcp:C(e.boolean()),cli:C(ot),cliCommands:C(T(st,`cliCommands`)),slashCommands:C(T(ct,`slashCommands`)),panels:C(Ee),actions:C(T(Oe,`actions`)),menus:C(T(ke,`menus`)),contextKeys:C(T(Ae,`contextKeys`)),providers:C(T(at,`providers`)),oauth:C(T(lt,`oauth`)),statusItems:C(T(ze,`statusItems`)),perfMetrics:C(T(rt,`perfMetrics`)),renderers:C(Te),fileViewers:C(Se),statusWidgets:C(we),a2uiComponents:C(Ce),contextSources:C(T(Ve,`contextSources`)),i18n:C(T(He,`i18n`)),themePresets:C(T(qe,`themePresets`)),i18nDir:C(w.refine(e=>!e.startsWith(`/`)&&!e.split(/[\\/]/).includes(`..`),`unsafe path (absolute or traversal)`)),inputEntries:C(w),sigilEntries:C(T(Re,`sigilEntries`)),a2uiRenderers:C(T(be,`a2uiRenderers`)),config:C(dt),credentialBackends:C(T(ft,`credentialBackends`)),events:C(T(pt,`events`))}),ht=e.enum(v),gt=e.array(e.unknown()).transform(e=>{let t=e.map(e=>ht.safeParse(e)).filter(e=>e.success).map(e=>e.data);return t.length>0?[...new Set(t)]:void 0}),_t=/^(onStartup|onCommand:[^\s]+|onView:[^\s]+|onProvider:[^\s]+)$/,vt=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&_t.test(e));return t.length>0?[...new Set(t)]:void 0}),yt=e.array(e.unknown()).transform(e=>{let t=e.filter(e=>typeof e==`string`&&S.test(e));return t.length>0?[...new Set(t)]:void 0}),bt=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}),xt=A.extend({headers:C(e.record(e.string(),e.string()))}),St=e.record(e.string(),e.unknown()).transform(e=>{let t={};for(let[n,r]of Object.entries(e)){let e=xt.safeParse(r);e.success&&(t[n]=e.data)}return Object.keys(t).length>0?t:void 0}),Ct=e.object({baseUrl:w,models:T(Ie,`codeProviderModels`),responseBodyPolicy:C(j)}).extend(k.shape),wt=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&&e.data.models&&e.data.models.length>0&&(t[n]=e.data)}return Object.keys(t).length>0?t:void 0}),Tt=e.object({postinstall:C(w),setup:C(w)}).catchall(w),Et=e.object({otto:C(w)}),Dt=e.object({plane:C(e.enum([`host`,`preset`])),id:e.string().regex(S),name:C(e.string()),version:C(e.string()),description:C(e.string()),engines:C(Et),contributes:C(mt),scripts:C(Tt),capabilities:C(gt),activationEvents:C(vt),dependsOn:C(yt),codeProviderIds:C(bt),codeProviderModelsEndpoints:C(St),codeProviderModels:C(wt)});function Ot(e){return e.plane??`preset`}function kt(e){return e.activationEvents?.length?e.activationEvents:[`onStartup`]}function N(e,t){let n;try{n=JSON.parse(e)}catch(e){return b.warn({sourcePath:t,err:String(e)},`plugin manifest invalid JSON, skipped`),null}let r=Dt.safeParse(n);return r.success?r.data:(b.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 At=[{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 jt(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: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 At)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=h(e,i);if(!F(n))continue;let o=h(n,x);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 Mt(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,Nt(e.id,o));return{ordered:[...r,...f.map(e=>o.get(e))],cyclic:t,missingDeps:n}}function Nt(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 Pt=1;function Ft(){return`1.0.0`}function L(e){if(e.scope===`builtin`)return!0;let t=e.manifest.engines?.otto;return t?de(t)?ue(Ft(),t):!1:!0}function It(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 Lt(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,events:e=>e.type};function Rt(...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=Lt(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`},zt={...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 Bt(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,oe);return t===`~`?fe():t.startsWith(`~/`)?fe()+t.slice(1):t}function U(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 Vt(e,t){let n={};for(let r of e??[])n[r.key]=U(r,t);return n}var W=class extends Error{constructor(e){super(`dispatch: 缺少 capability「${e}」——宿主未注入`),this.name=`MissingCapabilityError`}};async function Ht(e,t){switch(e.type){case`builtin`:if(!t.builtin)throw new W(`builtin`);await t.builtin(e.id);return;case`mcp`:if(!t.callMcp)throw new W(`mcp`);await t.callMcp(e.server,e.tool,e.params);return;case`open`:if(!t.open)throw new W(`open`);await t.open(e.target);return;case`command`:if(!t.runCommand)throw new W(`command`);await t.runCommand(e.name,e.args);return;case`script`:if(!t.runScript)throw new W(`script`);await t.runScript(e.name);return;default:throw new W(e.type)}}function Ut(e){return e}function G(e){return`${e.kind}\u0000${e.label}`}function Wt(e,t){if(t===``)return 0;let n=e.label.toLowerCase();return n.startsWith(t)?3:e.description&&e.description.toLowerCase().includes(t)?2:n.includes(t)?1:e.kind.toLowerCase()===t?0:-1}function Gt(e){return e.startsWith(`!`)||e.startsWith(`#`)}function Kt(){let e=new Map,t=new Map;function n(t){for(let n of t){if(Gt(n.value))throw Error(`[plugin-input] sigil value 不得以 '!' 或 '#' 开头(会被误判为 bash/memory 模式):${JSON.stringify(n.value)}`);let t=G(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=Wt(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=>!Gt(e.value)));let l=new Set(a.map(e=>G(e))),u=[...a];for(let e of s){let t=G(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 qt(){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 Jt=r(`@x-otto/plugin:file-provider`),Yt=new Set([`node_modules`,`.git`,`dist`,`build`,`out`,`coverage`,`.next`,`.nuxt`,`.turbo`,`.nx`,`.cache`,`.otto`,`.venv`,`__pycache__`]);async function Xt(e,t,n){let r=[];async function i(a,o){if(r.length>=t)return;let s;try{s=await me(a,{withFileTypes:!0})}catch(e){o&&n?.(e);return}for(let n of s){if(r.length>=t)return;if(n.isDirectory()){if(Yt.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 Zt(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 Qt(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(`
|
|
2
2
|
`).slice(0,n).join(`
|
|
3
3
|
`);return s.length>r?`${s.slice(0,r-1)}…`:s}catch{return``}finally{if(i!==void 0)try{o(i)}catch{}}}function $t(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=Xt(t,r,e=>{Jt.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=Qt(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=Zt(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 en=r(`@x-otto/plugin:trust-store`),tn={"ui.renderer":`tui.renderer`};function nn(e){return[...new Set(e.map(e=>tn[e]??e))]}const K=2e3;function q(e,t,r){let o=ie(e),s=m(e),c=n(o,s,{timeoutMs:K});if(c||a(o,s,{timeoutMs:K})&&(c=n(o,s,{timeoutMs:K})),!c&&r?.requireLock)throw Error(`Failed to acquire trust store lock within ${K}ms; revocation aborted to avoid silently losing it to a concurrent writer`);try{return t()}finally{c&&i(o,s)}}function J(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]=nn(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[g(e)]=t);return{trusted:Array.isArray(n)?n.filter(e=>typeof e==`string`):[],capabilities:r,versions:a}}catch{return{trusted:[],capabilities:{},versions:{}}}}function rn(e,t){let n=`${e}.${he()}.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 Y(e,t,n){if(!ce())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),t.versions&&Object.keys(t.versions).length>0&&(n.versions=t.versions),rn(e,JSON.stringify(n,null,2))}catch(t){throw en.warn({storePath:e,err:t},`Failed to persist ${n} store`),t}}function an(e,t){return J(e).trusted.includes(g(t))}function on(e,t,n){return q(e,()=>{let r=g(t),i=J(e);return i.trusted.includes(r)?!1:(i.trusted.push(r),Y(e,i,n),!0)})}function X(e,t,n){return q(e,()=>{let r=g(t),i=J(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:(Y(e,{trusted:a,capabilities:o,versions:c},n),!0)},{requireLock:!0})}function sn(e,t){return J(e).capabilities?.[g(t)]??[]}function cn(e,t,n,r){return q(e,()=>{let i=g(t),a=J(e),o=new Set(a.capabilities?.[i]??[]),s=o.size;for(let e of n)o.add(e);return o.size===s?!1:(Y(e,{trusted:a.trusted,capabilities:{...a.capabilities,[i]:[...o]}},r),!0)})}function ln(e,t,n,r,i){return q(e,()=>{let a=g(t),o=J(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:(Y(e,{trusted:o.trusted,capabilities:{...o.capabilities,[a]:[...s]},versions:{...o.versions,[a]:r}},i),!0)})}function un(e,t,n){let r=g(t),i=J(e);return i.versions?.[r]===n?i.capabilities?.[r]??[]:[]}const Z=r(`@x-otto/plugin:trust`);function Q(){return process.env.OTTO_PLUGIN_TRUST_PATH||h(oe,`plugin-trust.json`)}function dn(e,t=Q()){return an(t,e)}function fn(e,t=Q()){on(t,e,`plugin-trust`)&&Z.info({dir:g(e)},`Plugin trusted`)}function pn(e,t=Q()){X(t,e,`plugin-trust`)&&Z.info({dir:g(e)},`Plugin trust revoked`)}function mn(e,t=Q()){return sn(t,e)}function hn(e){try{let t=h(e,x);if(!s(t))return[];let n=N(d(t,`utf8`),t);return n?.capabilities?n.capabilities.filter(e=>y.includes(e)):[]}catch{return[]}}function gn(e,t,n=Q()){cn(n,e,t,`plugin-trust`)&&Z.info({dir:g(e),caps:t},`Plugin capabilities granted`)}function _n(e,t,n,r=Q()){ln(r,e,t,n,`plugin-trust`)&&Z.info({dir:g(e),caps:t,version:n},`Plugin capabilities granted (version-bound)`)}function vn(e,t,n=Q()){return un(n,e,t)}function $(e){try{return p(e).length>0}catch{return!1}}function yn(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=>y.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 bn(e,t=Q()){let n=yn(e.dir,e.manifest),r=dn(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(sn(t,e.dir));return{surface:n,trusted:r,gated:i,pendingCapabilities:y.filter(e=>a.has(e)&&!o.has(e)),grantedCapabilities:y.filter(e=>a.has(e)&&o.has(e))}}const xn=[`apikey`,`secret`,`token`,`password`,`credential`,`privatekey`,`accesskey`];function Sn(e){let t=e.toLowerCase();return xn.some(e=>t.includes(e))}function Cn(e){let t={};for(let[n,r]of Object.entries(e))Sn(n)?t[n]=`[REDACTED]`:r&&typeof r==`object`&&!Array.isArray(r)?t[n]=Cn(r):t[n]=r;return t}function wn(e,t){let n={...t},r=e?.properties;if(r&&typeof r==`object`)for(let[e,t]of Object.entries(r))!(e in n)&&t&&typeof t==`object`&&`default`in t&&(n[e]=t.default);return n}function Tn(e){return e.split(/[-_]/)[0]??e}var En=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=Tn(t);for(let[e,t]of Object.entries(n))if(Tn(e)===i)return t;return n.en===void 0?Object.values(n)[0]??e:n.en}};export{z as EXTENSION_POINTS,y as HIGH_RISK_CAPABILITIES,S as ID_RE,$e as MAX_CONFIG_FIELDS_PER_PLUGIN,nt as MAX_CONFIG_FIELD_DESCRIPTION_CHARS,tt as MAX_CONFIG_SCHEMA_BYTES,et as MAX_CONFIG_SCHEMA_DEPTH,Ye as MAX_I18N_ENTRIES_PER_PLUGIN,Qe as MAX_I18N_FILE_BYTES,Ze as MAX_I18N_LOCALE_FILES_PER_PLUGIN,Xe as MAX_I18N_TRANSLATION_BYTES,Je as MAX_THEME_PRESETS_PER_PLUGIN,Pt as PLUGIN_API_VERSION,v as PLUGIN_CAPABILITIES,x as PLUGIN_MANIFEST_FILENAME,zt as POINT,En as PluginI18nRegistry,B as SHELL_POINTS,qt as createBuiltinHashtags,$t as createFileProvider,Kt as createPluginInputRegistry,Ut as definePlugin,yn as detectPluginExecutableSurface,jt as discoverPlugins,Ht as dispatch,U as evaluateContextKey,Vt as evaluateContextKeys,bn as evaluatePluginTrust,V as evaluateWhen,H as expandPath,It as filterEngineCompatible,ln as grantCapabilitiesWithVersion,gn as grantPluginCapabilities,_n as grantPluginCapabilitiesWithVersion,un as grantedCapabilitiesForVersion,mn as grantedPluginCapabilities,vn as grantedPluginCapabilitiesForVersion,L as isEngineCompatible,an as isPathTrusted,dn as isPluginTrusted,Sn as isSensitiveField,wn as mergeConfigWithDefaults,Rt as mergeContributions,N as parsePluginManifest,Q as pluginTrustStorePath,hn as readHighRiskCapabilities,Cn as redactSensitiveFields,Bt as resolveActions,kt as resolveActivationEvents,Ot as resolveManifestPlane,pe as sigilOf,Mt as topoSortPlugins,on as trustPath,fn as trustPlugin,X as untrustPath,pn 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.9",
|
|
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/
|
|
26
|
-
"@x-otto/
|
|
27
|
-
"@x-otto/
|
|
28
|
-
"@x-otto/
|
|
29
|
-
"@x-otto/shared": "0.1.0-alpha.
|
|
25
|
+
"@x-otto/env": "0.1.0-alpha.9",
|
|
26
|
+
"@x-otto/hook-contracts": "0.1.0-alpha.9",
|
|
27
|
+
"@x-otto/interchange": "0.1.0-alpha.9",
|
|
28
|
+
"@x-otto/provider": "0.1.0-alpha.9",
|
|
29
|
+
"@x-otto/shared": "0.1.0-alpha.9"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/semver": "7.7.1"
|