@x-otto/cli 0.0.1-alpha.2 → 0.0.1-alpha.20

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.
Files changed (121) hide show
  1. package/README.md +65 -65
  2. package/bin/otto.js +6 -0
  3. package/dist/{auth-command-CXAB3_hs.js → auth-command-kkJh-O5y.js} +3 -3
  4. package/dist/auth-command-kkJh-O5y.js.map +1 -0
  5. package/dist/build-id-D7Wf4tQw.js +3 -0
  6. package/dist/build-id-D7Wf4tQw.js.map +1 -0
  7. package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/meta.json +1 -1
  8. package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/plugin.cjs +2083 -18164
  9. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/anthropic-messages.ts +2 -2
  10. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-completions.ts +12 -5
  11. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-responses.ts +4 -1
  12. package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/meta.json +1 -1
  13. package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/plugin.cjs +78 -52294
  14. package/dist/bundled-extensions-C3n8jg20.js +3 -0
  15. package/dist/bundled-extensions-C3n8jg20.js.map +1 -0
  16. package/dist/command-dispatcher-CahuAJnO.js +17 -0
  17. package/dist/command-dispatcher-CahuAJnO.js.map +1 -0
  18. package/dist/{config-migration-DatflIxs.js → config-migration-0zdXsEDB.js} +1 -1
  19. package/dist/{config-migration-DatflIxs.js.map → config-migration-0zdXsEDB.js.map} +1 -1
  20. package/dist/create-host-app-BXQ8bU-B.js +3 -0
  21. package/dist/create-host-app-BXQ8bU-B.js.map +1 -0
  22. package/dist/create-host-app-DXII2WTM.js +2 -0
  23. package/dist/{daemon-COESl-zU.js → daemon-BtiaXSxn.js} +1 -1
  24. package/dist/{daemon-COESl-zU.js.map → daemon-BtiaXSxn.js.map} +1 -1
  25. package/dist/{daemon-base-YOWgYYN7.js → daemon-base-BzhYY4FS.js} +1 -1
  26. package/dist/{daemon-base-YOWgYYN7.js.map → daemon-base-BzhYY4FS.js.map} +1 -1
  27. package/dist/{debug-CiosFwme.js → debug-DfgcrVCI.js} +2 -2
  28. package/dist/{debug-CiosFwme.js.map → debug-DfgcrVCI.js.map} +1 -1
  29. package/dist/discover-with-bundled-B5d4q___.js +2 -0
  30. package/dist/discover-with-bundled-CF8qFP41.js +3 -0
  31. package/dist/discover-with-bundled-CF8qFP41.js.map +1 -0
  32. package/dist/doctor-DbQC8Z7p.js +36 -0
  33. package/dist/doctor-DbQC8Z7p.js.map +1 -0
  34. package/dist/{doctor-attach-_zeIJewx.js → doctor-attach-OJHn8L52.js} +2 -2
  35. package/dist/{doctor-attach-_zeIJewx.js.map → doctor-attach-OJHn8L52.js.map} +1 -1
  36. package/dist/{doctor-cdp-K_mIFRfA.js → doctor-cdp-D66ChCCN.js} +1 -1
  37. package/dist/{doctor-cdp-K_mIFRfA.js.map → doctor-cdp-D66ChCCN.js.map} +1 -1
  38. package/dist/{doctor-heap-Rr2nNvcw.js → doctor-heap-Brsro5QJ.js} +2 -2
  39. package/dist/{doctor-heap-Rr2nNvcw.js.map → doctor-heap-Brsro5QJ.js.map} +1 -1
  40. package/dist/{doctor-ps-OL3FGQYu.js → doctor-ps-B4Syy9qa.js} +2 -2
  41. package/dist/{doctor-ps-OL3FGQYu.js.map → doctor-ps-B4Syy9qa.js.map} +1 -1
  42. package/dist/{extension-BKXIr86F.js → extension-DLFCUBJr.js} +41 -33
  43. package/dist/extension-DLFCUBJr.js.map +1 -0
  44. package/dist/extension-I87MkZYp.js +2 -0
  45. package/dist/extension-plugin-BEQlmUET.js +2 -0
  46. package/dist/extension-plugin-BYR-MJ1U.js +7 -0
  47. package/dist/extension-plugin-BYR-MJ1U.js.map +1 -0
  48. package/dist/feedback-DjNFsjeN.js +2 -0
  49. package/dist/feedback-DrUjMuKw.js +21 -0
  50. package/dist/feedback-DrUjMuKw.js.map +1 -0
  51. package/dist/{feishu-setup-D6vnuNLw.js → feishu-setup-C2UDdpIm.js} +2 -2
  52. package/dist/{feishu-setup-D6vnuNLw.js.map → feishu-setup-C2UDdpIm.js.map} +1 -1
  53. package/dist/ftue-doctor-recovery-DhiseXnP.js +3 -0
  54. package/dist/ftue-doctor-recovery-DhiseXnP.js.map +1 -0
  55. package/dist/index.js +10 -12
  56. package/dist/index.js.map +1 -1
  57. package/dist/install-DkJgbg2p.js +2 -0
  58. package/dist/{install-DlVJRHRT.js → install-hTDjeo61.js} +1 -1
  59. package/dist/{install-DlVJRHRT.js.map → install-hTDjeo61.js.map} +1 -1
  60. package/dist/{mcp-DbTYT7hB.js → mcp-BDfBf6gx.js} +1 -1
  61. package/dist/{mcp-DbTYT7hB.js.map → mcp-BDfBf6gx.js.map} +1 -1
  62. package/dist/{migrate-boDXhEyV.js → migrate-4QDwYLf6.js} +2 -2
  63. package/dist/{migrate-boDXhEyV.js.map → migrate-4QDwYLf6.js.map} +1 -1
  64. package/dist/{model-auth-qY5zeBF7.js → model-auth-BMY86Bk4.js} +2 -2
  65. package/dist/{model-auth-qY5zeBF7.js.map → model-auth-BMY86Bk4.js.map} +1 -1
  66. package/dist/model-auth-CSLiEFJ-.js +2 -0
  67. package/dist/{observe-CVg-MRM2.js → observe-DSrUS2YI.js} +10 -8
  68. package/dist/observe-DSrUS2YI.js.map +1 -0
  69. package/dist/parse-cli-klcuBpG2.js +3 -0
  70. package/dist/parse-cli-klcuBpG2.js.map +1 -0
  71. package/dist/{persistenced-CCwHu1Ww.js → persistenced-CW8sdhd0.js} +2 -2
  72. package/dist/{persistenced-CCwHu1Ww.js.map → persistenced-CW8sdhd0.js.map} +1 -1
  73. package/dist/plugin-assembly-check-CvuJ4dHf.js +3 -0
  74. package/dist/plugin-assembly-check-CvuJ4dHf.js.map +1 -0
  75. package/dist/{proxy-evg7ZvoD.js → proxy-CMQBvdZC.js} +1 -1
  76. package/dist/{proxy-evg7ZvoD.js.map → proxy-CMQBvdZC.js.map} +1 -1
  77. package/dist/proxy-TzOLqtkc.js +2 -0
  78. package/dist/{ps-DKrluEuy.js → ps-Kb6ZsEpC.js} +1 -1
  79. package/dist/{ps-DKrluEuy.js.map → ps-Kb6ZsEpC.js.map} +1 -1
  80. package/dist/{registry-D0NONjLv.js → registry-Cr5To_5r.js} +3 -3
  81. package/dist/registry-Cr5To_5r.js.map +1 -0
  82. package/dist/registry-DAJ7oZTe.js +2 -0
  83. package/dist/{remote-persistence-server-B_hONoVR.js → remote-persistence-server-BLBIop0u.js} +2 -2
  84. package/dist/{remote-persistence-server-B_hONoVR.js.map → remote-persistence-server-BLBIop0u.js.map} +1 -1
  85. package/dist/{run-attach-Ci5jYyCa.js → run-attach-CgYMem8s.js} +2 -2
  86. package/dist/{run-attach-Ci5jYyCa.js.map → run-attach-CgYMem8s.js.map} +1 -1
  87. package/dist/{serve-pVR6UIT9.js → serve-Bq21zw1O.js} +3 -3
  88. package/dist/serve-Bq21zw1O.js.map +1 -0
  89. package/dist/{service-client-BLNeugM9.js → service-client-Cr4sxDVM.js} +1 -1
  90. package/dist/{service-client-BLNeugM9.js.map → service-client-Cr4sxDVM.js.map} +1 -1
  91. package/dist/{time-travel-ZozuPPI-.js → time-travel-8X-uAgHw.js} +2 -2
  92. package/dist/{time-travel-ZozuPPI-.js.map → time-travel-8X-uAgHw.js.map} +1 -1
  93. package/dist/{version-C0qM-qJM.js → version-Cq2kcrXL.js} +2 -2
  94. package/dist/{version-C0qM-qJM.js.map → version-Cq2kcrXL.js.map} +1 -1
  95. package/package.json +5 -5
  96. package/dist/auth-command-CXAB3_hs.js.map +0 -1
  97. package/dist/build-id-BX4QHzM0.js +0 -3
  98. package/dist/build-id-BX4QHzM0.js.map +0 -1
  99. package/dist/command-dispatcher-Vb_AJSAR.js +0 -13
  100. package/dist/command-dispatcher-Vb_AJSAR.js.map +0 -1
  101. package/dist/discover-with-bundled-CznLjkkw.js +0 -3
  102. package/dist/discover-with-bundled-CznLjkkw.js.map +0 -1
  103. package/dist/doctor-C84-hhEr.js +0 -30
  104. package/dist/doctor-C84-hhEr.js.map +0 -1
  105. package/dist/extension-BKXIr86F.js.map +0 -1
  106. package/dist/extension-CnON6Xum.js +0 -2
  107. package/dist/extension-plugin-Bmqlc6as.js +0 -2
  108. package/dist/extension-plugin-C2Yhv77x.js +0 -7
  109. package/dist/extension-plugin-C2Yhv77x.js.map +0 -1
  110. package/dist/feedback-BES_6ebi.js +0 -21
  111. package/dist/feedback-BES_6ebi.js.map +0 -1
  112. package/dist/feedback-BRbLUn9u.js +0 -2
  113. package/dist/install-WCcQ9k9M.js +0 -2
  114. package/dist/model-auth-DUPFPOjY.js +0 -2
  115. package/dist/observe-CVg-MRM2.js.map +0 -1
  116. package/dist/parse-cli-BCfFgFAC.js +0 -3
  117. package/dist/parse-cli-BCfFgFAC.js.map +0 -1
  118. package/dist/proxy-BvdBsB1O.js +0 -2
  119. package/dist/registry-B3nfN62q.js +0 -2
  120. package/dist/registry-D0NONjLv.js.map +0 -1
  121. package/dist/serve-pVR6UIT9.js.map +0 -1
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- import{discoverPlugins as e}from"@x-otto/coding";import{dirname as t,join as n}from"node:path";import{existsSync as r}from"node:fs";import{fileURLToPath as i}from"node:url";function a(){let e=n(t(i(import.meta.url)),`bundled-extensions`);return r(e)?e:``}function o(t){let n=a();return e({...t,bundledDirs:n?[n]:[]})}export{o as t};
3
- //# sourceMappingURL=discover-with-bundled-CznLjkkw.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"discover-with-bundled-CznLjkkw.js","names":[],"sources":["../src/bundled-extensions.ts","../src/discover-with-bundled.ts"],"sourcesContent":["/**\n * bundled-extensions.ts —— 随 otto 分发的内置扩展目录解析。\n *\n * cli 构建时(scripts/bundle-builtin-extensions.mjs)把内置扩展的编译产物拷进\n * `dist/bundled-extensions/<id>/`。本模块解析该目录,供 `discoverPlugins` 作为\n * 最高优先扫描层传入。\n *\n * ## 为什么在 cli 层解析而不是 plugin 层\n * `@x-otto/plugin` 是库,不该知道 cli 的 dist 布局(反向依赖)。目录位置只有 cli\n * 自己知道(它拥有 `import.meta.url` → dist 的路径)。\n *\n * ## 分布式的可移植性\n * 发布物 = `dist/`(package.json files:[\"dist\"])。本目录与 dist 同级,\n * 打包后相对路径关系保持不变——`import.meta.url` 解析到 dist 内的一个 js,\n * `../bundled-extensions` 即发布物内的扩展目录。\n */\nimport { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** cli 发布物内内置扩展目录(不存在 = 该发布物没带内置扩展,返回空)。 */\nexport function bundledExtensionsDir(): string {\n // dist 里的这个 js 文件 → dist/bundled-extensions(同目录,非父级——这是 cli 构建\n // 时拷进去的发布物内目录,见 scripts/bundle-builtin-extensions.mjs)。\n const dir = join(dirname(fileURLToPath(import.meta.url)), 'bundled-extensions')\n return existsSync(dir) ? dir : ''\n}\n","/**\n * discover-with-bundled.ts —— `discoverPlugins` 的 cli 侧统一包装。\n *\n * 所有 cli 内部调用 `discoverPlugins` 的地方都应该走这里:把 cli 自己的\n * bundled-extensions 目录作为最高优先扫描层传入(否则内置扩展只在部分调用点可见,\n * 行为分裂)。\n */\n// cli 不直接依赖 @x-otto/plugin(经 @x-otto/coding 间接),从 coding 的 barrel 取。\nimport { discoverPlugins } from '@x-otto/coding'\n\nimport { bundledExtensionsDir } from './bundled-extensions'\n\nimport type { DiscoveredPlugin, DiscoverPluginsOptions } from '@x-otto/coding'\n\nexport function discoverPluginsWithBundled(opts: DiscoverPluginsOptions): DiscoveredPlugin[] {\n const bundled = bundledExtensionsDir()\n return discoverPlugins({\n ...opts,\n bundledDirs: bundled ? [bundled] : [],\n })\n}\n"],"mappings":";6KAqBA,SAAgB,GAA+B,CAG7C,IAAM,EAAM,EAAK,EAAQ,EAAc,OAAO,KAAK,IAAI,CAAC,CAAE,qBAAqB,CAC/E,OAAO,EAAW,EAAI,CAAG,EAAM,GCXjC,SAAgB,EAA2B,EAAkD,CAC3F,IAAM,EAAU,GAAsB,CACtC,OAAO,EAAgB,CACrB,GAAG,EACH,YAAa,EAAU,CAAC,EAAQ,CAAG,EAAE,CACtC,CAAC"}
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env node
2
- import{arch as e,cpus as t,freemem as n,homedir as r,platform as i,release as a,totalmem as o}from"node:os";import{join as s}from"node:path";import{accessSync as c,existsSync as l,statSync as u}from"node:fs";import{SESSION_MAX_HISTORY_MESSAGES as d,SESSION_MAX_PER_SESSION_BYTES as f,SESSION_MIN_PER_SESSION_BYTES as p,SESSION_RESUME_WINDOW_MESSAGES as m,SESSION_TOTAL_RESIDENCY_BUDGET as h}from"@x-otto/session-contract";const g=s(r(),`.otto`);async function _(e,t){let n=t.stdout,r=t.stderr,i=e.find(e=>!e.startsWith(`-`));if(i===`ps`||i===`attach`||i===`heap`){let n=e.filter(e=>e!==i);switch(i){case`ps`:{let{runDoctorPs:e}=await import(`./doctor-ps-OL3FGQYu.js`);return e(n,t)}case`attach`:{let{runDoctorAttach:e}=await import(`./doctor-attach-_zeIJewx.js`);return e(n,t)}case`heap`:{let{runDoctorHeap:e}=await import(`./doctor-heap-Rr2nNvcw.js`);return e(n,t)}}}if(i!==void 0)return r(`otto doctor: unknown subcommand "${i}" (available: ps, attach, heap)\n`),1;if(e.includes(`--help`)||e.includes(`-h`))return D(n),0;let a=e.includes(`--json`)?`json`:e.includes(`--summary`)?`summary`:`detailed`;try{let e=await v();return a===`json`?n(JSON.stringify(e,null,2)+`
3
- `):a===`summary`?T(n,e):E(n,e),0}catch(e){return r(`otto doctor: ${e instanceof Error?e.message:String(e)}\n`),1}}async function v(){return{environment:await y(),configuration:await b(),state:await x(),connectivity:await S()}}async function y(){return{runtime:`Node.js ${process.version}`,installPath:process.execPath,shell:process.env.SHELL??`(unknown)`,terminal:process.env.TERM??`(unknown)`,cpu:`${t().length}×${e()}`,memory:{total:o(),free:n()},platform:i(),release:a()}}async function b(){let e=process.env.ANTHROPIC_API_KEY?`api-key (ANTHROPIC_API_KEY)`:process.env.OTTO_API_KEY?`api-key (OTTO_API_KEY)`:process.env.AWS_ACCESS_KEY_ID?`aws-bedrock (env keys)`:`not configured`;return{workspaceDir:process.cwd(),storageDir:g,hasRipgrep:await C(`rg`),hasGit:await C(`git`),authMode:e,residencyBudget:{totalBytes:h,minPerSessionBytes:p,maxPerSessionBytes:f,historyMessages:d,resumeWindowMessages:m}}}async function x(){let e=s(g,`storage.db`);if(!l(e))return{dbPath:e,dbExists:!1};try{let{SqlitePersistence:t}=await import(`@x-otto/persistence`),n=new t({dbPath:e,namespace:`doctor`,wal:!1});try{let t=n.getInfo();return{dbPath:e,dbExists:!0,dbSize:w(e),...t}}finally{n.close()}}catch{return{dbPath:e,dbExists:!0,integrity:`error (unable to open)`}}}async function S(){let e=`api.anthropic.com`,t=!1,n,r=Date.now();try{let i=new AbortController,a=setTimeout(()=>i.abort(),5e3);await fetch(`https://${e}`,{method:`HEAD`,signal:i.signal}),clearTimeout(a),t=!0,n=Date.now()-r}catch{t=!1}return{providerHost:e,providerReachable:t,providerLatency:n,storageHostConfigured:!1}}async function C(e){try{return c(s(`/usr/local/bin`,e),1),!0}catch{try{return c(s(`/usr/bin`,e),1),!0}catch{return!1}}}function w(e){try{return u(e).size}catch{return 0}}function T(e,t){let{environment:n,configuration:r,state:i,connectivity:a}=t,o=i?.dbExists&&i.integrity===`ok`?`✅`:`❌`,s=a.providerReachable?`✅`:`❌`;e([`otto doctor — ${n.runtime} | ${r.storageDir} | DB:${o} ${s}`,i?` ${i.snapshots??`?`} snapshots, ${i.changeLogEntries??`?`} change_log`:``].join(`
4
- `)+`
5
- `)}function E(e,t){let{environment:n,configuration:r,state:i,connectivity:a}=t;if(e(`
6
- Environment
7
- `),e(` Runtime: ${n.runtime}\n`),e(` Install: ${n.installPath}\n`),e(` Shell: ${n.shell}\n`),e(` Terminal: ${n.terminal} (${n.release})\n`),e(` CPU: ${n.cpu}\n`),e(` Memory: ${O(n.memory.free)} / ${O(n.memory.total)} free\n`),r.residencyBudget){let t=r.residencyBudget;e(` Residency: budget=${O(t.totalBytes)} per-session=${O(t.minPerSessionBytes)}~${O(t.maxPerSessionBytes)} history=${t.historyMessages} resume=${t.resumeWindowMessages}\n`)}e(`
8
- Configuration
9
- `),e(` Workspace: ${r.workspaceDir}\n`),e(` Storage: ${r.storageDir} ${l(r.storageDir)?`✅`:`❌`}\n`),e(` ripgrep: ${r.hasRipgrep?`✅`:`❌`}\n`),e(` git: ${r.hasGit?`✅`:`❌`}\n`),e(` Auth: ${r.authMode}\n`),i&&(e(`
10
- State
11
- `),e(` SQLite DB: ${i.dbPath}\n`),i.dbExists?(e(` size: ${O(i.dbSize??0)}\n`),e(` integrity: ${i.integrity===`ok`?`ok ✅`:i.integrity??`N/A`}\n`),i.snapshots!==void 0&&e(` snapshots: ${i.snapshots}\n`),i.changeLogEntries!==void 0&&e(` change_log: ${i.changeLogEntries} entries\n`),i.sessions!==void 0&&e(` sessions: ${i.sessions}\n`),i.walEnabled!==void 0&&e(` WAL: ${i.walEnabled?`enabled`:`disabled`}\n`)):e(` (no storage.db found — start otto to create one)
12
- `)),e(`
13
- Connectivity
14
- `),e(` ${a.providerHost}: ${a.providerReachable?`reachable (${a.providerLatency}ms) ✅`:`unreachable ❌`}\n`),e(` storage host: ${a.storageHostConfigured?`configured`:`not configured (local only)`}\n`),e(`
15
- `)}function D(e){e(`otto doctor — 环境健康诊断
16
-
17
- 用法:
18
- otto doctor [--summary|--json]
19
-
20
- 选项:
21
- --summary 紧凑输出(一行摘要)
22
- --json 结构化 JSON(redacted)
23
- --help, -h 帮助信息
24
-
25
- 示例:
26
- otto doctor
27
- otto doctor --summary
28
- otto doctor --json
29
- `)}function O(e){if(e===0)return`0 B`;let t=[`B`,`KB`,`MB`,`GB`,`TB`],n=Math.floor(Math.log(e)/Math.log(1024));return`${(e/1024**n).toFixed(1)} ${t[n]}`}export{_ as runDoctorCommand};
30
- //# sourceMappingURL=doctor-C84-hhEr.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"doctor-C84-hhEr.js","names":[],"sources":["../src/commands/doctor.ts"],"sourcesContent":["import { homedir, release, platform, arch, cpus, totalmem, freemem } from 'node:os'\nimport { existsSync, statSync, accessSync } from 'node:fs'\nimport { join } from 'node:path'\nimport {\n SESSION_TOTAL_RESIDENCY_BUDGET,\n SESSION_MIN_PER_SESSION_BYTES,\n SESSION_MAX_PER_SESSION_BYTES,\n SESSION_MAX_HISTORY_MESSAGES,\n SESSION_RESUME_WINDOW_MESSAGES,\n} from '@x-otto/session-contract'\nexport interface DoctorDeps {\n stdout: (s: string) => void\n stderr: (s: string) => void\n}\n\nexport interface DoctorReport {\n environment: EnvironmentSection\n configuration: ConfigurationSection\n state?: StateSection\n connectivity: ConnectivitySection\n}\n\ninterface EnvironmentSection {\n runtime: string\n installPath: string\n shell: string\n terminal: string\n cpu: string\n memory: { total: number; free: number }\n platform: string\n release: string\n}\n\ninterface ConfigurationSection {\n workspaceDir: string\n storageDir: string\n hasRipgrep: boolean\n hasGit: boolean\n authMode: string\n /** RFC-323:驻留预算配置(只读展示,使基线估算偏差可被校准) */\n residencyBudget?: {\n totalBytes: number\n minPerSessionBytes: number\n maxPerSessionBytes: number\n historyMessages: number\n resumeWindowMessages: number\n }\n}\n\ninterface StateSection {\n dbPath: string\n dbExists: boolean\n dbSize?: number\n walEnabled?: boolean\n snapshots?: number\n changeLogEntries?: number\n sessions?: number\n integrity?: string\n}\n\ninterface ConnectivitySection {\n providerHost: string\n providerReachable: boolean\n providerLatency?: number\n storageHostConfigured: boolean\n}\n\nconst OTTO_DIR = join(homedir(), '.otto')\n\nexport async function runDoctorCommand(\n args: string[],\n deps: DoctorDeps,\n): Promise<number> {\n const stdout = deps.stdout\n const stderr = deps.stderr\n\n // RFC-200 D1.0:子命令路由——首个位置参数(非 -- 开头)分发到诊断子命令;\n // 无子命令时走既有健康检查(行为零变化)。\n const sub = args.find((a) => !a.startsWith('-'))\n if (sub === 'ps' || sub === 'attach' || sub === 'heap') {\n const rest = args.filter((a) => a !== sub)\n switch (sub) {\n case 'ps': {\n const { runDoctorPs } = await import('./handlers/doctor-ps')\n return runDoctorPs(rest, deps)\n }\n case 'attach': {\n const { runDoctorAttach } = await import('./handlers/doctor-attach')\n return runDoctorAttach(rest, deps)\n }\n case 'heap': {\n const { runDoctorHeap } = await import('./handlers/doctor-heap')\n return runDoctorHeap(rest, deps)\n }\n }\n }\n if (sub !== undefined) {\n stderr(`otto doctor: unknown subcommand \"${sub}\" (available: ps, attach, heap)\\n`)\n return 1\n }\n\n if (args.includes('--help') || args.includes('-h')) {\n printHelp(stdout)\n return 0\n }\n\n const outputFormat = args.includes('--json') ? 'json' : args.includes('--summary') ? 'summary' : 'detailed'\n\n try {\n const report = await collectReport()\n\n if (outputFormat === 'json') {\n stdout(JSON.stringify(report, null, 2) + '\\n')\n } else if (outputFormat === 'summary') {\n printSummary(stdout, report)\n } else {\n printDetailed(stdout, report)\n }\n\n return 0\n } catch (error) {\n stderr(`otto doctor: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n }\n}\n\n/** 收集结构化诊断报告(TUI /doctor 折叠档复用;不打印)。 */\nexport async function collectReport(): Promise<DoctorReport> {\n const env = await collectEnvironment()\n const config = await collectConfiguration()\n const state = await collectState()\n const connectivity = await collectConnectivity()\n\n return { environment: env, configuration: config, state, connectivity }\n}\n\nasync function collectEnvironment(): Promise<EnvironmentSection> {\n return {\n runtime: `Node.js ${process.version}`,\n installPath: process.execPath,\n shell: process.env.SHELL ?? '(unknown)',\n terminal: process.env.TERM ?? '(unknown)',\n cpu: `${cpus().length}×${arch()}`,\n memory: { total: totalmem(), free: freemem() },\n platform: platform(),\n release: release(),\n }\n}\n\nasync function collectConfiguration(): Promise<ConfigurationSection> {\n const authMode = process.env.ANTHROPIC_API_KEY\n ? 'api-key (ANTHROPIC_API_KEY)'\n : process.env.OTTO_API_KEY\n ? 'api-key (OTTO_API_KEY)'\n : process.env.AWS_ACCESS_KEY_ID\n ? 'aws-bedrock (env keys)'\n : 'not configured'\n\n return {\n workspaceDir: process.cwd(),\n storageDir: OTTO_DIR,\n hasRipgrep: await checkBinary('rg'),\n hasGit: await checkBinary('git'),\n authMode,\n // RFC-323 M3:暴露驻留预算配置,使\"基线 1500MB 估算偏差\"(R-2)可被真机数据校准。\n // 纯静态读常量,不接运行时状态——运行时有效分配值取决于活跃会话数 N,在 ps/doctor-ps 展示。\n residencyBudget: {\n totalBytes: SESSION_TOTAL_RESIDENCY_BUDGET,\n minPerSessionBytes: SESSION_MIN_PER_SESSION_BYTES,\n maxPerSessionBytes: SESSION_MAX_PER_SESSION_BYTES,\n historyMessages: SESSION_MAX_HISTORY_MESSAGES,\n resumeWindowMessages: SESSION_RESUME_WINDOW_MESSAGES,\n },\n }\n}\n\nasync function collectState(): Promise<StateSection | undefined> {\n const dbPath = join(OTTO_DIR, 'storage.db')\n if (!existsSync(dbPath)) {\n return { dbPath, dbExists: false }\n }\n\n try {\n const { SqlitePersistence } = await import('@x-otto/persistence')\n const store = new SqlitePersistence({ dbPath, namespace: 'doctor', wal: false })\n try {\n const info = store.getInfo() as {\n walEnabled: boolean\n snapshots: number\n changeLogEntries: number\n sessions: number\n integrity: string\n }\n\n return {\n dbPath,\n dbExists: true,\n dbSize: getFileSizeSync(dbPath),\n ...info,\n }\n } finally {\n store.close()\n }\n } catch {\n return { dbPath, dbExists: true, integrity: 'error (unable to open)' }\n }\n}\n\nasync function collectConnectivity(): Promise<ConnectivitySection> {\n let providerHost = 'api.anthropic.com'\n let providerReachable = false\n let providerLatency: number | undefined\n\n const start = Date.now()\n try {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 5000)\n await fetch(`https://${providerHost}`, {\n method: 'HEAD',\n signal: controller.signal,\n })\n clearTimeout(timeout)\n providerReachable = true\n providerLatency = Date.now() - start\n } catch {\n providerReachable = false\n }\n\n return {\n providerHost,\n providerReachable,\n providerLatency,\n storageHostConfigured: false,\n }\n}\n\nasync function checkBinary(name: string): Promise<boolean> {\n try {\n accessSync(join('/usr/local/bin', name), 1)\n return true\n } catch {\n try {\n accessSync(join('/usr/bin', name), 1)\n return true\n } catch {\n return false\n }\n }\n}\n\nfunction getFileSizeSync(path: string): number {\n try {\n return statSync(path).size\n } catch {\n return 0\n }\n}\n\nfunction printSummary(stdout: (s: string) => void, report: DoctorReport): void {\n const { environment: env, configuration: config, state, connectivity: conn } = report\n const dbOk = state?.dbExists && state.integrity === 'ok' ? '✅' : '❌'\n const netOk = conn.providerReachable ? '✅' : '❌'\n stdout([\n `otto doctor — ${env.runtime} | ${config.storageDir} | DB:${dbOk} ${netOk}`,\n state ? ` ${state.snapshots ?? '?'} snapshots, ${state.changeLogEntries ?? '?'} change_log` : '',\n ].join('\\n') + '\\n')\n}\n\nfunction printDetailed(stdout: (s: string) => void, report: DoctorReport): void {\n const { environment: env, configuration: config, state, connectivity: conn } = report\n\n stdout('\\nEnvironment\\n')\n stdout(` Runtime: ${env.runtime}\\n`)\n stdout(` Install: ${env.installPath}\\n`)\n stdout(` Shell: ${env.shell}\\n`)\n stdout(` Terminal: ${env.terminal} (${env.release})\\n`)\n stdout(` CPU: ${env.cpu}\\n`)\n stdout(` Memory: ${formatBytes(env.memory.free)} / ${formatBytes(env.memory.total)} free\\n`)\n if (config.residencyBudget) {\n const rb = config.residencyBudget\n stdout(` Residency: budget=${formatBytes(rb.totalBytes)} per-session=${formatBytes(rb.minPerSessionBytes)}~${formatBytes(rb.maxPerSessionBytes)} history=${rb.historyMessages} resume=${rb.resumeWindowMessages}\\n`)\n }\n\n stdout('\\nConfiguration\\n')\n stdout(` Workspace: ${config.workspaceDir}\\n`)\n stdout(` Storage: ${config.storageDir} ${existsSync(config.storageDir) ? '✅' : '❌'}\\n`)\n stdout(` ripgrep: ${config.hasRipgrep ? '✅' : '❌'}\\n`)\n stdout(` git: ${config.hasGit ? '✅' : '❌'}\\n`)\n stdout(` Auth: ${config.authMode}\\n`)\n\n if (state) {\n stdout('\\nState\\n')\n stdout(` SQLite DB: ${state.dbPath}\\n`)\n if (state.dbExists) {\n stdout(` size: ${formatBytes(state.dbSize ?? 0)}\\n`)\n stdout(` integrity: ${state.integrity === 'ok' ? 'ok ✅' : state.integrity ?? 'N/A'}\\n`)\n if (state.snapshots !== undefined) stdout(` snapshots: ${state.snapshots}\\n`)\n if (state.changeLogEntries !== undefined) stdout(` change_log: ${state.changeLogEntries} entries\\n`)\n if (state.sessions !== undefined) stdout(` sessions: ${state.sessions}\\n`)\n if (state.walEnabled !== undefined) stdout(` WAL: ${state.walEnabled ? 'enabled' : 'disabled'}\\n`)\n } else {\n stdout(` (no storage.db found — start otto to create one)\\n`)\n }\n }\n\n stdout('\\nConnectivity\\n')\n stdout(` ${conn.providerHost}: ${conn.providerReachable ? `reachable (${conn.providerLatency}ms) ✅` : 'unreachable ❌'}\\n`)\n stdout(` storage host: ${conn.storageHostConfigured ? 'configured' : 'not configured (local only)'}\\n`)\n stdout('\\n')\n}\n\nfunction printHelp(stdout: (s: string) => void): void {\n stdout(`otto doctor — 环境健康诊断\n\n用法:\n otto doctor [--summary|--json]\n\n选项:\n --summary 紧凑输出(一行摘要)\n --json 结构化 JSON(redacted)\n --help, -h 帮助信息\n\n示例:\n otto doctor\n otto doctor --summary\n otto doctor --json\n`)\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B'\n const units = ['B', 'KB', 'MB', 'GB', 'TB']\n const i = Math.floor(Math.log(bytes) / Math.log(1024))\n return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension-BKXIr86F.js","names":[],"sources":["../src/commands/extension-scaffold.ts","../src/commands/extension-link.ts","../src/commands/extension-lifecycle.ts","../src/commands/extension.ts"],"sourcesContent":["import { existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, symlinkSync, watch, writeFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\n\n/**\n * extension-scaffold.ts — 脚手架/编译/热重载域(RFC-303 M6 拆分)。\n */\n\n/** 子命令 IO 契约(原 extension.ts 头部定义,拆分后归 scaffold 域并导出共享)。 */\nexport interface ExtensionCommandIO {\n stdout: (s: string) => void\n stderr: (s: string) => void\n}\n\nimport {\n compilePluginPanels,\n compilePluginModule,\n} from '@x-otto/coding'\n\n/** 插件 id 须 kebab-case(对齐 `@x-otto/plugin` manifest 校验)。 */\nconst ID_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/\n\nexport interface ScaffoldResult {\n dir: string\n files: string[]\n}\n\n/** 插件骨架选项。 */\nexport interface ScaffoldOptions {\n /** 生成 plugin.ts 定义式工具示例(需 `otto extension build` 编译才能加载)。 */\n withTool?: boolean\n /** 在 manifest 中追加声明式 provider 示例(零编译、可被 reload 发现)。 */\n withProvider?: boolean\n /** 生成 message renderer 示例源码 + manifest 条目。 */\n withRenderer?: boolean\n /**\n * 生成 locale/ 目录 i18n 示例(RFC-141)+ manifest `contributes.i18nDir` 声明。\n * **默认开启**(缺省/`undefined` 视为 `true`)——i18n 是插件面向用户文本的基础能力,\n * 新插件应开箱即支持多语言;显式传 `false`(CLI `--no-i18n`)可关闭。\n */\n withI18n?: boolean\n}\nfunction manifestJson(name: string, opts: ScaffoldOptions = {}): string {\n const contributes: Record<string, unknown> = {\n commands: true,\n panels: [{ id: 'status', title: `${name} status`, entry: 'panels-dist/status.mjs' }],\n }\n const capabilities: string[] = []\n if (opts.withProvider) {\n contributes.providers = [\n {\n id: 'example',\n baseUrl: 'https://api.example.com/v1',\n wireApi: 'openai-completions',\n envKey: 'EXAMPLE_API_KEY',\n models: [{ id: 'example/gpt-tiny', contextWindow: 8192, maxOutput: 512 }],\n },\n ]\n capabilities.push('provider')\n }\n if (opts.withRenderer) {\n contributes.renderers = [{ id: 'screenshot', matcher: { toolName: 'browser_screenshot' }, entry: 'renderers-dist/screenshot.js' }]\n capabilities.push('tui.renderer')\n }\n if (opts.withI18n !== false) {\n contributes.i18nDir = 'locale'\n }\n\n const manifest: Record<string, unknown> = {\n id: name,\n name,\n version: '0.1.0',\n description: `An otto extension: ${name}`,\n contributes,\n scripts: { postinstall: 'otto extension build' },\n }\n if (capabilities.length) manifest.capabilities = capabilities\n return `${JSON.stringify(manifest, null, 2)}\\n`\n}\n\n/** locale/en.json 示例(RFC-141 D2:扁平 key-value,key 与 hello 命令的描述文本对应)。 */\nfunction localeEnJson(name: string): string {\n return `${JSON.stringify({ 'hello.description': `Say hello from the ${name} plugin` }, null, 2)}\\n`\n}\n\n/** locale/zh.json 示例(同 key,中文翻译)。 */\nfunction localeZhJson(name: string): string {\n return `${JSON.stringify({ 'hello.description': `从 ${name} 插件说 hello` }, null, 2)}\\n`\n}\n\n/** 定义式工具模板(definePlugin tools 轴,RFC-105 D4)。插件作者以此为起点。 */\nfunction toolPluginTs(name: string): string {\n return `/**\n * ${name} — definePlugin 工具示例(RFC-105 D4)。\n * 需 manifest 声明 capabilities: [\"tools\"] 并经信任门后才加载执行。\n * 运行 otto extension build → 编译 → /reload 生效。\n */\nimport { definePlugin } from '@x-otto/plugin'\n\nexport default definePlugin((ctx) => ({\n tools: [\n {\n name: 'word_count',\n description: 'Count words in a given text',\n parameters: {\n safeParse(input: unknown) {\n if (typeof input === 'object' && input !== null && 'text' in input) {\n return { success: true, data: input as { text: string } }\n }\n return { success: false, error: '缺少参数 \"text\"' }\n },\n },\n async execute(args: unknown) {\n const { text } = args as { text: string }\n const count = text.split(/\\\\s+/).filter(Boolean).length\n return \\`文本共 \\${count} 个单词\\`\n },\n },\n ],\n}))\n`\n}\n\n/** 消息渲染器模板(RFC-105 D6 renderers 轴)。返回 ANSI 字符串数组,与 renderMessageLines 同构。 */\nfunction rendererTs(name: string): string {\n return `/**\n * ${name} screenshot renderer — 自定义 browser_screenshot 工具结果的渲染方式。\n * 签名:(message: unknown, columns: number) => string[]\n * 返回 ANSI 着色字符串数组,与 renderMessageLines 同构(不引入 React)。\n */\nmodule.exports = function screenshotRenderer(message: unknown, columns: number): string[] {\n const m = message as Record<string, unknown>\n const name = String(m.name ?? '')\n const isError = Boolean(m.isError)\n return [\n '\\\\\\\\x1b[34m[ SCREENSHOT ]\\\\\\\\x1b[0m ' + name + (isError ? ' (failed)' : ''),\n ' use /extension → ${name} → open panel → preview',\n ]\n}\n`\n}\n\nfunction helloCommandMd(name: string): string {\n return `---\ndescription: Say hello from the ${name} plugin\nargument-hint: [your name]\n---\nHello from the **${name}** plugin! 👋\n\nYou passed: $ARGUMENTS\n`\n}\n\nfunction statusPanelTsx(name: string): string {\n return `import React from 'react'\nimport { Box, Text } from 'ink'\n\n/**\n * ${name} 状态面板(RFC-081 Ink 组件)。\n * \\`otto extension build\\`(或安装时 postinstall)用 esbuild 编译为 panels-dist/status.mjs(ESM)。\n */\nexport default function StatusPanel(): React.ReactElement {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"green\">\n ✓ ${name} plugin active\n </Text>\n <Text dimColor>编辑 panels/status.tsx,运行 \\`otto extension dev\\` 热重载。</Text>\n </Box>\n )\n}\n`\n}\n\nfunction readmeMd(name: string): string {\n return `# ${name}\n\n一个 otto 插件骨架(RFC-082 Tier 0:纯文件,零编译即可用)。\n\n## 结构\n\n- \\`otto-plugin.json\\` — 插件清单(id / 贡献声明)\n- \\`commands/hello.md\\` — 一条斜杠命令 \\`/hello\\`(立即可用)\n- \\`panels/status.tsx\\` — 一个 TUI 状态面板(需编译)\n\n## 装入工作区\n\n把本目录放到工作区的 \\`.otto/plugins/\\` 下:\n\n\\`\\`\\`sh\nmkdir -p <workspace>/.otto/plugins\ncp -r ${name} <workspace>/.otto/plugins/\n\\`\\`\\`\n\n在 otto 内 \\`/reload\\` 即生效,运行 \\`/hello world\\` 试试。\n含可执行面(面板/命令体)的项目级插件首次需 \\`/trust plugin ${name}\\` 信任后加载。\n\n## 开发\n\n\\`\\`\\`sh\notto extension dev --link <workspace> # 链接进工作区 + 监视改动(声明式热重载)\n\\`\\`\\`\n\n改命令/agent/skill/manifest 后,在 otto 内 \\`/reload\\` 即生效。\n(面板 \\`panels/*.tsx\\` 的热编译待 esbuild 依赖接线,当前需手动编译为 \\`panels-dist/*.js\\`。)\n`\n}\n\n/**\n * 生成 Tier 0 文件 hello-world 插件骨架。纯文件写入,`baseDir` 可注入供测试。\n * fail-closed:name 非 kebab-case 或目标目录已存在 → 抛错。\n */\nexport function scaffoldPlugin(name: string, baseDir: string, opts: ScaffoldOptions = {}): ScaffoldResult {\n if (!ID_RE.test(name)) {\n throw new Error(`插件名须 kebab-case(小写字母/数字/连字符,首尾非连字符):${name}`)\n }\n const dir = join(baseDir, name)\n if (existsSync(dir)) throw new Error(`目录已存在,拒绝覆盖:${dir}`)\n\n mkdirSync(join(dir, 'commands'), { recursive: true })\n mkdirSync(join(dir, 'panels'), { recursive: true })\n\n const files: string[] = []\n const write = (rel: string, content: string): void => {\n mkdirSync(join(dir, rel, '..'), { recursive: true })\n writeFileSync(join(dir, rel), content)\n files.push(rel)\n }\n write('otto-plugin.json', manifestJson(name, opts))\n write('commands/hello.md', helloCommandMd(name))\n write('panels/status.tsx', statusPanelTsx(name))\n // RFC-303 R9(dist 策略单一真源):scaffold 生而合规——产物目录不入库 +\n // manifest 带 postinstall(manifestJson 已含),防止新插件复制旧模式。\n write('.gitignore', 'panels-dist/\\nrenderers-dist/\\nplugin-dist/\\ndist/\\nbin/\\n')\n if (opts.withTool) write('plugin.ts', toolPluginTs(name))\n if (opts.withRenderer) {\n mkdirSync(join(dir, 'renderers'), { recursive: true })\n write('renderers/screenshot.ts', rendererTs(name))\n }\n if (opts.withI18n !== false) {\n mkdirSync(join(dir, 'locale'), { recursive: true })\n write('locale/en.json', localeEnJson(name))\n write('locale/zh.json', localeZhJson(name))\n }\n write('README.md', readmeMd(name))\n\n return { dir, files }\n}\n\nexport function extensionCreate(args: string[], io: ExtensionCommandIO): number {\n const name = args.find((a) => !a.startsWith('-'))\n if (!name) {\n io.stderr('用法: otto extension create <name> [--with-tool] [--with-provider] [--with-renderer] [--no-i18n]\\n')\n return 1\n }\n const opts: ScaffoldOptions = {\n withTool: args.includes('--with-tool'),\n withProvider: args.includes('--with-provider'),\n withRenderer: args.includes('--with-renderer'),\n // i18n 默认开启(RFC-141 追加:新插件开箱即支持多语言),`--no-i18n` 显式关闭。\n withI18n: !args.includes('--no-i18n'),\n }\n try {\n const { files } = scaffoldPlugin(name, process.cwd(), opts)\n io.stdout(`✓ 已创建插件 ${name}/\\n`)\n for (const f of files) io.stdout(` ${name}/${f}\\n`)\n const hints = [\n `cp -r ${name} <workspace>/.otto/plugins/ # 装入工作区`,\n '# 在 otto 内 /reload,然后运行 /hello world',\n `# 项目级插件含可执行面需 /trust plugin ${name}`,\n ]\n if (opts.withTool) hints.push('# plugin.ts 编译/加载需信任门后 —— otto extension build → /reload')\n if (opts.withProvider) hints.push('# 声明式 provider 无需编译,manifest 已含示例 —— /reload 后即生效')\n if (opts.withRenderer) hints.push('# renderer 源码在 renderers/,需编译:otto extension build → /reload')\n if (opts.withI18n) hints.push('# locale/*.json 无需编译,manifest 已声明 i18nDir —— /reload 后即生效')\n io.stdout('\\n下一步:\\n ' + hints.join('\\n ') + '\\n')\n return 0\n } catch (err) {\n io.stderr(`otto extension create 失败: ${(err as Error).message}\\n`)\n return 1\n }\n}\n\n/** 编译插件面板并把结果写到 io(build 与 dev 共用)。返回失败面板数。 */\nfunction compileAndReport(pluginDir: string, io: ExtensionCommandIO): number {\n const { compiled, errors, skipped } = compilePluginPanels(pluginDir)\n if (skipped) {\n io.stdout(' (无 panels/*.tsx,跳过面板编译)\\n')\n return 0\n }\n for (const c of compiled) io.stdout(` ✓ 编译 ${c}\\n`)\n for (const e of errors) io.stderr(` ✗ ${e.entry}: ${e.message}\\n`)\n return errors.length\n}\n\n/**\n * 编译插件代码模块(`plugin.ts`,definePlugin 代码轴:tools/providerFactories/renderers-代码式/\n * hooks)并把结果写到 io(RFC-105 M105-6 dev 热重载缺口修复)。只做**编译期语法/类型检查反馈**,\n * 不加载执行(执行仍由运行中的 otto 进程在 `/reload` 时经 `loadPluginModule` 完成,且该函数本身\n * 每次都重新编译 + 清 require cache,天然支持热重载——本函数只是让 esbuild 语法错误在 `dev` watch\n * 循环内**即时**可见,不必等用户切到 otto 里敲 `/reload` 才发现拼写错误)。\n * 返回 1 = 编译失败,0 = 成功或无入口(无 plugin.ts 不是错误,静默跳过)。\n */\nexport async function compileCodeModuleAndReport(pluginDir: string, io: ExtensionCommandIO): Promise<number> {\n try {\n const outfile = await compilePluginModule(pluginDir)\n if (outfile) io.stdout(` ✓ 编译 ${join('plugin-dist', 'plugin.cjs')}\\n`)\n return 0\n } catch (err) {\n io.stderr(` ✗ plugin.ts: ${(err as Error).message}\\n`)\n return 1\n }\n}\n\nexport function extensionBuild(args: string[], io: ExtensionCommandIO): number {\n const pluginDir = resolve(optValue(args, '--dir') ?? process.cwd())\n if (!existsSync(join(pluginDir, 'otto-plugin.json'))) {\n io.stderr(`otto extension build 失败: 未找到 otto-plugin.json:${pluginDir}\\n`)\n return 1\n }\n io.stdout(`▶ otto extension build:${pluginDir}\\n`)\n return compileAndReport(pluginDir, io) > 0 ? 1 : 0\n}\n\n/** 取 `--flag value` 形式的值(简单线性扫描,dev 子命令用)。 */\nexport function optValue(args: string[], flag: string): string | undefined {\n const i = args.indexOf(flag)\n return i >= 0 && i + 1 < args.length ? args[i + 1] : undefined\n}\n\n/**\n * 读取插件目录的 id(轻量解析,仅取 id 给链接命名;完整校验在 `@x-otto/plugin parsePluginManifest`)。\n * fail-closed:无 manifest / 非法 JSON / 缺合法 id → 抛错。\n */\nexport function readPluginId(pluginDir: string): string {\n const mpath = join(pluginDir, 'otto-plugin.json')\n if (!existsSync(mpath)) {\n throw new Error(`未找到 otto-plugin.json:${pluginDir}(请在插件目录内运行,或用 --dir <path>)`)\n }\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(mpath, 'utf-8'))\n } catch {\n throw new Error(`otto-plugin.json 非法 JSON:${mpath}`)\n }\n const id = (raw as { id?: unknown })?.id\n if (typeof id !== 'string' || !ID_RE.test(id)) {\n throw new Error(`otto-plugin.json 缺少合法 id(kebab-case):${mpath}`)\n }\n return id\n}\n\n/**\n * 把插件 dev 目录链接进工作区 `.otto/plugins/<id>`(npm-link 式:在项目里改、otto 直接看见)。\n * 幂等:已是指向本目录的 symlink → 原样返回;已存在但非本链接 → 抛错(不覆盖他物)。\n */\nexport function linkIntoWorkspace(pluginDir: string, id: string, workspaceDir: string): string {\n const pluginsRoot = join(workspaceDir, '.otto', 'plugins')\n mkdirSync(pluginsRoot, { recursive: true })\n const linkPath = join(pluginsRoot, id)\n if (existsSync(linkPath) || isSymlink(linkPath)) {\n if (isSymlink(linkPath)) {\n try {\n if (resolve(readlinkSync(linkPath)) === resolve(pluginDir)) return linkPath // 幂等\n } catch {\n /* fall through to conflict */\n }\n }\n throw new Error(`目标已存在且非本插件 dev 链接,拒绝覆盖:${linkPath}`)\n }\n symlinkSync(resolve(pluginDir), linkPath)\n return linkPath\n}\n\nfunction isSymlink(p: string): boolean {\n try {\n return lstatSync(p).isSymbolicLink()\n } catch {\n return false\n }\n}\n\n/** 改动通知行(纯函数,时间注入便于测试)。 */\nexport function formatChangeNotice(file: string, hhmmss: string): string {\n return `[${hhmmss}] ${file} 改动 → 在 otto 内 /reload 生效\\n`\n}\n\nfunction hhmmss(d: Date): string {\n return [d.getHours(), d.getMinutes(), d.getSeconds()].map((n) => String(n).padStart(2, '0')).join(':')\n}\n\nexport async function extensionDev(args: string[], io: ExtensionCommandIO): Promise<number> {\n const pluginDir = resolve(optValue(args, '--dir') ?? process.cwd())\n let id: string\n try {\n id = readPluginId(pluginDir)\n } catch (err) {\n io.stderr(`otto extension dev 失败: ${(err as Error).message}\\n`)\n return Promise.resolve(1)\n }\n\n io.stdout(`▶ otto extension dev:${id}\\n 监视 ${pluginDir}\\n`)\n\n const linkWs = optValue(args, '--link')\n if (linkWs) {\n try {\n const lp = linkIntoWorkspace(pluginDir, id, resolve(linkWs))\n io.stdout(` 已链接 → ${lp}\\n`)\n } catch (err) {\n io.stderr(` 链接失败:${(err as Error).message}\\n`)\n }\n }\n\n const hasPanels = existsSync(join(pluginDir, 'panels'))\n if (hasPanels) {\n io.stdout(' 编译面板…\\n')\n compileAndReport(pluginDir, io)\n }\n // RFC-105 M105-6 dev 热重载缺口修复:plugin.ts(definePlugin 代码轴)存在时,\n // dev 启动即做一次编译反馈——对齐 panels 的既有行为,语法错误立即可见而非等到 /reload。\n const hasCodeModule = existsSync(join(pluginDir, 'plugin.ts')) || existsSync(join(pluginDir, 'plugin.tsx'))\n if (hasCodeModule) {\n io.stdout(' 编译 plugin.ts…\\n')\n await compileCodeModuleAndReport(pluginDir, io)\n }\n io.stdout(` 改动后在 otto 内 /reload 生效。Ctrl+C 退出。\\n`)\n\n // 长循环外壳(fs.watch + SIGINT 优雅退出)。纯逻辑已抽为可测函数(readPluginId/linkIntoWorkspace/formatChangeNotice)。\n return new Promise<number>((resolveDone) => {\n let watcher: ReturnType<typeof watch> | undefined\n try {\n watcher = watch(pluginDir, { recursive: true }, (_event, filename) => {\n if (!filename) return\n const f = String(filename)\n // 忽略产物/隐藏文件,防自触发(plugin-dist 是 compilePluginModule 的编译产物目录)。\n if (f.includes('panels-dist') || f.includes('plugin-dist') || f.startsWith('.')) return\n io.stdout(formatChangeNotice(f, hhmmss(new Date())))\n // 精确排除 plugin.tsx(走下方 code 分支),而非宽泛前缀匹配 'plugin.'——避免误伤\n // 插件目录内其他以 'plugin.' 开头的 .tsx 文件(如未来出现的 plugin.config.tsx)\n // 被两个分支都跳过(对抗审核 finding F2)。\n if (hasPanels && f.endsWith('.tsx') && f !== 'plugin.tsx') compileAndReport(pluginDir, io) // 面板源改动 → 即时重编译\n if (hasCodeModule && (f === 'plugin.ts' || f === 'plugin.tsx')) void compileCodeModuleAndReport(pluginDir, io) // 代码模块改动 → 即时重编译反馈\n })\n } catch (err) {\n io.stderr(` 监视启动失败(平台可能不支持递归监视):${(err as Error).message}\\n`)\n resolveDone(1)\n return\n }\n const onSigint = (): void => {\n watcher?.close()\n process.off('SIGINT', onSigint)\n io.stdout('\\n停止 dev。\\n')\n resolveDone(0)\n }\n process.on('SIGINT', onSigint)\n })\n}\n\n/** `otto extension <create|dev>` 入口。 */\n/** 首方能力的友好别名 → npm 包名(`otto extension install <alias>`)。其余参数按 npm 包名直接解析。 */\n/**\n * 官方推荐插件目录:别名 → 真实安装信息。\n *\n * 供 `otto extension install <alias>` / 向导 / model-auth 三个入口的「安装建议列表」消费。\n * 每个条目显式声明 `capabilities`(来自对应插件的真实 manifest,非命名约定推断),\n * 消费方据此过滤(如向导只展示 `capabilities.includes('provider')` 的条目),\n * 不再依赖脆弱的别名末尾 `-provider` 后缀或包名 `@x-otto/plugin-` 前缀反推。\n */","import { existsSync, readFileSync } from 'node:fs'\nimport { createInterface } from 'node:readline/promises'\nimport { homedir } from 'node:os'\nimport { join, resolve, sep } from 'node:path'\nimport { OTTO_HOME, OTTO_CONFIG_FILENAME } from '@x-otto/env'\nimport React from 'react'\nimport { runStandaloneTui, ConfirmDialog } from '@x-otto/tui/standalone'\nimport {\n installPluginFromPackage,\n installPluginFromDir,\n resolvePackageDir,\n findWorkspaceRoot,\n trustPlugin,\n grantPluginCapabilities,\n readHighRiskCapabilities,\n recordPluginInstalled,\n scanInstalledVersions,\n checkUpgrades,\n InstallManager,\n defaultPostInstallExecutor,\n} from '@x-otto/coding'\nimport type { InstallPluginResult, VersionInfo } from '@x-otto/coding'\nimport { SettingsManager } from '@x-otto/setting'\nimport { readPluginId, optValue, type ExtensionCommandIO } from './extension-scaffold'\n\n/**\n * extension-link.ts — 安装/链接/更新域(RFC-303 M6 拆分)。\n */\nexport const EXTENSION_ALIASES: Record<string, { pkg: string; capabilities: string[] }> = {\n 'plugin-browser-use': { pkg: '@x-otto/plugin-browser-use-mcp', capabilities: ['tui.renderer', 'panel.backend'] },\n 'plugin-mobile-use': { pkg: '@x-otto/plugin-mobile-use-mcp', capabilities: ['tui.renderer', 'panel.backend'] },\n 'plugin-feishu-use': { pkg: '@x-otto/plugin-feishu-use', capabilities: [] },\n 'plugin-miniprogram-use': { pkg: '@x-otto/plugin-miniprogram-use-mcp', capabilities: ['tui.renderer', 'panel.backend'] },\n 'plugin-schedule': { pkg: '@x-otto/plugin-schedule-panel', capabilities: ['tui.renderer'] },\n 'anthropic-provider': { pkg: '@x-otto/plugin-anthropic', capabilities: ['provider'] },\n 'deepseek-provider': { pkg: '@x-otto/plugin-deepseek', capabilities: ['provider'] },\n 'oxygen-provider': { pkg: '@x-otto/plugin-oxygen', capabilities: ['provider'] },\n 'openai-codex-provider': { pkg: '@x-otto/plugin-openai-codex', capabilities: ['provider'] },\n 'github-copilot-provider': { pkg: '@x-otto/plugin-github-copilot', capabilities: ['provider'] },\n 'cursor-provider': { pkg: '@x-otto/plugin-cursor', capabilities: ['provider'] },\n // RFC-148 D6:三个标准 wire 协议实现(anthropic-messages/openai-responses/\n // openai-completions)的运行时依赖——任何声明这三个 wireApi 的 provider 插件\n // (首方 anthropic/deepseek/openai-codex 及第三方兼容端点)都需要它才能建流。\n 'wire-protocols': { pkg: '@x-otto/plugin-otto-wire-protocols', capabilities: ['wire-protocol'] },\n}\n\n\n/**\n * 高危能力 id → CLI 文案(RFC-125 D1 的 TUI 版本对应物,纯文本供终端输出,无 i18n 依赖——\n * `otto extension install` 是脚本化/CI 也会用到的入口,不接 TUI i18n 表)。\n */\nconst CAPABILITY_CLI_LABEL: Record<string, string> = {\n provider: 'provider —— 注册 LLM provider,将处理你的凭据与请求流量',\n tools: 'tools —— 注册可被 agent 调用的工具',\n 'tui.renderer': 'tui.renderer —— 在 TUI 进程内运行自定义渲染代码',\n network: 'network —— 发起任意网络请求',\n 'wire-protocol': 'wire-protocol —— 注册 wire 协议实现,可截获全部模型请求与响应流量(含凭据)',\n // 补齐(终局巡检,同 extension-plugin.ts pluginCapabilityLabel 覆盖率修复):\n // 此前这 3 项缺失时靠 `?? c` 兜底显示裸能力 id(非静默丢失,但用户体验降级为\n // \"看不懂是什么意思\"),补上说明文案与 TUI 侧 i18n 文案语义对齐。\n 'a2ui.component': 'a2ui.component —— 在 TUI 进程内运行自定义渲染组件代码',\n 'panel.backend': 'panel.backend —— 面板可驱动有副作用的后端业务操作',\n 'input.resolver': 'input.resolver —— 在提交时展开 @ 引用,把内容注入 prompt',\n 'a2ui.renderer': 'a2ui.renderer —— 运行自定义代码,跨 TUI 与 web-ui 渲染消息',\n}\n\n/**\n * 安装前对高危能力的一次性交互确认(RFC-125 D2/D3):仅当目标声明了 `HIGH_RISK_CAPABILITIES`\n * 子集时触发;`--yes`/`-y` 跳过交互但不跳过下方 `applyHighRiskGrant` 的写入动作本身\n * (非交互式场景/CI 仍需要一个明确的\"用户已同意\"信号,这里由显式传入的 flag 承担)。\n * 返回 true = 继续安装,false = 用户拒绝,调用方应中止。\n */\nasync function confirmHighRiskInstall(\n io: ExtensionCommandIO,\n pluginId: string,\n caps: readonly string[],\n yes: boolean,\n): Promise<boolean> {\n if (caps.length === 0) return true\n // RFC-304 D3:交互式终端(stdout+stdin 均 TTY)且未传 --yes 时走 TUI 渲染路径\n // (ConfirmDialog 弹窗);非 TTY/--yes 场景回退既有纯文本路径,脚本化/CI 零回归。\n if (!yes && process.stdout.isTTY && process.stdin.isTTY) {\n return confirmHighRiskTui(pluginId, caps)\n }\n const lines = [\n `插件 '${pluginId}' 申请以下高危能力:`,\n ...caps.map((c) => ` • ${CAPABILITY_CLI_LABEL[c] ?? c}`),\n ]\n io.stdout(`${lines.join('\\n')}\\n`)\n if (yes) {\n io.stdout('(--yes 已跳过交互确认,继续安装)\\n')\n return true\n }\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n try {\n const answer = (await rl.question('继续安装并授予以上能力?[y/N] ')).trim().toLowerCase()\n return answer === 'y' || answer === 'yes'\n } finally {\n rl.close()\n }\n}\n\n/**\n * RFC-304 D3:高危能力确认的 TUI 渲染路径——runStandaloneTui 挂载宿主 ConfirmDialog。\n * 只换渲染层:确认/取消的决策结果与信任/授权落盘逻辑(trustPlugin/grantPluginCapabilities)\n * 完全不变。resolveRef 解耦渲染与 resolve 时机(routes 先 render,ctx 在挂载后才可用)。\n */\nfunction confirmHighRiskTui(pluginId: string, caps: readonly string[]): Promise<boolean> {\n const resolveRef: { current: ((v: boolean) => void) | null } = { current: null }\n const message = [\n `插件 '${pluginId}' 申请以下高危能力:`,\n ...caps.map((c) => ` • ${CAPABILITY_CLI_LABEL[c] ?? c}`),\n '',\n '继续安装即视为同意授予以上能力(等价于 /trust plugin 的确认动作)。',\n ]\n return runStandaloneTui<boolean>(\n {\n routes: React.createElement(ConfirmDialog, {\n title: '高危能力确认',\n message,\n confirmLabel: '继续安装',\n cancelLabel: '取消',\n confirmColor: 'amber',\n onConfirm: () => resolveRef.current?.(true),\n onCancel: () => resolveRef.current?.(false),\n }),\n },\n (ctx) => {\n resolveRef.current = ctx.resolve\n },\n )\n}\n\n/**\n * 安装成功后,若安装源声明了高危能力,一次性完成\"信任 + 授予\"(RFC-125 D3/R4):\n * 复用与 `/trust plugin`/`promptPluginTrust` 完全相同的 `trustPlugin`/`grantPluginCapabilities`\n * 写入函数(审计路径单一——无论走 CLI 安装确认还是 TUI `/extension` 确认,落盘到\n * `~/.otto/plugin-trust.json` 的函数都是同一个,不另开一条特权写入通道)。\n */\nfunction applyHighRiskGrant(res: InstallPluginResult, caps: readonly string[]): void {\n if (caps.length === 0) return\n trustPlugin(res.dir)\n grantPluginCapabilities(res.dir, caps)\n}\n\n/**\n * `otto extension install` 是脚本化子命令入口(无 `ctx.app.settings` 可用,见 `runExtensionCommand`\n * 独立于 TUI `CliContext` 的调用形态),故本函数独立构造一个仅指向用户级配置文件的\n * `SettingsManager` 完成 `installed_plugins` 记录(RFC-219 重要事项规则 5:清单恒定路由\n * user 级配置,不受安装 scope 参数 project/repo 影响——即使 `--project`/`--repo` 安装,\n * 清单记录本身仍写入 `~/.otto/config.json`)。\n */\nasync function recordInstalledPluginEntry(entry: {\n id: string\n source: 'npm' | 'dir'\n sourceSpec: string\n version?: string\n scope: 'global' | 'project' | 'repository'\n capabilities?: string[]\n}): Promise<void> {\n const settings = new SettingsManager({ projectConfigPath: join(OTTO_HOME, OTTO_CONFIG_FILENAME) })\n await settings.load()\n await recordPluginInstalled(settings, entry)\n}\n\n/**\n * `otto extension install <name|package>` / `otto extension install --link <dir>`:\n * 把自带 `otto-plugin.json` 的 npm 包或本地目录 symlink 到插件目录作为 otto 插件。\n * 默认装用户级(`~/.otto/plugins/`,**永远受信任**,零弹窗);`--project` 装工作区\n * (`<cwd>/.otto/plugins/`,需信任门,走既有 `/trust plugin` 提示);`--repo` 装仓库级\n * (`<workspaceRoot>/.otto/plugins/`,同 project 需信任门,RFC-130)。\n * 该路径已在 TUI 侧由 `promptPluginTrust` 处理高危能力确认,见 RFC-125 D1。\n * `--link <dir>`:从本地目录直接安装(开发/首方 provider 插件用);\n * 不传 `--link` 时 `<name>` 命中友好别名则映射到包名,否则按 npm 包名解析。\n * `--yes`/`-y`:跳过高危能力交互确认(脚本化/CI 场景,RFC-125 D3;不影响 `--project`/`--repo` 分支,\n * 项目/仓库级插件的信任确认由 TUI `/trust plugin` 承担,本标志只作用于本函数内的用户级安装确认)。\n */\nexport async function extensionInstall(args: string[], io: ExtensionCommandIO): Promise<number> {\n const linkDir = optValue(args, '--link')\n const arg = linkDir ? undefined : args.find((a) => !a.startsWith('-'))\n const project = args.includes('--project')\n const repo = args.includes('--repo')\n const yes = args.includes('--yes') || args.includes('-y')\n\n if (linkDir) {\n if (project || repo) {\n io.stderr('--link 仅支持用户级安装(`~/.otto/plugins/`),不接受 --project/--repo。\\n')\n return 1\n }\n const absDir = resolve(linkDir)\n // RFC-125 D3:安装前读取来源目录声明的高危能力,触发一次性交互确认(--yes 可跳过交互)。\n const caps = readHighRiskCapabilities(absDir)\n const linkPluginId = caps.length > 0 ? readPluginId(absDir) : ''\n const proceed = await confirmHighRiskInstall(io, linkPluginId, caps, yes)\n if (!proceed) {\n io.stdout('已取消安装。\\n')\n return 1\n }\n try {\n const res = await installPluginFromDir(absDir)\n applyHighRiskGrant(res, caps)\n // RFC-219 M1/D1c:`--link` 仅支持用户级安装(函数开头已拒绝 --project/--repo),\n // 记录到 installed_plugins 清单恒为 'global' scope;capabilities 复用上方已计算的 caps。\n await recordInstalledPluginEntry({\n id: res.id,\n source: 'dir',\n sourceSpec: absDir,\n version: res.version,\n scope: 'global',\n capabilities: caps,\n })\n io.stdout(\n `${res.created ? '已安装' : '已是最新'}插件 '${res.id}' → ${res.dir}\\n` +\n ` 来源: 本地 (${absDir})(软链)\\n` +\n ` 用户级插件默认受信任。otto 内 /reload 生效。\\n` +\n (caps.length > 0\n ? ` 已授予高危能力:${caps.join(', ')}(等价于 /trust plugin ${res.id} 的确认动作,\\n` +\n ` 可随时用 /extension → ${res.id} → 撤销信任 关闭)。\\n`\n : ''),\n )\n // RFC-150 D5:安装时编译的非致命警告——显式展示,不静默(module-loader.ts 既有\n // \"编译失败 fail-soft 吞掉\"曾导致真实生产 bug 长期不可见,安装期用户正盯着终端,\n // 是暴露编译问题的最佳时机)。\n for (const w of res.compileWarnings) io.stderr(` ✗ ${w}\\n`)\n return 0\n } catch (e) {\n io.stderr(`插件安装失败:${(e as Error).message}\\n`)\n return 1\n }\n }\n\n if (!arg) {\n io.stderr('otto extension install <name|package> [--project] [--repo] 或 --link <dir>\\n')\n return 1\n }\n const pkg = EXTENSION_ALIASES[arg]?.pkg ?? arg\n try {\n const ottoDir = project\n ? join(process.cwd(), '.otto')\n : repo\n ? (() => {\n const wr = findWorkspaceRoot(process.cwd())\n if (!wr) {\n io.stderr('--repo 要求当前目录在 pnpm workspace 内(向上未找到 pnpm-workspace.yaml)。\\n')\n return ''\n }\n return join(wr, '.otto')\n })()\n : join(homedir(), '.otto')\n if (repo && !ottoDir) return 1\n\n // RFC-125 D3:用户级安装前读取来源包声明的高危能力,触发一次性交互确认。\n // 项目级(--project)与仓库级(--repo)不在此确认——项目/仓库级插件的信任+能力确认统一走 TUI `/trust plugin`\n // (已由 promptPluginTrust 处理,RFC-125 D1),避免同一插件被两条独立确认路径重复询问。\n let pkgCaps: string[] = []\n if (!project && !repo) {\n const pkgDir = resolvePackageDir(pkg, process.cwd())\n if (pkgDir) pkgCaps = readHighRiskCapabilities(pkgDir)\n }\n const proceed = await confirmHighRiskInstall(io, arg, pkgCaps, yes)\n if (!proceed) {\n io.stdout('已取消安装。\\n')\n return 1\n }\n // RFC-341 修复:`resolvePackageDir` 只能解析\"已在宿主 node_modules 可 require 到,\n // 或落在本仓 workspace glob 内\"的包——这是本仓首方扩展场景的既有设计(避免依赖\n // node_modules 软链的脆弱性),但对**用户从公网 npm 安装陌生插件**(如\n // `otto extension install @x-otto/plugin-weather`,用户机器上该包既不在\n // node_modules 也不在任何 workspace glob)完全不成立——此前会直接报\n // \"无法解析包\",`otto extension install` 命令对公网 npm 插件形同虚设。\n // 修复:找不到本地包时,回退到 `InstallManager.install()`(真实 npm pack 下载路径,\n // 与 TUI `/install` 同一套机制),而非直接报错。\n const localPkgDir = !project && !repo ? resolvePackageDir(pkg, process.cwd()) : undefined\n if (!project && !repo && !localPkgDir) {\n const manager = new InstallManager()\n try {\n const result = await manager.install(pkg, {\n global: true,\n as: 'plugin',\n postInstallExecutor: defaultPostInstallExecutor,\n })\n if (!result.success) {\n io.stderr(`插件安装失败:${result.error ?? '未知错误'}\\n`)\n return 1\n }\n const capsFromManifest = result.targetPath ? readHighRiskCapabilities(result.targetPath) : []\n if (capsFromManifest.length > 0 && result.targetPath) {\n try {\n trustPlugin(result.targetPath)\n grantPluginCapabilities(result.targetPath, capsFromManifest)\n } catch {\n /* best-effort:授权失败不影响安装成功,用户后续可手动 /trust */\n }\n }\n await recordInstalledPluginEntry({\n id: result.name ?? pkg,\n source: 'npm',\n sourceSpec: pkg,\n version: result.manifest?.version,\n scope: 'global',\n capabilities: capsFromManifest,\n })\n io.stdout(\n `已安装插件 '${result.name ?? pkg}' → ${result.targetPath}\\n` +\n ` 来源: npm registry(${pkg})\\n` +\n (capsFromManifest.length > 0\n ? ` 已授予高危能力:${capsFromManifest.join(', ')}(等价于 /trust plugin ${result.name} 的确认动作)。\\n`\n : '') +\n ` 用户级插件默认受信任。otto 内 /reload 生效。\\n`,\n )\n return 0\n } catch (e) {\n io.stderr(`插件安装失败:${(e as Error).message}\\n`)\n return 1\n }\n }\n const res = await installPluginFromPackage({ pkg, ottoDir, cwd: process.cwd() })\n if (!project && !repo) applyHighRiskGrant(res, pkgCaps)\n // RFC-219 M1/D1c:记录到 installed_plugins 清单,恒定路由 user 级配置存储(重要事项\n // 规则 5),scope 字段按安装目标目录记录审计语义('global'/'project'/'repository')。\n // capabilities:project/repo 安装路径不在本函数内读取高危能力(信任确认统一走 TUI\n // `/trust plugin`,见上方注释),故 pkgCaps 此时恒为空数组,如实记录(非缺失数据)。\n await recordInstalledPluginEntry({\n id: res.id,\n source: 'npm',\n sourceSpec: pkg,\n version: res.version,\n scope: project ? 'project' : repo ? 'repository' : 'global',\n capabilities: pkgCaps,\n })\n io.stdout(\n `${res.created ? '已安装' : '已是最新'}插件 '${res.id}' → ${res.dir}\\n` +\n ` 来源包: ${pkg}(软链 → ${res.packageDir})\\n` +\n (pkgCaps.length > 0\n ? ` 已授予高危能力:${pkgCaps.join(', ')}(等价于 /trust plugin ${res.id} 的确认动作,\\n` +\n ` 可随时用 /extension → ${res.id} → 撤销信任 关闭)。\\n`\n : '') +\n (project\n ? ` 项目级插件需先信任方激活:在 otto 内运行 /trust plugin ${res.id}(或 /extension)信任该插件。\\n`\n : repo\n ? ` 仓库级插件需先信任方激活:在 otto 内运行 /trust plugin ${res.id}(或 /extension)信任该插件。\\n`\n : arg === 'plugin-feishu-use'\n ? ' 用户级插件默认受信任(feishu-use 不提供 MCP 工具,是独立长连接进程,见下)。\\n'\n : ` 用户级插件默认受信任,工具 mcp__${res.id}__* 即刻可用(otto 内 /reload 生效)。\\n`) +\n (arg === 'plugin-browser-use'\n ? ' 默认 auto:有 relay 用真 Chrome,否则本地 headless(自动探测浏览器,零配置)。\\n' +\n ' relay native host 首次 /reload 由插件 setup 钩子自动注入(前提 cargo build --release -p otto-relay);\\n' +\n ' 之后 chrome://extensions 加载 packages/extension/dist 即可接管真实 Chrome。\\n'\n : '') +\n (arg === 'plugin-mobile-use'\n ? ' 默认 Android:零配置自动探测首个已连接设备/模拟器(`adb devices` 可见即用)。\\n' +\n ' iOS 支持:在 ~/.otto/mobile-use.json 或 <project>/.otto/mobile-use.json 设\\n' +\n ' {\"platform\":\"ios\"}(需本机已装 Xcode,默认连首个已 Booted 的 Simulator)。\\n' +\n ' 默认 unified 单工具模式(`mobile_action` 按 type 分发);设 {\"unified\":false}\\n' +\n ' 回退逐工具模式(12 个 mobile_* 工具,只读动作享有 readOnlyHint 并发优化)。\\n' +\n ' 真机连接默认拒绝(安全 fail-closed):需显式设 {\"real_device_confirmation\":\"allow\"}\\n' +\n ' 才允许连接真实 Android/iOS 设备(模拟器/Simulator 不受此限制,零配置可用)。\\n'\n : '') +\n (arg === 'plugin-feishu-use'\n ? ' feishu-use 是一个 7×24 独立长连接进程,不由 otto 插件系统管理其常驻生命周期:\\n' +\n ` 1) otto feishu config init # 交互式配置向导(${project ? `写入 ${res.dir}/../../feishu-use.json` : repo ? '写入 <workspaceRoot>/.otto/feishu-use.json' : '默认写入项目级 <.otto>/feishu-use.json;--global 写 ~/.otto/'})\\n` +\n ' 2) otto feishu config get # 核对配置(appSecret 自动脱敏)\\n' +\n ` 3) node ${res.dir}/bin/bridge-cli.mjs # 前台启动(或用 launchd/systemd/pm2 常驻)\\n` +\n ' 也可用 `otto feishu config set <key> <value>` 随时修改单个字段。\\n' +\n ' 配置按作用域自动选择(project > repo > user,first-found wins)。\\n' +\n ' 详见 extensions/plugin-feishu-use/README.md(含\"用来迭代 otto 框架\"专项章节)。\\n'\n : '') +\n (arg === 'plugin-miniprogram-use'\n ? ' miniprogram-use 需要两步配置:\\n' +\n ' 1) 创建 ~/.otto/miniprogram-use.json(appid + private_key_path + project_path 必填;\\n' +\n ' submit_audit/release 需额外配置 app_secret)\\n' +\n ' 2) otto miniprogram login # 扫码登录 mp.weixin.qq.com(一次性,用于后台管理只读能力)\\n' +\n ' 3) otto /reload 即可使用 mcp__miniprogram-use__* 工具。\\n' +\n ' 可选:在 ~/.otto/miniprogram-use.json 配置 admin_endpoints(需真实抓包获得)后,\\n' +\n ' 才会注册后台管理只读工具(get_members/get_permission_status/\\n' +\n ' get_experience_qrcode)。详见 docs/rfc/RFC-121-miniprogram-use.md。\\n'\n : ''),\n )\n // RFC-150 D5:同上(--link 分支)——安装时编译的非致命警告显式展示,不静默。\n for (const w of res.compileWarnings) io.stderr(` ✗ ${w}\\n`)\n return 0\n } catch (e) {\n io.stderr(`插件安装失败:${(e as Error).message}\\n`)\n return 1\n }\n}\n\n/**\n * `otto extension update [name]`:扫描已安装(带 version 字段的)skill/plugin,\n * 对比 npm registry / git remote 最新版本,对\"确认有更新\"的条目自动重装(`InstallManager`\n * + `force:true`,复用与 `otto install --force` 完全相同的安装路径——信任/高危能力/\n * postinstall 均走既有装置,本命令不新开一条特权安装通道)。\n *\n * 不传 `<name>` 时对全部三级(project/repository/global)已发现的过期条目逐一重装;\n * 传 `<name>` 时仅处理该 id 匹配的条目(未命中报错退出)。\n *\n * 收编原两步流程(`/upgrade --check` 报告 → 用户手动 `/install --force <source>`)——\n * npm/git 来源可从 `.otto-install.json`(RFC-131 `sourceMetadata`)重建 source 字符串\n * 自动重装;file/url 来源不支持版本比对(同 `checkUpgrades` 既有语义),跳过并提示。\n */\nexport async function extensionUpdate(args: string[], io: ExtensionCommandIO): Promise<number> {\n const filterName = args.find((a) => !a.startsWith('-'))\n\n const installed = scanInstalledVersions({\n workspaceDir: process.cwd(),\n workspaceRoot: findWorkspaceRoot(process.cwd()),\n scope: 'all',\n })\n\n const targets = filterName ? installed.filter((v) => v.name === filterName) : installed\n\n if (targets.length === 0) {\n io.stdout(\n filterName\n ? `未找到已安装且带版本号的扩展 \"${filterName}\"。\\n`\n : '未发现带版本号的已安装扩展(skill/plugin)。\\n',\n )\n return filterName ? 1 : 0\n }\n\n const results = await checkUpgrades(targets)\n const toUpdate = results.filter((r) => r.newer)\n\n if (toUpdate.length === 0) {\n io.stdout('全部扩展已是最新版本。\\n')\n for (const r of results) {\n if (r.error) io.stdout(` [${r.type}] ${r.name} v${r.installed} — ${r.error}\\n`)\n }\n return 0\n }\n\n io.stdout(`发现 ${toUpdate.length} 个可更新扩展,开始重装:\\n`)\n\n const manager = new InstallManager()\n let ok = 0\n let fail = 0\n\n for (const r of toUpdate) {\n const info = targets.find((v) => v.name === r.name && v.type === r.type)\n const source = info ? readReinstallSource(info) : undefined\n if (!source) {\n io.stderr(` ✗ ${r.name}:无法确定重装来源(.otto-install.json 缺失或来源类型不支持自动重装)\\n`)\n fail++\n continue\n }\n // scope 判定:路径含 <homedir>/.otto/ → global;否则 project/repository 视为项目级\n // (install() 的 workspaceDir 仅用于 project 级目标定位,reinstall 保持原安装 scope)。\n const isGlobal = info!.path.startsWith(join(homedir(), '.otto') + sep)\n try {\n const result = await manager.install(source, {\n workspaceDir: process.cwd(),\n global: isGlobal,\n force: true,\n as: r.type,\n postInstallExecutor: defaultPostInstallExecutor,\n })\n if (result.success) {\n io.stdout(` ✓ ${r.name} v${r.installed} → v${r.latest ?? '?'}\\n`)\n ok++\n } else {\n // RFC-290 T290-11:非 TUI 路径不消费 `nextStep`(那是进度面板的通道),因此在这里\n // 按 `failureKind` 自行补一句补救措施——安装器的 `error` 现在只陈述事实。\n // 本分支恒 `force: true`,理论上撞不到 conflict;保留分支是防御性的,避免将来\n // 改动 force 语义时这条路径静默丢失可执行提示。\n const hint = result.failureKind === 'conflict' ? '(已存在且未覆盖)' : ''\n io.stderr(` ✗ ${r.name}:${result.error ?? '重装失败'}${hint}\\n`)\n fail++\n }\n } catch (e) {\n io.stderr(` ✗ ${r.name}:${(e as Error).message}\\n`)\n fail++\n }\n }\n\n io.stdout(`\\n完成:${ok} 个已更新${fail > 0 ? `,${fail} 个失败` : ''}。\\n`)\n return fail > 0 ? 1 : 0\n}\n\n/**\n * 从 `.otto-install.json`(RFC-131 `InstallSourceMetadata`)重建可传给 `InstallManager.install()`\n * 的 source 字符串。npm → 裸包名(sourceUrl 形如 `npm:<pkg>`,剥离前缀);git → sourceUrl\n * 本身即合法 git 地址。file/url 来源不支持重装(同 `checkUpgrades` 既有语义,无版本比对基准)。\n */\nfunction readReinstallSource(info: VersionInfo): string | undefined {\n const metaPath = join(info.path, '.otto-install.json')\n if (!existsSync(metaPath)) return undefined\n try {\n const meta = JSON.parse(readFileSync(metaPath, 'utf-8')) as {\n sourceType?: string\n sourceUrl?: string\n }\n if (meta.sourceType === 'npm' && meta.sourceUrl?.startsWith('npm:')) {\n return meta.sourceUrl.slice('npm:'.length)\n }\n if (meta.sourceType === 'git' && meta.sourceUrl) {\n return meta.sourceUrl\n }\n return undefined\n } catch {\n return undefined\n }\n}\n","import { existsSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { OTTO_HOME, OTTO_CONFIG_FILENAME } from '@x-otto/env'\nimport { SettingsManager } from '@x-otto/setting'\nimport { purgePluginState } from '@x-otto/coding'\nimport { discoverPluginsWithBundled } from '../discover-with-bundled'\nimport type { ExtensionCommandIO } from './extension-scaffold'\n\n/**\n * extension-lifecycle.ts —— `otto extension <enable|disable|list|purge>` 子命令\n * (RFC-303 M6 拆分 + D3 新增)。\n *\n * 直接读写 settings(不装配完整 App,绕开 RFC-287 M1\"加 CLI 子命令需重型 App 装配\"\n * 的顾虑——本里程碑显式反转 `RFC-287-milestone-m1.md:18` 的设计偏离,反转原因:\n * 轻量路径(直写 settings)即可完成同语义的落盘,无需 TUI 会话实例)。\n *\n * **kill 逻辑的 CLI 路径限制(独立评审 2026-07-31 发现,显式标注)**:\n * `ExtensionManager.setEnabled` 的 `ProcessRuntime.killAll` 追踪的是**进程内状态**——\n * CLI standalone 命令新建的 SettingsManager 天然没有运行中 TUI 会话的进程句柄,\n * 故 CLI 的 `disable` 命令**无法**杀掉运行中 TUI 会话里由该插件启动的子进程。\n * 这不是 bug(输出提示规避,见 extensionDisable 注释),kill 逻辑对 CLI 路径实质是\n * no-op,仅落盘 disabled_plugins;真正的进程清理发生在下次读到该状态的进程重启/\n * reload 时。避免后续维护者误读为\"两条路径的 kill 都生效\"。\n */\n\n/** CLI 专用的 settings 句柄(仅指向用户级配置,与 runExtensionCommand 调用形态对齐)。 */\nfunction cliSettings(): SettingsManager {\n const settings = new SettingsManager({ projectConfigPath: join(OTTO_HOME, OTTO_CONFIG_FILENAME) })\n return settings\n}\n\n/** 发现全部插件(含已禁用——与 ExtensionManager.list 同源 discoverPlugins 语义)。 */\nexport function allPlugins(homeDir?: string): Array<{ id: string; name: string; scope: string }> {\n return discoverPluginsWithBundled({\n cwd: process.cwd(),\n // discoverPlugins 内部 join(homedir, '.otto') → <home>/.otto/plugins 是用户级插件根\n // (CLI 语义:用户级安装目标,对齐 extension install 默认路径)。\n // homeDir 可注入(测试用隔离目录;缺省读真实 home)。\n homedir: homeDir ?? homedir(),\n }).map((p) => ({ id: p.id, name: p.manifest.name ?? p.id, scope: p.scope }))\n}\n\n/**\n * `otto extension enable <id>`:从 disabled_plugins 移除该插件(落盘)。\n * 运行中的 otto 会话内执行 /reload 或重启生效(提示语承担\"kill 未必生效\"的隐含语义)。\n */\nexport async function extensionEnable(args: string[], io: ExtensionCommandIO): Promise<number> {\n const id = args.find((a) => !a.startsWith('-'))\n if (!id) {\n io.stderr('otto extension enable <id>\\n')\n return 1\n }\n const settings = cliSettings()\n await settings.load()\n const disabled = new Set(settings.get('disabled_plugins') ?? [])\n if (!disabled.has(id)) {\n io.stdout(`插件 '${id}' 当前已是启用状态。\\n`)\n return 0\n }\n disabled.delete(id)\n await settings.persist({ disabled_plugins: [...disabled] })\n io.stdout(`已启用插件 '${id}'。运行中的 otto 会话内执行 /reload 或重启生效。\\n`)\n return 0\n}\n\n/**\n * `otto extension disable <id>`:把该插件加入 disabled_plugins(落盘)。\n * **CLI 路径不杀运行中进程**(见文件头 kill no-op 说明)——提示语同时承担\n * \"需 reload 才彻底清理\"的隐含语义。\n */\nexport async function extensionDisable(args: string[], io: ExtensionCommandIO): Promise<number> {\n const id = args.find((a) => !a.startsWith('-'))\n if (!id) {\n io.stderr('otto extension disable <id>\\n')\n return 1\n }\n // builtin 插件恒定启用(对齐 ExtensionManager.setEnabled 的 RFC-128 语义:显式拒绝\n // 而非静默忽略——用户看到\"禁用成功\"下次发现还在启用是谜)。\n const plugin = allPlugins().find((p) => p.id === id)\n if (plugin?.scope === 'builtin') {\n io.stderr(`插件 \"${id}\" 是 otto 内置插件,恒定信任+启用,无法禁用。\\n`)\n return 1\n }\n const settings = cliSettings()\n await settings.load()\n const disabled = new Set(settings.get('disabled_plugins') ?? [])\n if (disabled.has(id)) {\n io.stdout(`插件 '${id}' 当前已是禁用状态。\\n`)\n return 0\n }\n disabled.add(id)\n await settings.persist({ disabled_plugins: [...disabled] })\n io.stdout(\n `已禁用插件 '${id}'。运行中的 otto 会话内执行 /reload 或重启生效(CLI 路径不杀运行中进程,reload 时统一清理)。\\n`,\n )\n return 0\n}\n\n/** `otto extension list`:列出全部插件及启用状态(含 disabled_plugins 过滤)。 */\nexport async function extensionList(_args: string[], io: ExtensionCommandIO): Promise<number> {\n const settings = cliSettings()\n await settings.load()\n const disabled = new Set(settings.get('disabled_plugins') ?? [])\n const plugins = allPlugins()\n if (plugins.length === 0) {\n io.stdout('(未发现任何插件)\\n')\n return 0\n }\n // 输出对齐 TUI /extension 面板语义:builtin 恒定启用(readonly),project/repository 标注 scope。\n for (const p of plugins) {\n const enabled = p.scope === 'builtin' ? true : !disabled.has(p.id)\n const state = enabled ? 'enabled ' : 'disabled'\n const readonly = p.scope === 'builtin' ? ' (builtin, readonly)' : ` (${p.scope})`\n io.stdout(` ${state} ${p.id}${readonly} ${p.name}\\n`)\n }\n return 0\n}\n\n/**\n * `otto extension purge <id>`:删除该插件全部持久化状态(plugin-state/<id>,不可逆)。\n * 高风险操作确认纪律:默认交互确认;`--yes` 跳过(脚本化场景)。\n */\nexport async function extensionPurge(args: string[], io: ExtensionCommandIO): Promise<number> {\n const id = args.find((a) => !a.startsWith('-'))\n const yes = args.includes('--yes') || args.includes('-y')\n if (!id) {\n io.stderr('otto extension purge <id> [--yes]\\n')\n return 1\n }\n const stateDir = join(OTTO_HOME, 'plugin-state', id)\n if (!existsSync(stateDir)) {\n io.stdout(`插件 '${id}' 无持久化状态(${stateDir} 不存在)。\\n`)\n return 0\n }\n if (!yes) {\n // 非 TTY(管道/脚本)且未显式 --yes → 拒绝(fail-closed:不可逆操作不做默认放行)。\n if (!process.stdout.isTTY) {\n io.stderr(`插件 '${id}' 有持久化状态,删除不可逆——非交互终端请显式传 --yes。\\n`)\n return 1\n }\n const { createInterface } = await import('node:readline/promises')\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n try {\n const answer = (await rl.question(`确认删除插件 '${id}' 的全部持久化状态?此操作不可逆 [y/N] `)).trim().toLowerCase()\n if (answer !== 'y' && answer !== 'yes') {\n io.stdout('已取消。\\n')\n return 0\n }\n } finally {\n rl.close()\n }\n }\n const res = purgePluginState(id)\n io.stdout(res.removed ? `已清除插件 '${id}' 的持久化状态。\\n` : `清除插件 '${id}' 状态失败(目录可能仍存在)。\\n`)\n return res.removed ? 0 : 1\n}\n","import type { ExtensionCommandIO } from './extension-scaffold'\nimport {\n extensionCreate,\n extensionBuild,\n extensionDev,\n scaffoldPlugin,\n formatChangeNotice,\n linkIntoWorkspace,\n readPluginId,\n compileCodeModuleAndReport,\n type ScaffoldOptions,\n type ScaffoldResult,\n} from './extension-scaffold'\nimport { extensionInstall, extensionUpdate, EXTENSION_ALIASES } from './extension-link'\n\nexport { EXTENSION_ALIASES }\nexport {\n scaffoldPlugin,\n formatChangeNotice,\n linkIntoWorkspace,\n readPluginId,\n compileCodeModuleAndReport,\n}\nexport type { ScaffoldOptions, ScaffoldResult }\nimport { extensionEnable, extensionDisable, extensionList, extensionPurge } from './extension-lifecycle'\n\n/**\n * extension.ts —— `otto extension <子命令>` 薄分发入口(RFC-303 M6 拆分)。\n * 各域实现见 extension-scaffold(create/build/dev)/ extension-link(install/update)/\n * extension-lifecycle(enable/disable/list/purge,RFC-303 D3 新增)。\n */\n\nexport function runExtensionCommand(args: string[], io: ExtensionCommandIO): number | Promise<number> {\n const [sub, ...rest] = args\n switch (sub) {\n case 'create':\n return extensionCreate(rest, io)\n case 'build':\n return extensionBuild(rest, io)\n case 'dev':\n return extensionDev(rest, io)\n case 'install':\n return extensionInstall(rest, io)\n case 'update':\n return extensionUpdate(rest, io)\n case 'enable':\n return extensionEnable(rest, io)\n case 'disable':\n return extensionDisable(rest, io)\n case 'list':\n return extensionList(rest, io)\n case 'purge':\n return extensionPurge(rest, io)\n default:\n io.stdout(\n 'otto extension <命令>\\n' +\n ' create <name> 生成插件骨架(命令 + 面板 + manifest)\\n' +\n ' install <name|pkg> [--project] [--repo] [--yes] 从 npm 安装插件(别名 browser-use/mobile-use/\\n' +\n ' feishu-use/miniprogram-use/anthropic-provider/\\n' +\n ' deepseek-provider/openai-codex-provider)\\n' +\n ' install --link <dir> [--yes] 从本地目录安装(symlink,开发/首方插件用,默认用户级)\\n' +\n ' 声明高危能力(provider/tools/tui.renderer/network)的插件\\n' +\n ' 安装前会交互确认;--yes 跳过交互但不跳过授权写入\\n' +\n ' --project 安装到 <cwd>/.otto/plugins/(需信任门)\\n' +\n ' --repo 安装到 <workspaceRoot>/.otto/plugins/(需信任门,RFC-130)\\n' +\n ' build [--dir <p>] 编译 panels/*.tsx → panels-dist/*.js(供 postinstall)\\n' +\n ' dev [--dir <p>] [--link <workspace>] 热重载(编译面板 + 监视 + 链接进工作区)\\n' +\n ' update [name] 检查并重装已过期的已安装 skill/plugin(npm/git 来源,自动重装)\\n' +\n ' enable <id> 启用插件(落盘 disabled_plugins;运行中会话 /reload 生效)\\n' +\n ' disable <id> 禁用插件(同上;CLI 路径不杀运行中进程,reload 时清理)\\n' +\n ' list 列出全部插件及启用状态\\n' +\n ' purge <id> 删除该插件全部持久化状态(plugin-state/<id>,不可逆)\\n',\n )\n return sub ? 1 : 0\n }\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import{n as e,t}from"./extension-BKXIr86F.js";export{e as EXTENSION_ALIASES,t as runExtensionCommand};
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import{c as e}from"./extension-plugin-C2Yhv77x.js";export{e as runPluginInstall};
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
- import{t as e}from"./discover-with-bundled-CznLjkkw.js";import{n as t}from"./extension-BKXIr86F.js";import{homedir as n}from"node:os";import{POINT as r,buildExtensionContext as i,dispatch as a,evaluatePluginTrust as o,findWorkspaceRoot as s,grantPluginCapabilities as c,installPluginFromPackage as l,openTarget as u,readHighRiskCapabilities as d,readPluginLogTail as f,recordPluginInstalled as p,recordPluginUninstalled as m,resolveActions as h,resolvePackageDir as g,runPluginScript as _,trustPlugin as v,uninstall as y,untrustPlugin as b}from"@x-otto/coding";import{join as x}from"node:path";import{existsSync as S,readFileSync as C}from"node:fs";import{i18next as w}from"@x-otto/tui";function T(e){switch(e){case`global`:return w.t(`cmd.scopeGlobal`);case`repository`:return w.t(`cmd.scopeRepository`);default:return w.t(`cmd.scopeProject`)}}function E(e,t,n){return{workspaceDir:e.app.workspaceDir,workspaceRoot:t===`repository`?s(e.app.workspaceDir):void 0,global:t===`global`,dryRun:n}}async function D(e){let{name:t,type:n,scope:r,ctx:i,onDone:a}=e,o=i.app.settings.get(`mcp_servers`),s=await y(t,n,E(i,r,!0),()=>o,async()=>{});if(!s.success){i.tui.info.showToast(w.t(`cmd.titleUninstall`),w.t(`cmd.uninstallFailed`,{err:s.error??w.t(`cmd.unknownError`)})),a?.();return}let c=s.plan??[];i.tui.modal.openModal({kind:`confirm`,title:w.t(`cmd.uninstallConfirmTitle`,{type:n,name:t}),message:[w.t(`cmd.uninstallConfirmScope`,{scopeLabel:T(r)}),...c].join(`
3
- `),confirmLabel:w.t(`cmd.uninstallConfirmYes`),dangerous:!0,onConfirm:()=>{O(e)},onCancel:()=>a?.()})}async function O(e){let{name:t,type:n,scope:r,ctx:i,onDone:a}=e,o=i.app.settings.get(`mcp_servers`),s=await y(t,n,E(i,r,!1),()=>o,async e=>{await i.app.settings.persist({mcp_servers:e})});if(!s.success){i.tui.info.showToast(w.t(`cmd.titleUninstall`),w.t(`cmd.uninstallFailed`,{err:s.error??w.t(`cmd.unknownError`)})),a?.();return}i.tui.info.showToast(w.t(`cmd.titleUninstall`),w.t(`cmd.uninstalled`,{type:n,name:t,scope:T(r)})),n===`plugin`&&await m(i.app.settings,t);try{await i.app.reloadCapabilities(),i.tui.plugins.setPluginRegistrations(i.app.getPluginRegistrations()),n===`plugin`&&i.app.pluginModuleManager.emitMonitorEvent({type:`plugin.uninstalled`,pluginId:t}),i.tui.info.showToast(w.t(`cmd.titleUninstall`),w.t(`cmd.uninstallRefreshed`))}catch(e){i.tui.messages.pushMessage({role:`command_echo`,command:`/extension`,status:`error`,summary:w.t(`cmd.uninstalledRefreshErr`,{err:e instanceof Error?e.message:String(e)})})}a?.()}function k(e){return e===`project`?`project`:e===`global`?`global`:null}function A(e,t){let n={project:`project`,global:`global`,plugin:`plugin`,inline:`inline`}[e]??e;return t?`${n} (${t})`:n}function j(e){let t=[];return e.providerApis>0&&t.push(`provider(${e.providerApis})`),e.tools>0&&t.push(`tools(${e.tools})`),e.renderers>0&&t.push(`renderers(${e.renderers})`),e.statusItems>0&&t.push(`statusItems(${e.statusItems})`),e.fileViewers&&e.fileViewers>0&&t.push(`fileViewers(${e.fileViewers})`),t.length?t.join(`, `):void 0}function M(e){switch(e){case`provider`:return w.t(`cmd.pluginCapabilityProvider`);case`tools`:return w.t(`cmd.pluginCapabilityTools`);case`tui.renderer`:return w.t(`cmd.pluginCapabilityTuiRenderer`);case`network`:return w.t(`cmd.pluginCapabilityNetwork`);case`wire-protocol`:return w.t(`cmd.pluginCapabilityWireProtocol`);case`a2ui.component`:return w.t(`cmd.pluginCapabilityA2uiComponent`);case`panel.backend`:return w.t(`cmd.pluginCapabilityPanelBackend`);case`input.resolver`:return w.t(`cmd.pluginCapabilityInputResolver`);case`a2ui.renderer`:return w.t(`cmd.pluginCapabilityA2uiRenderer`);default:return}}function N(e,t){let n=o(t),r=n.pendingCapabilities,i=()=>{v(t.dir),r.length>0&&c(t.dir,r),e.tui.info.showToast(w.t(`cmd.titlePlugin`),w.t(`cmd.pluginTrusting`,{id:t.id})),e.app.reloadCapabilities().then(n=>{e.tui.plugins.setPluginRegistrations(e.app.getPluginRegistrations()),e.tui.info.showToast(w.t(`cmd.titlePlugin`),w.t(`cmd.pluginTrusted`,{id:t.id,count:n.mcpTools}))},t=>e.tui.info.showToast(w.t(`cmd.titlePlugin`),w.t(`cmd.compatReloadFailed`,{err:t instanceof Error?t.message:String(t)})))},a=n.surface;if(!a.any&&r.length===0){i();return}let s=[w.t(`cmd.pluginTrustConfirmIntro`)];if(a.mcpJson&&s.push(`• ${w.t(`cmd.pluginSurfaceMcp`)}`),a.commands&&s.push(`• ${w.t(`cmd.pluginSurfaceCommands`)}`),a.scripts&&s.push(`• ${w.t(`cmd.pluginSurfaceScripts`)}`),a.panels&&s.push(`• ${w.t(`cmd.pluginSurfacePanels`)}`),a.skills&&s.push(`• ${w.t(`cmd.pluginSurfaceSkills`)}`),a.agents&&s.push(`• ${w.t(`cmd.pluginSurfaceAgents`)}`),r.length>0){s.push(``,w.t(`cmd.pluginCapabilityIntro`));for(let e of r){let t=M(e);t&&s.push(`• ${t}`)}}s.push(``,w.t(`cmd.pluginTrustConfirmHint`)),e.tui.modal.openModal({kind:`select`,title:w.t(`cmd.pluginTrustConfirmTitle`,{id:t.id}),subtitle:s.join(`
4
- `),layout:`buttons`,options:[{label:w.t(`cmd.pluginTrustConfirmAction`),value:`trust`,color:`accent`},{label:w.t(`cmd.commonCancel`),value:`cancel`,color:`gray`}],onSelect:e=>{e===`trust`&&i()},onCancel:()=>{}})}async function P(e,t,n){if(t.length===0)return!0;let r=[w.t(`cmd.pluginCapabilityIntro`)];for(let e of t){let t=M(e);t&&r.push(`• ${t}`)}return new Promise(t=>{n.tui.modal.openModal({kind:`select`,title:w.t(`cmd.pluginInstallConfirmTitle`,{name:e}),subtitle:r.join(`
5
- `),layout:`buttons`,options:[{label:w.t(`cmd.pluginInstallConfirmAction`),value:`install`,color:`accent`},{label:w.t(`cmd.commonCancel`),value:`cancel`,color:`gray`}],onSelect:e=>t(e===`install`),onCancel:()=>t(!1)})})}async function F(e,n,r){let i=r?.autoTrust??!1,a=r?.onProgress,o=r?.onPendingCapabilities;if(e.includes(`..`)||e.startsWith(`/`)||e.startsWith(`./`)||e.startsWith(`~/`))return i||n.tui.info.showToast(w.t(`cmd.titleInstall`),w.t(`cmd.pluginInstallInvalidName`,{name:e})),!1;let s=t[e]?.pkg??e,u=g(s);if(!u)return i||n.tui.info.showToast(w.t(`cmd.titleInstall`),w.t(`cmd.pluginInstallNotFound`,{pkg:s})),!1;let f=d(u);if(!i&&!await P(e,f,n))return n.tui.info.showToast(w.t(`cmd.titleInstall`),w.t(`cmd.pluginInstallCancelled`)),!1;let m;try{a?.(`Installing plugin`),m=await l({pkg:s})}catch(e){return i||n.tui.info.showToast(w.t(`cmd.titleInstall`),w.t(`cmd.pluginInstallFailed`,{err:e instanceof Error?e.message:String(e)})),!1}i||n.tui.info.showToast(w.t(`cmd.titleInstall`),w.t(`cmd.pluginInstalled`,{seg:m.created?`Installed ${m.id}`:`${m.id} already up to date`})),await p(n.app.settings,{id:m.id,source:`npm`,sourceSpec:s,version:m.version,scope:`global`,capabilities:f}),n.app.pluginModuleManager.emitMonitorEvent({type:`plugin.installed`,pluginId:m.id});for(let e of m.compileWarnings)i||n.tui.info.showToast(w.t(`cmd.titleInstall`),w.t(`cmd.pluginInstallFailed`,{err:e}));f.length>0&&(a?.(`Authorizing capabilities`),v(m.dir),c(m.dir,f));try{a?.(`Reloading capabilities`),(await n.app.reloadCapabilities()).pendingProviderCapabilities.includes(m.id)&&(i||n.tui.info.showToast(w.t(`cmd.titleInstall`),w.t(`cmd.pluginInstallPendingCaps`,{id:m.id})),o?.(m.id))}catch(e){i||n.tui.info.showToast(w.t(`cmd.titleInstall`),w.t(`cmd.compatReloadFailed`,{err:e instanceof Error?e.message:String(e)}))}if(await n.callbacks.refreshModelsNow().catch(()=>{}),e===`plugin-feishu-use`){try{let{launchFeishuSetup:e}=await import(`./feishu-setup-D6vnuNLw.js`);await e(n)}catch{}return!0}if(!i){await new Promise(e=>setImmediate(e));let{handleModel:e}=await import(`./model-auth-DUPFPOjY.js`);await e([],n)}return!0}async function I(e,t){t.tui.panes.togglePane(`backgroundTasks`)}async function L(e,t){let n=e.join(` `);if(!n){t.tui.info.showToast(w.t(`cmd.titleJob`),w.t(`cmd.jobUsage`));return}let r=/^revert\s+(\S+)$/.exec(n);if(r){let e=r[1],n=t.app.agentJobs.revert(e);if(n.applied)t.tui.info.showToast(w.t(`cmd.titleReverted`),w.t(`cmd.jobReverted`,{id:e,msg:n.message}));else{let r=n.conflictPaths.length?w.t(`cmd.jobConflictFiles`,{joinedPaths:n.conflictPaths.join(`
6
- `)}):``;t.tui.messages.pushMessage({role:`command_echo`,command:`/go revert ${e}`,status:`error`,summary:w.t(`cmd.jobRevertFailed`,{id:e,status:n.status,msg:n.message,conflicts:r})})}return}try{let e=t.app.agentJobs.start({title:n.slice(0,60),prompt:n,sessionId:t.state.session.id,origin:`user`});t.tui.info.showToast(w.t(`cmd.titleDelegated`),w.t(`cmd.delegated`,{title:e.title}))}catch(e){t.tui.messages.pushMessage({role:`command_echo`,command:`/go`,status:`error`,summary:w.t(`cmd.delegateFailed`,{err:e instanceof Error?e.message:String(e)})})}}async function R(t,s){let c=(e,t)=>s.tui.info.showToast(e,t),l=e=>s.tui.messages.pushMessage({role:`command_echo`,command:`/extension`,status:`error`,summary:e}),d=()=>{let e=s.app.extensions.list(),t=s.app.getGlobalMenuItems?.()??[],n=new Map(t.map(e=>[e.id,e.command]));s.tui.modal.openModal({kind:`extensionsList`,entries:e.map(e=>({category:e.category,id:e.id,name:e.name,enabled:e.enabled,description:e.description,applies:e.applies})),menuContributions:[{id:`__discover__`,title:`Discover plugins (search registries)…`},...t.map(e=>({id:e.id,title:e.title}))],onSelect:e=>{if(!e)return;if(e===`contrib:__discover__`){p();return}if(e.startsWith(`contrib:`)){let t=e.slice(8),r=n.get(t);if(!r)return;a(r,{open:e=>u(e),runCommand:async(e,t)=>{let{dispatchSlashCommand:n}=await import(`./command-dispatcher-Vb_AJSAR.js`).then(e=>e.a);await n(e,t?t.split(/\s+/).filter(Boolean):[],s)}}).catch(e=>c(w.t(`cmd.titlePlugin`),e instanceof Error?e.message:String(e)));return}let t=e.indexOf(`:`);v(e.slice(0,t),e.slice(t+1))},onCancel:()=>{}})},p=async()=>{let e=await s.tui.modal.openModal({kind:`input`,title:`Discover plugins`,description:[`搜索已注册 registry(/registry list 查看源;/registry add 注册第三方源)`],placeholder:`keyword… (e.g. weather / oauth / provider)`});if(!e?.trim()){d();return}let{searchRegistries:t}=await import(`./registry-B3nfN62q.js`),{hits:n,warnings:r}=await t(s,e.trim());if(n.length===0){c(w.t(`cmd.titlePlugin`),`No registry entries match '${e.trim()}'${r.length?` (${r.length} source warning)`:``}`),d();return}s.tui.modal.openModal({kind:`select`,title:`Discover: '${e.trim()}' (${n.length})`,subtitle:r.length>0?`⚠ ${r.join(` · `)}`:void 0,options:n.map((e,t)=>({label:`${e.entry.id} (${e.entry.type}) [${(e.entry.capabilities??[]).join(`, `)||`-`}] @${e.sourceName}${e.stale?` (stale)`:``}`,description:e.entry.description,value:String(t)})),onSelect:e=>{let t=n[Number(e)];if(!t)return;let r=[`--as`,t.entry.type,...t.entry.subPath?[`--sub-path`,t.entry.subPath]:[],t.entry.source];(async()=>{let{dispatchSlashCommand:e}=await import(`./command-dispatcher-Vb_AJSAR.js`).then(e=>e.a);await e(`install`,r,s)})().catch(e=>c(w.t(`cmd.titlePlugin`),e instanceof Error?e.message:String(e)))},onCancel:()=>d()})},m=e=>{let t=s.app.skillRegistry?.get(e);if(!t){d();return}let n=t.status!==`disabled`,r=t.definition.metadata,i=[{kind:`group`,title:w.t(`cmd.extSkillGroupOverview`)},{kind:`row`,label:w.t(`cmd.extSkillLabelDesc`),value:r.description||`—`},{kind:`row`,label:w.t(`cmd.extSkillLabelStatus`),value:t.status,tone:t.status===`error`||t.status===`disabled`?`error`:`success`},{kind:`row`,label:w.t(`cmd.extSkillLabelSource`),value:w.t(`cmd.extSkillSourceLabel`,{seg:A(t.source.type,t.source.id)}),tone:`muted`},{kind:`group`,title:w.t(`cmd.extSkillGroupTrigger`)},{kind:`row`,label:w.t(`cmd.extSkillLabelTriggerMode`),value:r.trigger===`manual`?w.t(`cmd.extSkillTriggerManual`):w.t(`cmd.extSkillTriggerAuto`)},...r.priority===void 0?[]:[{kind:`row`,label:w.t(`cmd.extSkillLabelPriority`),value:String(r.priority)}],{kind:`group`,title:w.t(`cmd.extSkillGroupDependencies`)},{kind:`row`,label:w.t(`cmd.extSkillLabelDependencies`),value:r.dependencies?.length?r.dependencies.join(`, `):w.t(`cmd.extSkillNoDependencies`),tone:r.dependencies?.length?void 0:`muted`},{kind:`group`,title:w.t(`cmd.extSkillGroupTags`)},{kind:`row`,label:w.t(`cmd.extSkillLabelTags`),value:r.tags?.length?r.tags.join(`, `):w.t(`cmd.extSkillNoTags`),tone:r.tags?.length?void 0:`muted`}];t.status===`error`&&t.error&&i.push({kind:`group`,title:w.t(`cmd.extSkillGroupError`)},{kind:`text`,value:t.error,tone:`error`}),s.tui.modal.openModal({kind:`extensionDetail`,title:w.t(`cmd.extSkillTitle`,{name:e,state:n?`enabled`:`disabled`}),meta:`skill · ${t.source.type}`,metaStatus:{label:n?w.t(`cmd.extEnableLabel`):w.t(`cmd.extDisableLabel`),enabled:n},actions:[{label:n?w.t(`cmd.extDisableLabel`):w.t(`cmd.extEnableLabel`),value:`toggle`,color:n?`amber`:`green`},{label:w.t(`cmd.extViewFullTextLabel`),value:`view-full-text`,color:`gray`},...k(t.source.type)?[{label:w.t(`cmd.uninstallActionLabel`),value:`uninstall`,color:`red`}]:[]],dataListEntries:i,fullText:{title:w.t(`cmd.extSkillFullTextTitle`,{name:e}),content:t.definition.body||w.t(`cmd.extSkillNoBody`)},onAction:async r=>{if(r===`toggle`){try{await s.app.extensions.setEnabled(`skill`,e,!n),c(w.t(`cmd.titleExtension`),w.t(`cmd.extToggled`,{name:e,verb:n?`Disabled`:`Enabled`,seg:` — applies next turn`}))}catch(e){l(w.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}m(e);return}if(r===`uninstall`){let n=k(t.source.type);if(!n)return;await D({name:e,type:`skill`,scope:n,ctx:s,onDone:()=>d()})}},onClose:()=>d()})},g=e=>{let t=s.app.declarativeAgentProfiles.find(t=>t.name===e);if(!t){d();return}let n=!new Set(s.app.settings.get(`disabled_agents`)??[]).has(e),r=[{kind:`group`,title:w.t(`cmd.extAgentGroupOverview`)},{kind:`row`,label:w.t(`cmd.extAgentLabelDesc`),value:t.description||`—`},{kind:`row`,label:w.t(`cmd.extAgentLabelStatus`),value:n?w.t(`cmd.extEnableLabel`):w.t(`cmd.extDisableLabel`),tone:n?`success`:`error`},{kind:`group`,title:w.t(`cmd.extAgentGroupCapabilities`)},{kind:`row`,label:w.t(`cmd.extAgentLabelTaskTypes`),value:t.taskTypes.length?t.taskTypes.join(`, `):w.t(`cmd.extAgentNoTaskTypes`),tone:t.taskTypes.length?void 0:`muted`},{kind:`row`,label:w.t(`cmd.extAgentLabelCapabilities`),value:t.capabilities.length?t.capabilities.join(`, `):w.t(`cmd.extAgentNoCapabilities`),tone:t.capabilities.length?void 0:`muted`},{kind:`group`,title:w.t(`cmd.extAgentGroupTools`)},{kind:`row`,label:w.t(`cmd.extAgentLabelDefaultTools`),value:t.defaultTools?.length?t.defaultTools.join(`, `):w.t(`cmd.extAgentAllTools`),tone:t.defaultTools?.length?void 0:`muted`},{kind:`row`,label:w.t(`cmd.extAgentLabelDisallowedTools`),value:t.disallowedTools?.length?t.disallowedTools.join(`, `):w.t(`cmd.extAgentNoDisallowedTools`),tone:t.disallowedTools?.length?void 0:`muted`},{kind:`group`,title:w.t(`cmd.extAgentGroupExecution`)},{kind:`row`,label:w.t(`cmd.extAgentLabelPromptMode`),value:t.promptMode??`append`,tone:`muted`},...t.preferredModelTier?[{kind:`row`,label:w.t(`cmd.extAgentLabelModelTier`),value:t.preferredModelTier}]:[],{kind:`row`,label:w.t(`cmd.extAgentLabelAllowSubagents`),value:t.allowSubagents?w.t(`cmd.extEnableLabel`):w.t(`cmd.extDisableLabel`),tone:t.allowSubagents?void 0:`muted`}];s.tui.modal.openModal({kind:`extensionDetail`,title:w.t(`cmd.extAgentTitle`,{name:e,state:n?`enabled`:`disabled`}),meta:`agent`,metaStatus:{label:n?w.t(`cmd.extEnableLabel`):w.t(`cmd.extDisableLabel`),enabled:n},actions:[{label:n?w.t(`cmd.extDisableLabel`):w.t(`cmd.extEnableLabel`),value:`toggle`,color:n?`amber`:`green`},{label:w.t(`cmd.extViewFullTextLabel`),value:`view-full-text`,color:`gray`}],dataListEntries:r,fullText:{title:w.t(`cmd.extAgentFullTextTitle`,{name:e}),content:t.systemPromptTemplate||w.t(`cmd.extSkillNoBody`)},onAction:async t=>{if(t===`toggle`){try{await s.app.extensions.setEnabled(`agent`,e,!n),c(w.t(`cmd.titleExtension`),w.t(`cmd.extToggled`,{name:e,verb:n?`Disabled`:`Enabled`,seg:` — applies next turn`}))}catch(e){l(w.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}g(e)}},onClose:()=>d()})},v=(e,t)=>{let n=s.app.extensions.list().find(n=>n.category===e&&n.id===t);if(!n){d();return}let r={label:n.enabled?w.t(`cmd.extDisableLabel`):w.t(`cmd.extEnableLabel`),value:`toggle`};if(e===`mcp`){let i=s.app.mcpManager?.getServerInfos().find(e=>e.name===t),a=n.enabled?i?String(i.status):`disconnected`:`disabled`;s.tui.modal.openModal({kind:`select`,title:w.t(`cmd.extMcpTitle`,{name:n.name,status:a}),options:[{label:w.t(`cmd.extReconnectLabel`),value:`reconnect`},r,...n.readonly?[]:[{label:w.t(`cmd.uninstallActionLabel`),value:`uninstall`}]],onSelect:async r=>{if(r===`toggle`){try{await s.app.extensions.setEnabled(n.category,t,!n.enabled),c(w.t(`cmd.titleExtension`),w.t(`cmd.extToggled`,{name:n.name,verb:n.enabled?`Disabled`:`Enabled`,seg:``}))}catch(e){l(w.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}v(e,t);return}if(r===`reconnect`){let n=s.app.mcpManager,r=(s.app.settings.get(`mcp_servers`)??{})[t];if(!n||!r){c(w.t(`cmd.titleExtension`),w.t(`cmd.extReconnectUnavailable`,{id:t})),v(e,t);return}try{await n.disconnect(t).catch(()=>{}),await n.connect(t,r),c(w.t(`cmd.titleExtension`),w.t(`cmd.extReconnected`,{id:t}))}catch(e){l(w.t(`cmd.extReconnectFailed`,{id:t,err:e instanceof Error?e.message:String(e)}))}v(e,t);return}if(r===`uninstall`){await D({name:t,type:`mcp`,scope:`project`,ctx:s,onDone:()=>d()});return}},onCancel:()=>d()})}else e===`plugin`?y(t,n.enabled,n.name,n.description):e===`skill`?m(t):e===`agent`?g(t):s.tui.modal.openModal({kind:`select`,title:w.t(`cmd.extItemTitle`,{name:n.name}),options:[r],onSelect:async r=>{if(r===`toggle`){try{await s.app.extensions.setEnabled(n.category,t,!n.enabled),c(w.t(`cmd.titleExtension`),w.t(`cmd.extToggled`,{name:n.name,verb:n.enabled?`Disabled`:`Enabled`,seg:n.applies===`live`?``:` — applies next turn`}))}catch(e){l(w.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}v(e,t);return}},onCancel:()=>d()})},y=(t,p,m,g,v)=>{let T=s.app.workspaceDir??process.cwd(),E=e({cwd:T,homedir:n()}).find(e=>e.id===t);if(!E){d();return}let O=o(E),k=O.surface.any?O.gated?w.t(`cmd.extPluginUntrusted`):O.pendingCapabilities.length>0?w.t(`cmd.extPluginCapabilityPending`,{caps:O.pendingCapabilities.join(`, `)}):w.t(`cmd.extPluginTrusted`):w.t(`cmd.extPluginNoSurface`),A=O.surface,M=[[A.mcpJson,`mcp`],[A.commands,`commands`],[A.scripts,`scripts`],[A.panels,`panels`],[A.skills,`skills`],[A.agents,`agents`]].filter(([e])=>e).map(([,e])=>e),P=M.length?` ${M.join(`, `)}`:` —`,F=(s.app.getPluginPanelRegistrations?.()??[]).filter(e=>e.pluginId===t),I=j({providerApis:s.app.providerRegistry.getApisBySource(t).length,tools:s.app.toolRegistry.getAll().filter(e=>e.metadata.source===t).length,renderers:s.app.getPluginRenderers().filter(e=>e.pluginId===t).length,statusItems:s.app.getPluginStatusItems().filter(e=>e.id.startsWith(`${t}:`)).length,fileViewers:(s.app.getPluginFileViewers?.()??[]).filter(e=>e.pluginId===t).length}),L=E.scope===`builtin`?[]:[{label:p?w.t(`cmd.extDisableLabel`):w.t(`cmd.extEnableLabel`),value:`toggle`,color:p?`amber`:`green`}];if(O.surface.any&&(E.scope===`project`||E.scope===`repository`||O.pendingCapabilities.length>0)){let e=E.scope===`project`||E.scope===`repository`?!O.trusted:O.pendingCapabilities.length>0;L.push({label:e?w.t(`cmd.extPluginTrustLabel`):w.t(`cmd.extPluginUntrustLabel`),value:e?`trust`:`untrust`,color:e?`accent`:`gray`})}L.push({label:w.t(`cmd.extReloadLabel`),value:`reload`}),E.scope!==`builtin`&&L.push({label:w.t(`cmd.uninstallActionLabel`),value:`uninstall`,color:`red`});let R=i(E,{"extension.id":t,"extension.enabled":p,"extension.trusted":O.trusted,"extension.scope":E.scope,"extension.category":`plugin`}),z=E.manifest.contributes,B=h(r.extensionDetailActions,z?.menus,z?.actions,R),V=new Map(B.map(e=>[e.id,e.command]));for(let e of B){let t=`${E.id}:action.${e.id}.title`,n=s.app.i18nRegistry.t(t),r=n===t?e.title:n;L.push({label:r,value:`contrib:${e.id}`,color:e.color})}let H={open:e=>u(e),runCommand:async(e,t)=>{let{dispatchSlashCommand:n}=await import(`./command-dispatcher-Vb_AJSAR.js`).then(e=>e.a);await n(e,t?t.split(/\s+/).filter(Boolean):[],s)},runScript:async e=>{if(o(E).gated){c(w.t(`cmd.titlePlugin`),w.t(`cmd.extPluginUntrusted`));return}await _(E,e)}},U=F[0];U&&s.app.firePluginActivationEvent?.(`onView:${U.id}`);let W;if(t===`plugin-feishu-use`){let e=x(T,`.otto`,`feishu-use.json`),t=!1;try{if(S(e)){let n=JSON.parse(C(e,`utf8`));if(n.app_id){t=!0;let r=typeof n.app_id==`string`&&n.app_id.length>12?n.app_id.slice(0,8)+`***`+n.app_id.slice(-4):String(n.app_id),i=Array.isArray(n.allowed_chats)?n.allowed_chats.length:0,a=Array.isArray(n.trusted_chats)?n.trusted_chats.length:0,o=E.scope===`user`?`全局`:E.scope===`repository`?`仓库`:`项目`;W=[{kind:`row`,label:`App ID`,value:r},{kind:`row`,label:`消息白名单`,value:i>0?`${i} 个`:`空 — 拒绝所有消息`,tone:i>0?void 0:`warning`},...a>0?[{kind:`row`,label:`可信任 chat`,value:`${a} 个`}]:[],{kind:`row`,label:`配置`,value:`${o}级 · ${e}`,tone:`muted`}]}}}catch{}t||(W=[{kind:`text`,value:`未配置 — 点击 [重新配置] 引导设置`,tone:`warning`},{kind:`row`,label:`预期配置路径`,value:e,tone:`muted`}]),L.push({label:`重新配置`,value:`feishu-reconfigure`,color:`amber`})}let G=()=>{let e=s.app.pluginHealthTracker?.query(t)??{hookTimeouts:0,toolTimeouts:0},n=f(t);return{healthStats:{hookTimeouts:e.hookTimeouts,toolTimeouts:e.toolTimeouts,lastError:e.lastError},logEntries:n.map(e=>({ts:e.ts,level:e.level,msg:e.msg}))}},K=[{kind:`group`,title:w.t(`cmd.extPluginGroupTitle`)},{kind:`row`,label:w.t(`cmd.extPluginLabelDesc`),value:g},{kind:`row`,label:w.t(`cmd.extPluginLabelScope`),value:E.scope},{kind:`row`,label:w.t(`cmd.extPluginLabelTrust`),value:k},{kind:`row`,label:w.t(`cmd.extPluginLabelSurfaces`),value:P.trim()||`—`},...I?[{kind:`row`,label:w.t(`cmd.extPluginLabelContributions`),value:I}]:[],{kind:`row`,label:w.t(`cmd.extPluginLabelStatus`),value:p?w.t(`cmd.extEnableLabel`):w.t(`cmd.extDisableLabel`),tone:p?`success`:`error`}],q=v?[{kind:`text`,value:v.message,tone:v.kind===`success`?`success`:`error`}]:[];s.tui.modal.openModal({kind:`extensionDetail`,title:w.t(`cmd.extPluginTitle`,{name:m,state:p?`enabled`:`disabled`}),meta:`plugin · ${E.scope}`,metaStatus:{label:p?w.t(`cmd.extEnableLabel`):w.t(`cmd.extDisableLabel`),enabled:p},metaSuffix:k,actions:L,panelInfo:U?{title:U.title,modulePath:U.modulePath,exportName:U.exportName,pluginId:t}:void 0,dataListEntries:[...q,...W??K],...G(),onRefresh:()=>y(t,p,m,g),onAction:async e=>{if(e.startsWith(`contrib:`)){let n=V.get(e.slice(8));if(n)try{await a(n,H)}catch(e){l(w.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}y(t,p,m,g)}else if(e===`trust`)N(s,E);else if(e===`untrust`)b(E.dir),s.app.reloadCapabilities?.().then(()=>{s.tui.plugins.setPluginRegistrations(s.app.getPluginRegistrations())}).finally(()=>{y(t,p,m,g,{kind:`success`,message:w.t(`cmd.pluginUntrusted`,{id:t})})});else if(e===`toggle`)try{await s.app.extensions.setEnabled(`plugin`,t,!p);try{await s.app.reloadCapabilities(),y(t,!p,m,g,{kind:`success`,message:w.t(`cmd.extToggled`,{name:m,verb:p?`Disabled`:`Enabled`,seg:``})})}catch(e){y(t,!p,m,g,{kind:`error`,message:w.t(`cmd.extToggleReloadFailed`,{verb:p?`Disabled`:`Enabled`,name:m,err:e instanceof Error?e.message:String(e)})})}}catch(e){y(t,p,m,g,{kind:`error`,message:w.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)})})}else if(e===`feishu-reconfigure`)try{let{launchFeishuSetup:e}=await import(`./feishu-setup-D6vnuNLw.js`);await e(s),c(`plugin-feishu-use`,`配置完成。`)}catch(e){l(`feishu-use 配置向导失败:${e instanceof Error?e.message:String(e)}`)}else e===`reload`?s.app.reloadCapabilities?.().then(e=>{y(t,p,m,g,{kind:`success`,message:w.t(`cmd.pluginTrusted`,{id:t,count:e.mcpTools})})}).catch(e=>{y(t,p,m,g,{kind:`error`,message:w.t(`cmd.compatReloadFailed`,{err:e instanceof Error?e.message:String(e)})})}):e===`uninstall`&&await D({name:t,type:`plugin`,scope:E.scope===`project`||E.scope===`repository`?E.scope:`global`,ctx:s,onDone:()=>d()})},onClose:()=>d()})};d()}export{L as a,F as c,R as i,j as n,M as o,I as r,N as s,P as t};
7
- //# sourceMappingURL=extension-plugin-C2Yhv77x.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension-plugin-C2Yhv77x.js","names":[],"sources":["../src/commands/handlers/uninstall-action.ts","../src/commands/handlers/extension-plugin.ts"],"sourcesContent":["/**\n * uninstall-action.ts — 卸载动作的共享实现(`/extension` 面板唯一入口)。\n *\n * ## 由来\n * 卸载原本是独立的 `/uninstall` 命令(含 `--global/--project/--repo/--dry-run` 标志)。\n * 用户定稿:**入口收口到 `/extension` 面板**——用户在面板里看着某个扩展的详情,\n * 直接对\"眼前这一个\"执行卸载,比先记住名字再敲命令、还要自己挑 scope 更自然。\n *\n * 收口后的语义变化(有意):\n * - **scope 不再由用户指定**:面板里每个条目的 scope 是已知事实(plugin 有 `scope` 字段,\n * skill/mcp 由 `detectUninstallTarget` 在其所在层解析),故卸载的永远是\"你正在看的这一个\"。\n * 代价:跨 scope 同名条目只能逐个卸——但那本就是两个不同的东西,逐个卸更不易误删。\n * - **`--dry-run` 升级成默认的确认步**:`confirmAndUninstall` 先跑一次 dryRun 拿到删除计划,\n * 把计划原文摆进确认框再执行。命令行时代 dry-run 是可选标志(默认直接删),现在变成\n * 不可跳过的一步——删文件是不可逆操作,多一次确认换不回来的东西值得。\n *\n * ## 覆盖范围\n * `UninstallTarget = 'skill' | 'plugin' | 'mcp'` 三类,与 `packages/install` 引擎能力一致。\n * **agent 不支持卸载**——引擎侧就没有该分支(`/uninstall` 时代同样卸不掉),属既有缺口,\n * 不是入口搬家造成的。若将来补 agent,需先想清楚\"删定义文件\"对内置/插件提供的 agent\n * 是否是正确语义。\n */\nimport { i18next } from '@x-otto/tui'\nimport { uninstall, findWorkspaceRoot, recordPluginUninstalled } from '@x-otto/coding'\n\nimport type { UninstallTarget, InstallScope } from '@x-otto/coding'\nimport type { McpServersConfig } from '@x-otto/setting'\nimport type { CliContext } from '../../command-dispatcher'\n\n/** scope → i18n 标签(RFC-130:'repository' 分支不可省,否则静默回退成 'project' 标签)。 */\nexport function scopeToLabel(scope: InstallScope): string {\n switch (scope) {\n case 'global':\n return i18next.t('cmd.scopeGlobal')\n case 'repository':\n return i18next.t('cmd.scopeRepository')\n case 'project':\n default:\n return i18next.t('cmd.scopeProject')\n }\n}\n\nexport interface UninstallRequest {\n /** 条目名(skill 名 / plugin id / MCP server 名)。 */\n name: string\n type: UninstallTarget\n scope: InstallScope\n ctx: CliContext\n /** 卸载流程结束后回到的界面(面板详情页/列表页),失败与取消同样回调。 */\n onDone?: () => void\n}\n\nfunction uninstallOptions(\n ctx: CliContext,\n scope: InstallScope,\n dryRun: boolean,\n): { workspaceDir: string; workspaceRoot: string | undefined; global: boolean; dryRun: boolean } {\n return {\n workspaceDir: ctx.app.workspaceDir,\n workspaceRoot: scope === 'repository' ? findWorkspaceRoot(ctx.app.workspaceDir) : undefined,\n global: scope === 'global',\n dryRun,\n }\n}\n\n/**\n * 卸载前置确认——先取删除计划,再让用户拍板。\n *\n * 先 dryRun 的意义:`uninstall()` 的 plan 列出**具体会删哪些路径**(目录/配置项),\n * 这是用户判断\"是不是我要删的那个\"的唯一依据。直接弹一句\"确定卸载吗\"没有信息量。\n * dryRun 失败(如目标已不存在)则不进确认框,直接报错返回。\n */\nexport async function confirmAndUninstall(req: UninstallRequest): Promise<void> {\n const { name, type, scope, ctx, onDone } = req\n const mcpServers = ctx.app.settings.get('mcp_servers') as Record<string, unknown> | undefined\n\n const preview = await uninstall(\n name,\n type,\n uninstallOptions(ctx, scope, true),\n () => mcpServers,\n async () => {}, // dryRun 不落盘\n )\n\n if (!preview.success) {\n ctx.tui.info.showToast(\n i18next.t('cmd.titleUninstall'),\n i18next.t('cmd.uninstallFailed', { err: preview.error ?? i18next.t('cmd.unknownError') }),\n )\n onDone?.()\n return\n }\n\n const plan = preview.plan ?? []\n ctx.tui.modal.openModal({\n kind: 'confirm',\n title: i18next.t('cmd.uninstallConfirmTitle', { type, name }),\n message: [\n i18next.t('cmd.uninstallConfirmScope', { scopeLabel: scopeToLabel(scope) }),\n ...plan,\n ].join('\\n'),\n confirmLabel: i18next.t('cmd.uninstallConfirmYes'),\n // 删文件不可逆——走 dangerous 形态(默认焦点落 cancel,避免连按 Enter 误删)。\n dangerous: true,\n onConfirm: () => {\n void executeUninstall(req)\n },\n onCancel: () => onDone?.(),\n })\n}\n\n/**\n * 真正执行卸载 + 卸载后编排。\n *\n * 导出供测试直接驱动**执行段**(RFC-219 清单联动 / RFC-327 事件终态序)——生产路径只经\n * `confirmAndUninstall` 的确认框进入,那里是 modal 的同步回调,只能 `void` 起飞,\n * 测试无法 await 到它完成。\n *\n * ⚠️ 下面这段顺序是既有契约,从 `/uninstall` 原样搬来,**不可重排**:\n * 磁盘删除 → 清单清理(recordPluginUninstalled)→ 贡献撤销/服务停止(reloadCapabilities)\n * → 最后才发 `plugin.uninstalled`。该事件语义是\"卸载已终态\",提前发会让 observer 看到\n * 一个还没删干净的中间态(RFC-327 D1)。\n */\nexport async function executeUninstall(req: UninstallRequest): Promise<void> {\n const { name, type, scope, ctx, onDone } = req\n const mcpServers = ctx.app.settings.get('mcp_servers') as Record<string, unknown> | undefined\n\n const result = await uninstall(\n name,\n type,\n uninstallOptions(ctx, scope, false),\n () => mcpServers,\n async (servers) => {\n await ctx.app.settings.persist({ mcp_servers: servers as McpServersConfig })\n },\n )\n\n if (!result.success) {\n ctx.tui.info.showToast(\n i18next.t('cmd.titleUninstall'),\n i18next.t('cmd.uninstallFailed', { err: result.error ?? i18next.t('cmd.unknownError') }),\n )\n onDone?.()\n return\n }\n\n ctx.tui.info.showToast(\n i18next.t('cmd.titleUninstall'),\n i18next.t('cmd.uninstalled', { type, name, scope: scopeToLabel(scope) }),\n )\n\n // RFC-219 M1:plugin 卸载后清理 installed_plugins 清单 + disabled_plugins /\n // plugin_lifecycle.setupDone 残留。\n if (type === 'plugin') {\n await recordPluginUninstalled(ctx.app.settings, name)\n }\n\n try {\n await ctx.app.reloadCapabilities()\n ctx.tui.plugins.setPluginRegistrations(ctx.app.getPluginRegistrations())\n\n // RFC-327 D1:卸载终态事件(observer-only)。purge 不是卸载的替身,不得复用此事件。\n if (type === 'plugin') {\n void ctx.app.pluginModuleManager.emitMonitorEvent({\n type: 'plugin.uninstalled',\n pluginId: name,\n })\n }\n\n ctx.tui.info.showToast(i18next.t('cmd.titleUninstall'), i18next.t('cmd.uninstallRefreshed'))\n } catch (err) {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/extension',\n status: 'error',\n summary: i18next.t('cmd.uninstalledRefreshErr', {\n err: err instanceof Error ? err.message : String(err),\n }),\n })\n }\n\n onDone?.()\n}\n","/**\n * handlers/extension-plugin.ts — 扩展/代理委派/插件指令处理\n *\n * /extension — 管理可插拔扩展(MCP / skills / agents / plugins / panels)\n * /delegates — 切换后台任务面板\n * /delegate — 委派后台 agent 作业\n */\nimport { i18next } from '@x-otto/tui'\nimport { homedir } from 'node:os'\nimport { readFileSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport {\n trustPlugin,\n untrustPlugin,\n evaluatePluginTrust,\n grantPluginCapabilities,\n buildExtensionContext,\n openTarget,\n runPluginScript,\n POINT,\n resolveActions,\n dispatch,\n installPluginFromPackage,\n resolvePackageDir,\n readHighRiskCapabilities,\n readPluginLogTail,\n recordPluginInstalled,\n} from '@x-otto/coding'\nimport { discoverPluginsWithBundled } from '../../discover-with-bundled'\nimport type { DiscoveredPlugin, DispatchCaps } from '@x-otto/coding'\nimport type { ExtensionDetailAction, DataListEntry } from '@x-otto/tui'\nimport type { CliContext } from '../../command-dispatcher'\nimport { EXTENSION_ALIASES } from '../extension'\nimport { confirmAndUninstall } from './uninstall-action'\n\nimport type { InstallScope } from '@x-otto/coding'\n\n/**\n * skill 来源 → 可卸载 scope。返回 null = 该 skill 不可独立卸载。\n *\n * `plugin` 来源的 skill 由宿主插件带入,卸它应该卸插件(删 skill 文件会在插件下次加载时\n * 重新出现,是假卸载);`inline` 是代码内建,没有文件可删。\n */\nfunction skillUninstallScope(sourceType: string): InstallScope | null {\n if (sourceType === 'project') return 'project'\n if (sourceType === 'global') return 'global'\n return null\n}\n\n/** ext skill 来源标签段(原 cmd.extSkillSourceLabel callSiteFmt 内联逻辑)。 */\nfunction extSkillSourceSeg(type: string, pluginId?: string): string {\n const labels: Record<string, string> = { project: 'project', global: 'global', plugin: 'plugin', inline: 'inline' }\n const base = labels[type] ?? type\n return pluginId ? `${base} (${pluginId})` : base\n}\n\n/**\n * 汇总某插件在 provider/tools/renderers/statusItems 四个新轴的贡献计数(RFC-124 P0-B,\n * R11 权限审计基线)——纯函数,输入为各轴按 source/pluginId 过滤后的原始计数,输出为一行\n * 摘要文本;全部计数为 0 时返回 `undefined`(不为无贡献插件的详情页添加噪音行)。\n */\nexport function formatPluginContributionsSummary(counts: {\n providerApis: number\n tools: number\n renderers: number\n statusItems: number\n fileViewers?: number\n}): string | undefined {\n const parts: string[] = []\n if (counts.providerApis > 0) parts.push(`provider(${counts.providerApis})`)\n if (counts.tools > 0) parts.push(`tools(${counts.tools})`)\n if (counts.renderers > 0) parts.push(`renderers(${counts.renderers})`)\n if (counts.statusItems > 0) parts.push(`statusItems(${counts.statusItems})`)\n if (counts.fileViewers && counts.fileViewers > 0) parts.push(`fileViewers(${counts.fileViewers})`)\n return parts.length ? parts.join(', ') : undefined\n}\n\n/**\n * 高危能力 id → 文案 key 的映射(RFC-125 D1)。`pendingCapabilities` 的元素是\n * `HIGH_RISK_CAPABILITIES` 的子集,未知值(理论不可达,防御式兜底)静默丢弃而非抛错——\n * 文案缺失不应阻塞信任流程。\n *\n * RFC-126 M2:改为 export,供 `confirmHighRiskCapabilities` 与 `runPluginInstall` 共用。\n * RFC-210 D1:capability 改名 `ui.renderer`→`tui.renderer`(旧名经 manifest transform 映射,\n * 此处只会看到新名)。\n *\n * 覆盖率修复(终局巡检发现):此前本函数只覆盖 `HIGH_RISK_CAPABILITIES`(当前 9 项)中的\n * 4 项(provider/tools/tui.renderer/network),其余 5 项(wire-protocol/a2ui.component/\n * panel.backend/input.resolver/a2ui.renderer)声明时命中 `default: undefined` 被\n * `if (label) body.push(...)` 静默省略——用户在信任确认弹窗里看不到这些高危能力的说明就\n * 点了确认,是信任流程的信息缺失(非崩溃/阻断类 bug,但削弱了 RFC-125 D1\"用户知情同意\"\n * 的设计意图)。补齐全部 9 项,与 `HIGH_RISK_CAPABILITIES` 数组一一对应。\n */\nexport function pluginCapabilityLabel(cap: string): string | undefined {\n switch (cap) {\n case 'provider':\n return i18next.t('cmd.pluginCapabilityProvider')\n case 'tools':\n return i18next.t('cmd.pluginCapabilityTools')\n case 'tui.renderer':\n return i18next.t('cmd.pluginCapabilityTuiRenderer')\n case 'network':\n return i18next.t('cmd.pluginCapabilityNetwork')\n case 'wire-protocol':\n return i18next.t('cmd.pluginCapabilityWireProtocol')\n case 'a2ui.component':\n return i18next.t('cmd.pluginCapabilityA2uiComponent')\n case 'panel.backend':\n return i18next.t('cmd.pluginCapabilityPanelBackend')\n case 'input.resolver':\n return i18next.t('cmd.pluginCapabilityInputResolver')\n case 'a2ui.renderer':\n return i18next.t('cmd.pluginCapabilityA2uiRenderer')\n default:\n return undefined\n }\n}\n\n/**\n * 插件信任交互动作(确认 → trustPlugin(+grantPluginCapabilities) → 热重载 → 同步面板)。\n * 由 `/extension` 与 `/trust` 两处共用,避免重复。\n *\n * 无可执行面且无待授权高危能力 → 直接信任(无风险);否则弹确认——列出即将放行的受信任门面\n * (P3 安全边界)与即将授予的高危能力(RFC-125 D1:`surface.any` 已含 `highRiskCapabilities`\n * 布尔位(RFC-124 C1),此前弹窗虽会命中但正文从未渲染 `pendingCapabilities` 列表、且确认后\n * 从未调用 `grantPluginCapabilities`——用户点了\"信任\"能力仍卡在 pending,装载路径仍会跳过,\n * 见 RFC-125 §0.2。本次修复:body 追加能力段 + applyTrust 追加真实授权调用)。\n */\nexport function promptPluginTrust(ctx: CliContext, p: DiscoveredPlugin): void {\n const verdict = evaluatePluginTrust(p)\n const pendingCapabilities = verdict.pendingCapabilities\n const applyTrust = (): void => {\n trustPlugin(p.dir)\n if (pendingCapabilities.length > 0) {\n grantPluginCapabilities(p.dir, pendingCapabilities)\n }\n ctx.tui.info.showToast(i18next.t('cmd.titlePlugin'), i18next.t('cmd.pluginTrusting', { id: p.id }))\n void ctx.app.reloadCapabilities().then(\n (r) => {\n // RFC-131 T8:统一入口同步全部 TUI 侧贡献(状态栏/渲染器/面板)。\n // 面板另经 /extension 详情 dialog 即时呈现(getPluginPanelRegistrations 现算),\n // 本调用是常驻状态栏/消息渲染器路径所需(RFC-105 D5/M105-4、D6/M105-5a)。\n ctx.tui.plugins.setPluginRegistrations(ctx.app.getPluginRegistrations())\n ctx.tui.info.showToast(i18next.t('cmd.titlePlugin'), i18next.t('cmd.pluginTrusted', { id: p.id, count: r.mcpTools }))\n },\n (e: unknown) =>\n ctx.tui.info.showToast(i18next.t('cmd.titlePlugin'), i18next.t('cmd.compatReloadFailed', { err: e instanceof Error ? e.message : String(e) })),\n )\n }\n\n const surface = verdict.surface\n if (!surface.any && pendingCapabilities.length === 0) {\n applyTrust()\n return\n }\n const body = [i18next.t('cmd.pluginTrustConfirmIntro')]\n if (surface.mcpJson) body.push(`• ${i18next.t('cmd.pluginSurfaceMcp')}`)\n if (surface.commands) body.push(`• ${i18next.t('cmd.pluginSurfaceCommands')}`)\n if (surface.scripts) body.push(`• ${i18next.t('cmd.pluginSurfaceScripts')}`)\n if (surface.panels) body.push(`• ${i18next.t('cmd.pluginSurfacePanels')}`)\n if (surface.skills) body.push(`• ${i18next.t('cmd.pluginSurfaceSkills')}`)\n if (surface.agents) body.push(`• ${i18next.t('cmd.pluginSurfaceAgents')}`)\n if (pendingCapabilities.length > 0) {\n body.push('', i18next.t('cmd.pluginCapabilityIntro'))\n for (const cap of pendingCapabilities) {\n const label = pluginCapabilityLabel(cap)\n if (label) body.push(`• ${label}`)\n }\n }\n body.push('', i18next.t('cmd.pluginTrustConfirmHint'))\n ctx.tui.modal.openModal({\n kind: 'select',\n title: i18next.t('cmd.pluginTrustConfirmTitle', { id: p.id }),\n subtitle: body.join('\\n'),\n layout: 'buttons',\n options: [\n { label: i18next.t('cmd.pluginTrustConfirmAction'), value: 'trust', color: 'accent' },\n { label: i18next.t('cmd.commonCancel'), value: 'cancel', color: 'gray' },\n ],\n onSelect: (v: string) => {\n if (v === 'trust') applyTrust()\n },\n onCancel: () => {},\n })\n}\n\n// ── RFC-126 M2:TUI 内插件安装 ──\n\n/**\n * 高危能力确认弹窗(RFC-126 D2)。\n *\n * 复用 `pluginCapabilityLabel` 映射(与 `promptPluginTrust` 共用同一渲染逻辑),\n * 但只做能力列表展示 + [安装并授权] / [取消],不涉及 trust/surface 检查。\n * 返回 true = 用户确认安装,false = 取消。\n */\nexport async function confirmHighRiskCapabilities(\n pluginId: string,\n caps: string[],\n ctx: CliContext,\n): Promise<boolean> {\n if (caps.length === 0) return true\n const body = [i18next.t('cmd.pluginCapabilityIntro')]\n for (const cap of caps) {\n const label = pluginCapabilityLabel(cap)\n if (label) body.push(`• ${label}`)\n }\n return new Promise<boolean>((resolve) => {\n ctx.tui.modal.openModal({\n kind: 'select',\n title: i18next.t('cmd.pluginInstallConfirmTitle', { name: pluginId }),\n subtitle: body.join('\\n'),\n layout: 'buttons',\n options: [\n { label: i18next.t('cmd.pluginInstallConfirmAction'), value: 'install', color: 'accent' },\n { label: i18next.t('cmd.commonCancel'), value: 'cancel', color: 'gray' },\n ],\n onSelect: (v: string) => resolve(v === 'install'),\n onCancel: () => resolve(false),\n })\n })\n}\n\n/**\n * 共用安装核心流程(RFC-126 D2)。供 `/model` 安装按钮(M2-04)、M3 welcome dialog 两处复用。\n *\n * 流程:resolve → read manifest.capabilities → confirm(如需)→ install →\n * applyHighRiskGrant → reloadCapabilities → setNeedsSetup(false) → open /model\n *\n * @returns 是否真正安装成功(round 7 UX 修复)——此前恒 `Promise<void>`,四个失败分支\n * (非法名/包未找到/高危能力被拒/安装抛异常)与成功路径对调用方不可区分。welcome dialog\n * 据此无条件展示\"下一步:/auth\",安装失败时会给用户一个误导性的\"已装好,去授权\"提示。\n * `false` 覆盖:输入校验失败、包未解析到、用户拒绝高危能力确认、`installPluginFromPackage`\n * 抛异常。`reloadCapabilities` 失败不计入 false——插件文件已落地,仅能力刷新失败,\n * 现有逻辑本就继续放行(用户可 /model refresh 手动重试),语义不变。\n */\nexport async function runPluginInstall(\n pluginName: string,\n ctx: CliContext,\n /**\n * autoTrust:向导模式——跳过信任弹窗(官方 provider 扩展默认信任),\n * 不自动打开 /model(向导有自己的步骤③选择模型的专属面板)。\n * 同时压制 toast:安装成功/失败由调用方自行处理。\n */\n opts?: {\n autoTrust?: boolean\n onProgress?: (phase: string) => void\n /**\n * RFC-326 M2-05:reload 后该插件仍有**未授予**的高危能力时回调。\n *\n * 为何需要:声明了 `provider` 能力但未获授权时,插件的 providers 会被\n * fail-closed **整体跳过**(plugin-provider-manager.ts:93)——安装\"成功\"却\n * 贡献零个模型。非向导路径靠 toast 告知,但向导模式压制了全部 toast\n * (见 autoTrust 注释),用户只看到 \"✓ Installed\" 却不知道还缺一次授权。\n *\n * 用回调而非改返回类型:`runPluginInstall` 另有两个调用方忽略返回值\n * (model-auth.ts:324/335),拓宽 `Promise<boolean>` 会波及它们且无收益。\n */\n onPendingCapabilities?: (pluginId: string) => void\n },\n): Promise<boolean> {\n const autoTrust = opts?.autoTrust ?? false\n const onProgress = opts?.onProgress\n const onPendingCapabilities = opts?.onPendingCapabilities\n\n // 输入校验:拒绝路径穿越、相对路径前缀、家目录前缀\n if (pluginName.includes('..') || pluginName.startsWith('/') || pluginName.startsWith('./') || pluginName.startsWith('~/')) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallInvalidName', { name: pluginName }))\n return false\n }\n\n const pkgName = EXTENSION_ALIASES[pluginName]?.pkg ?? pluginName\n\n // 解析包目录\n const pkgDir = resolvePackageDir(pkgName)\n if (!pkgDir) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallNotFound', { pkg: pkgName }))\n return false\n }\n\n // 读取 manifest 获取声明的高危能力(单一真相源,@x-otto/plugin readHighRiskCapabilities——\n // 终局架构 review 2026-07-20:此前与 plugin.ts 的 CLI 安装路径各自内联实现同一段逻辑)。\n const caps = readHighRiskCapabilities(pkgDir)\n\n // 高危能力确认——向导模式下跳过(官方 provider 扩展默认信任)\n if (!autoTrust) {\n const confirmed = await confirmHighRiskCapabilities(pluginName, caps, ctx)\n if (!confirmed) {\n ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallCancelled'))\n return false\n }\n }\n\n // 安装\n let result: { id: string; dir: string; created: boolean; compileWarnings: string[]; version?: string }\n try {\n onProgress?.('Installing plugin')\n result = await installPluginFromPackage({ pkg: pkgName })\n } catch (e) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallFailed', { err: e instanceof Error ? e.message : String(e) }))\n return false\n }\n\n // 向导模式静默安装,不弹 toast\n if (!autoTrust) {\n ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstalled', { seg: result.created ? `Installed ${result.id}` : `${result.id} already up to date` }))\n }\n\n // RFC-219 M1/D1c:记录到 installed_plugins 清单(本路径恒为用户级/'global' 安装,\n // installPluginFromPackage 未传 ottoDir 时默认 OTTO_HOME)。capabilities 复用上方已计算\n // 的 caps(信任确认弹窗同一份数据),不重新读盘。\n await recordPluginInstalled(ctx.app.settings, {\n id: result.id,\n source: 'npm',\n sourceSpec: pkgName,\n version: result.version,\n scope: 'global',\n capabilities: caps,\n })\n\n // RFC-303 D4:安装完成 → monitor 事件(observer-only)。仅 TUI 路径广播——CLI\n // standalone 安装与运行中会话是不同进程,跨进程无法触达 monitor(同 M6 kill no-op 边界)。\n void ctx.app.pluginModuleManager.emitMonitorEvent({\n type: 'plugin.installed',\n pluginId: result.id,\n })\n\n // 编译警告——向导模式静默忽略,非向导模式 toast\n for (const w of result.compileWarnings) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallFailed', { err: w }))\n }\n\n // 高危能力授权(复用与 CLI 完全相同的写入路径)\n if (caps.length > 0) {\n onProgress?.('Authorizing capabilities')\n trustPlugin(result.dir)\n grantPluginCapabilities(result.dir, caps)\n }\n\n // 自动 reload\n try {\n onProgress?.('Reloading capabilities')\n const r = await ctx.app.reloadCapabilities()\n if (r.pendingProviderCapabilities.includes(result.id)) {\n // toast 维持原条件(向导模式压制全部 toast);回调则**无条件**触发——\n // 它正是向导用来在成功态显示\"还缺一次授权\"的通道。\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallPendingCaps', { id: result.id }))\n onPendingCapabilities?.(result.id)\n }\n } catch (e) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.compatReloadFailed', { err: e instanceof Error ? e.message : String(e) }))\n }\n\n // RFC-326 M1-04:不再硬编码 `setNeedsSetup(false)`。\n //\n // 装上 provider 插件**不等于**已经可用——还差凭据与一个真正能用的模型。此前这里\n // 无条件宣布\"已配置\",导致:输入框 placeholder 从引导文案变回普通提示、Home 恢复\n // 卡消失,而用户其实连登录都还没做,下一步该干什么反而没了线索。\n //\n // 改为走 `refreshModelsNow()`——它重算 availableModelIds 后按**真实结果**调用\n // setNeedsSetup(见 interactive.ts 的 refreshModelsNow 实现),装完即可用时同样会\n // 翻成 false,只是不再\"假设\"可用。刷新失败不阻断安装主流程。\n await ctx.callbacks.refreshModelsNow().catch(() => {})\n\n // RFC-134 M1:feishu-use 专属安装后引导——弹出交互式配置对话框(QR 扫码或手动输入凭据)。\n // 其他插件的安装完成后直接打开 /model 选模型(它们的 MCP 工具立刻可用,无需配置向导)。\n if (pluginName === 'plugin-feishu-use') {\n try {\n const { launchFeishuSetup } = await import('./feishu-setup')\n await launchFeishuSetup(ctx)\n } catch {\n // setup 失败不影响安装本身——用户可稍后用 `otto feishu config init` 手动配置。\n }\n return true\n }\n\n // 向导模式不打开 /model(向导有自己的步骤③选择模型)\n if (!autoTrust) {\n await new Promise<void>((resolve) => setImmediate(resolve))\n const { handleModel } = await import('./model-auth')\n await handleModel([], ctx)\n }\n return true\n}\n\n/**\n * /delegates — Toggle the Delegates panel.\n */\nexport async function handleDelegatesToggle(\n _args: string[],\n ctx: CliContext,\n): Promise<void> {\n ctx.tui.panes.togglePane('backgroundTasks')\n}\n\n/**\n * /job <directive>\n * Start a background agent job (worktree-isolated, full tools incl. MCP).\n * /job revert <id> to undo an applied job.\n */\nexport async function handleJobStart(\n args: string[],\n ctx: CliContext,\n): Promise<void> {\n const directive = args.join(' ')\n if (!directive) {\n ctx.tui.info.showToast(i18next.t('cmd.titleJob'), i18next.t('cmd.jobUsage'))\n return\n }\n\n const revertMatch = /^revert\\s+(\\S+)$/.exec(directive)\n if (revertMatch) {\n const id = revertMatch[1]!\n const out = ctx.app.agentJobs.revert(id)\n if (out.applied) {\n ctx.tui.info.showToast(i18next.t('cmd.titleReverted'), i18next.t('cmd.jobReverted', { id: id, msg: out.message }))\n } else {\n const conflicts = out.conflictPaths.length\n ? i18next.t('cmd.jobConflictFiles', { joinedPaths: out.conflictPaths.join('\\n ') })\n : ''\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: `/go revert ${id}`,\n status: 'error',\n summary: i18next.t('cmd.jobRevertFailed', { id: id, status: out.status, msg: out.message, conflicts: conflicts }),\n })\n }\n return\n }\n\n try {\n const job = ctx.app.agentJobs.start({\n title: directive.slice(0, 60),\n prompt: directive,\n sessionId: ctx.state.session.id,\n origin: 'user',\n })\n ctx.tui.info.showToast(i18next.t('cmd.titleDelegated'), i18next.t('cmd.delegated', { title: job.title }))\n } catch (e) {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/go',\n status: 'error',\n summary: i18next.t('cmd.delegateFailed', { err: e instanceof Error ? e.message : String(e) }),\n })\n }\n}\n\n/**\n * /extension | /extensions | /ext — Manage pluggable extensions (MCP / skills / agents).\n * List → select item for detail → toggle enable / reconnect (MCP).\n */\nexport async function handleExtension(\n _args: string[],\n ctx: CliContext,\n): Promise<void> {\n const notify = (title: string, content: string) =>\n ctx.tui.info.showToast(title, content)\n\n const say = (text: string) =>\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/extension',\n status: 'error',\n summary: text,\n })\n\n\n // RFC-166:`/extension` 列表页——独立 hard modal(Select.Group 组合式规范),不再走\n // 通用 kind:'select' 通道(扁平 SelectOption[] 字符串 group 字段的旧路径)。分组/单行详情\n // 折叠交由 ExtensionsListDialog 渲染层负责,本处只装配纯数据 entries/menuContributions。\n const openList = () => {\n const items = ctx.app.extensions.list()\n // 插件面板不再作为独立列表项——它是插件详情 dialog 的 body(选中插件即在详情里渲染)。\n\n // RFC-105 D5/M105-4:全局菜单贡献点(shell/menu/item)——任意插件可往此列表追加固定入口\n // (区别于 extensionDetailActions 的「查看某插件详情」per-viewed-plugin scope)。\n // 宿主拥有布局(R6):贡献条目统一归入独立分组,追加在内置列表之后,不打散既有分类。\n const menuContribs = ctx.app.getGlobalMenuItems?.() ?? []\n const menuCmdById = new Map(menuContribs.map((r) => [r.id, r.command]))\n\n ctx.tui.modal.openModal({\n kind: 'extensionsList',\n entries: items.map((i) => ({\n category: i.category,\n id: i.id,\n name: i.name,\n enabled: i.enabled,\n description: i.description,\n applies: i.applies,\n })),\n menuContributions: [\n // RFC-201 M3:发现入口——搜索已注册 registry 并一键安装(数据面复用 /search)。\n { id: '__discover__', title: 'Discover plugins (search registries)…' },\n ...menuContribs.map((r) => ({ id: r.id, title: r.title })),\n ],\n onSelect: (value: string) => {\n if (!value) return\n if (value === 'contrib:__discover__') {\n void openDiscover()\n return\n }\n if (value.startsWith('contrib:')) {\n const id = value.slice('contrib:'.length)\n const cmd = menuCmdById.get(id)\n if (!cmd) return\n void dispatch(cmd, {\n open: (t) => openTarget(t),\n runCommand: async (cmdName, cmdArgs) => {\n const { dispatchSlashCommand } = await import('../../command-dispatcher')\n await dispatchSlashCommand(cmdName, cmdArgs ? cmdArgs.split(/\\s+/).filter(Boolean) : [], ctx)\n },\n }).catch((e: unknown) => notify(i18next.t('cmd.titlePlugin'), e instanceof Error ? e.message : String(e)))\n return\n }\n const sep = value.indexOf(':')\n const category = value.slice(0, sep)\n const id = value.slice(sep + 1)\n openDetail(category, id)\n },\n onCancel: () => {},\n })\n }\n\n /**\n * RFC-201 M3:发现入口——openInput 收搜索词 → searchRegistries 数据面 → openSelect 结果\n * (capabilities 预披露 + @来源 + stale 标注)→ 选中即 dispatch 既有 /install 命令\n * (--sub-path 透传;安装确认/信任门走既有流,本入口零新增安装逻辑)。\n */\n const openDiscover = async () => {\n const keyword = await ctx.tui.modal.openModal({\n kind: 'input',\n title: 'Discover plugins',\n description: ['搜索已注册 registry(/registry list 查看源;/registry add 注册第三方源)'],\n placeholder: 'keyword… (e.g. weather / oauth / provider)',\n })\n if (!keyword?.trim()) { openList(); return }\n\n const { searchRegistries } = await import('./registry')\n const { hits, warnings } = await searchRegistries(ctx, keyword.trim())\n if (hits.length === 0) {\n notify(i18next.t('cmd.titlePlugin'), `No registry entries match '${keyword.trim()}'${warnings.length ? ` (${warnings.length} source warning)` : ''}`)\n openList()\n return\n }\n\n ctx.tui.modal.openModal({\n kind: 'select',\n title: `Discover: '${keyword.trim()}' (${hits.length})`,\n subtitle: warnings.length > 0 ? `⚠ ${warnings.join(' · ')}` : undefined,\n options: hits.map((h, i) => ({\n label: `${h.entry.id} (${h.entry.type}) [${(h.entry.capabilities ?? []).join(', ') || '-'}] @${h.sourceName}${h.stale ? ' (stale)' : ''}`,\n description: h.entry.description,\n value: String(i),\n })),\n onSelect: (value: string) => {\n const hit = hits[Number(value)]\n if (!hit) return\n const args = ['--as', hit.entry.type, ...(hit.entry.subPath ? ['--sub-path', hit.entry.subPath] : []), hit.entry.source]\n void (async () => {\n const { dispatchSlashCommand } = await import('../../command-dispatcher')\n await dispatchSlashCommand('install', args, ctx)\n })().catch((e: unknown) => notify(i18next.t('cmd.titlePlugin'), e instanceof Error ? e.message : String(e)))\n },\n onCancel: () => openList(),\n })\n }\n\n /**\n * Skill 详情面板(对齐插件详情 UI/UX,RFC-166 D10 后续):分组展示概述/触发方式/依赖/标签/\n * 加载错误,聚合可用动作(启用/禁用)。此前 skill 类别走 openDetail 末尾的通用 select 回退\n * (单个启用/禁用选项,无元数据展示),用户看不到来源/触发模式/依赖这些排障关键信息。\n */\n const openSkillDetail = (id: string) => {\n const reg = ctx.app.skillRegistry?.get(id)\n if (!reg) {\n openList()\n return\n }\n const enabled = reg.status !== 'disabled'\n const meta = reg.definition.metadata\n\n const entries: DataListEntry[] = [\n { kind: 'group', title: i18next.t('cmd.extSkillGroupOverview') },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelDesc'), value: meta.description || '—' },\n {\n kind: 'row',\n label: i18next.t('cmd.extSkillLabelStatus'),\n value: reg.status,\n // 真实用户反馈修复(启用=绿/禁用=红):此前 `enabled ? 'success' : 'muted'` 只有\n // 两态,`error` 状态因 `enabled = status !== 'disabled'` 恒为 true 被误标成绿色——\n // 改为精确基于 reg.status 三态判定(available/loaded=绿,disabled=红,error=红)。\n tone: reg.status === 'error' || reg.status === 'disabled' ? 'error' : 'success',\n },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelSource'), value: i18next.t('cmd.extSkillSourceLabel', { seg: extSkillSourceSeg(reg.source.type, reg.source.id) }), tone: 'muted' },\n\n { kind: 'group', title: i18next.t('cmd.extSkillGroupTrigger') },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelTriggerMode'), value: meta.trigger === 'manual' ? i18next.t('cmd.extSkillTriggerManual') : i18next.t('cmd.extSkillTriggerAuto') },\n ...(meta.priority !== undefined\n ? [{ kind: 'row', label: i18next.t('cmd.extSkillLabelPriority'), value: String(meta.priority) } as DataListEntry]\n : []),\n\n { kind: 'group', title: i18next.t('cmd.extSkillGroupDependencies') },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelDependencies'), value: meta.dependencies?.length ? meta.dependencies.join(', ') : i18next.t('cmd.extSkillNoDependencies'), tone: meta.dependencies?.length ? undefined : 'muted' },\n\n { kind: 'group', title: i18next.t('cmd.extSkillGroupTags') },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelTags'), value: meta.tags?.length ? meta.tags.join(', ') : i18next.t('cmd.extSkillNoTags'), tone: meta.tags?.length ? undefined : 'muted' },\n ]\n\n if (reg.status === 'error' && reg.error) {\n entries.push(\n { kind: 'group', title: i18next.t('cmd.extSkillGroupError') },\n { kind: 'text', value: reg.error, tone: 'error' },\n )\n }\n\n // \"查看正文\"按钮(SKILL.md 去 frontmatter 后的完整正文,元数据外唯一未暴露给用户的\n // 内容)——正文可能很长,不适合塞进 DataList 行内,走 ExtensionDetailDialog 内建的\n // FileContentViewer 子视图(可滚动,Esc 退回本详情页)。\n ctx.tui.modal.openModal({\n kind: 'extensionDetail',\n title: i18next.t('cmd.extSkillTitle', { name: id, state: (enabled) ? 'enabled' : 'disabled' }),\n meta: `skill · ${reg.source.type}`,\n metaStatus: { label: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), enabled },\n actions: [\n {\n label: enabled ? i18next.t('cmd.extDisableLabel') : i18next.t('cmd.extEnableLabel'),\n value: 'toggle',\n color: enabled ? 'amber' : 'green',\n },\n { label: i18next.t('cmd.extViewFullTextLabel'), value: 'view-full-text', color: 'gray' },\n // 卸载入口(原 `/uninstall` 命令收口至此)。**仅独立安装的 skill 可卸**:\n // `plugin` 来源的随宿主插件走(卸插件才对),`inline` 是代码内建、无文件可删。\n ...(skillUninstallScope(reg.source.type)\n ? [{ label: i18next.t('cmd.uninstallActionLabel'), value: 'uninstall', color: 'red' as const }]\n : []),\n ],\n dataListEntries: entries,\n fullText: { title: i18next.t('cmd.extSkillFullTextTitle', { name: id }), content: reg.definition.body || i18next.t('cmd.extSkillNoBody') },\n onAction: async (v: string) => {\n if (v === 'toggle') {\n try {\n await ctx.app.extensions.setEnabled('skill', id, !enabled)\n notify(\n i18next.t('cmd.titleExtension'),\n i18next.t('cmd.extToggled', { name: id, verb: (enabled) ? 'Disabled' : 'Enabled', seg: (true) ? ' — applies next turn' : '' }),\n )\n } catch (e) {\n say(i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }))\n }\n openSkillDetail(id)\n return\n }\n if (v === 'uninstall') {\n const scope = skillUninstallScope(reg.source.type)\n if (!scope) return\n await confirmAndUninstall({\n name: id,\n type: 'skill',\n scope,\n ctx,\n // 卸载后该 skill 已不存在,回详情页会落空 → 回列表。\n onDone: () => openList(),\n })\n }\n },\n onClose: () => openList(),\n })\n }\n\n /**\n * Agent 详情面板(对齐 Skill 详情 UI/UX):分组展示概述/任务类型能力/工具权限/执行配置,\n * 聚合可用动作(启用/禁用 · 查看系统提示词模板正文)。此前 agent 类别走 openDetail 末尾的\n * 通用 select 回退(单个启用/禁用选项),`AgentProfile` 的 taskTypes/capabilities/\n * defaultTools/disallowedTools/preferredModelTier/allowSubagents 等字段完全没有暴露。\n */\n const openAgentDetail = (name: string) => {\n const profile = ctx.app.declarativeAgentProfiles.find((p) => p.name === name)\n if (!profile) {\n openList()\n return\n }\n const disabledAgents = new Set(ctx.app.settings.get('disabled_agents') ?? [])\n const enabled = !disabledAgents.has(name)\n\n const entries: DataListEntry[] = [\n { kind: 'group', title: i18next.t('cmd.extAgentGroupOverview') },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelDesc'), value: profile.description || '—' },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelStatus'), value: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), tone: enabled ? 'success' : 'error' },\n\n { kind: 'group', title: i18next.t('cmd.extAgentGroupCapabilities') },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelTaskTypes'), value: profile.taskTypes.length ? profile.taskTypes.join(', ') : i18next.t('cmd.extAgentNoTaskTypes'), tone: profile.taskTypes.length ? undefined : 'muted' },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelCapabilities'), value: profile.capabilities.length ? profile.capabilities.join(', ') : i18next.t('cmd.extAgentNoCapabilities'), tone: profile.capabilities.length ? undefined : 'muted' },\n\n { kind: 'group', title: i18next.t('cmd.extAgentGroupTools') },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelDefaultTools'), value: profile.defaultTools?.length ? profile.defaultTools.join(', ') : i18next.t('cmd.extAgentAllTools'), tone: profile.defaultTools?.length ? undefined : 'muted' },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelDisallowedTools'), value: profile.disallowedTools?.length ? profile.disallowedTools.join(', ') : i18next.t('cmd.extAgentNoDisallowedTools'), tone: profile.disallowedTools?.length ? undefined : 'muted' },\n\n { kind: 'group', title: i18next.t('cmd.extAgentGroupExecution') },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelPromptMode'), value: profile.promptMode ?? 'append', tone: 'muted' },\n ...(profile.preferredModelTier\n ? [{ kind: 'row', label: i18next.t('cmd.extAgentLabelModelTier'), value: profile.preferredModelTier } as DataListEntry]\n : []),\n { kind: 'row', label: i18next.t('cmd.extAgentLabelAllowSubagents'), value: profile.allowSubagents ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), tone: profile.allowSubagents ? undefined : 'muted' },\n ]\n\n ctx.tui.modal.openModal({\n kind: 'extensionDetail',\n title: i18next.t('cmd.extAgentTitle', { name: name, state: (enabled) ? 'enabled' : 'disabled' }),\n meta: 'agent',\n metaStatus: { label: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), enabled },\n actions: [\n {\n label: enabled ? i18next.t('cmd.extDisableLabel') : i18next.t('cmd.extEnableLabel'),\n value: 'toggle',\n color: enabled ? 'amber' : 'green',\n },\n { label: i18next.t('cmd.extViewFullTextLabel'), value: 'view-full-text', color: 'gray' },\n ],\n dataListEntries: entries,\n fullText: { title: i18next.t('cmd.extAgentFullTextTitle', { name: name }), content: profile.systemPromptTemplate || i18next.t('cmd.extSkillNoBody') },\n onAction: async (v: string) => {\n if (v === 'toggle') {\n try {\n await ctx.app.extensions.setEnabled('agent', name, !enabled)\n notify(i18next.t('cmd.titleExtension'), i18next.t('cmd.extToggled', { name: name, verb: (enabled) ? 'Disabled' : 'Enabled', seg: (true) ? ' — applies next turn' : '' }))\n } catch (e) {\n say(i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }))\n }\n openAgentDetail(name)\n }\n },\n onClose: () => openList(),\n })\n }\n\n const openDetail = (category: string, id: string) => {\n const item = ctx.app.extensions\n .list()\n .find((i) => i.category === category && i.id === id)\n if (!item) {\n openList()\n return\n }\n const toggleOpt = {\n label: item.enabled ? i18next.t('cmd.extDisableLabel') : i18next.t('cmd.extEnableLabel'),\n value: 'toggle',\n }\n if (category === 'mcp') {\n const info = ctx.app.mcpManager\n ?.getServerInfos()\n .find((s) => s.name === id)\n const status = item.enabled\n ? info\n ? String(info.status)\n : 'disconnected'\n : 'disabled'\n ctx.tui.modal.openModal({\n kind: 'select',\n title: i18next.t('cmd.extMcpTitle', { name: item.name, status: status }),\n options: [\n { label: i18next.t('cmd.extReconnectLabel'), value: 'reconnect' },\n toggleOpt,\n // 卸载入口(原 `/uninstall` 命令收口至此)。**managed server 不给此项**——\n // 它由插件带入(RFC-081 `readonly`),移除方式是卸载宿主插件;单独删配置项\n // 会在插件下次加载时重新出现,是假卸载。\n ...(item.readonly\n ? []\n : [{ label: i18next.t('cmd.uninstallActionLabel'), value: 'uninstall' }]),\n ],\n onSelect: async (v: string) => {\n if (v === 'toggle') {\n try {\n await ctx.app.extensions.setEnabled(\n item.category,\n id,\n !item.enabled,\n )\n notify(\n i18next.t('cmd.titleExtension'),\n i18next.t('cmd.extToggled', { name: item.name, verb: (item.enabled) ? 'Disabled' : 'Enabled', seg: (false) ? ' — applies next turn' : '' }),\n )\n } catch (e) {\n say(\n i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }),\n )\n }\n openDetail(category, id)\n return\n }\n if (v === 'reconnect') {\n const mgr = ctx.app.mcpManager\n const cfg = ((ctx.app.settings.get('mcp_servers') ?? {}) as Record<string, unknown>)[id]\n if (!mgr || !cfg) {\n notify(i18next.t('cmd.titleExtension'), i18next.t('cmd.extReconnectUnavailable', { id: id }))\n openDetail(category, id)\n return\n }\n try {\n await mgr.disconnect(id).catch(() => {})\n await mgr.connect(id, cfg)\n notify(i18next.t('cmd.titleExtension'), i18next.t('cmd.extReconnected', { id: id }))\n } catch (e) {\n say(\n i18next.t('cmd.extReconnectFailed', { id: id, err: e instanceof Error ? e.message : String(e) }),\n )\n }\n openDetail(category, id)\n return\n }\n if (v === 'uninstall') {\n // MCP server 的\"安装物\"就是 `settings.mcp_servers` 里的配置项——卸载走的是\n // settings 读写回调(uninstaller.ts case 'mcp'),**不碰文件系统**,故 scope\n // 在此仅影响确认框里的层级文案,不改变删除目标。传 'project'(即当前工作区\n // 生效的那份配置,与用户在面板中看到的条目同源)。\n await confirmAndUninstall({\n name: id,\n type: 'mcp',\n scope: 'project',\n ctx,\n // 卸载后该 server 已不存在,回详情页会落空 → 回列表。\n onDone: () => openList(),\n })\n return\n }\n },\n onCancel: () => openList(),\n })\n } else if (category === 'plugin') {\n openPluginDetail(id, item.enabled, item.name, item.description)\n } else if (category === 'skill') {\n openSkillDetail(id)\n } else if (category === 'agent') {\n openAgentDetail(id)\n } else {\n ctx.tui.modal.openModal({\n kind: 'select',\n title: i18next.t('cmd.extItemTitle', { name: item.name }),\n options: [toggleOpt],\n onSelect: async (v: string) => {\n if (v === 'toggle') {\n try {\n await ctx.app.extensions.setEnabled(\n item.category,\n id,\n !item.enabled,\n )\n notify(\n i18next.t('cmd.titleExtension'),\n i18next.t('cmd.extToggled', { name: item.name, verb: (item.enabled) ? 'Disabled' : 'Enabled', seg: (item.applies !== 'live') ? ' — applies next turn' : '' }),\n )\n } catch (e) {\n say(\n i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }),\n )\n }\n openDetail(category, id)\n return\n }\n },\n onCancel: () => openList(),\n })\n }\n }\n\n /**\n * 插件详情面板(RFC-044/081):展示描述/作用域/信任态/贡献面/面板,聚合可用动作——\n * 启停 bundle、信任/撤销可执行面(项目级)、打开其贡献的面板。比通用条目详情富。\n */\n const openPluginDetail = (\n id: string,\n enabled: boolean,\n name: string,\n description: string,\n /**\n * RFC-278:untrust/toggle/reload 完成后重开详情页时携带的操作结果——渲染为\n * `dataListEntries` 首行的 `{kind:'text', tone}` 提示行,取代原来的 `notify()` toast。\n * 常驻本次重开的这一屏(无自动隐藏 timer——`handle-modal.ts` 的 `case 'extensionDetail'`\n * 在 onAction 时无条件先 `close()` 再执行业务逻辑,面板重开属于全新一次 openModal 调用,\n * 不存在\"计时器触发时面板已被其他方式关闭\"的竞态,见 RFC-278 §0.3/§2.2)。\n */\n resultBanner?: { kind: 'success' | 'error'; message: string },\n ) => {\n const ws = ctx.app.workspaceDir ?? process.cwd()\n const plugin = discoverPluginsWithBundled({ cwd: ws, homedir: homedir() }).find((p) => p.id === id)\n if (!plugin) {\n openList()\n return\n }\n const verdict = evaluatePluginTrust(plugin)\n // 信任文本:user scope 无门控 → \"已信任\",project/repo scope 按 gated+trusted 判定。\n // 此前只判 `verdict.trusted`,user scope 插件有 scripts 面但不在 trust store 时会被判为\n // \"未信任(项目级——可执行面被门控)\"——对 user 作用域来说这是误导(user 永不受信任门限制)。\n const trustText = !verdict.surface.any\n ? i18next.t('cmd.extPluginNoSurface')\n : verdict.gated\n ? i18next.t('cmd.extPluginUntrusted')\n : verdict.pendingCapabilities.length > 0\n ? i18next.t('cmd.extPluginCapabilityPending', { caps: verdict.pendingCapabilities.join(', ') })\n : i18next.t('cmd.extPluginTrusted')\n\n const s = verdict.surface\n const surfaceLabels: Array<[boolean, string]> = [\n [s.mcpJson, 'mcp'],\n [s.commands, 'commands'],\n [s.scripts, 'scripts'],\n [s.panels, 'panels'],\n [s.skills, 'skills'],\n [s.agents, 'agents'],\n ]\n const surfaces = surfaceLabels.filter(([has]) => has).map(([, l]) => l)\n const surfaceText = surfaces.length ? ` ${surfaces.join(', ')}` : ' —'\n\n // 该插件贡献的已注册面板(经信任门过滤)。\n const panels = (ctx.app.getPluginPanelRegistrations?.() ?? []).filter(\n (p) => p.pluginId === id,\n )\n\n // RFC-124 P0-B(R11 权限审计基线):`/extension` 详情页此前只展示 surface(可执行面)+\n // panels + 菜单动作,遗漏了 provider/tools/renderers/statusItems 四个新轴——用户装了声明\n // 这些能力的插件后,无法从详情页一眼看清它到底注册了什么。此处按 pluginId 过滤四轴数据,\n // 组装成一行摘要文本,喂给 `genericPluginEntries` 的 DataList 贡献轴行(无贡献时该行不\n // 展示,不为空插件添加噪音)。\n const contributionsText = formatPluginContributionsSummary({\n providerApis: ctx.app.providerRegistry.getApisBySource(id).length,\n tools: ctx.app.toolRegistry.getAll().filter((t) => t.metadata.source === id).length,\n renderers: ctx.app.getPluginRenderers().filter((r) => r.pluginId === id).length,\n // statusItems 前缀匹配 `${id}:` 安全性说明(复审 finding B-1 核实结论):插件顶层\n // `id` 受 manifest.ts `ID_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/` 强制 kebab-case 校验,\n // 不可能含冒号——不存在插件 A(id=\"foo\")被插件 B(id=\"foo-bar\")的 status item\n // (id 格式 `${pluginId}:${itemId}`)误匹配的场景(冒号天然分隔,且 pluginId 本身\n // 无冒号可插入制造歧义前缀)。\n statusItems: ctx.app.getPluginStatusItems().filter((it) => it.id.startsWith(`${id}:`)).length,\n // RFC-187 M2:文件查看器候选贡献计数(同 renderers 过滤逻辑,pluginId 精确匹配)。\n // 可选链防御(同 getPluginPanelRegistrations 先例):新方法,既有测试 mock 的 ctx.app\n // 未必提供该方法,缺失时视为零贡献而非抛错。\n fileViewers: (ctx.app.getPluginFileViewers?.() ?? []).filter((v) => v.pluginId === id).length,\n })\n\n // 控制按钮:启用/禁用 · [信任/不信任 或 授权待授权能力] · 重载。\n // RFC-128:builtin scope 恒定信任、恒定启用——`ExtensionManager.setEnabled` 对\n // builtin 插件会直接抛错拒绝(见 extension-manager.ts PluginExtensionProvider.\n // setEnabled)。此前本处无条件塞\"禁用\"按钮,用户点击后必然踩中那个 throw(体验上\n // 是个死胡同:按钮看起来能点,点了却报错)。builtin 插件不可能被禁用,UI 上不应该\n // 出现\"禁用\"这个选项——同既有 managed MCP server / isBuiltin 的 readonly 语义\n // (不渲染可切换控件,而非让用户点了才发现不行)。\n const actions: ExtensionDetailAction[] = plugin.scope === 'builtin'\n ? []\n : [\n {\n label: enabled ? i18next.t('cmd.extDisableLabel') : i18next.t('cmd.extEnableLabel'),\n value: 'toggle',\n color: enabled ? 'amber' : 'green',\n },\n ]\n // 回归修复(真实用户反馈:DeepSeek/Anthropic 配了凭据仍无模型/无 OAuth URL):此前本按钮\n // 仅在 `plugin.scope === 'project'` 时出现——但 `pendingCapabilities`(RFC-124 C2/RFC-125)\n // 与 scope 正交,用户级插件(`~/.otto/plugins/`,如三个首方 provider 插件)同样可能卡在\n // \"已布尔信任但高危能力未授予\"状态,此前完全没有可点的按钮把它从 pendingCapabilities 里\n // 捞出来——RFC-125 D5 新增的 `/model` \"去授权\"逃生口跳转到这里后是个死胡同。\n // 现在只要 `verdict.surface.any`(含 highRiskCapabilities,RFC-124 C1)且\n // (项目级/仓库级 或 有待授权能力)就显示按钮;`promptPluginTrust` 本身已同时处理布尔信任\n // 与能力授予(M125-1),点击后两件事一次做完。\n // repository scope(RFC-130)对齐 project:需信任门。\n if (verdict.surface.any && (plugin.scope === 'project' || plugin.scope === 'repository' || verdict.pendingCapabilities.length > 0)) {\n const needsAction = (plugin.scope === 'project' || plugin.scope === 'repository') ? !verdict.trusted : verdict.pendingCapabilities.length > 0\n actions.push({\n label: needsAction ? i18next.t('cmd.extPluginTrustLabel') : i18next.t('cmd.extPluginUntrustLabel'),\n value: needsAction ? 'trust' : 'untrust',\n color: needsAction ? 'accent' : 'gray',\n })\n }\n actions.push({ label: i18next.t('cmd.extReloadLabel'), value: 'reload' })\n // 卸载入口(原 `/uninstall` 命令收口至此)。**builtin 插件不可卸**——它随 otto 二进制\n // 分发,没有独立安装物可删(同上方 builtin 不给\"禁用\"按钮的判断,理由一致:不渲染\n // 点了必然失败的控件)。放在贡献动作之前,保证它的位置不随插件贡献数量漂移。\n if (plugin.scope !== 'builtin') {\n actions.push({ label: i18next.t('cmd.uninstallActionLabel'), value: 'uninstall', color: 'red' })\n }\n\n // RFC-084 M084-03:追加**该插件贡献**的动作(per-viewed-plugin scope,§4.4b)。\n // context = 内置 extension.* 键 + 该插件声明的 context-key providers(文件探针求值)。\n const ctxMap = buildExtensionContext(plugin, {\n 'extension.id': id,\n 'extension.enabled': enabled,\n 'extension.trusted': verdict.trusted,\n 'extension.scope': plugin.scope,\n 'extension.category': 'plugin',\n })\n const contribs = plugin.manifest.contributes\n const resolved = resolveActions(POINT.extensionDetailActions, contribs?.menus, contribs?.actions, ctxMap)\n const cmdById = new Map(resolved.map((r) => [r.id, r.command]))\n for (const r of resolved) {\n // i18n 化标题(约定 key:`action.<actionId>.title`,同 feishu-use command 描述的\n // `pluginId:id` 复合标识约定)——插件未声明对应 i18n 条目时 `t()` 回退到 fullKey\n // 本身(PluginI18nRegistry 三级回退语义),此时改用 action 声明的字面量 title\n // 兜底,不展示裸 key 给用户。\n const i18nKey = `${plugin.id}:action.${r.id}.title`\n const translated = ctx.app.i18nRegistry.t(i18nKey)\n const label = translated === i18nKey ? r.title : translated\n actions.push({ label, value: `contrib:${r.id}`, color: r.color })\n }\n // 分发 caps:open 就近装配;runCommand 动态 import 避免与 command-dispatcher 静态成环;\n // callMcp 暂不装(当前无 mcp 型动作,缺则 dispatch 抛 MissingCapability)。\n const caps: DispatchCaps = {\n open: (t) => openTarget(t),\n runCommand: async (cmdName, cmdArgs) => {\n const { dispatchSlashCommand } = await import('../../command-dispatcher')\n await dispatchSlashCommand(cmdName, cmdArgs ? cmdArgs.split(/\\s+/).filter(Boolean) : [], ctx)\n },\n // script 型(装配副作用):过信任门(可执行面)——项目级未信任插件不得跑脚本。\n runScript: async (scriptName) => {\n if (evaluatePluginTrust(plugin).gated) {\n notify(i18next.t('cmd.titlePlugin'), i18next.t('cmd.extPluginUntrusted'))\n return\n }\n await runPluginScript(plugin, scriptName)\n },\n }\n\n // body = 插件自写详情组件(取该插件第一个已注册面板)。无则回退基本信息。\n const panelReg = panels[0]\n\n // RFC-124 P0-C(RFC-105 D7 懒激活):`onView:<id>` 此前只有 schema 允许声明,\n // 无任何真实触发点——声明了 `activationEvents:[\"onView:<panelId>\"]` 的插件,即使用户\n // 打开了该面板,其代码轴(hooks/tools/providerFactories/renderers)仍不会被激活。\n // 打开某插件的面板即是\"该 view 被展示\"的天然事件时机,fire-and-forget(不阻塞详情页渲染)。\n if (panelReg) {\n void ctx.app.firePluginActivationEvent?.(`onView:${panelReg.id}`)\n }\n\n // RFC-134 M3:feishu-use 扩展面板定制——通用插件详情对飞书用户是噪声(贡献面/作用域等\n // 引擎内部概念),替换为飞书特有的配置状态、bot 身份、\"重新配置\"操作。\n // RFC-166 D10 后续:改用 DataListEntry[](分组 + label/value 双色),取代此前手拼的\n // `string[]` 行(`App ID xxx` 这类整行内嵌 label 无法单独着色 value)。\n let feishuEntries: DataListEntry[] | undefined\n if (id === 'plugin-feishu-use') {\n const configPath = join(ws, '.otto', 'feishu-use.json')\n let configOk = false\n try {\n if (existsSync(configPath)) {\n const cfg = JSON.parse(readFileSync(configPath, 'utf8')) as Record<string, unknown>\n if (cfg.app_id) {\n configOk = true\n const maskedId = typeof cfg.app_id === 'string' && cfg.app_id.length > 12\n ? cfg.app_id.slice(0, 8) + '***' + cfg.app_id.slice(-4)\n : String(cfg.app_id)\n const chatCount = Array.isArray(cfg.allowed_chats) ? cfg.allowed_chats.length : 0\n const trustedCount = Array.isArray(cfg.trusted_chats) ? cfg.trusted_chats.length : 0\n const scopeLabel = plugin.scope === 'user' ? '全局' : plugin.scope === 'repository' ? '仓库' : '项目'\n feishuEntries = [\n { kind: 'row', label: 'App ID', value: maskedId },\n { kind: 'row', label: '消息白名单', value: chatCount > 0 ? `${chatCount} 个` : '空 — 拒绝所有消息', tone: chatCount > 0 ? undefined : 'warning' },\n ...(trustedCount > 0 ? [{ kind: 'row', label: '可信任 chat', value: `${trustedCount} 个` } as DataListEntry] : []),\n { kind: 'row', label: '配置', value: `${scopeLabel}级 · ${configPath}`, tone: 'muted' },\n ]\n }\n }\n } catch {\n /* 读取失败走未配置分支 */\n }\n if (!configOk) {\n feishuEntries = [\n { kind: 'text', value: '未配置 — 点击 [重新配置] 引导设置', tone: 'warning' },\n { kind: 'row', label: '预期配置路径', value: configPath, tone: 'muted' },\n ]\n }\n actions.push({ label: '重新配置', value: 'feishu-reconfigure', color: 'amber' })\n }\n\n // RFC-153 D4:健康度摘要 + 最近日志——打开时查询(同 /perf 的\"打开时查询,非订阅\"模式)。\n // fail-soft:`pluginHealthTracker` 缺失(测试 fixture 用简化 ctx.app mock,或未来其他\n // 精简装配路径)时降级为全零健康度 + 空日志,不阻断详情页渲染(规则 3 的同款克制精神)。\n const queryHealthAndLogs = () => {\n const stats = ctx.app.pluginHealthTracker?.query(id) ?? { hookTimeouts: 0, toolTimeouts: 0 }\n const logs = readPluginLogTail(id)\n return {\n healthStats: {\n hookTimeouts: stats.hookTimeouts,\n toolTimeouts: stats.toolTimeouts,\n lastError: stats.lastError,\n },\n logEntries: logs.map((e) => ({ ts: e.ts, level: e.level, msg: e.msg })),\n }\n }\n\n // \"extension 默认面板\"(RFC-166 D10 后续):无自定义 panel 时的通用回退——分组键值信息,\n // 与插件自写面板(如 browser-use host)、`/about` 统一走同一套 DataList 双色渲染。\n const genericPluginEntries: DataListEntry[] = [\n { kind: 'group', title: i18next.t('cmd.extPluginGroupTitle') },\n { kind: 'row', label: i18next.t('cmd.extPluginLabelDesc'), value: description },\n { kind: 'row', label: i18next.t('cmd.extPluginLabelScope'), value: plugin.scope },\n { kind: 'row', label: i18next.t('cmd.extPluginLabelTrust'), value: trustText },\n { kind: 'row', label: i18next.t('cmd.extPluginLabelSurfaces'), value: surfaceText.trim() || '—' },\n ...(contributionsText\n ? [{ kind: 'row', label: i18next.t('cmd.extPluginLabelContributions'), value: contributionsText } as DataListEntry]\n : []),\n { kind: 'row', label: i18next.t('cmd.extPluginLabelStatus'), value: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), tone: enabled ? 'success' : 'error' },\n ]\n\n // RFC-278:操作结果提示行——untrust/toggle/reload 完成后重开详情页时插在最前面,\n // 取代原 notify() toast。无 resultBanner 时零改动(保持原有渲染)。\n const bannerEntry: DataListEntry[] = resultBanner\n ? [{ kind: 'text', value: resultBanner.message, tone: resultBanner.kind === 'success' ? 'success' : 'error' }]\n : []\n\n ctx.tui.modal.openModal({\n kind: 'extensionDetail',\n title: i18next.t('cmd.extPluginTitle', { name: name, state: (enabled) ? 'enabled' : 'disabled' }),\n meta: `plugin · ${plugin.scope}`,\n metaStatus: { label: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), enabled },\n metaSuffix: trustText,\n actions,\n panelInfo: panelReg\n ? { title: panelReg.title, modulePath: panelReg.modulePath, exportName: panelReg.exportName, pluginId: id }\n : undefined,\n // RFC-167:面板不再内嵌详情页 body(改走独立 dialog,经\"打开面板 →\"按钮跳转)——\n // 有面板时详情页仍展示基础信息(DataList),不再因为 panelReg 存在就完全让位给空白。\n dataListEntries: [...bannerEntry, ...(feishuEntries ?? genericPluginEntries)],\n ...queryHealthAndLogs(),\n // RFC-153 D4:手动刷新键——复用本文件已有的\"重开详情页刷新\"模式(同 contrib: 动作后\n // 重算 context 的做法),非独立的 store setter,避免新增 TuiHandle 方法面。\n onRefresh: () => openPluginDetail(id, enabled, name, description),\n onAction: async (v: string) => {\n if (v.startsWith('contrib:')) {\n const cmd = cmdById.get(v.slice('contrib:'.length))\n if (cmd) {\n try {\n await dispatch(cmd, caps)\n } catch (e) {\n say(i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }))\n }\n }\n // OQ1:动作后重算 context 重解析 → 重开详情刷新按钮(如 relayState none→all)。\n openPluginDetail(id, enabled, name, description)\n } else if (v === 'trust') {\n promptPluginTrust(ctx, plugin)\n } else if (v === 'untrust') {\n // RFC-278:untrust 完成后重开详情页并展示结果行,取代原 notify() toast——\n // handle-modal.ts 的 onAction 包装器已在调用本回调前无条件 close() 了面板,\n // reloadCapabilities 完成后必须重开才能让用户看到最新的信任状态。\n untrustPlugin(plugin.dir)\n void ctx.app.reloadCapabilities?.().then(() => {\n ctx.tui.plugins.setPluginRegistrations(ctx.app.getPluginRegistrations())\n }).finally(() => {\n openPluginDetail(id, enabled, name, description, {\n kind: 'success', message: i18next.t('cmd.pluginUntrusted', { id: id }),\n })\n })\n } else if (v === 'toggle') {\n // RFC-278:toggle 完成后重开详情页(此前该分支唯独没有重开调用,是与\n // contrib:/skill/agent 详情页 toggle 分支不一致的实现缺口——本次修复对齐)。\n // RFC-287 M1:setEnabled 只落盘 disabled_plugins + kill 关联子进程,不刷新已注册\n // 的 hooks/tools/providers——此前需要用户额外手动点独立的 [reload] 才真正生效,\n // 与\"用户点了禁用就该立即禁用\"的直觉不符。补 reloadCapabilities()(对齐 untrust/\n // reload 两个既有分支的同款模式),使 toggle 成为自迭代回路\"授权→立即可用\"闭环的\n // 最后一环。reloadCapabilities 失败时仅提示不阻断——disabled_plugins 已落盘,\n // 状态不丢,用户可再触发一次 reload 重试。\n try {\n await ctx.app.extensions.setEnabled('plugin', id, !enabled)\n try {\n await ctx.app.reloadCapabilities()\n openPluginDetail(id, !enabled, name, description, {\n kind: 'success',\n message: i18next.t('cmd.extToggled', { name: name, verb: (enabled) ? 'Disabled' : 'Enabled', seg: '' }),\n })\n } catch (reloadErr) {\n openPluginDetail(id, !enabled, name, description, {\n kind: 'error',\n message: i18next.t('cmd.extToggleReloadFailed', {\n verb: (enabled) ? 'Disabled' : 'Enabled',\n name: name,\n err: reloadErr instanceof Error ? reloadErr.message : String(reloadErr),\n }),\n })\n }\n } catch (e) {\n openPluginDetail(id, enabled, name, description, {\n kind: 'error', message: i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }),\n })\n }\n } else if (v === 'feishu-reconfigure') {\n // RFC-134 M3:feishu-use \"重新配置\"按钮——触发交互式配置向导\n try {\n const { launchFeishuSetup } = await import('./feishu-setup')\n await launchFeishuSetup(ctx)\n notify('plugin-feishu-use', '配置完成。')\n } catch (e) {\n say(`feishu-use 配置向导失败:${e instanceof Error ? e.message : String(e)}`)\n }\n } else if (v === 'reload') {\n // RFC-278:reload 完成后重开详情页展示结果,取代原 notify() toast。\n void ctx.app\n .reloadCapabilities?.()\n .then((r: { mcpTools: number }) => {\n openPluginDetail(id, enabled, name, description, {\n kind: 'success', message: i18next.t('cmd.pluginTrusted', { id: id, count: r.mcpTools }),\n })\n })\n .catch((e: unknown) => {\n openPluginDetail(id, enabled, name, description, {\n kind: 'error', message: i18next.t('cmd.compatReloadFailed', { err: e instanceof Error ? e.message : String(e) }),\n })\n })\n } else if (v === 'uninstall') {\n // plugin.scope 与 InstallScope 的取值域不同:前者是 user/project/repository/builtin,\n // 后者是 global/project/repository。user 级插件装在 `~/.otto/plugins` = 卸载引擎的\n // global 层,故此处映射;builtin 上方已不给按钮,走不到这里。\n const scope: InstallScope =\n plugin.scope === 'project' || plugin.scope === 'repository' ? plugin.scope : 'global'\n await confirmAndUninstall({\n name: id,\n type: 'plugin',\n scope,\n ctx,\n // 卸载后该插件已不存在,回详情页会落空 → 回列表。\n onDone: () => openList(),\n })\n }\n },\n onClose: () => openList(),\n })\n }\n\n openList()\n}\n\n\n"],"mappings":";+qBA8BA,SAAgB,EAAa,EAA6B,CACxD,OAAQ,EAAR,CACE,IAAK,SACH,OAAO,EAAQ,EAAE,kBAAkB,CACrC,IAAK,aACH,OAAO,EAAQ,EAAE,sBAAsB,CAEzC,QACE,OAAO,EAAQ,EAAE,mBAAmB,EAc1C,SAAS,EACP,EACA,EACA,EAC+F,CAC/F,MAAO,CACL,aAAc,EAAI,IAAI,aACtB,cAAe,IAAU,aAAe,EAAkB,EAAI,IAAI,aAAa,CAAG,IAAA,GAClF,OAAQ,IAAU,SAClB,SACD,CAUH,eAAsB,EAAoB,EAAsC,CAC9E,GAAM,CAAE,OAAM,OAAM,QAAO,MAAK,UAAW,EACrC,EAAa,EAAI,IAAI,SAAS,IAAI,cAAc,CAEhD,EAAU,MAAM,EACpB,EACA,EACA,EAAiB,EAAK,EAAO,GAAK,KAC5B,EACN,SAAY,GACb,CAED,GAAI,CAAC,EAAQ,QAAS,CACpB,EAAI,IAAI,KAAK,UACX,EAAQ,EAAE,qBAAqB,CAC/B,EAAQ,EAAE,sBAAuB,CAAE,IAAK,EAAQ,OAAS,EAAQ,EAAE,mBAAmB,CAAE,CAAC,CAC1F,CACD,KAAU,CACV,OAGF,IAAM,EAAO,EAAQ,MAAQ,EAAE,CAC/B,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,UACN,MAAO,EAAQ,EAAE,4BAA6B,CAAE,OAAM,OAAM,CAAC,CAC7D,QAAS,CACP,EAAQ,EAAE,4BAA6B,CAAE,WAAY,EAAa,EAAM,CAAE,CAAC,CAC3E,GAAG,EACJ,CAAC,KAAK;EAAK,CACZ,aAAc,EAAQ,EAAE,0BAA0B,CAElD,UAAW,GACX,cAAiB,CACV,EAAiB,EAAI,EAE5B,aAAgB,KAAU,CAC3B,CAAC,CAeJ,eAAsB,EAAiB,EAAsC,CAC3E,GAAM,CAAE,OAAM,OAAM,QAAO,MAAK,UAAW,EACrC,EAAa,EAAI,IAAI,SAAS,IAAI,cAAc,CAEhD,EAAS,MAAM,EACnB,EACA,EACA,EAAiB,EAAK,EAAO,GAAM,KAC7B,EACN,KAAO,IAAY,CACjB,MAAM,EAAI,IAAI,SAAS,QAAQ,CAAE,YAAa,EAA6B,CAAC,EAE/E,CAED,GAAI,CAAC,EAAO,QAAS,CACnB,EAAI,IAAI,KAAK,UACX,EAAQ,EAAE,qBAAqB,CAC/B,EAAQ,EAAE,sBAAuB,CAAE,IAAK,EAAO,OAAS,EAAQ,EAAE,mBAAmB,CAAE,CAAC,CACzF,CACD,KAAU,CACV,OAGF,EAAI,IAAI,KAAK,UACX,EAAQ,EAAE,qBAAqB,CAC/B,EAAQ,EAAE,kBAAmB,CAAE,OAAM,OAAM,MAAO,EAAa,EAAM,CAAE,CAAC,CACzE,CAIG,IAAS,UACX,MAAM,EAAwB,EAAI,IAAI,SAAU,EAAK,CAGvD,GAAI,CACF,MAAM,EAAI,IAAI,oBAAoB,CAClC,EAAI,IAAI,QAAQ,uBAAuB,EAAI,IAAI,wBAAwB,CAAC,CAGpE,IAAS,UACN,EAAI,IAAI,oBAAoB,iBAAiB,CAChD,KAAM,qBACN,SAAU,EACX,CAAC,CAGJ,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,qBAAqB,CAAE,EAAQ,EAAE,yBAAyB,CAAC,OACrF,EAAK,CACZ,EAAI,IAAI,SAAS,YAAY,CAC3B,KAAM,eACN,QAAS,aACT,OAAQ,QACR,QAAS,EAAQ,EAAE,4BAA6B,CAC9C,IAAK,aAAe,MAAQ,EAAI,QAAU,OAAO,EAAI,CACtD,CAAC,CACH,CAAC,CAGJ,KAAU,CC1IZ,SAAS,EAAoB,EAAyC,CAGpE,OAFI,IAAe,UAAkB,UACjC,IAAe,SAAiB,SAC7B,KAIT,SAAS,EAAkB,EAAc,EAA2B,CAElE,IAAM,EADiC,CAAE,QAAS,UAAW,OAAQ,SAAU,OAAQ,SAAU,OAAQ,SAAU,CAC/F,IAAS,EAC7B,OAAO,EAAW,GAAG,EAAK,IAAI,EAAS,GAAK,EAQ9C,SAAgB,EAAiC,EAM1B,CACrB,IAAM,EAAkB,EAAE,CAM1B,OALI,EAAO,aAAe,GAAG,EAAM,KAAK,YAAY,EAAO,aAAa,GAAG,CACvE,EAAO,MAAQ,GAAG,EAAM,KAAK,SAAS,EAAO,MAAM,GAAG,CACtD,EAAO,UAAY,GAAG,EAAM,KAAK,aAAa,EAAO,UAAU,GAAG,CAClE,EAAO,YAAc,GAAG,EAAM,KAAK,eAAe,EAAO,YAAY,GAAG,CACxE,EAAO,aAAe,EAAO,YAAc,GAAG,EAAM,KAAK,eAAe,EAAO,YAAY,GAAG,CAC3F,EAAM,OAAS,EAAM,KAAK,KAAK,CAAG,IAAA,GAmB3C,SAAgB,EAAsB,EAAiC,CACrE,OAAQ,EAAR,CACE,IAAK,WACH,OAAO,EAAQ,EAAE,+BAA+B,CAClD,IAAK,QACH,OAAO,EAAQ,EAAE,4BAA4B,CAC/C,IAAK,eACH,OAAO,EAAQ,EAAE,kCAAkC,CACrD,IAAK,UACH,OAAO,EAAQ,EAAE,8BAA8B,CACjD,IAAK,gBACH,OAAO,EAAQ,EAAE,mCAAmC,CACtD,IAAK,iBACH,OAAO,EAAQ,EAAE,oCAAoC,CACvD,IAAK,gBACH,OAAO,EAAQ,EAAE,mCAAmC,CACtD,IAAK,iBACH,OAAO,EAAQ,EAAE,oCAAoC,CACvD,IAAK,gBACH,OAAO,EAAQ,EAAE,mCAAmC,CACtD,QACE,QAcN,SAAgB,EAAkB,EAAiB,EAA2B,CAC5E,IAAM,EAAU,EAAoB,EAAE,CAChC,EAAsB,EAAQ,oBAC9B,MAAyB,CAC7B,EAAY,EAAE,IAAI,CACd,EAAoB,OAAS,GAC/B,EAAwB,EAAE,IAAK,EAAoB,CAErD,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,kBAAkB,CAAE,EAAQ,EAAE,qBAAsB,CAAE,GAAI,EAAE,GAAI,CAAC,CAAC,CAC9F,EAAI,IAAI,oBAAoB,CAAC,KAC/B,GAAM,CAIL,EAAI,IAAI,QAAQ,uBAAuB,EAAI,IAAI,wBAAwB,CAAC,CACxE,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,kBAAkB,CAAE,EAAQ,EAAE,oBAAqB,CAAE,GAAI,EAAE,GAAI,MAAO,EAAE,SAAU,CAAC,CAAC,EAEtH,GACC,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,kBAAkB,CAAE,EAAQ,EAAE,yBAA0B,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CAAC,CACjJ,EAGG,EAAU,EAAQ,QACxB,GAAI,CAAC,EAAQ,KAAO,EAAoB,SAAW,EAAG,CACpD,GAAY,CACZ,OAEF,IAAM,EAAO,CAAC,EAAQ,EAAE,8BAA8B,CAAC,CAOvD,GANI,EAAQ,SAAS,EAAK,KAAK,KAAK,EAAQ,EAAE,uBAAuB,GAAG,CACpE,EAAQ,UAAU,EAAK,KAAK,KAAK,EAAQ,EAAE,4BAA4B,GAAG,CAC1E,EAAQ,SAAS,EAAK,KAAK,KAAK,EAAQ,EAAE,2BAA2B,GAAG,CACxE,EAAQ,QAAQ,EAAK,KAAK,KAAK,EAAQ,EAAE,0BAA0B,GAAG,CACtE,EAAQ,QAAQ,EAAK,KAAK,KAAK,EAAQ,EAAE,0BAA0B,GAAG,CACtE,EAAQ,QAAQ,EAAK,KAAK,KAAK,EAAQ,EAAE,0BAA0B,GAAG,CACtE,EAAoB,OAAS,EAAG,CAClC,EAAK,KAAK,GAAI,EAAQ,EAAE,4BAA4B,CAAC,CACrD,IAAK,IAAM,KAAO,EAAqB,CACrC,IAAM,EAAQ,EAAsB,EAAI,CACpC,GAAO,EAAK,KAAK,KAAK,IAAQ,EAGtC,EAAK,KAAK,GAAI,EAAQ,EAAE,6BAA6B,CAAC,CACtD,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,SACN,MAAO,EAAQ,EAAE,8BAA+B,CAAE,GAAI,EAAE,GAAI,CAAC,CAC7D,SAAU,EAAK,KAAK;EAAK,CACzB,OAAQ,UACR,QAAS,CACP,CAAE,MAAO,EAAQ,EAAE,+BAA+B,CAAE,MAAO,QAAS,MAAO,SAAU,CACrF,CAAE,MAAO,EAAQ,EAAE,mBAAmB,CAAE,MAAO,SAAU,MAAO,OAAQ,CACzE,CACD,SAAW,GAAc,CACnB,IAAM,SAAS,GAAY,EAEjC,aAAgB,GACjB,CAAC,CAYJ,eAAsB,EACpB,EACA,EACA,EACkB,CAClB,GAAI,EAAK,SAAW,EAAG,MAAO,GAC9B,IAAM,EAAO,CAAC,EAAQ,EAAE,4BAA4B,CAAC,CACrD,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAQ,EAAsB,EAAI,CACpC,GAAO,EAAK,KAAK,KAAK,IAAQ,CAEpC,OAAO,IAAI,QAAkB,GAAY,CACvC,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,SACN,MAAO,EAAQ,EAAE,gCAAiC,CAAE,KAAM,EAAU,CAAC,CACrE,SAAU,EAAK,KAAK;EAAK,CACzB,OAAQ,UACR,QAAS,CACP,CAAE,MAAO,EAAQ,EAAE,iCAAiC,CAAE,MAAO,UAAW,MAAO,SAAU,CACzF,CAAE,MAAO,EAAQ,EAAE,mBAAmB,CAAE,MAAO,SAAU,MAAO,OAAQ,CACzE,CACD,SAAW,GAAc,EAAQ,IAAM,UAAU,CACjD,aAAgB,EAAQ,GAAM,CAC/B,CAAC,EACF,CAgBJ,eAAsB,EACpB,EACA,EAMA,EAgBkB,CAClB,IAAM,EAAY,GAAM,WAAa,GAC/B,EAAa,GAAM,WACnB,EAAwB,GAAM,sBAGpC,GAAI,EAAW,SAAS,KAAK,EAAI,EAAW,WAAW,IAAI,EAAI,EAAW,WAAW,KAAK,EAAI,EAAW,WAAW,KAAK,CAEvH,OADK,GAAW,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,+BAAgC,CAAE,KAAM,EAAY,CAAC,CAAC,CAC/H,GAGT,IAAM,EAAU,EAAkB,IAAa,KAAO,EAGhD,EAAS,EAAkB,EAAQ,CACzC,GAAI,CAAC,EAEH,OADK,GAAW,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,4BAA6B,CAAE,IAAK,EAAS,CAAC,CAAC,CACxH,GAKT,IAAM,EAAO,EAAyB,EAAO,CAG7C,GAAI,CAAC,GAEC,CADc,MAAM,EAA4B,EAAY,EAAM,EAAI,CAGxE,OADA,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,6BAA6B,CAAC,CACvF,GAKX,IAAI,EACJ,GAAI,CACF,IAAa,oBAAoB,CACjC,EAAS,MAAM,EAAyB,CAAE,IAAK,EAAS,CAAC,OAClD,EAAG,CAEV,OADK,GAAW,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,0BAA2B,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CAAC,CACzJ,GAIJ,GACH,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,sBAAuB,CAAE,IAAK,EAAO,QAAU,aAAa,EAAO,KAAO,GAAG,EAAO,GAAG,qBAAsB,CAAC,CAAC,CAMjL,MAAM,EAAsB,EAAI,IAAI,SAAU,CAC5C,GAAI,EAAO,GACX,OAAQ,MACR,WAAY,EACZ,QAAS,EAAO,QAChB,MAAO,SACP,aAAc,EACf,CAAC,CAIG,EAAI,IAAI,oBAAoB,iBAAiB,CAChD,KAAM,mBACN,SAAU,EAAO,GAClB,CAAC,CAGF,IAAK,IAAM,KAAK,EAAO,gBAChB,GAAW,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,0BAA2B,CAAE,IAAK,EAAG,CAAC,CAAC,CAIrH,EAAK,OAAS,IAChB,IAAa,2BAA2B,CACxC,EAAY,EAAO,IAAI,CACvB,EAAwB,EAAO,IAAK,EAAK,EAI3C,GAAI,CACF,IAAa,yBAAyB,EAC5B,MAAM,EAAI,IAAI,oBAAoB,EACtC,4BAA4B,SAAS,EAAO,GAAG,GAG9C,GAAW,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,+BAAgC,CAAE,GAAI,EAAO,GAAI,CAAC,CAAC,CACnI,IAAwB,EAAO,GAAG,QAE7B,EAAG,CACL,GAAW,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,yBAA0B,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CAAC,CAgBjK,GAJA,MAAM,EAAI,UAAU,kBAAkB,CAAC,UAAY,GAAG,CAIlD,IAAe,oBAAqB,CACtC,GAAI,CACF,GAAM,CAAE,qBAAsB,MAAM,OAAO,8BAC3C,MAAM,EAAkB,EAAI,MACtB,EAGR,MAAO,GAIT,GAAI,CAAC,EAAW,CACd,MAAM,IAAI,QAAe,GAAY,aAAa,EAAQ,CAAC,CAC3D,GAAM,CAAE,eAAgB,MAAM,OAAO,4BACrC,MAAM,EAAY,EAAE,CAAE,EAAI,CAE5B,MAAO,GAMT,eAAsB,EACpB,EACA,EACe,CACf,EAAI,IAAI,MAAM,WAAW,kBAAkB,CAQ7C,eAAsB,EACpB,EACA,EACe,CACf,IAAM,EAAY,EAAK,KAAK,IAAI,CAChC,GAAI,CAAC,EAAW,CACd,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,eAAe,CAAE,EAAQ,EAAE,eAAe,CAAC,CAC5E,OAGF,IAAM,EAAc,mBAAmB,KAAK,EAAU,CACtD,GAAI,EAAa,CACf,IAAM,EAAK,EAAY,GACjB,EAAM,EAAI,IAAI,UAAU,OAAO,EAAG,CACxC,GAAI,EAAI,QACN,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,oBAAoB,CAAE,EAAQ,EAAE,kBAAmB,CAAM,KAAI,IAAK,EAAI,QAAS,CAAC,CAAC,KAC7G,CACL,IAAM,EAAY,EAAI,cAAc,OAChC,EAAQ,EAAE,uBAAwB,CAAE,YAAa,EAAI,cAAc,KAAK;IAAO,CAAE,CAAC,CAClF,GACJ,EAAI,IAAI,SAAS,YAAY,CAC3B,KAAM,eACN,QAAS,cAAc,IACvB,OAAQ,QACR,QAAS,EAAQ,EAAE,sBAAuB,CAAM,KAAI,OAAQ,EAAI,OAAQ,IAAK,EAAI,QAAoB,YAAW,CAAC,CAClH,CAAC,CAEJ,OAGF,GAAI,CACF,IAAM,EAAM,EAAI,IAAI,UAAU,MAAM,CAClC,MAAO,EAAU,MAAM,EAAG,GAAG,CAC7B,OAAQ,EACR,UAAW,EAAI,MAAM,QAAQ,GAC7B,OAAQ,OACT,CAAC,CACF,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,qBAAqB,CAAE,EAAQ,EAAE,gBAAiB,CAAE,MAAO,EAAI,MAAO,CAAC,CAAC,OAClG,EAAG,CACV,EAAI,IAAI,SAAS,YAAY,CAC3B,KAAM,eACN,QAAS,MACT,OAAQ,QACR,QAAS,EAAQ,EAAE,qBAAsB,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CAC9F,CAAC,EAQN,eAAsB,EACpB,EACA,EACe,CACf,IAAM,GAAU,EAAe,IAC7B,EAAI,IAAI,KAAK,UAAU,EAAO,EAAQ,CAElC,EAAO,GACX,EAAI,IAAI,SAAS,YAAY,CAC3B,KAAM,eACN,QAAS,aACT,OAAQ,QACR,QAAS,EACV,CAAC,CAME,MAAiB,CACrB,IAAM,EAAQ,EAAI,IAAI,WAAW,MAAM,CAMjC,EAAe,EAAI,IAAI,sBAAsB,EAAI,EAAE,CACnD,EAAc,IAAI,IAAI,EAAa,IAAK,GAAM,CAAC,EAAE,GAAI,EAAE,QAAQ,CAAC,CAAC,CAEvE,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,iBACN,QAAS,EAAM,IAAK,IAAO,CACzB,SAAU,EAAE,SACZ,GAAI,EAAE,GACN,KAAM,EAAE,KACR,QAAS,EAAE,QACX,YAAa,EAAE,YACf,QAAS,EAAE,QACZ,EAAE,CACH,kBAAmB,CAEjB,CAAE,GAAI,eAAgB,MAAO,wCAAyC,CACtE,GAAG,EAAa,IAAK,IAAO,CAAE,GAAI,EAAE,GAAI,MAAO,EAAE,MAAO,EAAE,CAC3D,CACD,SAAW,GAAkB,CAC3B,GAAI,CAAC,EAAO,OACZ,GAAI,IAAU,uBAAwB,CAC/B,GAAc,CACnB,OAEF,GAAI,EAAM,WAAW,WAAW,CAAE,CAChC,IAAM,EAAK,EAAM,MAAM,EAAkB,CACnC,EAAM,EAAY,IAAI,EAAG,CAC/B,GAAI,CAAC,EAAK,OACL,EAAS,EAAK,CACjB,KAAO,GAAM,EAAW,EAAE,CAC1B,WAAY,MAAO,EAAS,IAAY,CACtC,GAAM,CAAE,wBAAyB,MAAM,OAAO,oCAAA,KAAA,GAAA,EAAA,EAAA,CAC9C,MAAM,EAAqB,EAAS,EAAU,EAAQ,MAAM,MAAM,CAAC,OAAO,QAAQ,CAAG,EAAE,CAAE,EAAI,EAEhG,CAAC,CAAC,MAAO,GAAe,EAAO,EAAQ,EAAE,kBAAkB,CAAE,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAC,CAAC,CAC1G,OAEF,IAAM,EAAM,EAAM,QAAQ,IAAI,CAG9B,EAFiB,EAAM,MAAM,EAAG,EAAI,CACzB,EAAM,MAAM,EAAM,EAAE,CACP,EAE1B,aAAgB,GACjB,CAAC,EAQE,EAAe,SAAY,CAC/B,IAAM,EAAU,MAAM,EAAI,IAAI,MAAM,UAAU,CAC5C,KAAM,QACN,MAAO,mBACP,YAAa,CAAC,0DAA0D,CACxE,YAAa,6CACd,CAAC,CACF,GAAI,CAAC,GAAS,MAAM,CAAE,CAAE,GAAU,CAAE,OAEpC,GAAM,CAAE,oBAAqB,MAAM,OAAO,0BACpC,CAAE,OAAM,YAAa,MAAM,EAAiB,EAAK,EAAQ,MAAM,CAAC,CACtE,GAAI,EAAK,SAAW,EAAG,CACrB,EAAO,EAAQ,EAAE,kBAAkB,CAAE,8BAA8B,EAAQ,MAAM,CAAC,GAAG,EAAS,OAAS,KAAK,EAAS,OAAO,kBAAoB,KAAK,CACrJ,GAAU,CACV,OAGF,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,SACN,MAAO,cAAc,EAAQ,MAAM,CAAC,KAAK,EAAK,OAAO,GACrD,SAAU,EAAS,OAAS,EAAI,KAAK,EAAS,KAAK,MAAM,GAAK,IAAA,GAC9D,QAAS,EAAK,KAAK,EAAG,KAAO,CAC3B,MAAO,GAAG,EAAE,MAAM,GAAG,KAAK,EAAE,MAAM,KAAK,OAAO,EAAE,MAAM,cAAgB,EAAE,EAAE,KAAK,KAAK,EAAI,IAAI,MAAM,EAAE,aAAa,EAAE,MAAQ,WAAa,KACxI,YAAa,EAAE,MAAM,YACrB,MAAO,OAAO,EAAE,CACjB,EAAE,CACH,SAAW,GAAkB,CAC3B,IAAM,EAAM,EAAK,OAAO,EAAM,EAC9B,GAAI,CAAC,EAAK,OACV,IAAM,EAAO,CAAC,OAAQ,EAAI,MAAM,KAAM,GAAI,EAAI,MAAM,QAAU,CAAC,aAAc,EAAI,MAAM,QAAQ,CAAG,EAAE,CAAG,EAAI,MAAM,OAAO,EAClH,SAAY,CAChB,GAAM,CAAE,wBAAyB,MAAM,OAAO,oCAAA,KAAA,GAAA,EAAA,EAAA,CAC9C,MAAM,EAAqB,UAAW,EAAM,EAAI,IAC9C,CAAC,MAAO,GAAe,EAAO,EAAQ,EAAE,kBAAkB,CAAE,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAC,CAAC,EAE9G,aAAgB,GAAU,CAC3B,CAAC,EAQE,EAAmB,GAAe,CACtC,IAAM,EAAM,EAAI,IAAI,eAAe,IAAI,EAAG,CAC1C,GAAI,CAAC,EAAK,CACR,GAAU,CACV,OAEF,IAAM,EAAU,EAAI,SAAW,WACzB,EAAO,EAAI,WAAW,SAEtB,EAA2B,CAC/B,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,4BAA4B,CAAE,CAChE,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,wBAAwB,CAAE,MAAO,EAAK,aAAe,IAAK,CAC1F,CACE,KAAM,MACN,MAAO,EAAQ,EAAE,0BAA0B,CAC3C,MAAO,EAAI,OAIX,KAAM,EAAI,SAAW,SAAW,EAAI,SAAW,WAAa,QAAU,UACvE,CACD,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,0BAA0B,CAAE,MAAO,EAAQ,EAAE,0BAA2B,CAAE,IAAK,EAAkB,EAAI,OAAO,KAAM,EAAI,OAAO,GAAG,CAAE,CAAC,CAAE,KAAM,QAAS,CAEpL,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,2BAA2B,CAAE,CAC/D,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,+BAA+B,CAAE,MAAO,EAAK,UAAY,SAAW,EAAQ,EAAE,4BAA4B,CAAG,EAAQ,EAAE,0BAA0B,CAAE,CACnL,GAAI,EAAK,WAAa,IAAA,GAElB,EAAE,CADF,CAAC,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,4BAA4B,CAAE,MAAO,OAAO,EAAK,SAAS,CAAE,CAAkB,CAGnH,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,gCAAgC,CAAE,CACpE,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,gCAAgC,CAAE,MAAO,EAAK,cAAc,OAAS,EAAK,aAAa,KAAK,KAAK,CAAG,EAAQ,EAAE,6BAA6B,CAAE,KAAM,EAAK,cAAc,OAAS,IAAA,GAAY,QAAS,CAEpO,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,wBAAwB,CAAE,CAC5D,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,wBAAwB,CAAE,MAAO,EAAK,MAAM,OAAS,EAAK,KAAK,KAAK,KAAK,CAAG,EAAQ,EAAE,qBAAqB,CAAE,KAAM,EAAK,MAAM,OAAS,IAAA,GAAY,QAAS,CAC7L,CAEG,EAAI,SAAW,SAAW,EAAI,OAChC,EAAQ,KACN,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,yBAAyB,CAAE,CAC7D,CAAE,KAAM,OAAQ,MAAO,EAAI,MAAO,KAAM,QAAS,CAClD,CAMH,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,kBACN,MAAO,EAAQ,EAAE,oBAAqB,CAAE,KAAM,EAAI,MAAQ,EAAW,UAAY,WAAY,CAAC,CAC9F,KAAM,WAAW,EAAI,OAAO,OAC5B,WAAY,CAAE,MAAO,EAAU,EAAQ,EAAE,qBAAqB,CAAG,EAAQ,EAAE,sBAAsB,CAAE,UAAS,CAC5G,QAAS,CACP,CACE,MAAO,EAAU,EAAQ,EAAE,sBAAsB,CAAG,EAAQ,EAAE,qBAAqB,CACnF,MAAO,SACP,MAAO,EAAU,QAAU,QAC5B,CACD,CAAE,MAAO,EAAQ,EAAE,2BAA2B,CAAE,MAAO,iBAAkB,MAAO,OAAQ,CAGxF,GAAI,EAAoB,EAAI,OAAO,KAAK,CACpC,CAAC,CAAE,MAAO,EAAQ,EAAE,2BAA2B,CAAE,MAAO,YAAa,MAAO,MAAgB,CAAC,CAC7F,EAAE,CACP,CACD,gBAAiB,EACjB,SAAU,CAAE,MAAO,EAAQ,EAAE,4BAA6B,CAAE,KAAM,EAAI,CAAC,CAAE,QAAS,EAAI,WAAW,MAAQ,EAAQ,EAAE,qBAAqB,CAAE,CAC1I,SAAU,KAAO,IAAc,CAC7B,GAAI,IAAM,SAAU,CAClB,GAAI,CACF,MAAM,EAAI,IAAI,WAAW,WAAW,QAAS,EAAI,CAAC,EAAQ,CAC1D,EACE,EAAQ,EAAE,qBAAqB,CAC/B,EAAQ,EAAE,iBAAkB,CAAE,KAAM,EAAI,KAAO,EAAW,WAAa,UAAW,IAAc,uBAA6B,CAAC,CAC/H,OACM,EAAG,CACV,EAAI,EAAQ,EAAE,sBAAuB,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CAAC,CAE5F,EAAgB,EAAG,CACnB,OAEF,GAAI,IAAM,YAAa,CACrB,IAAM,EAAQ,EAAoB,EAAI,OAAO,KAAK,CAClD,GAAI,CAAC,EAAO,OACZ,MAAM,EAAoB,CACxB,KAAM,EACN,KAAM,QACN,QACA,MAEA,WAAc,GAAU,CACzB,CAAC,GAGN,YAAe,GAAU,CAC1B,CAAC,EASE,EAAmB,GAAiB,CACxC,IAAM,EAAU,EAAI,IAAI,yBAAyB,KAAM,GAAM,EAAE,OAAS,EAAK,CAC7E,GAAI,CAAC,EAAS,CACZ,GAAU,CACV,OAGF,IAAM,EAAU,CADO,IAAI,IAAI,EAAI,IAAI,SAAS,IAAI,kBAAkB,EAAI,EAAE,CAAC,CAC7C,IAAI,EAAK,CAEnC,EAA2B,CAC/B,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,4BAA4B,CAAE,CAChE,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,wBAAwB,CAAE,MAAO,EAAQ,aAAe,IAAK,CAC7F,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,0BAA0B,CAAE,MAAO,EAAU,EAAQ,EAAE,qBAAqB,CAAG,EAAQ,EAAE,sBAAsB,CAAE,KAAM,EAAU,UAAY,QAAS,CAEtL,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,gCAAgC,CAAE,CACpE,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,6BAA6B,CAAE,MAAO,EAAQ,UAAU,OAAS,EAAQ,UAAU,KAAK,KAAK,CAAG,EAAQ,EAAE,0BAA0B,CAAE,KAAM,EAAQ,UAAU,OAAS,IAAA,GAAY,QAAS,CAC5N,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,gCAAgC,CAAE,MAAO,EAAQ,aAAa,OAAS,EAAQ,aAAa,KAAK,KAAK,CAAG,EAAQ,EAAE,6BAA6B,CAAE,KAAM,EAAQ,aAAa,OAAS,IAAA,GAAY,QAAS,CAE3O,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,yBAAyB,CAAE,CAC7D,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,gCAAgC,CAAE,MAAO,EAAQ,cAAc,OAAS,EAAQ,aAAa,KAAK,KAAK,CAAG,EAAQ,EAAE,uBAAuB,CAAE,KAAM,EAAQ,cAAc,OAAS,IAAA,GAAY,QAAS,CACvO,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,mCAAmC,CAAE,MAAO,EAAQ,iBAAiB,OAAS,EAAQ,gBAAgB,KAAK,KAAK,CAAG,EAAQ,EAAE,gCAAgC,CAAE,KAAM,EAAQ,iBAAiB,OAAS,IAAA,GAAY,QAAS,CAE5P,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,6BAA6B,CAAE,CACjE,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,8BAA8B,CAAE,MAAO,EAAQ,YAAc,SAAU,KAAM,QAAS,CACtH,GAAI,EAAQ,mBACR,CAAC,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,6BAA6B,CAAE,MAAO,EAAQ,mBAAoB,CAAkB,CACrH,EAAE,CACN,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,kCAAkC,CAAE,MAAO,EAAQ,eAAiB,EAAQ,EAAE,qBAAqB,CAAG,EAAQ,EAAE,sBAAsB,CAAE,KAAM,EAAQ,eAAiB,IAAA,GAAY,QAAS,CAC7N,CAED,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,kBACN,MAAO,EAAQ,EAAE,oBAAqB,CAAQ,OAAM,MAAQ,EAAW,UAAY,WAAY,CAAC,CAChG,KAAM,QACN,WAAY,CAAE,MAAO,EAAU,EAAQ,EAAE,qBAAqB,CAAG,EAAQ,EAAE,sBAAsB,CAAE,UAAS,CAC5G,QAAS,CACP,CACE,MAAO,EAAU,EAAQ,EAAE,sBAAsB,CAAG,EAAQ,EAAE,qBAAqB,CACnF,MAAO,SACP,MAAO,EAAU,QAAU,QAC5B,CACD,CAAE,MAAO,EAAQ,EAAE,2BAA2B,CAAE,MAAO,iBAAkB,MAAO,OAAQ,CACzF,CACD,gBAAiB,EACjB,SAAU,CAAE,MAAO,EAAQ,EAAE,4BAA6B,CAAQ,OAAM,CAAC,CAAE,QAAS,EAAQ,sBAAwB,EAAQ,EAAE,qBAAqB,CAAE,CACrJ,SAAU,KAAO,IAAc,CAC7B,GAAI,IAAM,SAAU,CAClB,GAAI,CACF,MAAM,EAAI,IAAI,WAAW,WAAW,QAAS,EAAM,CAAC,EAAQ,CAC5D,EAAO,EAAQ,EAAE,qBAAqB,CAAE,EAAQ,EAAE,iBAAkB,CAAQ,OAAM,KAAO,EAAW,WAAa,UAAW,IAAc,uBAA6B,CAAC,CAAC,OAClK,EAAG,CACV,EAAI,EAAQ,EAAE,sBAAuB,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CAAC,CAE5F,EAAgB,EAAK,GAGzB,YAAe,GAAU,CAC1B,CAAC,EAGE,GAAc,EAAkB,IAAe,CACnD,IAAM,EAAO,EAAI,IAAI,WAClB,MAAM,CACN,KAAM,GAAM,EAAE,WAAa,GAAY,EAAE,KAAO,EAAG,CACtD,GAAI,CAAC,EAAM,CACT,GAAU,CACV,OAEF,IAAM,EAAY,CAChB,MAAO,EAAK,QAAU,EAAQ,EAAE,sBAAsB,CAAG,EAAQ,EAAE,qBAAqB,CACxF,MAAO,SACR,CACD,GAAI,IAAa,MAAO,CACtB,IAAM,EAAO,EAAI,IAAI,YACjB,gBAAgB,CACjB,KAAM,GAAM,EAAE,OAAS,EAAG,CACvB,EAAS,EAAK,QAChB,EACE,OAAO,EAAK,OAAO,CACnB,eACF,WACJ,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,SACN,MAAO,EAAQ,EAAE,kBAAmB,CAAE,KAAM,EAAK,KAAc,SAAQ,CAAC,CACxE,QAAS,CACP,CAAE,MAAO,EAAQ,EAAE,wBAAwB,CAAE,MAAO,YAAa,CACjE,EAIA,GAAI,EAAK,SACL,EAAE,CACF,CAAC,CAAE,MAAO,EAAQ,EAAE,2BAA2B,CAAE,MAAO,YAAa,CAAC,CAC3E,CACD,SAAU,KAAO,IAAc,CAC7B,GAAI,IAAM,SAAU,CAClB,GAAI,CACF,MAAM,EAAI,IAAI,WAAW,WACvB,EAAK,SACL,EACA,CAAC,EAAK,QACP,CACD,EACE,EAAQ,EAAE,qBAAqB,CAC/B,EAAQ,EAAE,iBAAkB,CAAE,KAAM,EAAK,KAAM,KAAO,EAAK,QAAW,WAAa,UAAW,IAAwC,GAAI,CAAC,CAC5I,OACM,EAAG,CACV,EACE,EAAQ,EAAE,sBAAuB,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CACtF,CAEH,EAAW,EAAU,EAAG,CACxB,OAEF,GAAI,IAAM,YAAa,CACrB,IAAM,EAAM,EAAI,IAAI,WACd,GAAQ,EAAI,IAAI,SAAS,IAAI,cAAc,EAAI,EAAE,EAA8B,GACrF,GAAI,CAAC,GAAO,CAAC,EAAK,CAChB,EAAO,EAAQ,EAAE,qBAAqB,CAAE,EAAQ,EAAE,8BAA+B,CAAM,KAAI,CAAC,CAAC,CAC7F,EAAW,EAAU,EAAG,CACxB,OAEF,GAAI,CACF,MAAM,EAAI,WAAW,EAAG,CAAC,UAAY,GAAG,CACxC,MAAM,EAAI,QAAQ,EAAI,EAAI,CAC1B,EAAO,EAAQ,EAAE,qBAAqB,CAAE,EAAQ,EAAE,qBAAsB,CAAM,KAAI,CAAC,CAAC,OAC7E,EAAG,CACV,EACE,EAAQ,EAAE,yBAA0B,CAAM,KAAI,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CACjG,CAEH,EAAW,EAAU,EAAG,CACxB,OAEF,GAAI,IAAM,YAAa,CAKrB,MAAM,EAAoB,CACxB,KAAM,EACN,KAAM,MACN,MAAO,UACP,MAEA,WAAc,GAAU,CACzB,CAAC,CACF,SAGJ,aAAgB,GAAU,CAC3B,CAAC,MACO,IAAa,SACtB,EAAiB,EAAI,EAAK,QAAS,EAAK,KAAM,EAAK,YAAY,CACtD,IAAa,QACtB,EAAgB,EAAG,CACV,IAAa,QACtB,EAAgB,EAAG,CAEnB,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,SACN,MAAO,EAAQ,EAAE,mBAAoB,CAAE,KAAM,EAAK,KAAM,CAAC,CACzD,QAAS,CAAC,EAAU,CACpB,SAAU,KAAO,IAAc,CAC7B,GAAI,IAAM,SAAU,CAClB,GAAI,CACF,MAAM,EAAI,IAAI,WAAW,WACvB,EAAK,SACL,EACA,CAAC,EAAK,QACP,CACD,EACE,EAAQ,EAAE,qBAAqB,CAC/B,EAAQ,EAAE,iBAAkB,CAAE,KAAM,EAAK,KAAM,KAAO,EAAK,QAAW,WAAa,UAAW,IAAM,EAAK,UAAY,OAAmC,GAAzB,uBAA6B,CAAC,CAC9J,OACM,EAAG,CACV,EACE,EAAQ,EAAE,sBAAuB,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CACtF,CAEH,EAAW,EAAU,EAAG,CACxB,SAGJ,aAAgB,GAAU,CAC3B,CAAC,EAQA,GACJ,EACA,EACA,EACA,EAQA,IACG,CACH,IAAM,EAAK,EAAI,IAAI,cAAgB,QAAQ,KAAK,CAC1C,EAAS,EAA2B,CAAE,IAAK,EAAI,QAAS,GAAS,CAAE,CAAC,CAAC,KAAM,GAAM,EAAE,KAAO,EAAG,CACnG,GAAI,CAAC,EAAQ,CACX,GAAU,CACV,OAEF,IAAM,EAAU,EAAoB,EAAO,CAIrC,EAAa,EAAQ,QAAQ,IAE/B,EAAQ,MACN,EAAQ,EAAE,yBAAyB,CACnC,EAAQ,oBAAoB,OAAS,EACnC,EAAQ,EAAE,iCAAkC,CAAE,KAAM,EAAQ,oBAAoB,KAAK,KAAK,CAAE,CAAC,CAC7F,EAAQ,EAAE,uBAAuB,CALrC,EAAQ,EAAE,yBAAyB,CAOjC,EAAI,EAAQ,QASZ,EAR0C,CAC9C,CAAC,EAAE,QAAS,MAAM,CAClB,CAAC,EAAE,SAAU,WAAW,CACxB,CAAC,EAAE,QAAS,UAAU,CACtB,CAAC,EAAE,OAAQ,SAAS,CACpB,CAAC,EAAE,OAAQ,SAAS,CACpB,CAAC,EAAE,OAAQ,SAAS,CACrB,CAC8B,QAAQ,CAAC,KAAS,EAAI,CAAC,KAAK,EAAG,KAAO,EAAE,CACjE,EAAc,EAAS,OAAS,IAAI,EAAS,KAAK,KAAK,GAAK,KAG5D,GAAU,EAAI,IAAI,+BAA+B,EAAI,EAAE,EAAE,OAC5D,GAAM,EAAE,WAAa,EACvB,CAOK,EAAoB,EAAiC,CACzD,aAAc,EAAI,IAAI,iBAAiB,gBAAgB,EAAG,CAAC,OAC3D,MAAO,EAAI,IAAI,aAAa,QAAQ,CAAC,OAAQ,GAAM,EAAE,SAAS,SAAW,EAAG,CAAC,OAC7E,UAAW,EAAI,IAAI,oBAAoB,CAAC,OAAQ,GAAM,EAAE,WAAa,EAAG,CAAC,OAMzE,YAAa,EAAI,IAAI,sBAAsB,CAAC,OAAQ,GAAO,EAAG,GAAG,WAAW,GAAG,EAAG,GAAG,CAAC,CAAC,OAIvF,aAAc,EAAI,IAAI,wBAAwB,EAAI,EAAE,EAAE,OAAQ,GAAM,EAAE,WAAa,EAAG,CAAC,OACxF,CAAC,CASI,EAAmC,EAAO,QAAU,UACtD,EAAE,CACF,CACE,CACE,MAAO,EAAU,EAAQ,EAAE,sBAAsB,CAAG,EAAQ,EAAE,qBAAqB,CACnF,MAAO,SACP,MAAO,EAAU,QAAU,QAC5B,CACF,CAUL,GAAI,EAAQ,QAAQ,MAAQ,EAAO,QAAU,WAAa,EAAO,QAAU,cAAgB,EAAQ,oBAAoB,OAAS,GAAI,CAClI,IAAM,EAAe,EAAO,QAAU,WAAa,EAAO,QAAU,aAAgB,CAAC,EAAQ,QAAU,EAAQ,oBAAoB,OAAS,EAC5I,EAAQ,KAAK,CACX,MAAO,EAAc,EAAQ,EAAE,0BAA0B,CAAG,EAAQ,EAAE,4BAA4B,CAClG,MAAO,EAAc,QAAU,UAC/B,MAAO,EAAc,SAAW,OACjC,CAAC,CAEJ,EAAQ,KAAK,CAAE,MAAO,EAAQ,EAAE,qBAAqB,CAAE,MAAO,SAAU,CAAC,CAIrE,EAAO,QAAU,WACnB,EAAQ,KAAK,CAAE,MAAO,EAAQ,EAAE,2BAA2B,CAAE,MAAO,YAAa,MAAO,MAAO,CAAC,CAKlG,IAAM,EAAS,EAAsB,EAAQ,CAC3C,eAAgB,EAChB,oBAAqB,EACrB,oBAAqB,EAAQ,QAC7B,kBAAmB,EAAO,MAC1B,qBAAsB,SACvB,CAAC,CACI,EAAW,EAAO,SAAS,YAC3B,EAAW,EAAe,EAAM,uBAAwB,GAAU,MAAO,GAAU,QAAS,EAAO,CACnG,EAAU,IAAI,IAAI,EAAS,IAAK,GAAM,CAAC,EAAE,GAAI,EAAE,QAAQ,CAAC,CAAC,CAC/D,IAAK,IAAM,KAAK,EAAU,CAKxB,IAAM,EAAU,GAAG,EAAO,GAAG,UAAU,EAAE,GAAG,QACtC,EAAa,EAAI,IAAI,aAAa,EAAE,EAAQ,CAC5C,EAAQ,IAAe,EAAU,EAAE,MAAQ,EACjD,EAAQ,KAAK,CAAE,QAAO,MAAO,WAAW,EAAE,KAAM,MAAO,EAAE,MAAO,CAAC,CAInE,IAAM,EAAqB,CACzB,KAAO,GAAM,EAAW,EAAE,CAC1B,WAAY,MAAO,EAAS,IAAY,CACtC,GAAM,CAAE,wBAAyB,MAAM,OAAO,oCAAA,KAAA,GAAA,EAAA,EAAA,CAC9C,MAAM,EAAqB,EAAS,EAAU,EAAQ,MAAM,MAAM,CAAC,OAAO,QAAQ,CAAG,EAAE,CAAE,EAAI,EAG/F,UAAW,KAAO,IAAe,CAC/B,GAAI,EAAoB,EAAO,CAAC,MAAO,CACrC,EAAO,EAAQ,EAAE,kBAAkB,CAAE,EAAQ,EAAE,yBAAyB,CAAC,CACzE,OAEF,MAAM,EAAgB,EAAQ,EAAW,EAE5C,CAGK,EAAW,EAAO,GAMpB,GACG,EAAI,IAAI,4BAA4B,UAAU,EAAS,KAAK,CAOnE,IAAI,EACJ,GAAI,IAAO,oBAAqB,CAC9B,IAAM,EAAa,EAAK,EAAI,QAAS,kBAAkB,CACnD,EAAW,GACf,GAAI,CACF,GAAI,EAAW,EAAW,CAAE,CAC1B,IAAM,EAAM,KAAK,MAAM,EAAa,EAAY,OAAO,CAAC,CACxD,GAAI,EAAI,OAAQ,CACd,EAAW,GACX,IAAM,EAAW,OAAO,EAAI,QAAW,UAAY,EAAI,OAAO,OAAS,GACnE,EAAI,OAAO,MAAM,EAAG,EAAE,CAAG,MAAQ,EAAI,OAAO,MAAM,GAAG,CACrD,OAAO,EAAI,OAAO,CAChB,EAAY,MAAM,QAAQ,EAAI,cAAc,CAAG,EAAI,cAAc,OAAS,EAC1E,EAAe,MAAM,QAAQ,EAAI,cAAc,CAAG,EAAI,cAAc,OAAS,EAC7E,EAAa,EAAO,QAAU,OAAS,KAAO,EAAO,QAAU,aAAe,KAAO,KAC3F,EAAgB,CACd,CAAE,KAAM,MAAO,MAAO,SAAU,MAAO,EAAU,CACjD,CAAE,KAAM,MAAO,MAAO,QAAS,MAAO,EAAY,EAAI,GAAG,EAAU,IAAM,aAAc,KAAM,EAAY,EAAI,IAAA,GAAY,UAAW,CACpI,GAAI,EAAe,EAAI,CAAC,CAAE,KAAM,MAAO,MAAO,WAAY,MAAO,GAAG,EAAa,IAAK,CAAkB,CAAG,EAAE,CAC7G,CAAE,KAAM,MAAO,MAAO,KAAM,MAAO,GAAG,EAAW,MAAM,IAAc,KAAM,QAAS,CACrF,QAGC,EAGH,IACH,EAAgB,CACd,CAAE,KAAM,OAAQ,MAAO,uBAAwB,KAAM,UAAW,CAChE,CAAE,KAAM,MAAO,MAAO,SAAU,MAAO,EAAY,KAAM,QAAS,CACnE,EAEH,EAAQ,KAAK,CAAE,MAAO,OAAQ,MAAO,qBAAsB,MAAO,QAAS,CAAC,CAM9E,IAAM,MAA2B,CAC/B,IAAM,EAAQ,EAAI,IAAI,qBAAqB,MAAM,EAAG,EAAI,CAAE,aAAc,EAAG,aAAc,EAAG,CACtF,EAAO,EAAkB,EAAG,CAClC,MAAO,CACL,YAAa,CACX,aAAc,EAAM,aACpB,aAAc,EAAM,aACpB,UAAW,EAAM,UAClB,CACD,WAAY,EAAK,IAAK,IAAO,CAAE,GAAI,EAAE,GAAI,MAAO,EAAE,MAAO,IAAK,EAAE,IAAK,EAAE,CACxE,EAKG,EAAwC,CAC5C,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,0BAA0B,CAAE,CAC9D,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,yBAAyB,CAAE,MAAO,EAAa,CAC/E,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,0BAA0B,CAAE,MAAO,EAAO,MAAO,CACjF,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,0BAA0B,CAAE,MAAO,EAAW,CAC9E,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,6BAA6B,CAAE,MAAO,EAAY,MAAM,EAAI,IAAK,CACjG,GAAI,EACA,CAAC,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,kCAAkC,CAAE,MAAO,EAAmB,CAAkB,CACjH,EAAE,CACN,CAAE,KAAM,MAAO,MAAO,EAAQ,EAAE,2BAA2B,CAAE,MAAO,EAAU,EAAQ,EAAE,qBAAqB,CAAG,EAAQ,EAAE,sBAAsB,CAAE,KAAM,EAAU,UAAY,QAAS,CACxL,CAIK,EAA+B,EACjC,CAAC,CAAE,KAAM,OAAQ,MAAO,EAAa,QAAS,KAAM,EAAa,OAAS,UAAY,UAAY,QAAS,CAAC,CAC5G,EAAE,CAEN,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,kBACN,MAAO,EAAQ,EAAE,qBAAsB,CAAQ,OAAM,MAAQ,EAAW,UAAY,WAAY,CAAC,CACjG,KAAM,YAAY,EAAO,QACzB,WAAY,CAAE,MAAO,EAAU,EAAQ,EAAE,qBAAqB,CAAG,EAAQ,EAAE,sBAAsB,CAAE,UAAS,CAC5G,WAAY,EACZ,UACA,UAAW,EACP,CAAE,MAAO,EAAS,MAAO,WAAY,EAAS,WAAY,WAAY,EAAS,WAAY,SAAU,EAAI,CACzG,IAAA,GAGJ,gBAAiB,CAAC,GAAG,EAAa,GAAI,GAAiB,EAAsB,CAC7E,GAAG,GAAoB,CAGvB,cAAiB,EAAiB,EAAI,EAAS,EAAM,EAAY,CACjE,SAAU,KAAO,IAAc,CAC7B,GAAI,EAAE,WAAW,WAAW,CAAE,CAC5B,IAAM,EAAM,EAAQ,IAAI,EAAE,MAAM,EAAkB,CAAC,CACnD,GAAI,EACF,GAAI,CACF,MAAM,EAAS,EAAK,EAAK,OAClB,EAAG,CACV,EAAI,EAAQ,EAAE,sBAAuB,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CAAC,CAI9F,EAAiB,EAAI,EAAS,EAAM,EAAY,SACvC,IAAM,QACf,EAAkB,EAAK,EAAO,SACrB,IAAM,UAIf,EAAc,EAAO,IAAI,CACpB,EAAI,IAAI,sBAAsB,CAAC,SAAW,CAC7C,EAAI,IAAI,QAAQ,uBAAuB,EAAI,IAAI,wBAAwB,CAAC,EACxE,CAAC,YAAc,CACf,EAAiB,EAAI,EAAS,EAAM,EAAa,CAC/C,KAAM,UAAW,QAAS,EAAQ,EAAE,sBAAuB,CAAM,KAAI,CAAC,CACvE,CAAC,EACF,SACO,IAAM,SASf,GAAI,CACF,MAAM,EAAI,IAAI,WAAW,WAAW,SAAU,EAAI,CAAC,EAAQ,CAC3D,GAAI,CACF,MAAM,EAAI,IAAI,oBAAoB,CAClC,EAAiB,EAAI,CAAC,EAAS,EAAM,EAAa,CAChD,KAAM,UACN,QAAS,EAAQ,EAAE,iBAAkB,CAAQ,OAAM,KAAO,EAAW,WAAa,UAAW,IAAK,GAAI,CAAC,CACxG,CAAC,OACK,EAAW,CAClB,EAAiB,EAAI,CAAC,EAAS,EAAM,EAAa,CAChD,KAAM,QACN,QAAS,EAAQ,EAAE,4BAA6B,CAC9C,KAAO,EAAW,WAAa,UACzB,OACN,IAAK,aAAqB,MAAQ,EAAU,QAAU,OAAO,EAAU,CACxE,CAAC,CACH,CAAC,QAEG,EAAG,CACV,EAAiB,EAAI,EAAS,EAAM,EAAa,CAC/C,KAAM,QAAS,QAAS,EAAQ,EAAE,sBAAuB,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CAC9G,CAAC,SAEK,IAAM,qBAEf,GAAI,CACF,GAAM,CAAE,qBAAsB,MAAM,OAAO,8BAC3C,MAAM,EAAkB,EAAI,CAC5B,EAAO,oBAAqB,QAAQ,OAC7B,EAAG,CACV,EAAI,qBAAqB,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,GAAG,MAE/D,IAAM,SAEV,EAAI,IACN,sBAAsB,CACtB,KAAM,GAA4B,CACjC,EAAiB,EAAI,EAAS,EAAM,EAAa,CAC/C,KAAM,UAAW,QAAS,EAAQ,EAAE,oBAAqB,CAAM,KAAI,MAAO,EAAE,SAAU,CAAC,CACxF,CAAC,EACF,CACD,MAAO,GAAe,CACrB,EAAiB,EAAI,EAAS,EAAM,EAAa,CAC/C,KAAM,QAAS,QAAS,EAAQ,EAAE,yBAA0B,CAAE,IAAK,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAAE,CAAC,CACjH,CAAC,EACF,CACK,IAAM,aAMf,MAAM,EAAoB,CACxB,KAAM,EACN,KAAM,SACN,MAJA,EAAO,QAAU,WAAa,EAAO,QAAU,aAAe,EAAO,MAAQ,SAK7E,MAEA,WAAc,GAAU,CACzB,CAAC,EAGN,YAAe,GAAU,CAC1B,CAAC,EAGJ,GAAU"}