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

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 (93) 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-D1T16ed-.js} +3 -3
  4. package/dist/auth-command-D1T16ed-.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 +2085 -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-ZyrfHgIk.js +21 -0
  17. package/dist/command-dispatcher-ZyrfHgIk.js.map +1 -0
  18. package/dist/create-host-app-BXQ8bU-B.js +3 -0
  19. package/dist/create-host-app-BXQ8bU-B.js.map +1 -0
  20. package/dist/create-host-app-DXII2WTM.js +2 -0
  21. package/dist/discover-with-bundled-B5d4q___.js +2 -0
  22. package/dist/discover-with-bundled-CF8qFP41.js +3 -0
  23. package/dist/discover-with-bundled-CF8qFP41.js.map +1 -0
  24. package/dist/doctor-CuNvDmyA.js +36 -0
  25. package/dist/doctor-CuNvDmyA.js.map +1 -0
  26. package/dist/{extension-BKXIr86F.js → extension-DLFCUBJr.js} +41 -33
  27. package/dist/extension-DLFCUBJr.js.map +1 -0
  28. package/dist/extension-I87MkZYp.js +2 -0
  29. package/dist/feedback-B-d-GV2J.js +2 -0
  30. package/dist/feedback-DuTIx3-K.js +21 -0
  31. package/dist/feedback-DuTIx3-K.js.map +1 -0
  32. package/dist/{feishu-setup-D6vnuNLw.js → feishu-setup-B6ETf-8Y.js} +2 -2
  33. package/dist/{feishu-setup-D6vnuNLw.js.map → feishu-setup-B6ETf-8Y.js.map} +1 -1
  34. package/dist/ftue-doctor-recovery-DeAf8jVo.js +3 -0
  35. package/dist/ftue-doctor-recovery-DeAf8jVo.js.map +1 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +10 -12
  38. package/dist/index.js.map +1 -1
  39. package/dist/observe-CZ96Vre9.js +83 -0
  40. package/dist/observe-CZ96Vre9.js.map +1 -0
  41. package/dist/parse-cli-CqocMnXY.js +3 -0
  42. package/dist/parse-cli-CqocMnXY.js.map +1 -0
  43. package/dist/persistence-CTXa-OyC.js +85 -0
  44. package/dist/persistence-CTXa-OyC.js.map +1 -0
  45. package/dist/plugin-assembly-check-C2TephMb.js +3 -0
  46. package/dist/plugin-assembly-check-C2TephMb.js.map +1 -0
  47. package/dist/registry-BOX7DaxS.js +2 -0
  48. package/dist/{registry-D0NONjLv.js → registry-BdCcwyGy.js} +2 -2
  49. package/dist/registry-BdCcwyGy.js.map +1 -0
  50. package/dist/{run-attach-Ci5jYyCa.js → run-attach-CvGCWF_i.js} +2 -2
  51. package/dist/{run-attach-Ci5jYyCa.js.map → run-attach-CvGCWF_i.js.map} +1 -1
  52. package/dist/{serve-pVR6UIT9.js → serve-Dbn3THez.js} +3 -3
  53. package/dist/serve-Dbn3THez.js.map +1 -0
  54. package/dist/{service-client-BLNeugM9.js → service-client-Cr4sxDVM.js} +1 -1
  55. package/dist/{service-client-BLNeugM9.js.map → service-client-Cr4sxDVM.js.map} +1 -1
  56. package/dist/{version-C0qM-qJM.js → version-Cq2kcrXL.js} +2 -2
  57. package/dist/{version-C0qM-qJM.js.map → version-Cq2kcrXL.js.map} +1 -1
  58. package/package.json +5 -5
  59. package/dist/auth-command-CXAB3_hs.js.map +0 -1
  60. package/dist/build-id-BX4QHzM0.js +0 -3
  61. package/dist/build-id-BX4QHzM0.js.map +0 -1
  62. package/dist/command-dispatcher-Vb_AJSAR.js +0 -13
  63. package/dist/command-dispatcher-Vb_AJSAR.js.map +0 -1
  64. package/dist/debug-CiosFwme.js +0 -34
  65. package/dist/debug-CiosFwme.js.map +0 -1
  66. package/dist/discover-with-bundled-CznLjkkw.js +0 -3
  67. package/dist/discover-with-bundled-CznLjkkw.js.map +0 -1
  68. package/dist/doctor-C84-hhEr.js +0 -30
  69. package/dist/doctor-C84-hhEr.js.map +0 -1
  70. package/dist/extension-BKXIr86F.js.map +0 -1
  71. package/dist/extension-CnON6Xum.js +0 -2
  72. package/dist/extension-plugin-Bmqlc6as.js +0 -2
  73. package/dist/extension-plugin-C2Yhv77x.js +0 -7
  74. package/dist/extension-plugin-C2Yhv77x.js.map +0 -1
  75. package/dist/feedback-BES_6ebi.js +0 -21
  76. package/dist/feedback-BES_6ebi.js.map +0 -1
  77. package/dist/feedback-BRbLUn9u.js +0 -2
  78. package/dist/model-auth-DUPFPOjY.js +0 -2
  79. package/dist/model-auth-qY5zeBF7.js +0 -3
  80. package/dist/model-auth-qY5zeBF7.js.map +0 -1
  81. package/dist/observe-CVg-MRM2.js +0 -32
  82. package/dist/observe-CVg-MRM2.js.map +0 -1
  83. package/dist/parse-cli-BCfFgFAC.js +0 -3
  84. package/dist/parse-cli-BCfFgFAC.js.map +0 -1
  85. package/dist/persistenced-CCwHu1Ww.js +0 -36
  86. package/dist/persistenced-CCwHu1Ww.js.map +0 -1
  87. package/dist/registry-B3nfN62q.js +0 -2
  88. package/dist/registry-D0NONjLv.js.map +0 -1
  89. package/dist/remote-persistence-server-B_hONoVR.js +0 -35
  90. package/dist/remote-persistence-server-B_hONoVR.js.map +0 -1
  91. package/dist/serve-pVR6UIT9.js.map +0 -1
  92. package/dist/time-travel-ZozuPPI-.js +0 -21
  93. package/dist/time-travel-ZozuPPI-.js.map +0 -1
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import{t as e}from"./bundled-extensions-C3n8jg20.js";import{createApp as t}from"@x-otto/coding";function n(){let t=e();return{pluginBundledDirs:t?[t]:[]}}function r(e){return t({...n(),...e})}export{n,r as t};
3
+ //# sourceMappingURL=create-host-app-BXQ8bU-B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-host-app-BXQ8bU-B.js","names":[],"sources":["../src/create-host-app.ts"],"sourcesContent":["/**\n * create-host-app.ts —— RFC-364 M1:cli 侧**唯一**的 App 装配入口。\n *\n * ## 要解决的问题\n * `createApp()` 有一类参数属于「**宿主自描述**」——otto 这个发布物自己带了什么、\n * 布局长什么样(当前是 `pluginBundledDirs`,即随 cli 分发的内置扩展目录)。这类信息\n * 调用方无从得知、也不该关心,但它此前是每个调用点都必须**记得传**的可选参数。\n *\n * 后果(RFC-364 §1.2 实测):RFC-341 引入 bundled 层时只接线了 `cli.ts` 一处,\n * `serve`/`feedback`/`auth` 三处全漏,表现为**静默少装插件**:\n *\n * ```\n * [serve 方式] createApp({workspaceDir}) → activePlugins: 12, bundled: []\n * [cli 方式] createApp({..., pluginBundledDirs}) → activePlugins: 13, bundled: ['plugin-skill-inductor']\n * ```\n *\n * 纯 npm 安装环境下更严重:任何 `dependsOn` bundled 插件的第三方 provider 插件会被\n * 拓扑排序判定 missingDeps 并 fail-closed 剔除。\n *\n * ## 为什么是「收敛装配点」而不是「把知识搬到下游包」\n * 初版方案曾想把 `bundledExtensionsDir()` 下沉到 `@x-otto/env`(coding 与 cli 的共同\n * 依赖),已**实测证伪**:该函数靠 `import.meta.url` 相对定位 cli 自己的 dist,而 env\n * 与 cli 的物理相对关系两种形态下都不固定——\n * pnpm workspace: packages/cli/node_modules/@x-otto/env -> ../../../env(符号链接)\n * npm 安装形态 : node_modules/@x-otto/env(hoist,与 cli/dist 无固定相对关系)\n * 即「宿主发布物布局」这个知识**只有 cli 有资格拥有**,搬到任何下游包都会把确定的 bug\n * 换成更隐蔽的路径 bug。所以正解是收敛**装配点**,而非搬迁**知识**。\n *\n * ## 契约(RFC-364 §4 规则 1)\n * 宿主自描述参数不得做成调用方必传参数。cli 内部**任何**需要 App 的地方都应调用本函数,\n * 不得直接 `createApp()`——新增入口默认就是对的,而不是\"新增一次漏传机会\"。\n * 有架构门禁测试钉住这条(`create-host-app-sole-assembly.test.ts`)。\n */\nimport { createApp } from '@x-otto/coding'\n\nimport { bundledExtensionsDir } from './bundled-extensions'\n\nimport type { App, AppOptions } from '@x-otto/coding'\n\n/**\n * 宿主自描述参数——「otto 这个发布物自己带了什么」。\n *\n * 单独导出而非内联,是因为 engine 子进程(`coding/src/engine-main.ts`,在 coding 包内、\n * 拿不到 cli 的 dist 布局)需要由父进程把该值显式下传(RFC-364 T364-03)。\n */\nexport function hostSelfDescribingOptions(): Pick<AppOptions, 'pluginBundledDirs'> {\n const dir = bundledExtensionsDir()\n return { pluginBundledDirs: dir ? [dir] : [] }\n}\n\n/**\n * 装配一个 App:调用方给业务参数,宿主自描述参数由本函数补齐。\n *\n * 调用方**可以**显式覆盖 `pluginBundledDirs`(测试隔离、非常规部署),\n * 显式传入时以调用方为准——保留逃生舱,但缺省不再是\"什么都没有\"。\n */\nexport function createHostApp(options: AppOptions): App {\n return createApp({\n ...hostSelfDescribingOptions(),\n ...options,\n })\n}\n"],"mappings":";gGA6CA,SAAgB,GAAmE,CACjF,IAAM,EAAM,GAAsB,CAClC,MAAO,CAAE,kBAAmB,EAAM,CAAC,EAAI,CAAG,EAAE,CAAE,CAShD,SAAgB,EAAc,EAA0B,CACtD,OAAO,EAAU,CACf,GAAG,GAA2B,CAC9B,GAAG,EACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{n as e}from"./create-host-app-BXQ8bU-B.js";export{e as hostSelfDescribingOptions};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{t as e}from"./discover-with-bundled-CF8qFP41.js";export{e as discoverPluginsWithBundled};
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import{t as e}from"./bundled-extensions-C3n8jg20.js";import{discoverPlugins as t}from"@x-otto/coding";function n(n){let r=e();return t({...n,bundledDirs:r?[r]:[]})}export{n as t};
3
+ //# sourceMappingURL=discover-with-bundled-CF8qFP41.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover-with-bundled-CF8qFP41.js","names":[],"sources":["../src/discover-with-bundled.ts"],"sourcesContent":["/**\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":";sGAcA,SAAgB,EAA2B,EAAkD,CAC3F,IAAM,EAAU,GAAsB,CACtC,OAAO,EAAgB,CACrB,GAAG,EACH,YAAa,EAAU,CAAC,EAAQ,CAAG,EAAE,CACtC,CAAC"}
@@ -0,0 +1,36 @@
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 k(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`?D(n,e):O(n,e),0}catch(e){return r(`otto doctor: ${e instanceof Error?e.message:String(e)}\n`),1}}async function v(){let e=await y(),t=await b(),n=await C(),r=await w(),{plugins:i,providerPluginDiscovered:a}=await x();return{environment:e,configuration:t,state:n,connectivity:r,plugins:i,ftue:await S(a)}}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 T(`rg`),hasGit:await T(`git`),authMode:e,residencyBudget:{totalBytes:h,minPerSessionBytes:p,maxPerSessionBytes:f,historyMessages:d,resumeWindowMessages:m}}}async function x(){try{let{discoverPluginsWithBundled:e}=await import(`./discover-with-bundled-B5d4q___.js`),{hostSelfDescribingOptions:t}=await import(`./create-host-app-DXII2WTM.js`),{checkPluginAssembly:n,formatAssemblyGap:i}=await import(`./plugin-assembly-check-C2TephMb.js`),a=e({cwd:process.cwd(),homedir:r()}),o=t().pluginBundledDirs??[],s=a.some(e=>e.manifest.capabilities?.includes(`provider`)),c=a.filter(e=>e.scope===`bundled`).length>0&&o.length===0,l=n({discovered:a.map(e=>({id:e.id,scope:e.scope})),active:c?a.filter(e=>e.scope!==`bundled`).map(e=>({id:e.id,scope:e.scope})):a.map(e=>({id:e.id,scope:e.scope}))});return{plugins:{discovered:a.length,gaps:i(l),missing:l.missingFromRuntime},providerPluginDiscovered:s}}catch{return{plugins:void 0,providerPluginDiscovered:!1}}}async function S(e){try{let{createDefaultAuthStore:t}=await import(`@x-otto/ai`),{deriveDoctorFtueGuidance:n}=await import(`./ftue-doctor-recovery-DeAf8jVo.js`),{SettingsManager:r}=await import(`@x-otto/setting`),{OTTO_CONFIG_FILENAME:i}=await import(`@x-otto/env`),a=t({watchForExternalChanges:!1});await a.ensureInitialized();let o=a.listProviders().length>0,c=new r({projectConfigPath:s(g,i)});return await c.load(),n({providerPluginDiscovered:e,hasAnyCredential:o,firstPromptSubmitted:typeof c.get(`first_prompt_submitted_at`)==`number`})}catch{return}}async function C(){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:E(e),...t}}finally{n.close()}}catch{return{dbPath:e,dbExists:!0,integrity:`error (unable to open)`}}}async function w(){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 T(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 E(e){try{return u(e).size}catch{return 0}}function D(e,t){let{environment:n,configuration:r,state:i,connectivity:a}=t,o=i?.dbExists&&i.integrity===`ok`?`✅`:`❌`,s=a.providerReachable?`✅`:`❌`,c=t.plugins&&t.plugins.gaps.length>0?` ⚠ 插件装配缺口 ${t.plugins.missing.length} 项(otto doctor 查看详情)`:``,l=t.ftue&&t.ftue.status!==`looks-configured`?` ⚠ ${t.ftue.summary} → ${t.ftue.recoveryAction}`:``;e([`otto doctor — ${n.runtime} | ${r.storageDir} | DB:${o} ${s}`,i?` ${i.snapshots??`?`} snapshots, ${i.changeLogEntries??`?`} change_log`:``,c,l].filter(Boolean).join(`
4
+ `)+`
5
+ `)}function O(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: ${A(n.memory.free)} / ${A(n.memory.total)} free\n`),r.residencyBudget){let t=r.residencyBudget;e(` Residency: budget=${A(t.totalBytes)} per-session=${A(t.minPerSessionBytes)}~${A(t.maxPerSessionBytes)} history=${t.historyMessages} resume=${t.resumeWindowMessages}\n`)}if(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`),t.plugins)if(e(`
10
+ Plugins
11
+ `),e(` Discovered: ${t.plugins.discovered}\n`),t.plugins.gaps.length>0){e(` Assembly: ❌ 检测到装配缺口
12
+ `);for(let n of t.plugins.gaps)e(` ${n}\n`)}else e(` Assembly: ✅
13
+ `);if(i&&(e(`
14
+ State
15
+ `),e(` SQLite DB: ${i.dbPath}\n`),i.dbExists?(e(` size: ${A(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)
16
+ `)),e(`
17
+ Connectivity
18
+ `),e(` ${a.providerHost}: ${a.providerReachable?`reachable (${a.providerLatency}ms) ✅`:`unreachable ❌`}\n`),e(` storage host: ${a.storageHostConfigured?`configured`:`not configured (local only)`}\n`),t.ftue){let n=t.ftue.status===`looks-configured`?`✅`:`⚠`;e(`
19
+ First-time setup
20
+ `),e(` ${n} ${t.ftue.summary}\n`),e(` → ${t.ftue.recoveryAction}\n`)}e(`
21
+ `)}function k(e){e(`otto doctor — 环境健康诊断
22
+
23
+ 用法:
24
+ otto doctor [--summary|--json]
25
+
26
+ 选项:
27
+ --summary 紧凑输出(一行摘要)
28
+ --json 结构化 JSON(redacted)
29
+ --help, -h 帮助信息
30
+
31
+ 示例:
32
+ otto doctor
33
+ otto doctor --summary
34
+ otto doctor --json
35
+ `)}function A(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};
36
+ //# sourceMappingURL=doctor-CuNvDmyA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-CuNvDmyA.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 /** RFC-364 M2:插件装配一致性——发现得到却未装载的缺口。 */\n plugins?: PluginsSection\n /** RFC-326 M6-01:FTUE 环境自检(廉价事实推导,非权威 readiness 判定,见模块头注释)。 */\n ftue?: import('./ftue/ftue-doctor-recovery').DoctorFtueGuidance\n}\n\ninterface PluginsSection {\n discovered: number\n /** 装配缺口的可执行诊断行(空数组 = 无缺口)。 */\n gaps: string[]\n /** 缺口涉及的插件 id(供 --json 消费方精确处理)。 */\n missing: Array<{ id: string; scope?: string }>\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 const { plugins, providerPluginDiscovered } = await collectPluginsAndProviderFlag()\n const ftue = await collectFtueGuidance(providerPluginDiscovered)\n\n return { environment: env, configuration: config, state, connectivity, plugins, ftue }\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\n/**\n * RFC-364 M2:插件装配一致性自检。\n *\n * 判据是「cli 侧完整发现(含 bundled 层)」与「App 实际装载」的差集。但 doctor 是\n * 轻量诊断命令(RFC-150 D3 明确要求不承担重依赖),构造真实 App 代价过高——\n * 故此处只做**发现侧**的自检:确认 bundled 目录可解析且其中的扩展能被发现。\n *\n * 真正的「发现 vs 装载」全量比对由运行时侧承担(App 启动路径),本处提供的是\n * 用户可主动运行的诊断入口:当用户报告\"插件不见了\"时,`otto doctor` 能立刻回答\n * \"otto 这一侧到底看见了哪些\"。\n */\n/**\n * RFC-326 M6-01:与 `providerPluginDiscovered` 标志合并计算——`discoverPluginsWithBundled`\n * 是本函数唯一的重量级调用,`collectFtueGuidance` 需要同一份发现结果的 `provider`\n * capability 过滤,若各自重新扫描一次会重复触发文件系统遍历。`providerPluginDiscovered`\n * 在扫描失败时缺省 `false`(诊断失败不阻断其余部分,与既有 fail-soft 语义一致;\n * FTUE 分支据此会诚实报告\"未发现 provider 插件\"而非静默跳过整个 ftue 字段)。\n */\nasync function collectPluginsAndProviderFlag(): Promise<{\n plugins: PluginsSection | undefined\n providerPluginDiscovered: boolean\n}> {\n try {\n const { discoverPluginsWithBundled } = await import('../discover-with-bundled')\n const { hostSelfDescribingOptions } = await import('../create-host-app')\n const { checkPluginAssembly, formatAssemblyGap } = await import('../plugin-assembly-check')\n\n const discovered = discoverPluginsWithBundled({ cwd: process.cwd(), homedir: homedir() })\n const bundledDirs = hostSelfDescribingOptions().pluginBundledDirs ?? []\n const providerPluginDiscovered = discovered.some((p) => p.manifest.capabilities?.includes('provider'))\n\n // 宿主自描述参数解析不出目录,但发现层却报告了 bundled 作用域插件(或反之)——\n // 这正是 M1 修的那类装配漂移,在此处显式暴露。\n const bundledSeen = discovered.filter((p) => p.scope === 'bundled')\n const assemblyBroken = bundledSeen.length > 0 && bundledDirs.length === 0\n\n const result = checkPluginAssembly({\n discovered: discovered.map((p) => ({ id: p.id, scope: p.scope })),\n // 发现侧自检:把「宿主参数解析失败」建模为\"全部 bundled 未装载\"\n active: assemblyBroken\n ? discovered.filter((p) => p.scope !== 'bundled').map((p) => ({ id: p.id, scope: p.scope }))\n : discovered.map((p) => ({ id: p.id, scope: p.scope })),\n })\n\n return {\n plugins: {\n discovered: discovered.length,\n gaps: formatAssemblyGap(result),\n missing: result.missingFromRuntime,\n },\n providerPluginDiscovered,\n }\n } catch {\n // 诊断失败不阻断 doctor 其余部分(与既有 collectState 的 fail-soft 一致)。\n return { plugins: undefined, providerPluginDiscovered: false }\n }\n}\n\n/**\n * RFC-326 M6-01:FTUE 环境自检——三个廉价事实推导诊断状态(见\n * `ftue-doctor-recovery.ts` 头注释的判据边界)。`AuthStore` 构造零 App 依赖\n * (默认读全局 `AUTH_PATH`),`listProviders()` 只读本地文件/env,不发网络请求。\n */\nasync function collectFtueGuidance(\n providerPluginDiscovered: boolean,\n): Promise<import('./ftue/ftue-doctor-recovery').DoctorFtueGuidance | undefined> {\n try {\n const { createDefaultAuthStore } = await import('@x-otto/ai')\n const { deriveDoctorFtueGuidance } = await import('./ftue/ftue-doctor-recovery')\n const { SettingsManager } = await import('@x-otto/setting')\n const { OTTO_CONFIG_FILENAME } = await import('@x-otto/env')\n\n // watchForExternalChanges:false —— doctor 是一次性命令,跑完即退出,不需要\n // fs.watch(同 auth-command.ts 的既有约定,见其头注释)。\n const authStore = createDefaultAuthStore({ watchForExternalChanges: false })\n // listProviders() 读内存缓存(this.cache),不像 getAuthSource/hasCredential 那样\n // 自带 ensureInitialized() 前置——首次调用前必须显式触发一次磁盘加载,否则读到空态。\n await authStore.ensureInitialized()\n const hasAnyCredential = authStore.listProviders().length > 0\n\n const settings = new SettingsManager({ projectConfigPath: join(OTTO_DIR, OTTO_CONFIG_FILENAME) })\n await settings.load()\n const firstPromptSubmitted = typeof settings.get('first_prompt_submitted_at') === 'number'\n\n return deriveDoctorFtueGuidance({ providerPluginDiscovered, hasAnyCredential, firstPromptSubmitted })\n } catch {\n // 诊断失败不阻断 doctor 其余部分。\n return undefined\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 const pluginGap = report.plugins && report.plugins.gaps.length > 0\n ? ` ⚠ 插件装配缺口 ${report.plugins.missing.length} 项(otto doctor 查看详情)`\n : ''\n // RFC-326 M6-01:只在未配置(非 looks-configured)时提示——已配置用户不需要看\n // 这行噪音,未配置用户能立刻看到下一步命令,不必翻到 detailed 输出。\n const ftueHint = report.ftue && report.ftue.status !== 'looks-configured'\n ? ` ⚠ ${report.ftue.summary} → ${report.ftue.recoveryAction}`\n : ''\n stdout([\n `otto doctor — ${env.runtime} | ${config.storageDir} | DB:${dbOk} ${netOk}`,\n state ? ` ${state.snapshots ?? '?'} snapshots, ${state.changeLogEntries ?? '?'} change_log` : '',\n pluginGap,\n ftueHint,\n ].filter(Boolean).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 // RFC-364 M2:插件装配一致性——有缺口才打印(无缺口时不制造噪音)。\n if (report.plugins) {\n stdout('\\nPlugins\\n')\n stdout(` Discovered: ${report.plugins.discovered}\\n`)\n if (report.plugins.gaps.length > 0) {\n stdout(' Assembly: ❌ 检测到装配缺口\\n')\n for (const line of report.plugins.gaps) stdout(` ${line}\\n`)\n } else {\n stdout(' Assembly: ✅\\n')\n }\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\n // RFC-326 M6-01:FTUE 环境自检——廉价事实推导,非权威 readiness 判定\n // (见 ftue-doctor-recovery.ts 头注释;权威判定只在 TUI 运行时的 FtueController)。\n if (report.ftue) {\n const icon = report.ftue.status === 'looks-configured' ? '✅' : '⚠'\n stdout('\\nFirst-time setup\\n')\n stdout(` ${icon} ${report.ftue.summary}\\n`)\n stdout(` → ${report.ftue.recoveryAction}\\n`)\n }\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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{t as e}from"./discover-with-bundled-CznLjkkw.js";import{homedir as t}from"node:os";import{InstallManager as n,checkUpgrades as r,compilePluginModule as i,compilePluginPanels as a,defaultPostInstallExecutor as o,findWorkspaceRoot as s,grantPluginCapabilities as c,installPluginFromDir as l,installPluginFromPackage as u,purgePluginState as d,readHighRiskCapabilities as f,recordPluginInstalled as p,resolvePackageDir as m,scanInstalledVersions as h,trustPlugin as g}from"@x-otto/coding";import{OTTO_CONFIG_FILENAME as _,OTTO_HOME as v}from"@x-otto/env";import{join as y,resolve as b,sep as ee}from"node:path";import{existsSync as x,lstatSync as te,mkdirSync as S,readFileSync as C,readlinkSync as w,symlinkSync as T,watch as E,writeFileSync as D}from"node:fs";import{createInterface as O}from"node:readline/promises";import k from"react";import{ConfirmDialog as A,runStandaloneTui as j}from"@x-otto/tui/standalone";import{SettingsManager as M}from"@x-otto/setting";const N=/^[a-z0-9]+(?:-[a-z0-9]+)*$/;function ne(e,t={}){let n={commands:!0,panels:[{id:`status`,title:`${e} status`,entry:`panels-dist/status.mjs`}]},r=[];t.withProvider&&(n.providers=[{id:`example`,baseUrl:`https://api.example.com/v1`,wireApi:`openai-completions`,envKey:`EXAMPLE_API_KEY`,models:[{id:`example/gpt-tiny`,contextWindow:8192,maxOutput:512}]}],r.push(`provider`)),t.withRenderer&&(n.renderers=[{id:`screenshot`,matcher:{toolName:`browser_screenshot`},entry:`renderers-dist/screenshot.js`}],r.push(`tui.renderer`)),t.withI18n!==!1&&(n.i18nDir=`locale`);let i={id:e,name:e,version:`0.1.0`,description:`An otto extension: ${e}`,contributes:n,scripts:{postinstall:`otto extension build`}};return r.length&&(i.capabilities=r),`${JSON.stringify(i,null,2)}\n`}function re(e){return`${JSON.stringify({"hello.description":`Say hello from the ${e} plugin`},null,2)}\n`}function ie(e){return`${JSON.stringify({"hello.description":`从 ${e} 插件说 hello`},null,2)}\n`}function ae(e){return`/**
2
+ import{t as e}from"./discover-with-bundled-CF8qFP41.js";import{homedir as t}from"node:os";import{HIGH_RISK_CAPABILITIES as n,InstallManager as r,checkUpgrades as i,compilePluginModule as a,compilePluginPanels as o,defaultPostInstallExecutor as s,evaluatePluginTrust as c,findWorkspaceRoot as l,grantPluginCapabilities as u,installPluginFromDir as d,installPluginFromPackage as f,purgePluginState as p,readHighRiskCapabilities as m,recordPluginInstalled as h,resolvePackageDir as g,scanInstalledVersions as _,trustPlugin as v,untrustPlugin as y}from"@x-otto/coding";import{OTTO_CONFIG_FILENAME as b,OTTO_HOME as x}from"@x-otto/env";import{join as S,resolve as C,sep as ee}from"node:path";import{existsSync as w,lstatSync as te,mkdirSync as T,readFileSync as E,readlinkSync as D,symlinkSync as O,watch as k,writeFileSync as A}from"node:fs";import{createInterface as j}from"node:readline/promises";import ne from"react";import{ConfirmDialog as re,runStandaloneTui as ie}from"@x-otto/tui/standalone";import{SettingsManager as M}from"@x-otto/setting";const N=/^[a-z0-9]+(?:-[a-z0-9]+)*$/;function ae(e,t={}){let n={commands:!0,panels:[{id:`status`,title:`${e} status`,entry:`panels-dist/status.mjs`}]},r=[];t.withProvider&&(n.providers=[{id:`example`,baseUrl:`https://api.example.com/v1`,wireApi:`openai-completions`,envKey:`EXAMPLE_API_KEY`,models:[{id:`example/gpt-tiny`,contextWindow:8192,maxOutput:512}]}],r.push(`provider`)),t.withRenderer&&(n.renderers=[{id:`screenshot`,matcher:{toolName:`browser_screenshot`},entry:`renderers-dist/screenshot.js`}],r.push(`tui.renderer`)),t.withI18n!==!1&&(n.i18nDir=`locale`);let i={id:e,name:e,version:`0.1.0`,description:`An otto extension: ${e}`,contributes:n,scripts:{postinstall:`otto extension build`}};return r.length&&(i.capabilities=r),`${JSON.stringify(i,null,2)}\n`}function oe(e){return`${JSON.stringify({"hello.description":`Say hello from the ${e} plugin`},null,2)}\n`}function se(e){return`${JSON.stringify({"hello.description":`从 ${e} 插件说 hello`},null,2)}\n`}function ce(e){return`/**
3
3
  * ${e} — definePlugin 工具示例(RFC-105 D4)。
4
4
  * 需 manifest 声明 capabilities: ["tools"] 并经信任门后才加载执行。
5
5
  * 运行 otto extension build → 编译 → /reload 生效。
@@ -27,7 +27,7 @@ export default definePlugin((ctx) => ({
27
27
  },
28
28
  ],
29
29
  }))
30
- `}function oe(e){return`/**
30
+ `}function le(e){return`/**
31
31
  * ${e} screenshot renderer — 自定义 browser_screenshot 工具结果的渲染方式。
32
32
  * 签名:(message: unknown, columns: number) => string[]
33
33
  * 返回 ANSI 着色字符串数组,与 renderMessageLines 同构(不引入 React)。
@@ -41,14 +41,14 @@ module.exports = function screenshotRenderer(message: unknown, columns: number):
41
41
  ' use /extension → ${e} → open panel → preview',
42
42
  ]
43
43
  }
44
- `}function se(e){return`---
44
+ `}function ue(e){return`---
45
45
  description: Say hello from the ${e} plugin
46
46
  argument-hint: [your name]
47
47
  ---
48
48
  Hello from the **${e}** plugin! 👋
49
49
 
50
50
  You passed: $ARGUMENTS
51
- `}function P(e){return`import React from 'react'
51
+ `}function de(e){return`import React from 'react'
52
52
  import { Box, Text } from 'ink'
53
53
 
54
54
  /**
@@ -65,7 +65,7 @@ export default function StatusPanel(): React.ReactElement {
65
65
  </Box>
66
66
  )
67
67
  }
68
- `}function F(e){return`# ${e}
68
+ `}function P(e){return`# ${e}
69
69
 
70
70
  一个 otto 插件骨架(RFC-082 Tier 0:纯文件,零编译即可用)。
71
71
 
@@ -95,31 +95,31 @@ otto extension dev --link <workspace> # 链接进工作区 + 监视改动(
95
95
 
96
96
  改命令/agent/skill/manifest 后,在 otto 内 \`/reload\` 即生效。
97
97
  (面板 \`panels/*.tsx\` 的热编译待 esbuild 依赖接线,当前需手动编译为 \`panels-dist/*.js\`。)
98
- `}function I(e,t,n={}){if(!N.test(e))throw Error(`插件名须 kebab-case(小写字母/数字/连字符,首尾非连字符):${e}`);let r=y(t,e);if(x(r))throw Error(`目录已存在,拒绝覆盖:${r}`);S(y(r,`commands`),{recursive:!0}),S(y(r,`panels`),{recursive:!0});let i=[],a=(e,t)=>{S(y(r,e,`..`),{recursive:!0}),D(y(r,e),t),i.push(e)};return a(`otto-plugin.json`,ne(e,n)),a(`commands/hello.md`,se(e)),a(`panels/status.tsx`,P(e)),a(`.gitignore`,`panels-dist/
98
+ `}function F(e,t,n={}){if(!N.test(e))throw Error(`插件名须 kebab-case(小写字母/数字/连字符,首尾非连字符):${e}`);let r=S(t,e);if(w(r))throw Error(`目录已存在,拒绝覆盖:${r}`);T(S(r,`commands`),{recursive:!0}),T(S(r,`panels`),{recursive:!0});let i=[],a=(e,t)=>{T(S(r,e,`..`),{recursive:!0}),A(S(r,e),t),i.push(e)};return a(`otto-plugin.json`,ae(e,n)),a(`commands/hello.md`,ue(e)),a(`panels/status.tsx`,de(e)),a(`.gitignore`,`panels-dist/
99
99
  renderers-dist/
100
100
  plugin-dist/
101
101
  dist/
102
102
  bin/
103
- `),n.withTool&&a(`plugin.ts`,ae(e)),n.withRenderer&&(S(y(r,`renderers`),{recursive:!0}),a(`renderers/screenshot.ts`,oe(e))),n.withI18n!==!1&&(S(y(r,`locale`),{recursive:!0}),a(`locale/en.json`,re(e)),a(`locale/zh.json`,ie(e))),a(`README.md`,F(e)),{dir:r,files:i}}function L(e,t){let n=e.find(e=>!e.startsWith(`-`));if(!n)return t.stderr(`用法: otto extension create <name> [--with-tool] [--with-provider] [--with-renderer] [--no-i18n]
104
- `),1;let r={withTool:e.includes(`--with-tool`),withProvider:e.includes(`--with-provider`),withRenderer:e.includes(`--with-renderer`),withI18n:!e.includes(`--no-i18n`)};try{let{files:e}=I(n,process.cwd(),r);t.stdout(`✓ 已创建插件 ${n}/\n`);for(let r of e)t.stdout(` ${n}/${r}\n`);let i=[`cp -r ${n} <workspace>/.otto/plugins/ # 装入工作区`,`# 在 otto 内 /reload,然后运行 /hello world`,`# 项目级插件含可执行面需 /trust plugin ${n}`];return r.withTool&&i.push(`# plugin.ts 编译/加载需信任门后 —— otto extension build → /reload`),r.withProvider&&i.push(`# 声明式 provider 无需编译,manifest 已含示例 —— /reload 后即生效`),r.withRenderer&&i.push(`# renderer 源码在 renderers/,需编译:otto extension build → /reload`),r.withI18n&&i.push(`# locale/*.json 无需编译,manifest 已声明 i18nDir —— /reload 后即生效`),t.stdout(`
103
+ `),n.withTool&&a(`plugin.ts`,ce(e)),n.withRenderer&&(T(S(r,`renderers`),{recursive:!0}),a(`renderers/screenshot.ts`,le(e))),n.withI18n!==!1&&(T(S(r,`locale`),{recursive:!0}),a(`locale/en.json`,oe(e)),a(`locale/zh.json`,se(e))),a(`README.md`,P(e)),{dir:r,files:i}}function I(e,t){let n=e.find(e=>!e.startsWith(`-`));if(!n)return t.stderr(`用法: otto extension create <name> [--with-tool] [--with-provider] [--with-renderer] [--no-i18n]
104
+ `),1;let r={withTool:e.includes(`--with-tool`),withProvider:e.includes(`--with-provider`),withRenderer:e.includes(`--with-renderer`),withI18n:!e.includes(`--no-i18n`)};try{let{files:e}=F(n,process.cwd(),r);t.stdout(`✓ 已创建插件 ${n}/\n`);for(let r of e)t.stdout(` ${n}/${r}\n`);let i=[`cp -r ${n} <workspace>/.otto/plugins/ # 装入工作区`,`# 在 otto 内 /reload,然后运行 /hello world`,`# 项目级插件含可执行面需 /trust plugin ${n}`];return r.withTool&&i.push(`# plugin.ts 编译/加载需信任门后 —— otto extension build → /reload`),r.withProvider&&i.push(`# 声明式 provider 无需编译,manifest 已含示例 —— /reload 后即生效`),r.withRenderer&&i.push(`# renderer 源码在 renderers/,需编译:otto extension build → /reload`),r.withI18n&&i.push(`# locale/*.json 无需编译,manifest 已声明 i18nDir —— /reload 后即生效`),t.stdout(`
105
105
  下一步:
106
106
  `+i.join(`
107
107
  `)+`
108
- `),0}catch(e){return t.stderr(`otto extension create 失败: ${e.message}\n`),1}}function R(e,t){let{compiled:n,errors:r,skipped:i}=a(e);if(i)return t.stdout(` (无 panels/*.tsx,跳过面板编译)
109
- `),0;for(let e of n)t.stdout(` ✓ 编译 ${e}\n`);for(let e of r)t.stderr(` ✗ ${e.entry}: ${e.message}\n`);return r.length}async function z(e,t){try{return await i(e)&&t.stdout(` ✓ 编译 ${y(`plugin-dist`,`plugin.cjs`)}\n`),0}catch(e){return t.stderr(` ✗ plugin.ts: ${e.message}\n`),1}}function B(e,t){let n=b(V(e,`--dir`)??process.cwd());return x(y(n,`otto-plugin.json`))?(t.stdout(`▶ otto extension build:${n}\n`),R(n,t)>0?1:0):(t.stderr(`otto extension build 失败: 未找到 otto-plugin.json:${n}\n`),1)}function V(e,t){let n=e.indexOf(t);return n>=0&&n+1<e.length?e[n+1]:void 0}function H(e){let t=y(e,`otto-plugin.json`);if(!x(t))throw Error(`未找到 otto-plugin.json:${e}(请在插件目录内运行,或用 --dir <path>)`);let n;try{n=JSON.parse(C(t,`utf-8`))}catch{throw Error(`otto-plugin.json 非法 JSON:${t}`)}let r=n?.id;if(typeof r!=`string`||!N.test(r))throw Error(`otto-plugin.json 缺少合法 id(kebab-case):${t}`);return r}function U(e,t,n){let r=y(n,`.otto`,`plugins`);S(r,{recursive:!0});let i=y(r,t);if(x(i)||W(i)){if(W(i))try{if(b(w(i))===b(e))return i}catch{}throw Error(`目标已存在且非本插件 dev 链接,拒绝覆盖:${i}`)}return T(b(e),i),i}function W(e){try{return te(e).isSymbolicLink()}catch{return!1}}function G(e,t){return`[${t}] ${e} 改动 → 在 otto 内 /reload 生效\n`}function ce(e){return[e.getHours(),e.getMinutes(),e.getSeconds()].map(e=>String(e).padStart(2,`0`)).join(`:`)}async function le(e,t){let n=b(V(e,`--dir`)??process.cwd()),r;try{r=H(n)}catch(e){return t.stderr(`otto extension dev 失败: ${e.message}\n`),Promise.resolve(1)}t.stdout(`▶ otto extension dev:${r}\n 监视 ${n}\n`);let i=V(e,`--link`);if(i)try{let e=U(n,r,b(i));t.stdout(` 已链接 → ${e}\n`)}catch(e){t.stderr(` 链接失败:${e.message}\n`)}let a=x(y(n,`panels`));a&&(t.stdout(` 编译面板…
110
- `),R(n,t));let o=x(y(n,`plugin.ts`))||x(y(n,`plugin.tsx`));return o&&(t.stdout(` 编译 plugin.ts…
111
- `),await z(n,t)),t.stdout(` 改动后在 otto 内 /reload 生效。Ctrl+C 退出。
112
- `),new Promise(e=>{let r;try{r=E(n,{recursive:!0},(e,r)=>{if(!r)return;let i=String(r);i.includes(`panels-dist`)||i.includes(`plugin-dist`)||i.startsWith(`.`)||(t.stdout(G(i,ce(new Date))),a&&i.endsWith(`.tsx`)&&i!==`plugin.tsx`&&R(n,t),o&&(i===`plugin.ts`||i===`plugin.tsx`)&&z(n,t))})}catch(n){t.stderr(` 监视启动失败(平台可能不支持递归监视):${n.message}\n`),e(1);return}let i=()=>{r?.close(),process.off(`SIGINT`,i),t.stdout(`
108
+ `),0}catch(e){return t.stderr(`otto extension create 失败: ${e.message}\n`),1}}function L(e,t){let{compiled:n,errors:r,skipped:i}=o(e);if(i)return t.stdout(` (无 panels/*.tsx,跳过面板编译)
109
+ `),0;for(let e of n)t.stdout(` ✓ 编译 ${e}\n`);for(let e of r)t.stderr(` ✗ ${e.entry}: ${e.message}\n`);return r.length}async function R(e,t){try{return await a(e)&&t.stdout(` ✓ 编译 ${S(`plugin-dist`,`plugin.cjs`)}\n`),0}catch(e){return t.stderr(` ✗ plugin.ts: ${e.message}\n`),1}}function z(e,t){let n=C(B(e,`--dir`)??process.cwd());return w(S(n,`otto-plugin.json`))?(t.stdout(`▶ otto extension build:${n}\n`),L(n,t)>0?1:0):(t.stderr(`otto extension build 失败: 未找到 otto-plugin.json:${n}\n`),1)}function B(e,t){let n=e.indexOf(t);return n>=0&&n+1<e.length?e[n+1]:void 0}function V(e){let t=S(e,`otto-plugin.json`);if(!w(t))throw Error(`未找到 otto-plugin.json:${e}(请在插件目录内运行,或用 --dir <path>)`);let n;try{n=JSON.parse(E(t,`utf-8`))}catch{throw Error(`otto-plugin.json 非法 JSON:${t}`)}let r=n?.id;if(typeof r!=`string`||!N.test(r))throw Error(`otto-plugin.json 缺少合法 id(kebab-case):${t}`);return r}function H(e,t,n){let r=S(n,`.otto`,`plugins`);T(r,{recursive:!0});let i=S(r,t);if(w(i)||U(i)){if(U(i))try{if(C(D(i))===C(e))return i}catch{}throw Error(`目标已存在且非本插件 dev 链接,拒绝覆盖:${i}`)}return O(C(e),i),i}function U(e){try{return te(e).isSymbolicLink()}catch{return!1}}function W(e,t){return`[${t}] ${e} 改动 → 在 otto 内 /reload 生效\n`}function fe(e){return[e.getHours(),e.getMinutes(),e.getSeconds()].map(e=>String(e).padStart(2,`0`)).join(`:`)}async function pe(e,t){let n=C(B(e,`--dir`)??process.cwd()),r;try{r=V(n)}catch(e){return t.stderr(`otto extension dev 失败: ${e.message}\n`),Promise.resolve(1)}t.stdout(`▶ otto extension dev:${r}\n 监视 ${n}\n`);let i=B(e,`--link`);if(i)try{let e=H(n,r,C(i));t.stdout(` 已链接 → ${e}\n`)}catch(e){t.stderr(` 链接失败:${e.message}\n`)}let a=w(S(n,`panels`));a&&(t.stdout(` 编译面板…
110
+ `),L(n,t));let o=w(S(n,`plugin.ts`))||w(S(n,`plugin.tsx`));return o&&(t.stdout(` 编译 plugin.ts…
111
+ `),await R(n,t)),t.stdout(` 改动后在 otto 内 /reload 生效。Ctrl+C 退出。
112
+ `),new Promise(e=>{let r;try{r=k(n,{recursive:!0},(e,r)=>{if(!r)return;let i=String(r);i.includes(`panels-dist`)||i.includes(`plugin-dist`)||i.startsWith(`.`)||(t.stdout(W(i,fe(new Date))),a&&i.endsWith(`.tsx`)&&i!==`plugin.tsx`&&L(n,t),o&&(i===`plugin.ts`||i===`plugin.tsx`)&&R(n,t))})}catch(n){t.stderr(` 监视启动失败(平台可能不支持递归监视):${n.message}\n`),e(1);return}let i=()=>{r?.close(),process.off(`SIGINT`,i),t.stdout(`
113
113
  停止 dev。
114
- `),e(0)};process.on(`SIGINT`,i)})}const K={"plugin-browser-use":{pkg:`@x-otto/plugin-browser-use-mcp`,capabilities:[`tui.renderer`,`panel.backend`]},"plugin-mobile-use":{pkg:`@x-otto/plugin-mobile-use-mcp`,capabilities:[`tui.renderer`,`panel.backend`]},"plugin-feishu-use":{pkg:`@x-otto/plugin-feishu-use`,capabilities:[]},"plugin-miniprogram-use":{pkg:`@x-otto/plugin-miniprogram-use-mcp`,capabilities:[`tui.renderer`,`panel.backend`]},"plugin-schedule":{pkg:`@x-otto/plugin-schedule-panel`,capabilities:[`tui.renderer`]},"anthropic-provider":{pkg:`@x-otto/plugin-anthropic`,capabilities:[`provider`]},"deepseek-provider":{pkg:`@x-otto/plugin-deepseek`,capabilities:[`provider`]},"oxygen-provider":{pkg:`@x-otto/plugin-oxygen`,capabilities:[`provider`]},"openai-codex-provider":{pkg:`@x-otto/plugin-openai-codex`,capabilities:[`provider`]},"github-copilot-provider":{pkg:`@x-otto/plugin-github-copilot`,capabilities:[`provider`]},"cursor-provider":{pkg:`@x-otto/plugin-cursor`,capabilities:[`provider`]},"wire-protocols":{pkg:`@x-otto/plugin-otto-wire-protocols`,capabilities:[`wire-protocol`]}},q={provider:`provider —— 注册 LLM provider,将处理你的凭据与请求流量`,tools:`tools —— 注册可被 agent 调用的工具`,"tui.renderer":`tui.renderer —— 在 TUI 进程内运行自定义渲染代码`,network:`network —— 发起任意网络请求`,"wire-protocol":`wire-protocol —— 注册 wire 协议实现,可截获全部模型请求与响应流量(含凭据)`,"a2ui.component":`a2ui.component —— 在 TUI 进程内运行自定义渲染组件代码`,"panel.backend":`panel.backend —— 面板可驱动有副作用的后端业务操作`,"input.resolver":`input.resolver —— 在提交时展开 @ 引用,把内容注入 prompt`,"a2ui.renderer":`a2ui.renderer —— 运行自定义代码,跨 TUI 与 web-ui 渲染消息`};async function J(e,t,n,r){if(n.length===0)return!0;if(!r&&process.stdout.isTTY&&process.stdin.isTTY)return ue(t,n);let i=[`插件 '${t}' 申请以下高危能力:`,...n.map(e=>` • ${q[e]??e}`)];if(e.stdout(`${i.join(`
114
+ `),e(0)};process.on(`SIGINT`,i)})}const G={"plugin-browser-use":{pkg:`@x-otto/plugin-browser-use-mcp`,capabilities:[`tui.renderer`,`panel.backend`]},"plugin-mobile-use":{pkg:`@x-otto/plugin-mobile-use-mcp`,capabilities:[`tui.renderer`,`panel.backend`]},"plugin-feishu-use":{pkg:`@x-otto/plugin-feishu-use`,capabilities:[]},"plugin-miniprogram-use":{pkg:`@x-otto/plugin-miniprogram-use-mcp`,capabilities:[`tui.renderer`,`panel.backend`]},"plugin-schedule":{pkg:`@x-otto/plugin-schedule-panel`,capabilities:[`tui.renderer`]},"anthropic-provider":{pkg:`@x-otto/plugin-anthropic`,capabilities:[`provider`]},"deepseek-provider":{pkg:`@x-otto/plugin-deepseek`,capabilities:[`provider`]},"oxygen-provider":{pkg:`@x-otto/plugin-oxygen`,capabilities:[`provider`]},"openai-codex-provider":{pkg:`@x-otto/plugin-openai-codex`,capabilities:[`provider`]},"github-copilot-provider":{pkg:`@x-otto/plugin-github-copilot`,capabilities:[`provider`]},"cursor-provider":{pkg:`@x-otto/plugin-cursor`,capabilities:[`provider`]},"wire-protocols":{pkg:`@x-otto/plugin-otto-wire-protocols`,capabilities:[`wire-protocol`]}},K={provider:`provider —— 注册 LLM provider,将处理你的凭据与请求流量`,tools:`tools —— 注册可被 agent 调用的工具`,"tui.renderer":`tui.renderer —— 在 TUI 进程内运行自定义渲染代码`,network:`network —— 发起任意网络请求`,"wire-protocol":`wire-protocol —— 注册 wire 协议实现,可截获全部模型请求与响应流量(含凭据)`,"a2ui.component":`a2ui.component —— 在 TUI 进程内运行自定义渲染组件代码`,"panel.backend":`panel.backend —— 面板可驱动有副作用的后端业务操作`,"input.resolver":`input.resolver —— 在提交时展开 @ 引用,把内容注入 prompt`,"a2ui.renderer":`a2ui.renderer —— 运行自定义代码,跨 TUI 与 web-ui 渲染消息`};async function q(e,t,n,r){if(n.length===0)return!0;if(!r&&process.stdout.isTTY&&process.stdin.isTTY)return me(t,n);let i=[`插件 '${t}' 申请以下高危能力:`,...n.map(e=>` • ${K[e]??e}`)];if(e.stdout(`${i.join(`
115
115
  `)}\n`),r)return e.stdout(`(--yes 已跳过交互确认,继续安装)
116
- `),!0;let a=O({input:process.stdin,output:process.stdout});try{let e=(await a.question(`继续安装并授予以上能力?[y/N] `)).trim().toLowerCase();return e===`y`||e===`yes`}finally{a.close()}}function ue(e,t){let n={current:null},r=[`插件 '${e}' 申请以下高危能力:`,...t.map(e=>` • ${q[e]??e}`),``,`继续安装即视为同意授予以上能力(等价于 /trust plugin 的确认动作)。`];return j({routes:k.createElement(A,{title:`高危能力确认`,message:r,confirmLabel:`继续安装`,cancelLabel:`取消`,confirmColor:`amber`,onConfirm:()=>n.current?.(!0),onCancel:()=>n.current?.(!1)})},e=>{n.current=e.resolve})}function Y(e,t){t.length!==0&&(g(e.dir),c(e.dir,t))}async function X(e){let t=new M({projectConfigPath:y(v,_)});await t.load(),await p(t,e)}async function de(e,r){let i=V(e,`--link`),a=i?void 0:e.find(e=>!e.startsWith(`-`)),d=e.includes(`--project`),p=e.includes(`--repo`),h=e.includes(`--yes`)||e.includes(`-y`);if(i){if(d||p)return r.stderr("--link 仅支持用户级安装(`~/.otto/plugins/`),不接受 --project/--repo。\n"),1;let e=b(i),t=f(e);if(!await J(r,t.length>0?H(e):``,t,h))return r.stdout(`已取消安装。
117
- `),1;try{let n=await l(e);Y(n,t),await X({id:n.id,source:`dir`,sourceSpec:e,version:n.version,scope:`global`,capabilities:t}),r.stdout(`${n.created?`已安装`:`已是最新`}插件 '${n.id}' → ${n.dir}\n 来源: 本地 (${e})(软链)\n 用户级插件默认受信任。otto 内 /reload 生效。\n`+(t.length>0?` 已授予高危能力:${t.join(`, `)}(等价于 /trust plugin ${n.id} 的确认动作,\n 可随时用 /extension → ${n.id} → 撤销信任 关闭)。\n`:``));for(let e of n.compileWarnings)r.stderr(` ✗ ${e}\n`);return 0}catch(e){return r.stderr(`插件安装失败:${e.message}\n`),1}}if(!a)return r.stderr(`otto extension install <name|package> [--project] [--repo] 或 --link <dir>
118
- `),1;let _=K[a]?.pkg??a;try{let e=d?y(process.cwd(),`.otto`):p?(()=>{let e=s(process.cwd());return e?y(e,`.otto`):(r.stderr(`--repo 要求当前目录在 pnpm workspace 内(向上未找到 pnpm-workspace.yaml)。
119
- `),``)})():y(t(),`.otto`);if(p&&!e)return 1;let i=[];if(!d&&!p){let e=m(_,process.cwd());e&&(i=f(e))}if(!await J(r,a,i,h))return r.stdout(`已取消安装。
120
- `),1;let l=!d&&!p?m(_,process.cwd()):void 0;if(!d&&!p&&!l){let e=new n;try{let t=await e.install(_,{global:!0,as:`plugin`,postInstallExecutor:o});if(!t.success)return r.stderr(`插件安装失败:${t.error??`未知错误`}\n`),1;let n=t.targetPath?f(t.targetPath):[];if(n.length>0&&t.targetPath)try{g(t.targetPath),c(t.targetPath,n)}catch{}return await X({id:t.name??_,source:`npm`,sourceSpec:_,version:t.manifest?.version,scope:`global`,capabilities:n}),r.stdout(`已安装插件 '${t.name??_}' → ${t.targetPath}\n 来源: npm registry(${_})\n`+(n.length>0?` 已授予高危能力:${n.join(`, `)}(等价于 /trust plugin ${t.name} 的确认动作)。\n`:``)+` 用户级插件默认受信任。otto 内 /reload 生效。
121
- `),0}catch(e){return r.stderr(`插件安装失败:${e.message}\n`),1}}let v=await u({pkg:_,ottoDir:e,cwd:process.cwd()});!d&&!p&&Y(v,i),await X({id:v.id,source:`npm`,sourceSpec:_,version:v.version,scope:d?`project`:p?`repository`:`global`,capabilities:i}),r.stdout(`${v.created?`已安装`:`已是最新`}插件 '${v.id}' → ${v.dir}\n 来源包: ${_}(软链 → ${v.packageDir})\n`+(i.length>0?` 已授予高危能力:${i.join(`, `)}(等价于 /trust plugin ${v.id} 的确认动作,\n 可随时用 /extension → ${v.id} → 撤销信任 关闭)。\n`:``)+(d?` 项目级插件需先信任方激活:在 otto 内运行 /trust plugin ${v.id}(或 /extension)信任该插件。\n`:p?` 仓库级插件需先信任方激活:在 otto 内运行 /trust plugin ${v.id}(或 /extension)信任该插件。\n`:a===`plugin-feishu-use`?` 用户级插件默认受信任(feishu-use 不提供 MCP 工具,是独立长连接进程,见下)。
122
- `:` 用户级插件默认受信任,工具 mcp__${v.id}__* 即刻可用(otto 内 /reload 生效)。\n`)+(a===`plugin-browser-use`?` 默认 auto:有 relay 用真 Chrome,否则本地 headless(自动探测浏览器,零配置)。
116
+ `),!0;let a=j({input:process.stdin,output:process.stdout});try{let e=(await a.question(`继续安装并授予以上能力?[y/N] `)).trim().toLowerCase();return e===`y`||e===`yes`}finally{a.close()}}function me(e,t){let n={current:null},r=[`插件 '${e}' 申请以下高危能力:`,...t.map(e=>` • ${K[e]??e}`),``,`继续安装即视为同意授予以上能力(等价于 /trust plugin 的确认动作)。`];return ie({routes:ne.createElement(re,{title:`高危能力确认`,message:r,confirmLabel:`继续安装`,cancelLabel:`取消`,confirmColor:`amber`,onConfirm:()=>n.current?.(!0),onCancel:()=>n.current?.(!1)})},e=>{n.current=e.resolve})}function J(e,t){t.length!==0&&(v(e.dir),u(e.dir,t))}async function Y(e){let t=new M({projectConfigPath:S(x,b)});await t.load(),await h(t,e)}async function he(e,n){let i=B(e,`--link`),a=i?void 0:e.find(e=>!e.startsWith(`-`)),o=e.includes(`--project`),c=e.includes(`--repo`),p=e.includes(`--yes`)||e.includes(`-y`);if(i){if(o||c)return n.stderr("--link 仅支持用户级安装(`~/.otto/plugins/`),不接受 --project/--repo。\n"),1;let e=C(i),t=m(e);if(!await q(n,t.length>0?V(e):``,t,p))return n.stdout(`已取消安装。
117
+ `),1;try{let r=await d(e);J(r,t),await Y({id:r.id,source:`dir`,sourceSpec:e,version:r.version,scope:`global`,capabilities:t}),n.stdout(`${r.created?`已安装`:`已是最新`}插件 '${r.id}' → ${r.dir}\n 来源: 本地 (${e})(软链)\n 用户级插件默认受信任。otto 内 /reload 生效。\n`+(t.length>0?` 已授予高危能力:${t.join(`, `)}(等价于 /trust plugin ${r.id} 的确认动作,\n 可随时用 /extension → ${r.id} → 撤销信任 关闭)。\n`:``));for(let e of r.compileWarnings)n.stderr(` ✗ ${e}\n`);return 0}catch(e){return n.stderr(`插件安装失败:${e.message}\n`),1}}if(!a)return n.stderr(`otto extension install <name|package> [--project] [--repo] 或 --link <dir>
118
+ `),1;let h=G[a]?.pkg??a;try{let e=o?S(process.cwd(),`.otto`):c?(()=>{let e=l(process.cwd());return e?S(e,`.otto`):(n.stderr(`--repo 要求当前目录在 pnpm workspace 内(向上未找到 pnpm-workspace.yaml)。
119
+ `),``)})():S(t(),`.otto`);if(c&&!e)return 1;let i=[];if(!o&&!c){let e=g(h,process.cwd());e&&(i=m(e))}if(!await q(n,a,i,p))return n.stdout(`已取消安装。
120
+ `),1;let d=!o&&!c?g(h,process.cwd()):void 0;if(!o&&!c&&!d){let e=new r;try{let t=await e.install(h,{global:!0,as:`plugin`,postInstallExecutor:s});if(!t.success)return n.stderr(`插件安装失败:${t.error??`未知错误`}\n`),1;let r=t.targetPath?m(t.targetPath):[];if(r.length>0&&t.targetPath)try{v(t.targetPath),u(t.targetPath,r)}catch{}return await Y({id:t.name??h,source:`npm`,sourceSpec:h,version:t.manifest?.version,scope:`global`,capabilities:r}),n.stdout(`已安装插件 '${t.name??h}' → ${t.targetPath}\n 来源: npm registry(${h})\n`+(r.length>0?` 已授予高危能力:${r.join(`, `)}(等价于 /trust plugin ${t.name} 的确认动作)。\n`:``)+` 用户级插件默认受信任。otto 内 /reload 生效。
121
+ `),0}catch(e){return n.stderr(`插件安装失败:${e.message}\n`),1}}let _=await f({pkg:h,ottoDir:e,cwd:process.cwd()});!o&&!c&&J(_,i),await Y({id:_.id,source:`npm`,sourceSpec:h,version:_.version,scope:o?`project`:c?`repository`:`global`,capabilities:i}),n.stdout(`${_.created?`已安装`:`已是最新`}插件 '${_.id}' → ${_.dir}\n 来源包: ${h}(软链 → ${_.packageDir})\n`+(i.length>0?` 已授予高危能力:${i.join(`, `)}(等价于 /trust plugin ${_.id} 的确认动作,\n 可随时用 /extension → ${_.id} → 撤销信任 关闭)。\n`:``)+(o?` 项目级插件需先信任方激活:在 otto 内运行 /trust plugin ${_.id}(或 /extension)信任该插件。\n`:c?` 仓库级插件需先信任方激活:在 otto 内运行 /trust plugin ${_.id}(或 /extension)信任该插件。\n`:a===`plugin-feishu-use`?` 用户级插件默认受信任(feishu-use 不提供 MCP 工具,是独立长连接进程,见下)。
122
+ `:` 用户级插件默认受信任,工具 mcp__${_.id}__* 即刻可用(otto 内 /reload 生效)。\n`)+(a===`plugin-browser-use`?` 默认 auto:有 relay 用真 Chrome,否则本地 headless(自动探测浏览器,零配置)。
123
123
  relay native host 首次 /reload 由插件 setup 钩子自动注入(前提 cargo build --release -p otto-relay);
124
124
  之后 chrome://extensions 加载 packages/extension/dist 即可接管真实 Chrome。
125
125
  `:``)+(a===`plugin-mobile-use`?` 默认 Android:零配置自动探测首个已连接设备/模拟器(\`adb devices\` 可见即用)。
@@ -130,8 +130,8 @@ bin/
130
130
  真机连接默认拒绝(安全 fail-closed):需显式设 {"real_device_confirmation":"allow"}
131
131
  才允许连接真实 Android/iOS 设备(模拟器/Simulator 不受此限制,零配置可用)。
132
132
  `:``)+(a===`plugin-feishu-use`?` feishu-use 是一个 7×24 独立长连接进程,不由 otto 插件系统管理其常驻生命周期:
133
- 1) otto feishu config init # 交互式配置向导(${d?`写入 ${v.dir}/../../feishu-use.json`:p?`写入 <workspaceRoot>/.otto/feishu-use.json`:`默认写入项目级 <.otto>/feishu-use.json;--global 写 ~/.otto/`})\n 2) otto feishu config get # 核对配置(appSecret 自动脱敏)
134
- 3) node ${v.dir}/bin/bridge-cli.mjs # 前台启动(或用 launchd/systemd/pm2 常驻)\n 也可用 \`otto feishu config set <key> <value>\` 随时修改单个字段。
133
+ 1) otto feishu config init # 交互式配置向导(${o?`写入 ${_.dir}/../../feishu-use.json`:c?`写入 <workspaceRoot>/.otto/feishu-use.json`:`默认写入项目级 <.otto>/feishu-use.json;--global 写 ~/.otto/`})\n 2) otto feishu config get # 核对配置(appSecret 自动脱敏)
134
+ 3) node ${_.dir}/bin/bridge-cli.mjs # 前台启动(或用 launchd/systemd/pm2 常驻)\n 也可用 \`otto feishu config set <key> <value>\` 随时修改单个字段。
135
135
  配置按作用域自动选择(project > repo > user,first-found wins)。
136
136
  详见 extensions/plugin-feishu-use/README.md(含"用来迭代 otto 框架"专项章节)。
137
137
  `:``)+(a===`plugin-miniprogram-use`?` miniprogram-use 需要两步配置:
@@ -142,14 +142,16 @@ bin/
142
142
  可选:在 ~/.otto/miniprogram-use.json 配置 admin_endpoints(需真实抓包获得)后,
143
143
  才会注册后台管理只读工具(get_members/get_permission_status/
144
144
  get_experience_qrcode)。详见 docs/rfc/RFC-121-miniprogram-use.md。
145
- `:``));for(let e of v.compileWarnings)r.stderr(` ✗ ${e}\n`);return 0}catch(e){return r.stderr(`插件安装失败:${e.message}\n`),1}}async function fe(e,i){let a=e.find(e=>!e.startsWith(`-`)),c=h({workspaceDir:process.cwd(),workspaceRoot:s(process.cwd()),scope:`all`}),l=a?c.filter(e=>e.name===a):c;if(l.length===0)return i.stdout(a?`未找到已安装且带版本号的扩展 "${a}"。\n`:`未发现带版本号的已安装扩展(skill/plugin)。
146
- `),a?1:0;let u=await r(l),d=u.filter(e=>e.newer);if(d.length===0){i.stdout(`全部扩展已是最新版本。
147
- `);for(let e of u)e.error&&i.stdout(` [${e.type}] ${e.name} v${e.installed} — ${e.error}\n`);return 0}i.stdout(`发现 ${d.length} 个可更新扩展,开始重装:\n`);let f=new n,p=0,m=0;for(let e of d){let n=l.find(t=>t.name===e.name&&t.type===e.type),r=n?pe(n):void 0;if(!r){i.stderr(` ✗ ${e.name}:无法确定重装来源(.otto-install.json 缺失或来源类型不支持自动重装)\n`),m++;continue}let a=n.path.startsWith(y(t(),`.otto`)+ee);try{let t=await f.install(r,{workspaceDir:process.cwd(),global:a,force:!0,as:e.type,postInstallExecutor:o});if(t.success)i.stdout(` ✓ ${e.name} v${e.installed} → v${e.latest??`?`}\n`),p++;else{let n=t.failureKind===`conflict`?`(已存在且未覆盖)`:``;i.stderr(` ✗ ${e.name}:${t.error??`重装失败`}${n}\n`),m++}}catch(t){i.stderr(` ✗ ${e.name}:${t.message}\n`),m++}}return i.stdout(`\n完成:${p} 个已更新${m>0?`,${m} 个失败`:``}。\n`),m>0?1:0}function pe(e){let t=y(e.path,`.otto-install.json`);if(x(t))try{let e=JSON.parse(C(t,`utf-8`));return e.sourceType===`npm`&&e.sourceUrl?.startsWith(`npm:`)?e.sourceUrl.slice(4):e.sourceType===`git`&&e.sourceUrl?e.sourceUrl:void 0}catch{return}}function Z(){return new M({projectConfigPath:y(v,_)})}function Q(n){return e({cwd:process.cwd(),homedir:n??t()}).map(e=>({id:e.id,name:e.manifest.name??e.id,scope:e.scope}))}async function $(e,t){let n=e.find(e=>!e.startsWith(`-`));if(!n)return t.stderr(`otto extension enable <id>
148
- `),1;let r=Z();await r.load();let i=new Set(r.get(`disabled_plugins`)??[]);return i.has(n)?(i.delete(n),await r.persist({disabled_plugins:[...i]}),t.stdout(`已启用插件 '${n}'。运行中的 otto 会话内执行 /reload 或重启生效。\n`),0):(t.stdout(`插件 '${n}' 当前已是启用状态。\n`),0)}async function me(e,t){let n=e.find(e=>!e.startsWith(`-`));if(!n)return t.stderr(`otto extension disable <id>
149
- `),1;if(Q().find(e=>e.id===n)?.scope===`builtin`)return t.stderr(`插件 "${n}" 是 otto 内置插件,恒定信任+启用,无法禁用。\n`),1;let r=Z();await r.load();let i=new Set(r.get(`disabled_plugins`)??[]);return i.has(n)?(t.stdout(`插件 '${n}' 当前已是禁用状态。\n`),0):(i.add(n),await r.persist({disabled_plugins:[...i]}),t.stdout(`已禁用插件 '${n}'。运行中的 otto 会话内执行 /reload 或重启生效(CLI 路径不杀运行中进程,reload 时统一清理)。\n`),0)}async function he(e,t){let n=Z();await n.load();let r=new Set(n.get(`disabled_plugins`)??[]),i=Q();if(i.length===0)return t.stdout(`(未发现任何插件)
150
- `),0;for(let e of i){let n=e.scope===`builtin`||!r.has(e.id)?`enabled `:`disabled`,i=e.scope===`builtin`?` (builtin, readonly)`:` (${e.scope})`;t.stdout(` ${n} ${e.id}${i} ${e.name}\n`)}return 0}async function ge(e,t){let n=e.find(e=>!e.startsWith(`-`)),r=e.includes(`--yes`)||e.includes(`-y`);if(!n)return t.stderr(`otto extension purge <id> [--yes]
151
- `),1;let i=y(v,`plugin-state`,n);if(!x(i))return t.stdout(`插件 '${n}' 无持久化状态(${i} 不存在)。\n`),0;if(!r){if(!process.stdout.isTTY)return t.stderr(`插件 '${n}' 有持久化状态,删除不可逆——非交互终端请显式传 --yes。\n`),1;let{createInterface:e}=await import(`node:readline/promises`),r=e({input:process.stdin,output:process.stdout});try{let e=(await r.question(`确认删除插件 '${n}' 的全部持久化状态?此操作不可逆 [y/N] `)).trim().toLowerCase();if(e!==`y`&&e!==`yes`)return t.stdout(`已取消。
152
- `),0}finally{r.close()}}let a=d(n);return t.stdout(a.removed?`已清除插件 '${n}' 的持久化状态。\n`:`清除插件 '${n}' 状态失败(目录可能仍存在)。\n`),a.removed?0:1}function _e(e,t){let[n,...r]=e;switch(n){case`create`:return L(r,t);case`build`:return B(r,t);case`dev`:return le(r,t);case`install`:return de(r,t);case`update`:return fe(r,t);case`enable`:return $(r,t);case`disable`:return me(r,t);case`list`:return he(r,t);case`purge`:return ge(r,t);default:return t.stdout(`otto extension <命令>
145
+ `:``));for(let e of _.compileWarnings)n.stderr(` ✗ ${e}\n`);return 0}catch(e){return n.stderr(`插件安装失败:${e.message}\n`),1}}async function ge(e,n){let a=e.find(e=>!e.startsWith(`-`)),o=_({workspaceDir:process.cwd(),workspaceRoot:l(process.cwd()),scope:`all`}),c=a?o.filter(e=>e.name===a):o;if(c.length===0)return n.stdout(a?`未找到已安装且带版本号的扩展 "${a}"。\n`:`未发现带版本号的已安装扩展(skill/plugin)。
146
+ `),a?1:0;let u=await i(c),d=u.filter(e=>e.newer);if(d.length===0){n.stdout(`全部扩展已是最新版本。
147
+ `);for(let e of u)e.error&&n.stdout(` [${e.type}] ${e.name} v${e.installed} — ${e.error}\n`);return 0}n.stdout(`发现 ${d.length} 个可更新扩展,开始重装:\n`);let f=new r,p=0,m=0;for(let e of d){let r=c.find(t=>t.name===e.name&&t.type===e.type),i=r?_e(r):void 0;if(!i){n.stderr(` ✗ ${e.name}:无法确定重装来源(.otto-install.json 缺失或来源类型不支持自动重装)\n`),m++;continue}let a=r.path.startsWith(S(t(),`.otto`)+ee);try{let t=await f.install(i,{workspaceDir:process.cwd(),global:a,force:!0,as:e.type,postInstallExecutor:s});if(t.success)n.stdout(` ✓ ${e.name} v${e.installed} → v${e.latest??`?`}\n`),p++;else{let r=t.failureKind===`conflict`?`(已存在且未覆盖)`:``;n.stderr(` ✗ ${e.name}:${t.error??`重装失败`}${r}\n`),m++}}catch(t){n.stderr(` ✗ ${e.name}:${t.message}\n`),m++}}return n.stdout(`\n完成:${p} 个已更新${m>0?`,${m} 个失败`:``}。\n`),m>0?1:0}function _e(e){let t=S(e.path,`.otto-install.json`);if(w(t))try{let e=JSON.parse(E(t,`utf-8`));return e.sourceType===`npm`&&e.sourceUrl?.startsWith(`npm:`)?e.sourceUrl.slice(4):e.sourceType===`git`&&e.sourceUrl?e.sourceUrl:void 0}catch{return}}function X(){return new M({projectConfigPath:S(x,b)})}function Z(n){return e({cwd:process.cwd(),homedir:n??t()}).map(e=>({id:e.id,name:e.manifest.name??e.id,scope:e.scope}))}async function Q(e,t){let n=e.find(e=>!e.startsWith(`-`));if(!n)return t.stderr(`otto extension enable <id>
148
+ `),1;let r=X();await r.load();let i=new Set(r.get(`disabled_plugins`)??[]);return i.has(n)?(i.delete(n),await r.persist({disabled_plugins:[...i]}),t.stdout(`已启用插件 '${n}'。运行中的 otto 会话内执行 /reload 或重启生效。\n`),0):(t.stdout(`插件 '${n}' 当前已是启用状态。\n`),0)}async function ve(e,t){let n=e.find(e=>!e.startsWith(`-`));if(!n)return t.stderr(`otto extension disable <id>
149
+ `),1;if(Z().find(e=>e.id===n)?.scope===`builtin`)return t.stderr(`插件 "${n}" 是 otto 内置插件,恒定信任+启用,无法禁用。\n`),1;let r=X();await r.load();let i=new Set(r.get(`disabled_plugins`)??[]);return i.has(n)?(t.stdout(`插件 '${n}' 当前已是禁用状态。\n`),0):(i.add(n),await r.persist({disabled_plugins:[...i]}),t.stdout(`已禁用插件 '${n}'。运行中的 otto 会话内执行 /reload 或重启生效(CLI 路径不杀运行中进程,reload 时统一清理)。\n`),0)}async function ye(e,t){let n=X();await n.load();let r=new Set(n.get(`disabled_plugins`)??[]),i=Z();if(i.length===0)return t.stdout(`(未发现任何插件)
150
+ `),0;for(let e of i){let n=e.scope===`builtin`||!r.has(e.id)?`enabled `:`disabled`,i=e.scope===`builtin`?` (builtin, readonly)`:` (${e.scope})`;t.stdout(` ${n} ${e.id}${i} ${e.name}\n`)}return 0}async function be(e,t){let n=e.find(e=>!e.startsWith(`-`)),r=e.includes(`--yes`)||e.includes(`-y`);if(!n)return t.stderr(`otto extension purge <id> [--yes]
151
+ `),1;let i=S(x,`plugin-state`,n);if(!w(i))return t.stdout(`插件 '${n}' 无持久化状态(${i} 不存在)。\n`),0;if(!r){if(!process.stdout.isTTY)return t.stderr(`插件 '${n}' 有持久化状态,删除不可逆——非交互终端请显式传 --yes。\n`),1;let{createInterface:e}=await import(`node:readline/promises`),r=e({input:process.stdin,output:process.stdout});try{let e=(await r.question(`确认删除插件 '${n}' 的全部持久化状态?此操作不可逆 [y/N] `)).trim().toLowerCase();if(e!==`y`&&e!==`yes`)return t.stdout(`已取消。
152
+ `),0}finally{r.close()}}let a=p(n);return t.stdout(a.removed?`已清除插件 '${n}' 的持久化状态。\n`:`清除插件 '${n}' 状态失败(目录可能仍存在)。\n`),a.removed?0:1}function $(n){return e({cwd:process.cwd(),homedir:t()}).find(e=>e.id===n)}async function xe(e,t){let r=e.find(e=>!e.startsWith(`-`));if(!r)return t.stderr(`otto extension trust: 需要插件 id(用法:otto extension trust <id> [--grant <caps>])
153
+ `),1;let i=$(r);if(!i)return t.stderr(`otto extension trust: 未找到插件 "${r}"(先 otto extension list 查看已安装插件)\n`),1;let a=(i.manifest?.capabilities??[]).filter(e=>n.includes(e)),o=e.find(e=>e.startsWith(`--grant=`)),s=e.indexOf(`--grant`),l=o?o.slice(8):s>=0?e[s+1]:void 0,d=e.includes(`--grant-declared`),f=[];if(d)f=[...a];else if(l){f=l.split(`,`).map(e=>e.trim()).filter(Boolean);let e=f.filter(e=>!n.includes(e));if(e.length>0)return t.stderr(`otto extension trust: 未知能力 ${e.join(`, `)}\n 可用高危能力:${n.join(`, `)}\n`),1;let i=f.filter(e=>!a.includes(e));if(i.length>0)return t.stderr(`otto extension trust: 插件 "${r}" 未声明能力 ${i.join(`, `)}——授予无效。\n 该插件声明的高危能力:${a.length?a.join(`, `):`(无)`}\n`),1}v(i.dir),f.length>0&&u(i.dir,f);let p=c(i);return t.stdout(`✓ 已信任 ${r}(${i.scope})\n`),f.length>0&&t.stdout(` 已授予能力:${f.join(`, `)}\n`),p.pendingCapabilities.length>0&&t.stdout(` ⚠ 仍待授权:${p.pendingCapabilities.join(`, `)}\n 这些能力未授予前对应贡献点不会注册(fail-closed)。\n 授予:otto extension trust ${r} --grant ${p.pendingCapabilities.join(`,`)}\n`),0}async function Se(e,t){let n=e.find(e=>!e.startsWith(`-`));if(!n)return t.stderr(`otto extension untrust: 需要插件 id
154
+ `),1;let r=$(n);return r?(y(r.dir),t.stdout(`✓ 已撤销信任 ${n}\n`),0):(t.stderr(`otto extension untrust: 未找到插件 "${n}"\n`),1)}function Ce(e,t){let[n,...r]=e;switch(n){case`create`:return I(r,t);case`build`:return z(r,t);case`dev`:return pe(r,t);case`install`:return he(r,t);case`update`:return ge(r,t);case`enable`:return Q(r,t);case`disable`:return ve(r,t);case`list`:return ye(r,t);case`purge`:return be(r,t);case`trust`:return xe(r,t);case`untrust`:return Se(r,t);default:return t.stdout(`otto extension <命令>
153
155
  create <name> 生成插件骨架(命令 + 面板 + manifest)
154
156
  install <name|pkg> [--project] [--repo] [--yes] 从 npm 安装插件(别名 browser-use/mobile-use/
155
157
  feishu-use/miniprogram-use/anthropic-provider/
@@ -166,5 +168,11 @@ bin/
166
168
  disable <id> 禁用插件(同上;CLI 路径不杀运行中进程,reload 时清理)
167
169
  list 列出全部插件及启用状态
168
170
  purge <id> 删除该插件全部持久化状态(plugin-state/<id>,不可逆)
169
- `),n?1:0}}export{U as a,G as i,K as n,H as o,z as r,I as s,_e as t};
170
- //# sourceMappingURL=extension-BKXIr86F.js.map
171
+ trust <id> [--grant <caps>] [--grant-declared]
172
+ 信任插件并(可选)授予其声明的高危能力——headless
173
+ 环境(CI/Docker/otto serve)的授权入口,与 TUI 的
174
+ /trust plugin 同一份落盘。未授予的能力对应贡献点
175
+ 不会注册(fail-closed)
176
+ untrust <id> 撤销信任
177
+ `),n?1:0}}export{H as a,W as i,G as n,V as o,R as r,F as s,Ce as t};
178
+ //# sourceMappingURL=extension-DLFCUBJr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-DLFCUBJr.js","names":[],"sources":["../src/commands/extension-scaffold.ts","../src/commands/extension-link.ts","../src/commands/extension-lifecycle.ts","../src/commands/extension-trust.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","/**\n * extension-trust.ts —— `otto extension trust|untrust` 子命令(RFC-364 M3)。\n *\n * ## 要解决的问题\n * 高危能力授权此前**只有 TUI 一条通道**(`/trust plugin`、`/extension` 面板)。\n * 实测:全新机器上即便插件装在用户级(`gated=false` 不门控),其声明的高危能力\n * (`provider`/`wire-protocol`/…)仍恒为 `pending`——`trust-store` 的语义是\n * 「未记录 = 空,fail-closed」。而 `code-provider-axis-handler.ts:45` 对 pending\n * 能力 fail-closed 跳过注册。\n *\n * 后果:CI / Docker / `otto serve` 在全新环境**无法自举**——provider 工厂全部不注册,\n * 一个模型都用不了,且只在日志 warn 一行。必须先有人在一台有终端的机器上跑 TUI\n * 手工授权。这是安全模型缺了「非交互但仍显式」的表达方式,不是接线遗漏。\n *\n * ## 设计约束(RFC-364 §4 规则 3/4/5)\n * - **非交互 ≠ 免授权**:本命令是显式授权动作的等价表达,不是豁免开关。\n * 不提供 `--all` 一类批量放行(`--grant` 必须逐能力列出)。\n * - **不改 fail-closed 语义**:本命令只增加*授权的表达通道*,未授权时的行为不变。\n * - **复用既有落盘函数**:走 `trustPlugin`/`grantPluginCapabilities`,与 TUI 同一份\n * `plugin-trust.json`,不另开特权写入路径。\n */\nimport { homedir } from 'node:os'\n\nimport {\n discoverPluginsWithBundled,\n} from '../discover-with-bundled'\nimport {\n evaluatePluginTrust,\n trustPlugin,\n untrustPlugin,\n grantPluginCapabilities,\n HIGH_RISK_CAPABILITIES,\n} from '@x-otto/coding'\n\nimport type { ExtensionCommandIO } from './extension-scaffold'\n\nfunction findPlugin(id: string) {\n return discoverPluginsWithBundled({ cwd: process.cwd(), homedir: homedir() }).find(\n (p) => p.id === id,\n )\n}\n\n/**\n * `otto extension trust <id> [--grant <cap,cap>] [--grant-declared]`\n *\n * - 无 `--grant`:只信任插件目录(解开 `gated`),不授予任何高危能力;\n * - `--grant a,b`:额外授予指定高危能力(逐项校验合法性);\n * - `--grant-declared`:授予该插件 manifest **已声明**的全部高危能力——仍是显式动作\n * (用户看到清单后确认),但省去手打一长串。不是 `--all`(不会授予未声明的能力)。\n */\nexport async function extensionTrust(args: string[], io: ExtensionCommandIO): Promise<number> {\n const id = args.find((a) => !a.startsWith('-'))\n if (!id) {\n io.stderr('otto extension trust: 需要插件 id(用法:otto extension trust <id> [--grant <caps>])\\n')\n return 1\n }\n\n const plugin = findPlugin(id)\n if (!plugin) {\n io.stderr(`otto extension trust: 未找到插件 \"${id}\"(先 otto extension list 查看已安装插件)\\n`)\n return 1\n }\n\n const declared = (plugin.manifest?.capabilities ?? []).filter((c) =>\n HIGH_RISK_CAPABILITIES.includes(c),\n )\n\n // --grant 解析:逗号分隔,逐项校验必须是「已声明的高危能力」——\n // 授予未声明的能力没有意义(axis handler 只看 manifest 声明∩已授予),\n // 且静默接受会让用户误以为授权成功。\n const grantFlag = args.find((a) => a.startsWith('--grant='))\n const grantIdx = args.indexOf('--grant')\n const grantRaw = grantFlag\n ? grantFlag.slice('--grant='.length)\n : grantIdx >= 0\n ? args[grantIdx + 1]\n : undefined\n const grantDeclared = args.includes('--grant-declared')\n\n let caps: string[] = []\n if (grantDeclared) {\n caps = [...declared]\n } else if (grantRaw) {\n caps = grantRaw.split(',').map((c) => c.trim()).filter(Boolean)\n const unknown = caps.filter((c) => !HIGH_RISK_CAPABILITIES.includes(c as never))\n if (unknown.length > 0) {\n io.stderr(\n `otto extension trust: 未知能力 ${unknown.join(', ')}\\n` +\n ` 可用高危能力:${HIGH_RISK_CAPABILITIES.join(', ')}\\n`,\n )\n return 1\n }\n const notDeclared = caps.filter((c) => !declared.includes(c as never))\n if (notDeclared.length > 0) {\n io.stderr(\n `otto extension trust: 插件 \"${id}\" 未声明能力 ${notDeclared.join(', ')}——授予无效。\\n` +\n ` 该插件声明的高危能力:${declared.length ? declared.join(', ') : '(无)'}\\n`,\n )\n return 1\n }\n }\n\n trustPlugin(plugin.dir)\n if (caps.length > 0) grantPluginCapabilities(plugin.dir, caps)\n\n const verdict = evaluatePluginTrust(plugin)\n io.stdout(`✓ 已信任 ${id}(${plugin.scope})\\n`)\n if (caps.length > 0) io.stdout(` 已授予能力:${caps.join(', ')}\\n`)\n if (verdict.pendingCapabilities.length > 0) {\n io.stdout(\n ` ⚠ 仍待授权:${verdict.pendingCapabilities.join(', ')}\\n` +\n ` 这些能力未授予前对应贡献点不会注册(fail-closed)。\\n` +\n ` 授予:otto extension trust ${id} --grant ${verdict.pendingCapabilities.join(',')}\\n`,\n )\n }\n return 0\n}\n\n/** `otto extension untrust <id>`:撤销信任(能力授予随之失效)。 */\nexport async function extensionUntrust(args: string[], io: ExtensionCommandIO): Promise<number> {\n const id = args.find((a) => !a.startsWith('-'))\n if (!id) {\n io.stderr('otto extension untrust: 需要插件 id\\n')\n return 1\n }\n const plugin = findPlugin(id)\n if (!plugin) {\n io.stderr(`otto extension untrust: 未找到插件 \"${id}\"\\n`)\n return 1\n }\n untrustPlugin(plugin.dir)\n io.stdout(`✓ 已撤销信任 ${id}\\n`)\n return 0\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// RFC-364 M3:非交互授权通道——headless 环境(CI/Docker/otto serve)的自举入口。\nimport { extensionTrust, extensionUntrust } from './extension-trust'\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 case 'trust':\n return extensionTrust(rest, io)\n case 'untrust':\n return extensionUntrust(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 ' trust <id> [--grant <caps>] [--grant-declared]\\n' +\n ' 信任插件并(可选)授予其声明的高危能力——headless\\n' +\n ' 环境(CI/Docker/otto serve)的授权入口,与 TUI 的\\n' +\n ' /trust plugin 同一份落盘。未授予的能力对应贡献点\\n' +\n ' 不会注册(fail-closed)\\n' +\n ' untrust <id> 撤销信任\\n',\n )\n return sub ? 1 : 0\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{n as e,t}from"./extension-DLFCUBJr.js";export{e as EXTENSION_ALIASES,t as runExtensionCommand};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{r as e}from"./feedback-DuTIx3-K.js";export{e as runFeedbackCommand};