@x-otto/cli 0.0.1-alpha.9 → 0.1.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -65
- package/bin/otto.js +6 -0
- package/dist/{auth-command-CXAB3_hs.js → auth-command-BdIvuKxx.js} +3 -3
- package/dist/auth-command-BdIvuKxx.js.map +1 -0
- package/dist/backend-handler-DUafuNWw.js +3 -0
- package/dist/backend-handler-DUafuNWw.js.map +1 -0
- package/dist/build-id-CWNug-3b.js +3 -0
- package/dist/build-id-CWNug-3b.js.map +1 -0
- package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/meta.json +1 -1
- package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/plugin.cjs +1261 -43086
- package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin.ts +4 -0
- package/dist/bundled-extensions/plugin-otto-wire-protocols/src/anthropic-messages.ts +14 -24
- package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-completions.ts +27 -10
- package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-responses.ts +20 -17
- package/dist/bundled-extensions-C3n8jg20.js +3 -0
- package/dist/bundled-extensions-C3n8jg20.js.map +1 -0
- package/dist/command-dispatcher-B2Xx3NP7.js +18 -0
- package/dist/command-dispatcher-B2Xx3NP7.js.map +1 -0
- package/dist/config-migration-DatflIxs.js.map +1 -1
- package/dist/create-host-app-iA84KNcI.js +3 -0
- package/dist/create-host-app-iA84KNcI.js.map +1 -0
- package/dist/{daemon-base-YOWgYYN7.js → daemon-base-D015ew5y.js} +1 -1
- package/dist/{daemon-base-YOWgYYN7.js.map → daemon-base-D015ew5y.js.map} +1 -1
- package/dist/discover-with-bundled-B5d4q___.js +2 -0
- package/dist/discover-with-bundled-CF8qFP41.js +3 -0
- package/dist/discover-with-bundled-CF8qFP41.js.map +1 -0
- package/dist/doctor-DMj8d4KD.js +36 -0
- package/dist/doctor-DMj8d4KD.js.map +1 -0
- package/dist/{doctor-attach-_zeIJewx.js → doctor-attach-DEoE7eFx.js} +2 -2
- package/dist/{doctor-attach-_zeIJewx.js.map → doctor-attach-DEoE7eFx.js.map} +1 -1
- package/dist/{doctor-cdp-K_mIFRfA.js → doctor-cdp-Cj9IFpEP.js} +1 -1
- package/dist/{doctor-cdp-K_mIFRfA.js.map → doctor-cdp-Cj9IFpEP.js.map} +1 -1
- package/dist/{doctor-heap-Rr2nNvcw.js → doctor-heap-SIxiRvL8.js} +2 -2
- package/dist/{doctor-heap-Rr2nNvcw.js.map → doctor-heap-SIxiRvL8.js.map} +1 -1
- package/dist/{doctor-ps-OL3FGQYu.js → doctor-ps-DNVOF6yY.js} +2 -2
- package/dist/{doctor-ps-OL3FGQYu.js.map → doctor-ps-DNVOF6yY.js.map} +1 -1
- package/dist/export-trajectory-D-BcH0Wt.js +14 -0
- package/dist/export-trajectory-D-BcH0Wt.js.map +1 -0
- package/dist/extension-CoJqqktf.js +2 -0
- package/dist/extension-Dpn8gltG.js +179 -0
- package/dist/extension-Dpn8gltG.js.map +1 -0
- package/dist/feedback-YXsFBBvG.js +30 -0
- package/dist/feedback-YXsFBBvG.js.map +1 -0
- package/dist/feedback-backend-methods-iVB3y7XG.js +3 -0
- package/dist/feedback-backend-methods-iVB3y7XG.js.map +1 -0
- package/dist/{feishu-setup-DIcd3PMK.js → feishu-setup-DbAq_BRH.js} +2 -2
- package/dist/feishu-setup-DbAq_BRH.js.map +1 -0
- package/dist/ftue-doctor-recovery-CKTMnxM8.js +3 -0
- package/dist/ftue-doctor-recovery-CKTMnxM8.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -13
- package/dist/index.js.map +1 -1
- package/dist/install-CEPczuet.js +11 -0
- package/dist/install-CEPczuet.js.map +1 -0
- package/dist/install-CG6b6fox.js +2 -0
- package/dist/interactive-schedule-DqvuQWjX.js +3 -0
- package/dist/interactive-schedule-DqvuQWjX.js.map +1 -0
- package/dist/{mcp-DbTYT7hB.js → mcp-DjReRjuu.js} +1 -1
- package/dist/{mcp-DbTYT7hB.js.map → mcp-DjReRjuu.js.map} +1 -1
- package/dist/mcp-server-CZtQjAbW.js +5 -0
- package/dist/mcp-server-CZtQjAbW.js.map +1 -0
- package/dist/{migrate-boDXhEyV.js → migrate-BBi3Txu2.js} +1 -1
- package/dist/{migrate-boDXhEyV.js.map → migrate-BBi3Txu2.js.map} +1 -1
- package/dist/observe-BqLCpmFD.js +83 -0
- package/dist/observe-BqLCpmFD.js.map +1 -0
- package/dist/parse-cli-CSd_QYJj.js +3 -0
- package/dist/parse-cli-CSd_QYJj.js.map +1 -0
- package/dist/persistence-BvLRnpfi.js +85 -0
- package/dist/persistence-BvLRnpfi.js.map +1 -0
- package/dist/plugin-assembly-check-BntT5W6H.js +3 -0
- package/dist/plugin-assembly-check-BntT5W6H.js.map +1 -0
- package/dist/{proxy-evg7ZvoD.js → proxy-CFZDVIwI.js} +1 -1
- package/dist/proxy-CFZDVIwI.js.map +1 -0
- package/dist/proxy-CkSf8ziQ.js +2 -0
- package/dist/{ps-DKrluEuy.js → ps-BVKv6rAo.js} +1 -1
- package/dist/{ps-DKrluEuy.js.map → ps-BVKv6rAo.js.map} +1 -1
- package/dist/{registry-D0NONjLv.js → registry-BUvu-es1.js} +4 -4
- package/dist/registry-BUvu-es1.js.map +1 -0
- package/dist/registry-DYHR-ONu.js +2 -0
- package/dist/{run-attach-Ci5jYyCa.js → run-attach-X35puJ_n.js} +2 -2
- package/dist/{run-attach-Ci5jYyCa.js.map → run-attach-X35puJ_n.js.map} +1 -1
- package/dist/schedule-C_uHI2M8.js +5 -0
- package/dist/schedule-C_uHI2M8.js.map +1 -0
- package/dist/serve-BIyu4x46.js +21 -0
- package/dist/serve-BIyu4x46.js.map +1 -0
- package/dist/{service-client-BLNeugM9.js → service-client-cfUUP4EE.js} +1 -1
- package/dist/{service-client-BLNeugM9.js.map → service-client-cfUUP4EE.js.map} +1 -1
- package/dist/{version-DaMJKLwj.js → version-Bt1T37Md.js} +2 -2
- package/dist/{version-DaMJKLwj.js.map → version-Bt1T37Md.js.map} +1 -1
- package/package.json +28 -25
- package/dist/auth-command-CXAB3_hs.js.map +0 -1
- package/dist/build-id-DMyumG97.js +0 -3
- package/dist/build-id-DMyumG97.js.map +0 -1
- package/dist/bundled-extensions/plugin-skill-inductor/otto-plugin.json +0 -8
- package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/meta.json +0 -1
- package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/plugin.cjs +0 -52781
- package/dist/bundled-extensions/plugin-skill-inductor/plugin.ts +0 -77
- package/dist/bundled-extensions/plugin-skill-inductor/src/candidate-detector.ts +0 -210
- package/dist/bundled-extensions/plugin-skill-inductor/src/candidate-digest.ts +0 -146
- package/dist/bundled-extensions/plugin-skill-inductor/src/candidate-store.ts +0 -87
- package/dist/bundled-extensions/plugin-skill-inductor/src/episode-store.ts +0 -122
- package/dist/bundled-extensions/plugin-skill-inductor/src/episode.ts +0 -125
- package/dist/bundled-extensions/plugin-skill-inductor/src/extraction-prompt.ts +0 -97
- package/dist/bundled-extensions/plugin-skill-inductor/src/inductor-service.ts +0 -183
- package/dist/bundled-extensions/plugin-skill-inductor/src/similarity.ts +0 -58
- package/dist/command-dispatcher-Dnh3vtf4.js +0 -13
- package/dist/command-dispatcher-Dnh3vtf4.js.map +0 -1
- package/dist/debug-CiosFwme.js +0 -34
- package/dist/debug-CiosFwme.js.map +0 -1
- package/dist/discover-with-bundled-wSuXDvNc.js +0 -3
- package/dist/discover-with-bundled-wSuXDvNc.js.map +0 -1
- package/dist/doctor-C84-hhEr.js +0 -30
- package/dist/doctor-C84-hhEr.js.map +0 -1
- package/dist/extension-C3AP3KxC.js +0 -2
- package/dist/extension-DAex6RFK.js +0 -170
- package/dist/extension-DAex6RFK.js.map +0 -1
- package/dist/extension-plugin-B10fpJA7.js +0 -7
- package/dist/extension-plugin-B10fpJA7.js.map +0 -1
- package/dist/extension-plugin-Dt5_McoL.js +0 -2
- package/dist/feedback-7qQsiabD.js +0 -21
- package/dist/feedback-7qQsiabD.js.map +0 -1
- package/dist/feedback-Cc1eBsqy.js +0 -2
- package/dist/feishu-setup-DIcd3PMK.js.map +0 -1
- package/dist/install-DlVJRHRT.js +0 -11
- package/dist/install-DlVJRHRT.js.map +0 -1
- package/dist/install-WCcQ9k9M.js +0 -2
- package/dist/model-auth-C3gJ1cpD.js +0 -2
- package/dist/model-auth-DvoaqZ1G.js +0 -3
- package/dist/model-auth-DvoaqZ1G.js.map +0 -1
- package/dist/observe-CVg-MRM2.js +0 -32
- package/dist/observe-CVg-MRM2.js.map +0 -1
- package/dist/parse-cli-CO__I2GZ.js +0 -3
- package/dist/parse-cli-CO__I2GZ.js.map +0 -1
- package/dist/persistenced-CCwHu1Ww.js +0 -36
- package/dist/persistenced-CCwHu1Ww.js.map +0 -1
- package/dist/proxy-BvdBsB1O.js +0 -2
- package/dist/proxy-evg7ZvoD.js.map +0 -1
- package/dist/registry-B3nfN62q.js +0 -2
- package/dist/registry-D0NONjLv.js.map +0 -1
- package/dist/remote-persistence-server-B_hONoVR.js +0 -35
- package/dist/remote-persistence-server-B_hONoVR.js.map +0 -1
- package/dist/serve-pVR6UIT9.js +0 -21
- package/dist/serve-pVR6UIT9.js.map +0 -1
- package/dist/time-travel-ZozuPPI-.js +0 -21
- package/dist/time-travel-ZozuPPI-.js.map +0 -1
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import{t as e}from"./discover-with-bundled-wSuXDvNc.js";import{n as t}from"./extension-DAex6RFK.js";import{homedir as n}from"node:os";import{InstallManager as r,POINT as i,buildExtensionContext as a,defaultPostInstallExecutor as o,dispatch as s,evaluatePluginTrust as c,findWorkspaceRoot as l,grantPluginCapabilities as u,installPluginFromPackage as d,openTarget as f,readHighRiskCapabilities as p,readPluginLogTail as m,recordPluginInstalled as h,recordPluginUninstalled as g,resolveActions as _,resolvePackageDir as v,runPluginScript as y,trustPlugin as b,uninstall as x,untrustPlugin as S}from"@x-otto/coding";import{join as C}from"node:path";import{existsSync as w,readFileSync as T}from"node:fs";import{i18next as E}from"@x-otto/tui";function D(e){switch(e){case`global`:return E.t(`cmd.scopeGlobal`);case`repository`:return E.t(`cmd.scopeRepository`);default:return E.t(`cmd.scopeProject`)}}function O(e,t,n){return{workspaceDir:e.app.workspaceDir,workspaceRoot:t===`repository`?l(e.app.workspaceDir):void 0,global:t===`global`,dryRun:n}}async function k(e){let{name:t,type:n,scope:r,ctx:i,onDone:a}=e,o=i.app.settings.get(`mcp_servers`),s=await x(t,n,O(i,r,!0),()=>o,async()=>{});if(!s.success){i.tui.info.showToast(E.t(`cmd.titleUninstall`),E.t(`cmd.uninstallFailed`,{err:s.error??E.t(`cmd.unknownError`)})),a?.();return}let c=s.plan??[];i.tui.modal.openModal({kind:`confirm`,title:E.t(`cmd.uninstallConfirmTitle`,{type:n,name:t}),message:[E.t(`cmd.uninstallConfirmScope`,{scopeLabel:D(r)}),...c].join(`
|
|
3
|
-
`),confirmLabel:E.t(`cmd.uninstallConfirmYes`),dangerous:!0,onConfirm:()=>{A(e)},onCancel:()=>a?.()})}async function A(e){let{name:t,type:n,scope:r,ctx:i,onDone:a}=e,o=i.app.settings.get(`mcp_servers`),s=await x(t,n,O(i,r,!1),()=>o,async e=>{await i.app.settings.persist({mcp_servers:e})});if(!s.success){i.tui.info.showToast(E.t(`cmd.titleUninstall`),E.t(`cmd.uninstallFailed`,{err:s.error??E.t(`cmd.unknownError`)})),a?.();return}i.tui.info.showToast(E.t(`cmd.titleUninstall`),E.t(`cmd.uninstalled`,{type:n,name:t,scope:D(r)})),n===`plugin`&&await g(i.app.settings,t);try{await i.app.reloadCapabilities(),i.tui.plugins.setPluginRegistrations(i.app.getPluginRegistrations()),n===`plugin`&&i.app.pluginModuleManager.emitMonitorEvent({type:`plugin.uninstalled`,pluginId:t}),i.tui.info.showToast(E.t(`cmd.titleUninstall`),E.t(`cmd.uninstallRefreshed`))}catch(e){i.tui.messages.pushMessage({role:`command_echo`,command:`/extension`,status:`error`,summary:E.t(`cmd.uninstalledRefreshErr`,{err:e instanceof Error?e.message:String(e)})})}a?.()}function j(e){return e===`project`?`project`:e===`global`?`global`:null}function M(e,t){let n={project:`project`,global:`global`,plugin:`plugin`,inline:`inline`}[e]??e;return t?`${n} (${t})`:n}function N(e){let t=[];return e.providerApis>0&&t.push(`provider(${e.providerApis})`),e.tools>0&&t.push(`tools(${e.tools})`),e.renderers>0&&t.push(`renderers(${e.renderers})`),e.statusItems>0&&t.push(`statusItems(${e.statusItems})`),e.fileViewers&&e.fileViewers>0&&t.push(`fileViewers(${e.fileViewers})`),t.length?t.join(`, `):void 0}function P(e){switch(e){case`provider`:return E.t(`cmd.pluginCapabilityProvider`);case`tools`:return E.t(`cmd.pluginCapabilityTools`);case`tui.renderer`:return E.t(`cmd.pluginCapabilityTuiRenderer`);case`network`:return E.t(`cmd.pluginCapabilityNetwork`);case`wire-protocol`:return E.t(`cmd.pluginCapabilityWireProtocol`);case`a2ui.component`:return E.t(`cmd.pluginCapabilityA2uiComponent`);case`panel.backend`:return E.t(`cmd.pluginCapabilityPanelBackend`);case`input.resolver`:return E.t(`cmd.pluginCapabilityInputResolver`);case`a2ui.renderer`:return E.t(`cmd.pluginCapabilityA2uiRenderer`);default:return}}function F(e,t){let n=c(t),r=n.pendingCapabilities,i=()=>{b(t.dir),r.length>0&&u(t.dir,r),e.tui.info.showToast(E.t(`cmd.titlePlugin`),E.t(`cmd.pluginTrusting`,{id:t.id})),e.app.reloadCapabilities().then(n=>{e.tui.plugins.setPluginRegistrations(e.app.getPluginRegistrations()),e.tui.info.showToast(E.t(`cmd.titlePlugin`),E.t(`cmd.pluginTrusted`,{id:t.id,count:n.mcpTools}))},t=>e.tui.info.showToast(E.t(`cmd.titlePlugin`),E.t(`cmd.compatReloadFailed`,{err:t instanceof Error?t.message:String(t)})))},a=n.surface;if(!a.any&&r.length===0){i();return}let o=[E.t(`cmd.pluginTrustConfirmIntro`)];if(a.mcpJson&&o.push(`• ${E.t(`cmd.pluginSurfaceMcp`)}`),a.commands&&o.push(`• ${E.t(`cmd.pluginSurfaceCommands`)}`),a.scripts&&o.push(`• ${E.t(`cmd.pluginSurfaceScripts`)}`),a.panels&&o.push(`• ${E.t(`cmd.pluginSurfacePanels`)}`),a.skills&&o.push(`• ${E.t(`cmd.pluginSurfaceSkills`)}`),a.agents&&o.push(`• ${E.t(`cmd.pluginSurfaceAgents`)}`),r.length>0){o.push(``,E.t(`cmd.pluginCapabilityIntro`));for(let e of r){let t=P(e);t&&o.push(`• ${t}`)}}o.push(``,E.t(`cmd.pluginTrustConfirmHint`)),e.tui.modal.openModal({kind:`select`,title:E.t(`cmd.pluginTrustConfirmTitle`,{id:t.id}),subtitle:o.join(`
|
|
4
|
-
`),layout:`buttons`,options:[{label:E.t(`cmd.pluginTrustConfirmAction`),value:`trust`,color:`accent`},{label:E.t(`cmd.commonCancel`),value:`cancel`,color:`gray`}],onSelect:e=>{e===`trust`&&i()},onCancel:()=>{}})}async function I(e,t,n){if(t.length===0)return!0;let r=[E.t(`cmd.pluginCapabilityIntro`)];for(let e of t){let t=P(e);t&&r.push(`• ${t}`)}return new Promise(t=>{n.tui.modal.openModal({kind:`select`,title:E.t(`cmd.pluginInstallConfirmTitle`,{name:e}),subtitle:r.join(`
|
|
5
|
-
`),layout:`buttons`,options:[{label:E.t(`cmd.pluginInstallConfirmAction`),value:`install`,color:`accent`},{label:E.t(`cmd.commonCancel`),value:`cancel`,color:`gray`}],onSelect:e=>t(e===`install`),onCancel:()=>t(!1)})})}async function L(e,n,i){let a=i?.autoTrust??!1,s=i?.onProgress,c=i?.onPendingCapabilities;if(e.includes(`..`)||e.startsWith(`/`)||e.startsWith(`./`)||e.startsWith(`~/`))return a||n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.pluginInstallInvalidName`,{name:e})),!1;let l=t[e]?.pkg??e,f=v(l),m;if(!f)try{s?.(`Downloading from npm registry`);let e=await new r().install(l,{global:!0,as:`plugin`,postInstallExecutor:o});e.success&&e.targetPath?(f=e.targetPath,m={id:e.name??l,dir:e.targetPath,created:!0,compileWarnings:[],version:e.manifest?.version}):a||n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.pluginInstallFailed`,{err:e.error??`未知错误`}))}catch(e){a||n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.pluginInstallFailed`,{err:e instanceof Error?e.message:String(e)}))}if(!f)return a||n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.pluginInstallNotFound`,{pkg:l})),!1;let g=p(f);if(!a&&!await I(e,g,n))return n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.pluginInstallCancelled`)),!1;let _;if(m)_=m;else try{s?.(`Installing plugin`),_=await d({pkg:l})}catch(e){return a||n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.pluginInstallFailed`,{err:e instanceof Error?e.message:String(e)})),!1}a||n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.pluginInstalled`,{seg:_.created?`Installed ${_.id}`:`${_.id} already up to date`})),await h(n.app.settings,{id:_.id,source:`npm`,sourceSpec:l,version:_.version,scope:`global`,capabilities:g}),n.app.pluginModuleManager.emitMonitorEvent({type:`plugin.installed`,pluginId:_.id});for(let e of _.compileWarnings)a||n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.pluginInstallFailed`,{err:e}));g.length>0&&(s?.(`Authorizing capabilities`),b(_.dir),u(_.dir,g));try{s?.(`Reloading capabilities`),(await n.app.reloadCapabilities()).pendingProviderCapabilities.includes(_.id)&&(a||n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.pluginInstallPendingCaps`,{id:_.id})),c?.(_.id))}catch(e){a||n.tui.info.showToast(E.t(`cmd.titleInstall`),E.t(`cmd.compatReloadFailed`,{err:e instanceof Error?e.message:String(e)}))}if(await n.callbacks.refreshModelsNow().catch(()=>{}),e===`plugin-feishu-use`){try{let{launchFeishuSetup:e}=await import(`./feishu-setup-DIcd3PMK.js`);await e(n)}catch{}return!0}if(!a){await new Promise(e=>setImmediate(e));let{handleModel:e}=await import(`./model-auth-C3gJ1cpD.js`);await e([],n)}return!0}async function R(e,t){t.tui.panes.togglePane(`backgroundTasks`)}async function z(e,t){let n=e.join(` `);if(!n){t.tui.info.showToast(E.t(`cmd.titleJob`),E.t(`cmd.jobUsage`));return}let r=/^revert\s+(\S+)$/.exec(n);if(r){let e=r[1],n=t.app.agentJobs.revert(e);if(n.applied)t.tui.info.showToast(E.t(`cmd.titleReverted`),E.t(`cmd.jobReverted`,{id:e,msg:n.message}));else{let r=n.conflictPaths.length?E.t(`cmd.jobConflictFiles`,{joinedPaths:n.conflictPaths.join(`
|
|
6
|
-
`)}):``;t.tui.messages.pushMessage({role:`command_echo`,command:`/go revert ${e}`,status:`error`,summary:E.t(`cmd.jobRevertFailed`,{id:e,status:n.status,msg:n.message,conflicts:r})})}return}try{let e=t.app.agentJobs.start({title:n.slice(0,60),prompt:n,sessionId:t.state.session.id,origin:`user`});t.tui.info.showToast(E.t(`cmd.titleDelegated`),E.t(`cmd.delegated`,{title:e.title}))}catch(e){t.tui.messages.pushMessage({role:`command_echo`,command:`/go`,status:`error`,summary:E.t(`cmd.delegateFailed`,{err:e instanceof Error?e.message:String(e)})})}}async function B(t,r){let o=(e,t)=>r.tui.info.showToast(e,t),l=e=>r.tui.messages.pushMessage({role:`command_echo`,command:`/extension`,status:`error`,summary:e}),u=()=>{let e=r.app.extensions.list(),t=r.app.getGlobalMenuItems?.()??[],n=new Map(t.map(e=>[e.id,e.command]));r.tui.modal.openModal({kind:`extensionsList`,entries:e.map(e=>({category:e.category,id:e.id,name:e.name,enabled:e.enabled,description:e.description,applies:e.applies})),menuContributions:[{id:`__discover__`,title:`Discover plugins (search registries)…`},...t.map(e=>({id:e.id,title:e.title}))],onSelect:e=>{if(!e)return;if(e===`contrib:__discover__`){d();return}if(e.startsWith(`contrib:`)){let t=e.slice(8),i=n.get(t);if(!i)return;s(i,{open:e=>f(e),runCommand:async(e,t)=>{let{dispatchSlashCommand:n}=await import(`./command-dispatcher-Dnh3vtf4.js`).then(e=>e.a);await n(e,t?t.split(/\s+/).filter(Boolean):[],r)}}).catch(e=>o(E.t(`cmd.titlePlugin`),e instanceof Error?e.message:String(e)));return}let t=e.indexOf(`:`);g(e.slice(0,t),e.slice(t+1))},onCancel:()=>{}})},d=async()=>{let e=await r.tui.modal.openModal({kind:`input`,title:`Discover plugins`,description:[`搜索已注册 registry(/registry list 查看源;/registry add 注册第三方源)`],placeholder:`keyword… (e.g. weather / oauth / provider)`});if(!e?.trim()){u();return}let{searchRegistries:t}=await import(`./registry-B3nfN62q.js`),{hits:n,warnings:i}=await t(r,e.trim());if(n.length===0){o(E.t(`cmd.titlePlugin`),`No registry entries match '${e.trim()}'${i.length?` (${i.length} source warning)`:``}`),u();return}r.tui.modal.openModal({kind:`select`,title:`Discover: '${e.trim()}' (${n.length})`,subtitle:i.length>0?`⚠ ${i.join(` · `)}`:void 0,options:n.map((e,t)=>({label:`${e.entry.id} (${e.entry.type}) [${(e.entry.capabilities??[]).join(`, `)||`-`}] @${e.sourceName}${e.stale?` (stale)`:``}`,description:e.entry.description,value:String(t)})),onSelect:e=>{let t=n[Number(e)];if(!t)return;let i=[`--as`,t.entry.type,...t.entry.subPath?[`--sub-path`,t.entry.subPath]:[],t.entry.source];(async()=>{let{dispatchSlashCommand:e}=await import(`./command-dispatcher-Dnh3vtf4.js`).then(e=>e.a);await e(`install`,i,r)})().catch(e=>o(E.t(`cmd.titlePlugin`),e instanceof Error?e.message:String(e)))},onCancel:()=>u()})},p=e=>{let t=r.app.skillRegistry?.get(e);if(!t){u();return}let n=t.status!==`disabled`,i=t.definition.metadata,a=[{kind:`group`,title:E.t(`cmd.extSkillGroupOverview`)},{kind:`row`,label:E.t(`cmd.extSkillLabelDesc`),value:i.description||`—`},{kind:`row`,label:E.t(`cmd.extSkillLabelStatus`),value:t.status,tone:t.status===`error`||t.status===`disabled`?`error`:`success`},{kind:`row`,label:E.t(`cmd.extSkillLabelSource`),value:E.t(`cmd.extSkillSourceLabel`,{seg:M(t.source.type,t.source.id)}),tone:`muted`},{kind:`group`,title:E.t(`cmd.extSkillGroupTrigger`)},{kind:`row`,label:E.t(`cmd.extSkillLabelTriggerMode`),value:i.trigger===`manual`?E.t(`cmd.extSkillTriggerManual`):E.t(`cmd.extSkillTriggerAuto`)},...i.priority===void 0?[]:[{kind:`row`,label:E.t(`cmd.extSkillLabelPriority`),value:String(i.priority)}],{kind:`group`,title:E.t(`cmd.extSkillGroupDependencies`)},{kind:`row`,label:E.t(`cmd.extSkillLabelDependencies`),value:i.dependencies?.length?i.dependencies.join(`, `):E.t(`cmd.extSkillNoDependencies`),tone:i.dependencies?.length?void 0:`muted`},{kind:`group`,title:E.t(`cmd.extSkillGroupTags`)},{kind:`row`,label:E.t(`cmd.extSkillLabelTags`),value:i.tags?.length?i.tags.join(`, `):E.t(`cmd.extSkillNoTags`),tone:i.tags?.length?void 0:`muted`}];t.status===`error`&&t.error&&a.push({kind:`group`,title:E.t(`cmd.extSkillGroupError`)},{kind:`text`,value:t.error,tone:`error`}),r.tui.modal.openModal({kind:`extensionDetail`,title:E.t(`cmd.extSkillTitle`,{name:e,state:n?`enabled`:`disabled`}),meta:`skill · ${t.source.type}`,metaStatus:{label:n?E.t(`cmd.extEnableLabel`):E.t(`cmd.extDisableLabel`),enabled:n},actions:[{label:n?E.t(`cmd.extDisableLabel`):E.t(`cmd.extEnableLabel`),value:`toggle`,color:n?`amber`:`green`},{label:E.t(`cmd.extViewFullTextLabel`),value:`view-full-text`,color:`gray`},...j(t.source.type)?[{label:E.t(`cmd.uninstallActionLabel`),value:`uninstall`,color:`red`}]:[]],dataListEntries:a,fullText:{title:E.t(`cmd.extSkillFullTextTitle`,{name:e}),content:t.definition.body||E.t(`cmd.extSkillNoBody`)},onAction:async i=>{if(i===`toggle`){try{await r.app.extensions.setEnabled(`skill`,e,!n),o(E.t(`cmd.titleExtension`),E.t(`cmd.extToggled`,{name:e,verb:n?`Disabled`:`Enabled`,seg:` — applies next turn`}))}catch(e){l(E.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}p(e);return}if(i===`uninstall`){let n=j(t.source.type);if(!n)return;await k({name:e,type:`skill`,scope:n,ctx:r,onDone:()=>u()})}},onClose:()=>u()})},h=e=>{let t=r.app.declarativeAgentProfiles.find(t=>t.name===e);if(!t){u();return}let n=!new Set(r.app.settings.get(`disabled_agents`)??[]).has(e),i=[{kind:`group`,title:E.t(`cmd.extAgentGroupOverview`)},{kind:`row`,label:E.t(`cmd.extAgentLabelDesc`),value:t.description||`—`},{kind:`row`,label:E.t(`cmd.extAgentLabelStatus`),value:n?E.t(`cmd.extEnableLabel`):E.t(`cmd.extDisableLabel`),tone:n?`success`:`error`},{kind:`group`,title:E.t(`cmd.extAgentGroupCapabilities`)},{kind:`row`,label:E.t(`cmd.extAgentLabelTaskTypes`),value:t.taskTypes.length?t.taskTypes.join(`, `):E.t(`cmd.extAgentNoTaskTypes`),tone:t.taskTypes.length?void 0:`muted`},{kind:`row`,label:E.t(`cmd.extAgentLabelCapabilities`),value:t.capabilities.length?t.capabilities.join(`, `):E.t(`cmd.extAgentNoCapabilities`),tone:t.capabilities.length?void 0:`muted`},{kind:`group`,title:E.t(`cmd.extAgentGroupTools`)},{kind:`row`,label:E.t(`cmd.extAgentLabelDefaultTools`),value:t.defaultTools?.length?t.defaultTools.join(`, `):E.t(`cmd.extAgentAllTools`),tone:t.defaultTools?.length?void 0:`muted`},{kind:`row`,label:E.t(`cmd.extAgentLabelDisallowedTools`),value:t.disallowedTools?.length?t.disallowedTools.join(`, `):E.t(`cmd.extAgentNoDisallowedTools`),tone:t.disallowedTools?.length?void 0:`muted`},{kind:`group`,title:E.t(`cmd.extAgentGroupExecution`)},{kind:`row`,label:E.t(`cmd.extAgentLabelPromptMode`),value:t.promptMode??`append`,tone:`muted`},...t.preferredModelTier?[{kind:`row`,label:E.t(`cmd.extAgentLabelModelTier`),value:t.preferredModelTier}]:[],{kind:`row`,label:E.t(`cmd.extAgentLabelAllowSubagents`),value:t.allowSubagents?E.t(`cmd.extEnableLabel`):E.t(`cmd.extDisableLabel`),tone:t.allowSubagents?void 0:`muted`}];r.tui.modal.openModal({kind:`extensionDetail`,title:E.t(`cmd.extAgentTitle`,{name:e,state:n?`enabled`:`disabled`}),meta:`agent`,metaStatus:{label:n?E.t(`cmd.extEnableLabel`):E.t(`cmd.extDisableLabel`),enabled:n},actions:[{label:n?E.t(`cmd.extDisableLabel`):E.t(`cmd.extEnableLabel`),value:`toggle`,color:n?`amber`:`green`},{label:E.t(`cmd.extViewFullTextLabel`),value:`view-full-text`,color:`gray`}],dataListEntries:i,fullText:{title:E.t(`cmd.extAgentFullTextTitle`,{name:e}),content:t.systemPromptTemplate||E.t(`cmd.extSkillNoBody`)},onAction:async t=>{if(t===`toggle`){try{await r.app.extensions.setEnabled(`agent`,e,!n),o(E.t(`cmd.titleExtension`),E.t(`cmd.extToggled`,{name:e,verb:n?`Disabled`:`Enabled`,seg:` — applies next turn`}))}catch(e){l(E.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}h(e)}},onClose:()=>u()})},g=(e,t)=>{let n=r.app.extensions.list().find(n=>n.category===e&&n.id===t);if(!n){u();return}let i={label:n.enabled?E.t(`cmd.extDisableLabel`):E.t(`cmd.extEnableLabel`),value:`toggle`};if(e===`mcp`){let a=r.app.mcpManager?.getServerInfos().find(e=>e.name===t),s=n.enabled?a?String(a.status):`disconnected`:`disabled`;r.tui.modal.openModal({kind:`select`,title:E.t(`cmd.extMcpTitle`,{name:n.name,status:s}),options:[{label:E.t(`cmd.extReconnectLabel`),value:`reconnect`},i,...n.readonly?[]:[{label:E.t(`cmd.uninstallActionLabel`),value:`uninstall`}]],onSelect:async i=>{if(i===`toggle`){try{await r.app.extensions.setEnabled(n.category,t,!n.enabled),o(E.t(`cmd.titleExtension`),E.t(`cmd.extToggled`,{name:n.name,verb:n.enabled?`Disabled`:`Enabled`,seg:``}))}catch(e){l(E.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}g(e,t);return}if(i===`reconnect`){let n=r.app.mcpManager,i=(r.app.settings.get(`mcp_servers`)??{})[t];if(!n||!i){o(E.t(`cmd.titleExtension`),E.t(`cmd.extReconnectUnavailable`,{id:t})),g(e,t);return}try{await n.disconnect(t).catch(()=>{}),await n.connect(t,i),o(E.t(`cmd.titleExtension`),E.t(`cmd.extReconnected`,{id:t}))}catch(e){l(E.t(`cmd.extReconnectFailed`,{id:t,err:e instanceof Error?e.message:String(e)}))}g(e,t);return}if(i===`uninstall`){await k({name:t,type:`mcp`,scope:`project`,ctx:r,onDone:()=>u()});return}},onCancel:()=>u()})}else e===`plugin`?v(t,n.enabled,n.name,n.description):e===`skill`?p(t):e===`agent`?h(t):r.tui.modal.openModal({kind:`select`,title:E.t(`cmd.extItemTitle`,{name:n.name}),options:[i],onSelect:async i=>{if(i===`toggle`){try{await r.app.extensions.setEnabled(n.category,t,!n.enabled),o(E.t(`cmd.titleExtension`),E.t(`cmd.extToggled`,{name:n.name,verb:n.enabled?`Disabled`:`Enabled`,seg:n.applies===`live`?``:` — applies next turn`}))}catch(e){l(E.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}g(e,t);return}},onCancel:()=>u()})},v=(t,d,p,h,g)=>{let b=r.app.workspaceDir??process.cwd(),x=e({cwd:b,homedir:n()}).find(e=>e.id===t);if(!x){u();return}let D=c(x),O=D.surface.any?D.gated?E.t(`cmd.extPluginUntrusted`):D.pendingCapabilities.length>0?E.t(`cmd.extPluginCapabilityPending`,{caps:D.pendingCapabilities.join(`, `)}):E.t(`cmd.extPluginTrusted`):E.t(`cmd.extPluginNoSurface`),A=D.surface,j=[[A.mcpJson,`mcp`],[A.commands,`commands`],[A.scripts,`scripts`],[A.panels,`panels`],[A.skills,`skills`],[A.agents,`agents`]].filter(([e])=>e).map(([,e])=>e),M=j.length?` ${j.join(`, `)}`:` —`,P=(r.app.getPluginPanelRegistrations?.()??[]).filter(e=>e.pluginId===t),I=N({providerApis:r.app.providerRegistry.getApisBySource(t).length,tools:r.app.toolRegistry.getAll().filter(e=>e.metadata.source===t).length,renderers:r.app.getPluginRenderers().filter(e=>e.pluginId===t).length,statusItems:r.app.getPluginStatusItems().filter(e=>e.id.startsWith(`${t}:`)).length,fileViewers:(r.app.getPluginFileViewers?.()??[]).filter(e=>e.pluginId===t).length}),L=x.scope===`builtin`?[]:[{label:d?E.t(`cmd.extDisableLabel`):E.t(`cmd.extEnableLabel`),value:`toggle`,color:d?`amber`:`green`}];if(D.surface.any&&(x.scope===`project`||x.scope===`repository`||D.pendingCapabilities.length>0)){let e=x.scope===`project`||x.scope===`repository`?!D.trusted:D.pendingCapabilities.length>0;L.push({label:e?E.t(`cmd.extPluginTrustLabel`):E.t(`cmd.extPluginUntrustLabel`),value:e?`trust`:`untrust`,color:e?`accent`:`gray`})}L.push({label:E.t(`cmd.extReloadLabel`),value:`reload`}),x.scope!==`builtin`&&L.push({label:E.t(`cmd.uninstallActionLabel`),value:`uninstall`,color:`red`});let R=a(x,{"extension.id":t,"extension.enabled":d,"extension.trusted":D.trusted,"extension.scope":x.scope,"extension.category":`plugin`}),z=x.manifest.contributes,B=_(i.extensionDetailActions,z?.menus,z?.actions,R),V=new Map(B.map(e=>[e.id,e.command]));for(let e of B){let t=`${x.id}:action.${e.id}.title`,n=r.app.i18nRegistry.t(t),i=n===t?e.title:n;L.push({label:i,value:`contrib:${e.id}`,color:e.color})}let H={open:e=>f(e),runCommand:async(e,t)=>{let{dispatchSlashCommand:n}=await import(`./command-dispatcher-Dnh3vtf4.js`).then(e=>e.a);await n(e,t?t.split(/\s+/).filter(Boolean):[],r)},runScript:async e=>{if(c(x).gated){o(E.t(`cmd.titlePlugin`),E.t(`cmd.extPluginUntrusted`));return}await y(x,e)}},U=P[0];U&&r.app.firePluginActivationEvent?.(`onView:${U.id}`);let W;if(t===`plugin-feishu-use`){let e=C(b,`.otto`,`feishu-use.json`),t=!1;try{if(w(e)){let n=JSON.parse(T(e,`utf8`));if(n.app_id){t=!0;let r=typeof n.app_id==`string`&&n.app_id.length>12?n.app_id.slice(0,8)+`***`+n.app_id.slice(-4):String(n.app_id),i=Array.isArray(n.allowed_chats)?n.allowed_chats.length:0,a=Array.isArray(n.trusted_chats)?n.trusted_chats.length:0,o=x.scope===`user`?`全局`:x.scope===`repository`?`仓库`:`项目`;W=[{kind:`row`,label:`App ID`,value:r},{kind:`row`,label:`消息白名单`,value:i>0?`${i} 个`:`空 — 拒绝所有消息`,tone:i>0?void 0:`warning`},...a>0?[{kind:`row`,label:`可信任 chat`,value:`${a} 个`}]:[],{kind:`row`,label:`配置`,value:`${o}级 · ${e}`,tone:`muted`}]}}}catch{}t||(W=[{kind:`text`,value:`未配置 — 点击 [重新配置] 引导设置`,tone:`warning`},{kind:`row`,label:`预期配置路径`,value:e,tone:`muted`}]),L.push({label:`重新配置`,value:`feishu-reconfigure`,color:`amber`})}let G=()=>{let e=r.app.pluginHealthTracker?.query(t)??{hookTimeouts:0,toolTimeouts:0},n=m(t);return{healthStats:{hookTimeouts:e.hookTimeouts,toolTimeouts:e.toolTimeouts,lastError:e.lastError},logEntries:n.map(e=>({ts:e.ts,level:e.level,msg:e.msg}))}},K=[{kind:`group`,title:E.t(`cmd.extPluginGroupTitle`)},{kind:`row`,label:E.t(`cmd.extPluginLabelDesc`),value:h},{kind:`row`,label:E.t(`cmd.extPluginLabelScope`),value:x.scope},{kind:`row`,label:E.t(`cmd.extPluginLabelTrust`),value:O},{kind:`row`,label:E.t(`cmd.extPluginLabelSurfaces`),value:M.trim()||`—`},...I?[{kind:`row`,label:E.t(`cmd.extPluginLabelContributions`),value:I}]:[],{kind:`row`,label:E.t(`cmd.extPluginLabelStatus`),value:d?E.t(`cmd.extEnableLabel`):E.t(`cmd.extDisableLabel`),tone:d?`success`:`error`}],q=g?[{kind:`text`,value:g.message,tone:g.kind===`success`?`success`:`error`}]:[];r.tui.modal.openModal({kind:`extensionDetail`,title:E.t(`cmd.extPluginTitle`,{name:p,state:d?`enabled`:`disabled`}),meta:`plugin · ${x.scope}`,metaStatus:{label:d?E.t(`cmd.extEnableLabel`):E.t(`cmd.extDisableLabel`),enabled:d},metaSuffix:O,actions:L,panelInfo:U?{title:U.title,modulePath:U.modulePath,exportName:U.exportName,pluginId:t}:void 0,dataListEntries:[...q,...W??K],...G(),onRefresh:()=>v(t,d,p,h),onAction:async e=>{if(e.startsWith(`contrib:`)){let n=V.get(e.slice(8));if(n)try{await s(n,H)}catch(e){l(E.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)}))}v(t,d,p,h)}else if(e===`trust`)F(r,x);else if(e===`untrust`)S(x.dir),r.app.reloadCapabilities?.().then(()=>{r.tui.plugins.setPluginRegistrations(r.app.getPluginRegistrations())}).finally(()=>{v(t,d,p,h,{kind:`success`,message:E.t(`cmd.pluginUntrusted`,{id:t})})});else if(e===`toggle`)try{await r.app.extensions.setEnabled(`plugin`,t,!d);try{await r.app.reloadCapabilities(),v(t,!d,p,h,{kind:`success`,message:E.t(`cmd.extToggled`,{name:p,verb:d?`Disabled`:`Enabled`,seg:``})})}catch(e){v(t,!d,p,h,{kind:`error`,message:E.t(`cmd.extToggleReloadFailed`,{verb:d?`Disabled`:`Enabled`,name:p,err:e instanceof Error?e.message:String(e)})})}}catch(e){v(t,d,p,h,{kind:`error`,message:E.t(`cmd.extToggleFailed`,{err:e instanceof Error?e.message:String(e)})})}else if(e===`feishu-reconfigure`)try{let{launchFeishuSetup:e}=await import(`./feishu-setup-DIcd3PMK.js`);await e(r),o(`plugin-feishu-use`,`配置完成。`)}catch(e){l(`feishu-use 配置向导失败:${e instanceof Error?e.message:String(e)}`)}else e===`reload`?r.app.reloadCapabilities?.().then(e=>{v(t,d,p,h,{kind:`success`,message:E.t(`cmd.pluginTrusted`,{id:t,count:e.mcpTools})})}).catch(e=>{v(t,d,p,h,{kind:`error`,message:E.t(`cmd.compatReloadFailed`,{err:e instanceof Error?e.message:String(e)})})}):e===`uninstall`&&await k({name:t,type:`plugin`,scope:x.scope===`project`||x.scope===`repository`?x.scope:`global`,ctx:r,onDone:()=>u()})},onClose:()=>u()})};u()}export{z as a,L as c,B as i,N as n,P as o,R as r,F as s,I as t};
|
|
7
|
-
//# sourceMappingURL=extension-plugin-B10fpJA7.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extension-plugin-B10fpJA7.js","names":[],"sources":["../src/commands/handlers/uninstall-action.ts","../src/commands/handlers/extension-plugin.ts"],"sourcesContent":["/**\n * uninstall-action.ts — 卸载动作的共享实现(`/extension` 面板唯一入口)。\n *\n * ## 由来\n * 卸载原本是独立的 `/uninstall` 命令(含 `--global/--project/--repo/--dry-run` 标志)。\n * 用户定稿:**入口收口到 `/extension` 面板**——用户在面板里看着某个扩展的详情,\n * 直接对\"眼前这一个\"执行卸载,比先记住名字再敲命令、还要自己挑 scope 更自然。\n *\n * 收口后的语义变化(有意):\n * - **scope 不再由用户指定**:面板里每个条目的 scope 是已知事实(plugin 有 `scope` 字段,\n * skill/mcp 由 `detectUninstallTarget` 在其所在层解析),故卸载的永远是\"你正在看的这一个\"。\n * 代价:跨 scope 同名条目只能逐个卸——但那本就是两个不同的东西,逐个卸更不易误删。\n * - **`--dry-run` 升级成默认的确认步**:`confirmAndUninstall` 先跑一次 dryRun 拿到删除计划,\n * 把计划原文摆进确认框再执行。命令行时代 dry-run 是可选标志(默认直接删),现在变成\n * 不可跳过的一步——删文件是不可逆操作,多一次确认换不回来的东西值得。\n *\n * ## 覆盖范围\n * `UninstallTarget = 'skill' | 'plugin' | 'mcp'` 三类,与 `packages/install` 引擎能力一致。\n * **agent 不支持卸载**——引擎侧就没有该分支(`/uninstall` 时代同样卸不掉),属既有缺口,\n * 不是入口搬家造成的。若将来补 agent,需先想清楚\"删定义文件\"对内置/插件提供的 agent\n * 是否是正确语义。\n */\nimport { i18next } from '@x-otto/tui'\nimport { uninstall, findWorkspaceRoot, recordPluginUninstalled } from '@x-otto/coding'\n\nimport type { UninstallTarget, InstallScope } from '@x-otto/coding'\nimport type { McpServersConfig } from '@x-otto/setting'\nimport type { CliContext } from '../../command-dispatcher'\n\n/** scope → i18n 标签(RFC-130:'repository' 分支不可省,否则静默回退成 'project' 标签)。 */\nexport function scopeToLabel(scope: InstallScope): string {\n switch (scope) {\n case 'global':\n return i18next.t('cmd.scopeGlobal')\n case 'repository':\n return i18next.t('cmd.scopeRepository')\n case 'project':\n default:\n return i18next.t('cmd.scopeProject')\n }\n}\n\nexport interface UninstallRequest {\n /** 条目名(skill 名 / plugin id / MCP server 名)。 */\n name: string\n type: UninstallTarget\n scope: InstallScope\n ctx: CliContext\n /** 卸载流程结束后回到的界面(面板详情页/列表页),失败与取消同样回调。 */\n onDone?: () => void\n}\n\nfunction uninstallOptions(\n ctx: CliContext,\n scope: InstallScope,\n dryRun: boolean,\n): { workspaceDir: string; workspaceRoot: string | undefined; global: boolean; dryRun: boolean } {\n return {\n workspaceDir: ctx.app.workspaceDir,\n workspaceRoot: scope === 'repository' ? findWorkspaceRoot(ctx.app.workspaceDir) : undefined,\n global: scope === 'global',\n dryRun,\n }\n}\n\n/**\n * 卸载前置确认——先取删除计划,再让用户拍板。\n *\n * 先 dryRun 的意义:`uninstall()` 的 plan 列出**具体会删哪些路径**(目录/配置项),\n * 这是用户判断\"是不是我要删的那个\"的唯一依据。直接弹一句\"确定卸载吗\"没有信息量。\n * dryRun 失败(如目标已不存在)则不进确认框,直接报错返回。\n */\nexport async function confirmAndUninstall(req: UninstallRequest): Promise<void> {\n const { name, type, scope, ctx, onDone } = req\n const mcpServers = ctx.app.settings.get('mcp_servers') as Record<string, unknown> | undefined\n\n const preview = await uninstall(\n name,\n type,\n uninstallOptions(ctx, scope, true),\n () => mcpServers,\n async () => {}, // dryRun 不落盘\n )\n\n if (!preview.success) {\n ctx.tui.info.showToast(\n i18next.t('cmd.titleUninstall'),\n i18next.t('cmd.uninstallFailed', { err: preview.error ?? i18next.t('cmd.unknownError') }),\n )\n onDone?.()\n return\n }\n\n const plan = preview.plan ?? []\n ctx.tui.modal.openModal({\n kind: 'confirm',\n title: i18next.t('cmd.uninstallConfirmTitle', { type, name }),\n message: [\n i18next.t('cmd.uninstallConfirmScope', { scopeLabel: scopeToLabel(scope) }),\n ...plan,\n ].join('\\n'),\n confirmLabel: i18next.t('cmd.uninstallConfirmYes'),\n // 删文件不可逆——走 dangerous 形态(默认焦点落 cancel,避免连按 Enter 误删)。\n dangerous: true,\n onConfirm: () => {\n void executeUninstall(req)\n },\n onCancel: () => onDone?.(),\n })\n}\n\n/**\n * 真正执行卸载 + 卸载后编排。\n *\n * 导出供测试直接驱动**执行段**(RFC-219 清单联动 / RFC-327 事件终态序)——生产路径只经\n * `confirmAndUninstall` 的确认框进入,那里是 modal 的同步回调,只能 `void` 起飞,\n * 测试无法 await 到它完成。\n *\n * ⚠️ 下面这段顺序是既有契约,从 `/uninstall` 原样搬来,**不可重排**:\n * 磁盘删除 → 清单清理(recordPluginUninstalled)→ 贡献撤销/服务停止(reloadCapabilities)\n * → 最后才发 `plugin.uninstalled`。该事件语义是\"卸载已终态\",提前发会让 observer 看到\n * 一个还没删干净的中间态(RFC-327 D1)。\n */\nexport async function executeUninstall(req: UninstallRequest): Promise<void> {\n const { name, type, scope, ctx, onDone } = req\n const mcpServers = ctx.app.settings.get('mcp_servers') as Record<string, unknown> | undefined\n\n const result = await uninstall(\n name,\n type,\n uninstallOptions(ctx, scope, false),\n () => mcpServers,\n async (servers) => {\n await ctx.app.settings.persist({ mcp_servers: servers as McpServersConfig })\n },\n )\n\n if (!result.success) {\n ctx.tui.info.showToast(\n i18next.t('cmd.titleUninstall'),\n i18next.t('cmd.uninstallFailed', { err: result.error ?? i18next.t('cmd.unknownError') }),\n )\n onDone?.()\n return\n }\n\n ctx.tui.info.showToast(\n i18next.t('cmd.titleUninstall'),\n i18next.t('cmd.uninstalled', { type, name, scope: scopeToLabel(scope) }),\n )\n\n // RFC-219 M1:plugin 卸载后清理 installed_plugins 清单 + disabled_plugins /\n // plugin_lifecycle.setupDone 残留。\n if (type === 'plugin') {\n await recordPluginUninstalled(ctx.app.settings, name)\n }\n\n try {\n await ctx.app.reloadCapabilities()\n ctx.tui.plugins.setPluginRegistrations(ctx.app.getPluginRegistrations())\n\n // RFC-327 D1:卸载终态事件(observer-only)。purge 不是卸载的替身,不得复用此事件。\n if (type === 'plugin') {\n void ctx.app.pluginModuleManager.emitMonitorEvent({\n type: 'plugin.uninstalled',\n pluginId: name,\n })\n }\n\n ctx.tui.info.showToast(i18next.t('cmd.titleUninstall'), i18next.t('cmd.uninstallRefreshed'))\n } catch (err) {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/extension',\n status: 'error',\n summary: i18next.t('cmd.uninstalledRefreshErr', {\n err: err instanceof Error ? err.message : String(err),\n }),\n })\n }\n\n onDone?.()\n}\n","/**\n * handlers/extension-plugin.ts — 扩展/代理委派/插件指令处理\n *\n * /extension — 管理可插拔扩展(MCP / skills / agents / plugins / panels)\n * /delegates — 切换后台任务面板\n * /delegate — 委派后台 agent 作业\n */\nimport { i18next } from '@x-otto/tui'\nimport { homedir } from 'node:os'\nimport { readFileSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport {\n trustPlugin,\n untrustPlugin,\n evaluatePluginTrust,\n grantPluginCapabilities,\n buildExtensionContext,\n openTarget,\n runPluginScript,\n POINT,\n resolveActions,\n dispatch,\n installPluginFromPackage,\n resolvePackageDir,\n readHighRiskCapabilities,\n readPluginLogTail,\n recordPluginInstalled,\n InstallManager,\n defaultPostInstallExecutor,\n} from '@x-otto/coding'\nimport { discoverPluginsWithBundled } from '../../discover-with-bundled'\nimport type { DiscoveredPlugin, DispatchCaps } from '@x-otto/coding'\nimport type { ExtensionDetailAction, DataListEntry } from '@x-otto/tui'\nimport type { CliContext } from '../../command-dispatcher'\nimport { EXTENSION_ALIASES } from '../extension'\nimport { confirmAndUninstall } from './uninstall-action'\n\nimport type { InstallScope } from '@x-otto/coding'\n\n/**\n * skill 来源 → 可卸载 scope。返回 null = 该 skill 不可独立卸载。\n *\n * `plugin` 来源的 skill 由宿主插件带入,卸它应该卸插件(删 skill 文件会在插件下次加载时\n * 重新出现,是假卸载);`inline` 是代码内建,没有文件可删。\n */\nfunction skillUninstallScope(sourceType: string): InstallScope | null {\n if (sourceType === 'project') return 'project'\n if (sourceType === 'global') return 'global'\n return null\n}\n\n/** ext skill 来源标签段(原 cmd.extSkillSourceLabel callSiteFmt 内联逻辑)。 */\nfunction extSkillSourceSeg(type: string, pluginId?: string): string {\n const labels: Record<string, string> = { project: 'project', global: 'global', plugin: 'plugin', inline: 'inline' }\n const base = labels[type] ?? type\n return pluginId ? `${base} (${pluginId})` : base\n}\n\n/**\n * 汇总某插件在 provider/tools/renderers/statusItems 四个新轴的贡献计数(RFC-124 P0-B,\n * R11 权限审计基线)——纯函数,输入为各轴按 source/pluginId 过滤后的原始计数,输出为一行\n * 摘要文本;全部计数为 0 时返回 `undefined`(不为无贡献插件的详情页添加噪音行)。\n */\nexport function formatPluginContributionsSummary(counts: {\n providerApis: number\n tools: number\n renderers: number\n statusItems: number\n fileViewers?: number\n}): string | undefined {\n const parts: string[] = []\n if (counts.providerApis > 0) parts.push(`provider(${counts.providerApis})`)\n if (counts.tools > 0) parts.push(`tools(${counts.tools})`)\n if (counts.renderers > 0) parts.push(`renderers(${counts.renderers})`)\n if (counts.statusItems > 0) parts.push(`statusItems(${counts.statusItems})`)\n if (counts.fileViewers && counts.fileViewers > 0) parts.push(`fileViewers(${counts.fileViewers})`)\n return parts.length ? parts.join(', ') : undefined\n}\n\n/**\n * 高危能力 id → 文案 key 的映射(RFC-125 D1)。`pendingCapabilities` 的元素是\n * `HIGH_RISK_CAPABILITIES` 的子集,未知值(理论不可达,防御式兜底)静默丢弃而非抛错——\n * 文案缺失不应阻塞信任流程。\n *\n * RFC-126 M2:改为 export,供 `confirmHighRiskCapabilities` 与 `runPluginInstall` 共用。\n * RFC-210 D1:capability 改名 `ui.renderer`→`tui.renderer`(旧名经 manifest transform 映射,\n * 此处只会看到新名)。\n *\n * 覆盖率修复(终局巡检发现):此前本函数只覆盖 `HIGH_RISK_CAPABILITIES`(当前 9 项)中的\n * 4 项(provider/tools/tui.renderer/network),其余 5 项(wire-protocol/a2ui.component/\n * panel.backend/input.resolver/a2ui.renderer)声明时命中 `default: undefined` 被\n * `if (label) body.push(...)` 静默省略——用户在信任确认弹窗里看不到这些高危能力的说明就\n * 点了确认,是信任流程的信息缺失(非崩溃/阻断类 bug,但削弱了 RFC-125 D1\"用户知情同意\"\n * 的设计意图)。补齐全部 9 项,与 `HIGH_RISK_CAPABILITIES` 数组一一对应。\n */\nexport function pluginCapabilityLabel(cap: string): string | undefined {\n switch (cap) {\n case 'provider':\n return i18next.t('cmd.pluginCapabilityProvider')\n case 'tools':\n return i18next.t('cmd.pluginCapabilityTools')\n case 'tui.renderer':\n return i18next.t('cmd.pluginCapabilityTuiRenderer')\n case 'network':\n return i18next.t('cmd.pluginCapabilityNetwork')\n case 'wire-protocol':\n return i18next.t('cmd.pluginCapabilityWireProtocol')\n case 'a2ui.component':\n return i18next.t('cmd.pluginCapabilityA2uiComponent')\n case 'panel.backend':\n return i18next.t('cmd.pluginCapabilityPanelBackend')\n case 'input.resolver':\n return i18next.t('cmd.pluginCapabilityInputResolver')\n case 'a2ui.renderer':\n return i18next.t('cmd.pluginCapabilityA2uiRenderer')\n default:\n return undefined\n }\n}\n\n/**\n * 插件信任交互动作(确认 → trustPlugin(+grantPluginCapabilities) → 热重载 → 同步面板)。\n * 由 `/extension` 与 `/trust` 两处共用,避免重复。\n *\n * 无可执行面且无待授权高危能力 → 直接信任(无风险);否则弹确认——列出即将放行的受信任门面\n * (P3 安全边界)与即将授予的高危能力(RFC-125 D1:`surface.any` 已含 `highRiskCapabilities`\n * 布尔位(RFC-124 C1),此前弹窗虽会命中但正文从未渲染 `pendingCapabilities` 列表、且确认后\n * 从未调用 `grantPluginCapabilities`——用户点了\"信任\"能力仍卡在 pending,装载路径仍会跳过,\n * 见 RFC-125 §0.2。本次修复:body 追加能力段 + applyTrust 追加真实授权调用)。\n */\nexport function promptPluginTrust(ctx: CliContext, p: DiscoveredPlugin): void {\n const verdict = evaluatePluginTrust(p)\n const pendingCapabilities = verdict.pendingCapabilities\n const applyTrust = (): void => {\n trustPlugin(p.dir)\n if (pendingCapabilities.length > 0) {\n grantPluginCapabilities(p.dir, pendingCapabilities)\n }\n ctx.tui.info.showToast(i18next.t('cmd.titlePlugin'), i18next.t('cmd.pluginTrusting', { id: p.id }))\n void ctx.app.reloadCapabilities().then(\n (r) => {\n // RFC-131 T8:统一入口同步全部 TUI 侧贡献(状态栏/渲染器/面板)。\n // 面板另经 /extension 详情 dialog 即时呈现(getPluginPanelRegistrations 现算),\n // 本调用是常驻状态栏/消息渲染器路径所需(RFC-105 D5/M105-4、D6/M105-5a)。\n ctx.tui.plugins.setPluginRegistrations(ctx.app.getPluginRegistrations())\n ctx.tui.info.showToast(i18next.t('cmd.titlePlugin'), i18next.t('cmd.pluginTrusted', { id: p.id, count: r.mcpTools }))\n },\n (e: unknown) =>\n ctx.tui.info.showToast(i18next.t('cmd.titlePlugin'), i18next.t('cmd.compatReloadFailed', { err: e instanceof Error ? e.message : String(e) })),\n )\n }\n\n const surface = verdict.surface\n if (!surface.any && pendingCapabilities.length === 0) {\n applyTrust()\n return\n }\n const body = [i18next.t('cmd.pluginTrustConfirmIntro')]\n if (surface.mcpJson) body.push(`• ${i18next.t('cmd.pluginSurfaceMcp')}`)\n if (surface.commands) body.push(`• ${i18next.t('cmd.pluginSurfaceCommands')}`)\n if (surface.scripts) body.push(`• ${i18next.t('cmd.pluginSurfaceScripts')}`)\n if (surface.panels) body.push(`• ${i18next.t('cmd.pluginSurfacePanels')}`)\n if (surface.skills) body.push(`• ${i18next.t('cmd.pluginSurfaceSkills')}`)\n if (surface.agents) body.push(`• ${i18next.t('cmd.pluginSurfaceAgents')}`)\n if (pendingCapabilities.length > 0) {\n body.push('', i18next.t('cmd.pluginCapabilityIntro'))\n for (const cap of pendingCapabilities) {\n const label = pluginCapabilityLabel(cap)\n if (label) body.push(`• ${label}`)\n }\n }\n body.push('', i18next.t('cmd.pluginTrustConfirmHint'))\n ctx.tui.modal.openModal({\n kind: 'select',\n title: i18next.t('cmd.pluginTrustConfirmTitle', { id: p.id }),\n subtitle: body.join('\\n'),\n layout: 'buttons',\n options: [\n { label: i18next.t('cmd.pluginTrustConfirmAction'), value: 'trust', color: 'accent' },\n { label: i18next.t('cmd.commonCancel'), value: 'cancel', color: 'gray' },\n ],\n onSelect: (v: string) => {\n if (v === 'trust') applyTrust()\n },\n onCancel: () => {},\n })\n}\n\n// ── RFC-126 M2:TUI 内插件安装 ──\n\n/**\n * 高危能力确认弹窗(RFC-126 D2)。\n *\n * 复用 `pluginCapabilityLabel` 映射(与 `promptPluginTrust` 共用同一渲染逻辑),\n * 但只做能力列表展示 + [安装并授权] / [取消],不涉及 trust/surface 检查。\n * 返回 true = 用户确认安装,false = 取消。\n */\nexport async function confirmHighRiskCapabilities(\n pluginId: string,\n caps: string[],\n ctx: CliContext,\n): Promise<boolean> {\n if (caps.length === 0) return true\n const body = [i18next.t('cmd.pluginCapabilityIntro')]\n for (const cap of caps) {\n const label = pluginCapabilityLabel(cap)\n if (label) body.push(`• ${label}`)\n }\n return new Promise<boolean>((resolve) => {\n ctx.tui.modal.openModal({\n kind: 'select',\n title: i18next.t('cmd.pluginInstallConfirmTitle', { name: pluginId }),\n subtitle: body.join('\\n'),\n layout: 'buttons',\n options: [\n { label: i18next.t('cmd.pluginInstallConfirmAction'), value: 'install', color: 'accent' },\n { label: i18next.t('cmd.commonCancel'), value: 'cancel', color: 'gray' },\n ],\n onSelect: (v: string) => resolve(v === 'install'),\n onCancel: () => resolve(false),\n })\n })\n}\n\n/**\n * 共用安装核心流程(RFC-126 D2)。供 `/model` 安装按钮(M2-04)、M3 welcome dialog 两处复用。\n *\n * 流程:resolve → read manifest.capabilities → confirm(如需)→ install →\n * applyHighRiskGrant → reloadCapabilities → setNeedsSetup(false) → open /model\n *\n * @returns 是否真正安装成功(round 7 UX 修复)——此前恒 `Promise<void>`,四个失败分支\n * (非法名/包未找到/高危能力被拒/安装抛异常)与成功路径对调用方不可区分。welcome dialog\n * 据此无条件展示\"下一步:/auth\",安装失败时会给用户一个误导性的\"已装好,去授权\"提示。\n * `false` 覆盖:输入校验失败、包未解析到、用户拒绝高危能力确认、`installPluginFromPackage`\n * 抛异常。`reloadCapabilities` 失败不计入 false——插件文件已落地,仅能力刷新失败,\n * 现有逻辑本就继续放行(用户可 /model refresh 手动重试),语义不变。\n */\nexport async function runPluginInstall(\n pluginName: string,\n ctx: CliContext,\n /**\n * autoTrust:向导模式——跳过信任弹窗(官方 provider 扩展默认信任),\n * 不自动打开 /model(向导有自己的步骤③选择模型的专属面板)。\n * 同时压制 toast:安装成功/失败由调用方自行处理。\n */\n opts?: {\n autoTrust?: boolean\n onProgress?: (phase: string) => void\n /**\n * RFC-326 M2-05:reload 后该插件仍有**未授予**的高危能力时回调。\n *\n * 为何需要:声明了 `provider` 能力但未获授权时,插件的 providers 会被\n * fail-closed **整体跳过**(plugin-provider-manager.ts:93)——安装\"成功\"却\n * 贡献零个模型。非向导路径靠 toast 告知,但向导模式压制了全部 toast\n * (见 autoTrust 注释),用户只看到 \"✓ Installed\" 却不知道还缺一次授权。\n *\n * 用回调而非改返回类型:`runPluginInstall` 另有两个调用方忽略返回值\n * (model-auth.ts:324/335),拓宽 `Promise<boolean>` 会波及它们且无收益。\n */\n onPendingCapabilities?: (pluginId: string) => void\n },\n): Promise<boolean> {\n const autoTrust = opts?.autoTrust ?? false\n const onProgress = opts?.onProgress\n const onPendingCapabilities = opts?.onPendingCapabilities\n\n // 输入校验:拒绝路径穿越、相对路径前缀、家目录前缀\n if (pluginName.includes('..') || pluginName.startsWith('/') || pluginName.startsWith('./') || pluginName.startsWith('~/')) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallInvalidName', { name: pluginName }))\n return false\n }\n\n const pkgName = EXTENSION_ALIASES[pluginName]?.pkg ?? pluginName\n\n // 解析包目录\n let pkgDir = resolvePackageDir(pkgName)\n // RFC-341 修复:`resolvePackageDir` 只能解析\"已在宿主 node_modules 可 require 到,\n // 或落在本仓 workspace glob 内\"的包——用户在自己机器上 npm install -g @x-otto/cli\n // 后,通过 Setup Wizard/`otto extension install` 装任意公网 npm 插件(如\n // deepseek-provider)时,该包既不在 node_modules 也不在任何 workspace glob,\n // 此前直接报\"无法解析包\"/`pluginInstallNotFound`——同一缺陷已在 `extension-link.ts`\n // 的 CLI 直接命令路径修复(RFC-341),本函数是 Setup Wizard 走的独立路径,需同样修复。\n // 找不到本地包时,回退到 `InstallManager.install()` 真实从 npm registry 下载安装,\n // 成功后把物化目录当作 pkgDir 使用,后续逻辑(能力确认/授权/reload)保持不变。\n let installedViaNpm: { id: string; dir: string; created: boolean; compileWarnings: string[]; version?: string } | undefined\n if (!pkgDir) {\n try {\n onProgress?.('Downloading from npm registry')\n const manager = new InstallManager()\n const npmResult = await manager.install(pkgName, {\n global: true,\n as: 'plugin',\n postInstallExecutor: defaultPostInstallExecutor,\n })\n if (npmResult.success && npmResult.targetPath) {\n pkgDir = npmResult.targetPath\n installedViaNpm = {\n id: npmResult.name ?? pkgName,\n dir: npmResult.targetPath,\n created: true,\n compileWarnings: [],\n version: npmResult.manifest?.version,\n }\n } else if (!autoTrust) {\n ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallFailed', { err: npmResult.error ?? '未知错误' }))\n }\n } catch (e) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallFailed', { err: e instanceof Error ? e.message : String(e) }))\n }\n }\n if (!pkgDir) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallNotFound', { pkg: pkgName }))\n return false\n }\n\n // 读取 manifest 获取声明的高危能力(单一真相源,@x-otto/plugin readHighRiskCapabilities——\n // 终局架构 review 2026-07-20:此前与 plugin.ts 的 CLI 安装路径各自内联实现同一段逻辑)。\n const caps = readHighRiskCapabilities(pkgDir)\n\n // 高危能力确认——向导模式下跳过(官方 provider 扩展默认信任)\n // 注:npm 回退路径已实际下载安装(不可逆的磁盘写入),但确认流程仍在此处统一执行——\n // 拒绝时留下已下载的插件目录不做清理,与既有 installPluginFromPackage 路径的失败语义\n // 一致(该路径本身也不做失败回滚),用户可通过 `otto extension uninstall` 清理。\n if (!autoTrust) {\n const confirmed = await confirmHighRiskCapabilities(pluginName, caps, ctx)\n if (!confirmed) {\n ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallCancelled'))\n return false\n }\n }\n\n // 安装(若已通过 npm 回退物化,跳过重复安装,直接复用其结果)\n let result: { id: string; dir: string; created: boolean; compileWarnings: string[]; version?: string }\n if (installedViaNpm) {\n result = installedViaNpm\n } else {\n try {\n onProgress?.('Installing plugin')\n result = await installPluginFromPackage({ pkg: pkgName })\n } catch (e) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallFailed', { err: e instanceof Error ? e.message : String(e) }))\n return false\n }\n }\n\n // 向导模式静默安装,不弹 toast\n if (!autoTrust) {\n ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstalled', { seg: result.created ? `Installed ${result.id}` : `${result.id} already up to date` }))\n }\n\n // RFC-219 M1/D1c:记录到 installed_plugins 清单(本路径恒为用户级/'global' 安装,\n // installPluginFromPackage 未传 ottoDir 时默认 OTTO_HOME)。capabilities 复用上方已计算\n // 的 caps(信任确认弹窗同一份数据),不重新读盘。\n await recordPluginInstalled(ctx.app.settings, {\n id: result.id,\n source: 'npm',\n sourceSpec: pkgName,\n version: result.version,\n scope: 'global',\n capabilities: caps,\n })\n\n // RFC-303 D4:安装完成 → monitor 事件(observer-only)。仅 TUI 路径广播——CLI\n // standalone 安装与运行中会话是不同进程,跨进程无法触达 monitor(同 M6 kill no-op 边界)。\n void ctx.app.pluginModuleManager.emitMonitorEvent({\n type: 'plugin.installed',\n pluginId: result.id,\n })\n\n // 编译警告——向导模式静默忽略,非向导模式 toast\n for (const w of result.compileWarnings) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallFailed', { err: w }))\n }\n\n // 高危能力授权(复用与 CLI 完全相同的写入路径)\n if (caps.length > 0) {\n onProgress?.('Authorizing capabilities')\n trustPlugin(result.dir)\n grantPluginCapabilities(result.dir, caps)\n }\n\n // 自动 reload\n try {\n onProgress?.('Reloading capabilities')\n const r = await ctx.app.reloadCapabilities()\n if (r.pendingProviderCapabilities.includes(result.id)) {\n // toast 维持原条件(向导模式压制全部 toast);回调则**无条件**触发——\n // 它正是向导用来在成功态显示\"还缺一次授权\"的通道。\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.pluginInstallPendingCaps', { id: result.id }))\n onPendingCapabilities?.(result.id)\n }\n } catch (e) {\n if (!autoTrust) ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.compatReloadFailed', { err: e instanceof Error ? e.message : String(e) }))\n }\n\n // RFC-326 M1-04:不再硬编码 `setNeedsSetup(false)`。\n //\n // 装上 provider 插件**不等于**已经可用——还差凭据与一个真正能用的模型。此前这里\n // 无条件宣布\"已配置\",导致:输入框 placeholder 从引导文案变回普通提示、Home 恢复\n // 卡消失,而用户其实连登录都还没做,下一步该干什么反而没了线索。\n //\n // 改为走 `refreshModelsNow()`——它重算 availableModelIds 后按**真实结果**调用\n // setNeedsSetup(见 interactive.ts 的 refreshModelsNow 实现),装完即可用时同样会\n // 翻成 false,只是不再\"假设\"可用。刷新失败不阻断安装主流程。\n await ctx.callbacks.refreshModelsNow().catch(() => {})\n\n // RFC-134 M1:feishu-use 专属安装后引导——弹出交互式配置对话框(QR 扫码或手动输入凭据)。\n // 其他插件的安装完成后直接打开 /model 选模型(它们的 MCP 工具立刻可用,无需配置向导)。\n if (pluginName === 'plugin-feishu-use') {\n try {\n const { launchFeishuSetup } = await import('./feishu-setup')\n await launchFeishuSetup(ctx)\n } catch {\n // setup 失败不影响安装本身——用户可稍后用 `otto feishu config init` 手动配置。\n }\n return true\n }\n\n // 向导模式不打开 /model(向导有自己的步骤③选择模型)\n if (!autoTrust) {\n await new Promise<void>((resolve) => setImmediate(resolve))\n const { handleModel } = await import('./model-auth')\n await handleModel([], ctx)\n }\n return true\n}\n\n/**\n * /delegates — Toggle the Delegates panel.\n */\nexport async function handleDelegatesToggle(\n _args: string[],\n ctx: CliContext,\n): Promise<void> {\n ctx.tui.panes.togglePane('backgroundTasks')\n}\n\n/**\n * /job <directive>\n * Start a background agent job (worktree-isolated, full tools incl. MCP).\n * /job revert <id> to undo an applied job.\n */\nexport async function handleJobStart(\n args: string[],\n ctx: CliContext,\n): Promise<void> {\n const directive = args.join(' ')\n if (!directive) {\n ctx.tui.info.showToast(i18next.t('cmd.titleJob'), i18next.t('cmd.jobUsage'))\n return\n }\n\n const revertMatch = /^revert\\s+(\\S+)$/.exec(directive)\n if (revertMatch) {\n const id = revertMatch[1]!\n const out = ctx.app.agentJobs.revert(id)\n if (out.applied) {\n ctx.tui.info.showToast(i18next.t('cmd.titleReverted'), i18next.t('cmd.jobReverted', { id: id, msg: out.message }))\n } else {\n const conflicts = out.conflictPaths.length\n ? i18next.t('cmd.jobConflictFiles', { joinedPaths: out.conflictPaths.join('\\n ') })\n : ''\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: `/go revert ${id}`,\n status: 'error',\n summary: i18next.t('cmd.jobRevertFailed', { id: id, status: out.status, msg: out.message, conflicts: conflicts }),\n })\n }\n return\n }\n\n try {\n const job = ctx.app.agentJobs.start({\n title: directive.slice(0, 60),\n prompt: directive,\n sessionId: ctx.state.session.id,\n origin: 'user',\n })\n ctx.tui.info.showToast(i18next.t('cmd.titleDelegated'), i18next.t('cmd.delegated', { title: job.title }))\n } catch (e) {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/go',\n status: 'error',\n summary: i18next.t('cmd.delegateFailed', { err: e instanceof Error ? e.message : String(e) }),\n })\n }\n}\n\n/**\n * /extension | /extensions | /ext — Manage pluggable extensions (MCP / skills / agents).\n * List → select item for detail → toggle enable / reconnect (MCP).\n */\nexport async function handleExtension(\n _args: string[],\n ctx: CliContext,\n): Promise<void> {\n const notify = (title: string, content: string) =>\n ctx.tui.info.showToast(title, content)\n\n const say = (text: string) =>\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/extension',\n status: 'error',\n summary: text,\n })\n\n\n // RFC-166:`/extension` 列表页——独立 hard modal(Select.Group 组合式规范),不再走\n // 通用 kind:'select' 通道(扁平 SelectOption[] 字符串 group 字段的旧路径)。分组/单行详情\n // 折叠交由 ExtensionsListDialog 渲染层负责,本处只装配纯数据 entries/menuContributions。\n const openList = () => {\n const items = ctx.app.extensions.list()\n // 插件面板不再作为独立列表项——它是插件详情 dialog 的 body(选中插件即在详情里渲染)。\n\n // RFC-105 D5/M105-4:全局菜单贡献点(shell/menu/item)——任意插件可往此列表追加固定入口\n // (区别于 extensionDetailActions 的「查看某插件详情」per-viewed-plugin scope)。\n // 宿主拥有布局(R6):贡献条目统一归入独立分组,追加在内置列表之后,不打散既有分类。\n const menuContribs = ctx.app.getGlobalMenuItems?.() ?? []\n const menuCmdById = new Map(menuContribs.map((r) => [r.id, r.command]))\n\n ctx.tui.modal.openModal({\n kind: 'extensionsList',\n entries: items.map((i) => ({\n category: i.category,\n id: i.id,\n name: i.name,\n enabled: i.enabled,\n description: i.description,\n applies: i.applies,\n })),\n menuContributions: [\n // RFC-201 M3:发现入口——搜索已注册 registry 并一键安装(数据面复用 /search)。\n { id: '__discover__', title: 'Discover plugins (search registries)…' },\n ...menuContribs.map((r) => ({ id: r.id, title: r.title })),\n ],\n onSelect: (value: string) => {\n if (!value) return\n if (value === 'contrib:__discover__') {\n void openDiscover()\n return\n }\n if (value.startsWith('contrib:')) {\n const id = value.slice('contrib:'.length)\n const cmd = menuCmdById.get(id)\n if (!cmd) return\n void dispatch(cmd, {\n open: (t) => openTarget(t),\n runCommand: async (cmdName, cmdArgs) => {\n const { dispatchSlashCommand } = await import('../../command-dispatcher')\n await dispatchSlashCommand(cmdName, cmdArgs ? cmdArgs.split(/\\s+/).filter(Boolean) : [], ctx)\n },\n }).catch((e: unknown) => notify(i18next.t('cmd.titlePlugin'), e instanceof Error ? e.message : String(e)))\n return\n }\n const sep = value.indexOf(':')\n const category = value.slice(0, sep)\n const id = value.slice(sep + 1)\n openDetail(category, id)\n },\n onCancel: () => {},\n })\n }\n\n /**\n * RFC-201 M3:发现入口——openInput 收搜索词 → searchRegistries 数据面 → openSelect 结果\n * (capabilities 预披露 + @来源 + stale 标注)→ 选中即 dispatch 既有 /install 命令\n * (--sub-path 透传;安装确认/信任门走既有流,本入口零新增安装逻辑)。\n */\n const openDiscover = async () => {\n const keyword = await ctx.tui.modal.openModal({\n kind: 'input',\n title: 'Discover plugins',\n description: ['搜索已注册 registry(/registry list 查看源;/registry add 注册第三方源)'],\n placeholder: 'keyword… (e.g. weather / oauth / provider)',\n })\n if (!keyword?.trim()) { openList(); return }\n\n const { searchRegistries } = await import('./registry')\n const { hits, warnings } = await searchRegistries(ctx, keyword.trim())\n if (hits.length === 0) {\n notify(i18next.t('cmd.titlePlugin'), `No registry entries match '${keyword.trim()}'${warnings.length ? ` (${warnings.length} source warning)` : ''}`)\n openList()\n return\n }\n\n ctx.tui.modal.openModal({\n kind: 'select',\n title: `Discover: '${keyword.trim()}' (${hits.length})`,\n subtitle: warnings.length > 0 ? `⚠ ${warnings.join(' · ')}` : undefined,\n options: hits.map((h, i) => ({\n label: `${h.entry.id} (${h.entry.type}) [${(h.entry.capabilities ?? []).join(', ') || '-'}] @${h.sourceName}${h.stale ? ' (stale)' : ''}`,\n description: h.entry.description,\n value: String(i),\n })),\n onSelect: (value: string) => {\n const hit = hits[Number(value)]\n if (!hit) return\n const args = ['--as', hit.entry.type, ...(hit.entry.subPath ? ['--sub-path', hit.entry.subPath] : []), hit.entry.source]\n void (async () => {\n const { dispatchSlashCommand } = await import('../../command-dispatcher')\n await dispatchSlashCommand('install', args, ctx)\n })().catch((e: unknown) => notify(i18next.t('cmd.titlePlugin'), e instanceof Error ? e.message : String(e)))\n },\n onCancel: () => openList(),\n })\n }\n\n /**\n * Skill 详情面板(对齐插件详情 UI/UX,RFC-166 D10 后续):分组展示概述/触发方式/依赖/标签/\n * 加载错误,聚合可用动作(启用/禁用)。此前 skill 类别走 openDetail 末尾的通用 select 回退\n * (单个启用/禁用选项,无元数据展示),用户看不到来源/触发模式/依赖这些排障关键信息。\n */\n const openSkillDetail = (id: string) => {\n const reg = ctx.app.skillRegistry?.get(id)\n if (!reg) {\n openList()\n return\n }\n const enabled = reg.status !== 'disabled'\n const meta = reg.definition.metadata\n\n const entries: DataListEntry[] = [\n { kind: 'group', title: i18next.t('cmd.extSkillGroupOverview') },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelDesc'), value: meta.description || '—' },\n {\n kind: 'row',\n label: i18next.t('cmd.extSkillLabelStatus'),\n value: reg.status,\n // 真实用户反馈修复(启用=绿/禁用=红):此前 `enabled ? 'success' : 'muted'` 只有\n // 两态,`error` 状态因 `enabled = status !== 'disabled'` 恒为 true 被误标成绿色——\n // 改为精确基于 reg.status 三态判定(available/loaded=绿,disabled=红,error=红)。\n tone: reg.status === 'error' || reg.status === 'disabled' ? 'error' : 'success',\n },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelSource'), value: i18next.t('cmd.extSkillSourceLabel', { seg: extSkillSourceSeg(reg.source.type, reg.source.id) }), tone: 'muted' },\n\n { kind: 'group', title: i18next.t('cmd.extSkillGroupTrigger') },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelTriggerMode'), value: meta.trigger === 'manual' ? i18next.t('cmd.extSkillTriggerManual') : i18next.t('cmd.extSkillTriggerAuto') },\n ...(meta.priority !== undefined\n ? [{ kind: 'row', label: i18next.t('cmd.extSkillLabelPriority'), value: String(meta.priority) } as DataListEntry]\n : []),\n\n { kind: 'group', title: i18next.t('cmd.extSkillGroupDependencies') },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelDependencies'), value: meta.dependencies?.length ? meta.dependencies.join(', ') : i18next.t('cmd.extSkillNoDependencies'), tone: meta.dependencies?.length ? undefined : 'muted' },\n\n { kind: 'group', title: i18next.t('cmd.extSkillGroupTags') },\n { kind: 'row', label: i18next.t('cmd.extSkillLabelTags'), value: meta.tags?.length ? meta.tags.join(', ') : i18next.t('cmd.extSkillNoTags'), tone: meta.tags?.length ? undefined : 'muted' },\n ]\n\n if (reg.status === 'error' && reg.error) {\n entries.push(\n { kind: 'group', title: i18next.t('cmd.extSkillGroupError') },\n { kind: 'text', value: reg.error, tone: 'error' },\n )\n }\n\n // \"查看正文\"按钮(SKILL.md 去 frontmatter 后的完整正文,元数据外唯一未暴露给用户的\n // 内容)——正文可能很长,不适合塞进 DataList 行内,走 ExtensionDetailDialog 内建的\n // FileContentViewer 子视图(可滚动,Esc 退回本详情页)。\n ctx.tui.modal.openModal({\n kind: 'extensionDetail',\n title: i18next.t('cmd.extSkillTitle', { name: id, state: (enabled) ? 'enabled' : 'disabled' }),\n meta: `skill · ${reg.source.type}`,\n metaStatus: { label: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), enabled },\n actions: [\n {\n label: enabled ? i18next.t('cmd.extDisableLabel') : i18next.t('cmd.extEnableLabel'),\n value: 'toggle',\n color: enabled ? 'amber' : 'green',\n },\n { label: i18next.t('cmd.extViewFullTextLabel'), value: 'view-full-text', color: 'gray' },\n // 卸载入口(原 `/uninstall` 命令收口至此)。**仅独立安装的 skill 可卸**:\n // `plugin` 来源的随宿主插件走(卸插件才对),`inline` 是代码内建、无文件可删。\n ...(skillUninstallScope(reg.source.type)\n ? [{ label: i18next.t('cmd.uninstallActionLabel'), value: 'uninstall', color: 'red' as const }]\n : []),\n ],\n dataListEntries: entries,\n fullText: { title: i18next.t('cmd.extSkillFullTextTitle', { name: id }), content: reg.definition.body || i18next.t('cmd.extSkillNoBody') },\n onAction: async (v: string) => {\n if (v === 'toggle') {\n try {\n await ctx.app.extensions.setEnabled('skill', id, !enabled)\n notify(\n i18next.t('cmd.titleExtension'),\n i18next.t('cmd.extToggled', { name: id, verb: (enabled) ? 'Disabled' : 'Enabled', seg: (true) ? ' — applies next turn' : '' }),\n )\n } catch (e) {\n say(i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }))\n }\n openSkillDetail(id)\n return\n }\n if (v === 'uninstall') {\n const scope = skillUninstallScope(reg.source.type)\n if (!scope) return\n await confirmAndUninstall({\n name: id,\n type: 'skill',\n scope,\n ctx,\n // 卸载后该 skill 已不存在,回详情页会落空 → 回列表。\n onDone: () => openList(),\n })\n }\n },\n onClose: () => openList(),\n })\n }\n\n /**\n * Agent 详情面板(对齐 Skill 详情 UI/UX):分组展示概述/任务类型能力/工具权限/执行配置,\n * 聚合可用动作(启用/禁用 · 查看系统提示词模板正文)。此前 agent 类别走 openDetail 末尾的\n * 通用 select 回退(单个启用/禁用选项),`AgentProfile` 的 taskTypes/capabilities/\n * defaultTools/disallowedTools/preferredModelTier/allowSubagents 等字段完全没有暴露。\n */\n const openAgentDetail = (name: string) => {\n const profile = ctx.app.declarativeAgentProfiles.find((p) => p.name === name)\n if (!profile) {\n openList()\n return\n }\n const disabledAgents = new Set(ctx.app.settings.get('disabled_agents') ?? [])\n const enabled = !disabledAgents.has(name)\n\n const entries: DataListEntry[] = [\n { kind: 'group', title: i18next.t('cmd.extAgentGroupOverview') },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelDesc'), value: profile.description || '—' },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelStatus'), value: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), tone: enabled ? 'success' : 'error' },\n\n { kind: 'group', title: i18next.t('cmd.extAgentGroupCapabilities') },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelTaskTypes'), value: profile.taskTypes.length ? profile.taskTypes.join(', ') : i18next.t('cmd.extAgentNoTaskTypes'), tone: profile.taskTypes.length ? undefined : 'muted' },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelCapabilities'), value: profile.capabilities.length ? profile.capabilities.join(', ') : i18next.t('cmd.extAgentNoCapabilities'), tone: profile.capabilities.length ? undefined : 'muted' },\n\n { kind: 'group', title: i18next.t('cmd.extAgentGroupTools') },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelDefaultTools'), value: profile.defaultTools?.length ? profile.defaultTools.join(', ') : i18next.t('cmd.extAgentAllTools'), tone: profile.defaultTools?.length ? undefined : 'muted' },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelDisallowedTools'), value: profile.disallowedTools?.length ? profile.disallowedTools.join(', ') : i18next.t('cmd.extAgentNoDisallowedTools'), tone: profile.disallowedTools?.length ? undefined : 'muted' },\n\n { kind: 'group', title: i18next.t('cmd.extAgentGroupExecution') },\n { kind: 'row', label: i18next.t('cmd.extAgentLabelPromptMode'), value: profile.promptMode ?? 'append', tone: 'muted' },\n ...(profile.preferredModelTier\n ? [{ kind: 'row', label: i18next.t('cmd.extAgentLabelModelTier'), value: profile.preferredModelTier } as DataListEntry]\n : []),\n { kind: 'row', label: i18next.t('cmd.extAgentLabelAllowSubagents'), value: profile.allowSubagents ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), tone: profile.allowSubagents ? undefined : 'muted' },\n ]\n\n ctx.tui.modal.openModal({\n kind: 'extensionDetail',\n title: i18next.t('cmd.extAgentTitle', { name: name, state: (enabled) ? 'enabled' : 'disabled' }),\n meta: 'agent',\n metaStatus: { label: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), enabled },\n actions: [\n {\n label: enabled ? i18next.t('cmd.extDisableLabel') : i18next.t('cmd.extEnableLabel'),\n value: 'toggle',\n color: enabled ? 'amber' : 'green',\n },\n { label: i18next.t('cmd.extViewFullTextLabel'), value: 'view-full-text', color: 'gray' },\n ],\n dataListEntries: entries,\n fullText: { title: i18next.t('cmd.extAgentFullTextTitle', { name: name }), content: profile.systemPromptTemplate || i18next.t('cmd.extSkillNoBody') },\n onAction: async (v: string) => {\n if (v === 'toggle') {\n try {\n await ctx.app.extensions.setEnabled('agent', name, !enabled)\n notify(i18next.t('cmd.titleExtension'), i18next.t('cmd.extToggled', { name: name, verb: (enabled) ? 'Disabled' : 'Enabled', seg: (true) ? ' — applies next turn' : '' }))\n } catch (e) {\n say(i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }))\n }\n openAgentDetail(name)\n }\n },\n onClose: () => openList(),\n })\n }\n\n const openDetail = (category: string, id: string) => {\n const item = ctx.app.extensions\n .list()\n .find((i) => i.category === category && i.id === id)\n if (!item) {\n openList()\n return\n }\n const toggleOpt = {\n label: item.enabled ? i18next.t('cmd.extDisableLabel') : i18next.t('cmd.extEnableLabel'),\n value: 'toggle',\n }\n if (category === 'mcp') {\n const info = ctx.app.mcpManager\n ?.getServerInfos()\n .find((s) => s.name === id)\n const status = item.enabled\n ? info\n ? String(info.status)\n : 'disconnected'\n : 'disabled'\n ctx.tui.modal.openModal({\n kind: 'select',\n title: i18next.t('cmd.extMcpTitle', { name: item.name, status: status }),\n options: [\n { label: i18next.t('cmd.extReconnectLabel'), value: 'reconnect' },\n toggleOpt,\n // 卸载入口(原 `/uninstall` 命令收口至此)。**managed server 不给此项**——\n // 它由插件带入(RFC-081 `readonly`),移除方式是卸载宿主插件;单独删配置项\n // 会在插件下次加载时重新出现,是假卸载。\n ...(item.readonly\n ? []\n : [{ label: i18next.t('cmd.uninstallActionLabel'), value: 'uninstall' }]),\n ],\n onSelect: async (v: string) => {\n if (v === 'toggle') {\n try {\n await ctx.app.extensions.setEnabled(\n item.category,\n id,\n !item.enabled,\n )\n notify(\n i18next.t('cmd.titleExtension'),\n i18next.t('cmd.extToggled', { name: item.name, verb: (item.enabled) ? 'Disabled' : 'Enabled', seg: (false) ? ' — applies next turn' : '' }),\n )\n } catch (e) {\n say(\n i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }),\n )\n }\n openDetail(category, id)\n return\n }\n if (v === 'reconnect') {\n const mgr = ctx.app.mcpManager\n const cfg = ((ctx.app.settings.get('mcp_servers') ?? {}) as Record<string, unknown>)[id]\n if (!mgr || !cfg) {\n notify(i18next.t('cmd.titleExtension'), i18next.t('cmd.extReconnectUnavailable', { id: id }))\n openDetail(category, id)\n return\n }\n try {\n await mgr.disconnect(id).catch(() => {})\n await mgr.connect(id, cfg)\n notify(i18next.t('cmd.titleExtension'), i18next.t('cmd.extReconnected', { id: id }))\n } catch (e) {\n say(\n i18next.t('cmd.extReconnectFailed', { id: id, err: e instanceof Error ? e.message : String(e) }),\n )\n }\n openDetail(category, id)\n return\n }\n if (v === 'uninstall') {\n // MCP server 的\"安装物\"就是 `settings.mcp_servers` 里的配置项——卸载走的是\n // settings 读写回调(uninstaller.ts case 'mcp'),**不碰文件系统**,故 scope\n // 在此仅影响确认框里的层级文案,不改变删除目标。传 'project'(即当前工作区\n // 生效的那份配置,与用户在面板中看到的条目同源)。\n await confirmAndUninstall({\n name: id,\n type: 'mcp',\n scope: 'project',\n ctx,\n // 卸载后该 server 已不存在,回详情页会落空 → 回列表。\n onDone: () => openList(),\n })\n return\n }\n },\n onCancel: () => openList(),\n })\n } else if (category === 'plugin') {\n openPluginDetail(id, item.enabled, item.name, item.description)\n } else if (category === 'skill') {\n openSkillDetail(id)\n } else if (category === 'agent') {\n openAgentDetail(id)\n } else {\n ctx.tui.modal.openModal({\n kind: 'select',\n title: i18next.t('cmd.extItemTitle', { name: item.name }),\n options: [toggleOpt],\n onSelect: async (v: string) => {\n if (v === 'toggle') {\n try {\n await ctx.app.extensions.setEnabled(\n item.category,\n id,\n !item.enabled,\n )\n notify(\n i18next.t('cmd.titleExtension'),\n i18next.t('cmd.extToggled', { name: item.name, verb: (item.enabled) ? 'Disabled' : 'Enabled', seg: (item.applies !== 'live') ? ' — applies next turn' : '' }),\n )\n } catch (e) {\n say(\n i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }),\n )\n }\n openDetail(category, id)\n return\n }\n },\n onCancel: () => openList(),\n })\n }\n }\n\n /**\n * 插件详情面板(RFC-044/081):展示描述/作用域/信任态/贡献面/面板,聚合可用动作——\n * 启停 bundle、信任/撤销可执行面(项目级)、打开其贡献的面板。比通用条目详情富。\n */\n const openPluginDetail = (\n id: string,\n enabled: boolean,\n name: string,\n description: string,\n /**\n * RFC-278:untrust/toggle/reload 完成后重开详情页时携带的操作结果——渲染为\n * `dataListEntries` 首行的 `{kind:'text', tone}` 提示行,取代原来的 `notify()` toast。\n * 常驻本次重开的这一屏(无自动隐藏 timer——`handle-modal.ts` 的 `case 'extensionDetail'`\n * 在 onAction 时无条件先 `close()` 再执行业务逻辑,面板重开属于全新一次 openModal 调用,\n * 不存在\"计时器触发时面板已被其他方式关闭\"的竞态,见 RFC-278 §0.3/§2.2)。\n */\n resultBanner?: { kind: 'success' | 'error'; message: string },\n ) => {\n const ws = ctx.app.workspaceDir ?? process.cwd()\n const plugin = discoverPluginsWithBundled({ cwd: ws, homedir: homedir() }).find((p) => p.id === id)\n if (!plugin) {\n openList()\n return\n }\n const verdict = evaluatePluginTrust(plugin)\n // 信任文本:user scope 无门控 → \"已信任\",project/repo scope 按 gated+trusted 判定。\n // 此前只判 `verdict.trusted`,user scope 插件有 scripts 面但不在 trust store 时会被判为\n // \"未信任(项目级——可执行面被门控)\"——对 user 作用域来说这是误导(user 永不受信任门限制)。\n const trustText = !verdict.surface.any\n ? i18next.t('cmd.extPluginNoSurface')\n : verdict.gated\n ? i18next.t('cmd.extPluginUntrusted')\n : verdict.pendingCapabilities.length > 0\n ? i18next.t('cmd.extPluginCapabilityPending', { caps: verdict.pendingCapabilities.join(', ') })\n : i18next.t('cmd.extPluginTrusted')\n\n const s = verdict.surface\n const surfaceLabels: Array<[boolean, string]> = [\n [s.mcpJson, 'mcp'],\n [s.commands, 'commands'],\n [s.scripts, 'scripts'],\n [s.panels, 'panels'],\n [s.skills, 'skills'],\n [s.agents, 'agents'],\n ]\n const surfaces = surfaceLabels.filter(([has]) => has).map(([, l]) => l)\n const surfaceText = surfaces.length ? ` ${surfaces.join(', ')}` : ' —'\n\n // 该插件贡献的已注册面板(经信任门过滤)。\n const panels = (ctx.app.getPluginPanelRegistrations?.() ?? []).filter(\n (p) => p.pluginId === id,\n )\n\n // RFC-124 P0-B(R11 权限审计基线):`/extension` 详情页此前只展示 surface(可执行面)+\n // panels + 菜单动作,遗漏了 provider/tools/renderers/statusItems 四个新轴——用户装了声明\n // 这些能力的插件后,无法从详情页一眼看清它到底注册了什么。此处按 pluginId 过滤四轴数据,\n // 组装成一行摘要文本,喂给 `genericPluginEntries` 的 DataList 贡献轴行(无贡献时该行不\n // 展示,不为空插件添加噪音)。\n const contributionsText = formatPluginContributionsSummary({\n providerApis: ctx.app.providerRegistry.getApisBySource(id).length,\n tools: ctx.app.toolRegistry.getAll().filter((t) => t.metadata.source === id).length,\n renderers: ctx.app.getPluginRenderers().filter((r) => r.pluginId === id).length,\n // statusItems 前缀匹配 `${id}:` 安全性说明(复审 finding B-1 核实结论):插件顶层\n // `id` 受 manifest.ts `ID_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/` 强制 kebab-case 校验,\n // 不可能含冒号——不存在插件 A(id=\"foo\")被插件 B(id=\"foo-bar\")的 status item\n // (id 格式 `${pluginId}:${itemId}`)误匹配的场景(冒号天然分隔,且 pluginId 本身\n // 无冒号可插入制造歧义前缀)。\n statusItems: ctx.app.getPluginStatusItems().filter((it) => it.id.startsWith(`${id}:`)).length,\n // RFC-187 M2:文件查看器候选贡献计数(同 renderers 过滤逻辑,pluginId 精确匹配)。\n // 可选链防御(同 getPluginPanelRegistrations 先例):新方法,既有测试 mock 的 ctx.app\n // 未必提供该方法,缺失时视为零贡献而非抛错。\n fileViewers: (ctx.app.getPluginFileViewers?.() ?? []).filter((v) => v.pluginId === id).length,\n })\n\n // 控制按钮:启用/禁用 · [信任/不信任 或 授权待授权能力] · 重载。\n // RFC-128:builtin scope 恒定信任、恒定启用——`ExtensionManager.setEnabled` 对\n // builtin 插件会直接抛错拒绝(见 extension-manager.ts PluginExtensionProvider.\n // setEnabled)。此前本处无条件塞\"禁用\"按钮,用户点击后必然踩中那个 throw(体验上\n // 是个死胡同:按钮看起来能点,点了却报错)。builtin 插件不可能被禁用,UI 上不应该\n // 出现\"禁用\"这个选项——同既有 managed MCP server / isBuiltin 的 readonly 语义\n // (不渲染可切换控件,而非让用户点了才发现不行)。\n const actions: ExtensionDetailAction[] = plugin.scope === 'builtin'\n ? []\n : [\n {\n label: enabled ? i18next.t('cmd.extDisableLabel') : i18next.t('cmd.extEnableLabel'),\n value: 'toggle',\n color: enabled ? 'amber' : 'green',\n },\n ]\n // 回归修复(真实用户反馈:DeepSeek/Anthropic 配了凭据仍无模型/无 OAuth URL):此前本按钮\n // 仅在 `plugin.scope === 'project'` 时出现——但 `pendingCapabilities`(RFC-124 C2/RFC-125)\n // 与 scope 正交,用户级插件(`~/.otto/plugins/`,如三个首方 provider 插件)同样可能卡在\n // \"已布尔信任但高危能力未授予\"状态,此前完全没有可点的按钮把它从 pendingCapabilities 里\n // 捞出来——RFC-125 D5 新增的 `/model` \"去授权\"逃生口跳转到这里后是个死胡同。\n // 现在只要 `verdict.surface.any`(含 highRiskCapabilities,RFC-124 C1)且\n // (项目级/仓库级 或 有待授权能力)就显示按钮;`promptPluginTrust` 本身已同时处理布尔信任\n // 与能力授予(M125-1),点击后两件事一次做完。\n // repository scope(RFC-130)对齐 project:需信任门。\n if (verdict.surface.any && (plugin.scope === 'project' || plugin.scope === 'repository' || verdict.pendingCapabilities.length > 0)) {\n const needsAction = (plugin.scope === 'project' || plugin.scope === 'repository') ? !verdict.trusted : verdict.pendingCapabilities.length > 0\n actions.push({\n label: needsAction ? i18next.t('cmd.extPluginTrustLabel') : i18next.t('cmd.extPluginUntrustLabel'),\n value: needsAction ? 'trust' : 'untrust',\n color: needsAction ? 'accent' : 'gray',\n })\n }\n actions.push({ label: i18next.t('cmd.extReloadLabel'), value: 'reload' })\n // 卸载入口(原 `/uninstall` 命令收口至此)。**builtin 插件不可卸**——它随 otto 二进制\n // 分发,没有独立安装物可删(同上方 builtin 不给\"禁用\"按钮的判断,理由一致:不渲染\n // 点了必然失败的控件)。放在贡献动作之前,保证它的位置不随插件贡献数量漂移。\n if (plugin.scope !== 'builtin') {\n actions.push({ label: i18next.t('cmd.uninstallActionLabel'), value: 'uninstall', color: 'red' })\n }\n\n // RFC-084 M084-03:追加**该插件贡献**的动作(per-viewed-plugin scope,§4.4b)。\n // context = 内置 extension.* 键 + 该插件声明的 context-key providers(文件探针求值)。\n const ctxMap = buildExtensionContext(plugin, {\n 'extension.id': id,\n 'extension.enabled': enabled,\n 'extension.trusted': verdict.trusted,\n 'extension.scope': plugin.scope,\n 'extension.category': 'plugin',\n })\n const contribs = plugin.manifest.contributes\n const resolved = resolveActions(POINT.extensionDetailActions, contribs?.menus, contribs?.actions, ctxMap)\n const cmdById = new Map(resolved.map((r) => [r.id, r.command]))\n for (const r of resolved) {\n // i18n 化标题(约定 key:`action.<actionId>.title`,同 feishu-use command 描述的\n // `pluginId:id` 复合标识约定)——插件未声明对应 i18n 条目时 `t()` 回退到 fullKey\n // 本身(PluginI18nRegistry 三级回退语义),此时改用 action 声明的字面量 title\n // 兜底,不展示裸 key 给用户。\n const i18nKey = `${plugin.id}:action.${r.id}.title`\n const translated = ctx.app.i18nRegistry.t(i18nKey)\n const label = translated === i18nKey ? r.title : translated\n actions.push({ label, value: `contrib:${r.id}`, color: r.color })\n }\n // 分发 caps:open 就近装配;runCommand 动态 import 避免与 command-dispatcher 静态成环;\n // callMcp 暂不装(当前无 mcp 型动作,缺则 dispatch 抛 MissingCapability)。\n const caps: DispatchCaps = {\n open: (t) => openTarget(t),\n runCommand: async (cmdName, cmdArgs) => {\n const { dispatchSlashCommand } = await import('../../command-dispatcher')\n await dispatchSlashCommand(cmdName, cmdArgs ? cmdArgs.split(/\\s+/).filter(Boolean) : [], ctx)\n },\n // script 型(装配副作用):过信任门(可执行面)——项目级未信任插件不得跑脚本。\n runScript: async (scriptName) => {\n if (evaluatePluginTrust(plugin).gated) {\n notify(i18next.t('cmd.titlePlugin'), i18next.t('cmd.extPluginUntrusted'))\n return\n }\n await runPluginScript(plugin, scriptName)\n },\n }\n\n // body = 插件自写详情组件(取该插件第一个已注册面板)。无则回退基本信息。\n const panelReg = panels[0]\n\n // RFC-124 P0-C(RFC-105 D7 懒激活):`onView:<id>` 此前只有 schema 允许声明,\n // 无任何真实触发点——声明了 `activationEvents:[\"onView:<panelId>\"]` 的插件,即使用户\n // 打开了该面板,其代码轴(hooks/tools/providerFactories/renderers)仍不会被激活。\n // 打开某插件的面板即是\"该 view 被展示\"的天然事件时机,fire-and-forget(不阻塞详情页渲染)。\n if (panelReg) {\n void ctx.app.firePluginActivationEvent?.(`onView:${panelReg.id}`)\n }\n\n // RFC-134 M3:feishu-use 扩展面板定制——通用插件详情对飞书用户是噪声(贡献面/作用域等\n // 引擎内部概念),替换为飞书特有的配置状态、bot 身份、\"重新配置\"操作。\n // RFC-166 D10 后续:改用 DataListEntry[](分组 + label/value 双色),取代此前手拼的\n // `string[]` 行(`App ID xxx` 这类整行内嵌 label 无法单独着色 value)。\n let feishuEntries: DataListEntry[] | undefined\n if (id === 'plugin-feishu-use') {\n const configPath = join(ws, '.otto', 'feishu-use.json')\n let configOk = false\n try {\n if (existsSync(configPath)) {\n const cfg = JSON.parse(readFileSync(configPath, 'utf8')) as Record<string, unknown>\n if (cfg.app_id) {\n configOk = true\n const maskedId = typeof cfg.app_id === 'string' && cfg.app_id.length > 12\n ? cfg.app_id.slice(0, 8) + '***' + cfg.app_id.slice(-4)\n : String(cfg.app_id)\n const chatCount = Array.isArray(cfg.allowed_chats) ? cfg.allowed_chats.length : 0\n const trustedCount = Array.isArray(cfg.trusted_chats) ? cfg.trusted_chats.length : 0\n const scopeLabel = plugin.scope === 'user' ? '全局' : plugin.scope === 'repository' ? '仓库' : '项目'\n feishuEntries = [\n { kind: 'row', label: 'App ID', value: maskedId },\n { kind: 'row', label: '消息白名单', value: chatCount > 0 ? `${chatCount} 个` : '空 — 拒绝所有消息', tone: chatCount > 0 ? undefined : 'warning' },\n ...(trustedCount > 0 ? [{ kind: 'row', label: '可信任 chat', value: `${trustedCount} 个` } as DataListEntry] : []),\n { kind: 'row', label: '配置', value: `${scopeLabel}级 · ${configPath}`, tone: 'muted' },\n ]\n }\n }\n } catch {\n /* 读取失败走未配置分支 */\n }\n if (!configOk) {\n feishuEntries = [\n { kind: 'text', value: '未配置 — 点击 [重新配置] 引导设置', tone: 'warning' },\n { kind: 'row', label: '预期配置路径', value: configPath, tone: 'muted' },\n ]\n }\n actions.push({ label: '重新配置', value: 'feishu-reconfigure', color: 'amber' })\n }\n\n // RFC-153 D4:健康度摘要 + 最近日志——打开时查询(同 /perf 的\"打开时查询,非订阅\"模式)。\n // fail-soft:`pluginHealthTracker` 缺失(测试 fixture 用简化 ctx.app mock,或未来其他\n // 精简装配路径)时降级为全零健康度 + 空日志,不阻断详情页渲染(规则 3 的同款克制精神)。\n const queryHealthAndLogs = () => {\n const stats = ctx.app.pluginHealthTracker?.query(id) ?? { hookTimeouts: 0, toolTimeouts: 0 }\n const logs = readPluginLogTail(id)\n return {\n healthStats: {\n hookTimeouts: stats.hookTimeouts,\n toolTimeouts: stats.toolTimeouts,\n lastError: stats.lastError,\n },\n logEntries: logs.map((e) => ({ ts: e.ts, level: e.level, msg: e.msg })),\n }\n }\n\n // \"extension 默认面板\"(RFC-166 D10 后续):无自定义 panel 时的通用回退——分组键值信息,\n // 与插件自写面板(如 browser-use host)、`/about` 统一走同一套 DataList 双色渲染。\n const genericPluginEntries: DataListEntry[] = [\n { kind: 'group', title: i18next.t('cmd.extPluginGroupTitle') },\n { kind: 'row', label: i18next.t('cmd.extPluginLabelDesc'), value: description },\n { kind: 'row', label: i18next.t('cmd.extPluginLabelScope'), value: plugin.scope },\n { kind: 'row', label: i18next.t('cmd.extPluginLabelTrust'), value: trustText },\n { kind: 'row', label: i18next.t('cmd.extPluginLabelSurfaces'), value: surfaceText.trim() || '—' },\n ...(contributionsText\n ? [{ kind: 'row', label: i18next.t('cmd.extPluginLabelContributions'), value: contributionsText } as DataListEntry]\n : []),\n { kind: 'row', label: i18next.t('cmd.extPluginLabelStatus'), value: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), tone: enabled ? 'success' : 'error' },\n ]\n\n // RFC-278:操作结果提示行——untrust/toggle/reload 完成后重开详情页时插在最前面,\n // 取代原 notify() toast。无 resultBanner 时零改动(保持原有渲染)。\n const bannerEntry: DataListEntry[] = resultBanner\n ? [{ kind: 'text', value: resultBanner.message, tone: resultBanner.kind === 'success' ? 'success' : 'error' }]\n : []\n\n ctx.tui.modal.openModal({\n kind: 'extensionDetail',\n title: i18next.t('cmd.extPluginTitle', { name: name, state: (enabled) ? 'enabled' : 'disabled' }),\n meta: `plugin · ${plugin.scope}`,\n metaStatus: { label: enabled ? i18next.t('cmd.extEnableLabel') : i18next.t('cmd.extDisableLabel'), enabled },\n metaSuffix: trustText,\n actions,\n panelInfo: panelReg\n ? { title: panelReg.title, modulePath: panelReg.modulePath, exportName: panelReg.exportName, pluginId: id }\n : undefined,\n // RFC-167:面板不再内嵌详情页 body(改走独立 dialog,经\"打开面板 →\"按钮跳转)——\n // 有面板时详情页仍展示基础信息(DataList),不再因为 panelReg 存在就完全让位给空白。\n dataListEntries: [...bannerEntry, ...(feishuEntries ?? genericPluginEntries)],\n ...queryHealthAndLogs(),\n // RFC-153 D4:手动刷新键——复用本文件已有的\"重开详情页刷新\"模式(同 contrib: 动作后\n // 重算 context 的做法),非独立的 store setter,避免新增 TuiHandle 方法面。\n onRefresh: () => openPluginDetail(id, enabled, name, description),\n onAction: async (v: string) => {\n if (v.startsWith('contrib:')) {\n const cmd = cmdById.get(v.slice('contrib:'.length))\n if (cmd) {\n try {\n await dispatch(cmd, caps)\n } catch (e) {\n say(i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }))\n }\n }\n // OQ1:动作后重算 context 重解析 → 重开详情刷新按钮(如 relayState none→all)。\n openPluginDetail(id, enabled, name, description)\n } else if (v === 'trust') {\n promptPluginTrust(ctx, plugin)\n } else if (v === 'untrust') {\n // RFC-278:untrust 完成后重开详情页并展示结果行,取代原 notify() toast——\n // handle-modal.ts 的 onAction 包装器已在调用本回调前无条件 close() 了面板,\n // reloadCapabilities 完成后必须重开才能让用户看到最新的信任状态。\n untrustPlugin(plugin.dir)\n void ctx.app.reloadCapabilities?.().then(() => {\n ctx.tui.plugins.setPluginRegistrations(ctx.app.getPluginRegistrations())\n }).finally(() => {\n openPluginDetail(id, enabled, name, description, {\n kind: 'success', message: i18next.t('cmd.pluginUntrusted', { id: id }),\n })\n })\n } else if (v === 'toggle') {\n // RFC-278:toggle 完成后重开详情页(此前该分支唯独没有重开调用,是与\n // contrib:/skill/agent 详情页 toggle 分支不一致的实现缺口——本次修复对齐)。\n // RFC-287 M1:setEnabled 只落盘 disabled_plugins + kill 关联子进程,不刷新已注册\n // 的 hooks/tools/providers——此前需要用户额外手动点独立的 [reload] 才真正生效,\n // 与\"用户点了禁用就该立即禁用\"的直觉不符。补 reloadCapabilities()(对齐 untrust/\n // reload 两个既有分支的同款模式),使 toggle 成为自迭代回路\"授权→立即可用\"闭环的\n // 最后一环。reloadCapabilities 失败时仅提示不阻断——disabled_plugins 已落盘,\n // 状态不丢,用户可再触发一次 reload 重试。\n try {\n await ctx.app.extensions.setEnabled('plugin', id, !enabled)\n try {\n await ctx.app.reloadCapabilities()\n openPluginDetail(id, !enabled, name, description, {\n kind: 'success',\n message: i18next.t('cmd.extToggled', { name: name, verb: (enabled) ? 'Disabled' : 'Enabled', seg: '' }),\n })\n } catch (reloadErr) {\n openPluginDetail(id, !enabled, name, description, {\n kind: 'error',\n message: i18next.t('cmd.extToggleReloadFailed', {\n verb: (enabled) ? 'Disabled' : 'Enabled',\n name: name,\n err: reloadErr instanceof Error ? reloadErr.message : String(reloadErr),\n }),\n })\n }\n } catch (e) {\n openPluginDetail(id, enabled, name, description, {\n kind: 'error', message: i18next.t('cmd.extToggleFailed', { err: e instanceof Error ? e.message : String(e) }),\n })\n }\n } else if (v === 'feishu-reconfigure') {\n // RFC-134 M3:feishu-use \"重新配置\"按钮——触发交互式配置向导\n try {\n const { launchFeishuSetup } = await import('./feishu-setup')\n await launchFeishuSetup(ctx)\n notify('plugin-feishu-use', '配置完成。')\n } catch (e) {\n say(`feishu-use 配置向导失败:${e instanceof Error ? e.message : String(e)}`)\n }\n } else if (v === 'reload') {\n // RFC-278:reload 完成后重开详情页展示结果,取代原 notify() toast。\n void ctx.app\n .reloadCapabilities?.()\n .then((r: { mcpTools: number }) => {\n openPluginDetail(id, enabled, name, description, {\n kind: 'success', message: i18next.t('cmd.pluginTrusted', { id: id, count: r.mcpTools }),\n })\n })\n .catch((e: unknown) => {\n openPluginDetail(id, enabled, name, description, {\n kind: 'error', message: i18next.t('cmd.compatReloadFailed', { err: e instanceof Error ? e.message : String(e) }),\n })\n })\n } else if (v === 'uninstall') {\n // plugin.scope 与 InstallScope 的取值域不同:前者是 user/project/repository/builtin,\n // 后者是 global/project/repository。user 级插件装在 `~/.otto/plugins` = 卸载引擎的\n // global 层,故此处映射;builtin 上方已不给按钮,走不到这里。\n const scope: InstallScope =\n plugin.scope === 'project' || plugin.scope === 'repository' ? plugin.scope : 'global'\n await confirmAndUninstall({\n name: id,\n type: 'plugin',\n scope,\n ctx,\n // 卸载后该插件已不存在,回详情页会落空 → 回列表。\n onDone: () => openList(),\n })\n }\n },\n onClose: () => openList(),\n })\n }\n\n openList()\n}\n\n\n"],"mappings":"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"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import{n as e,t}from"./build-id-DMyumG97.js";import{platform as n,release as r}from"node:os";import{execFile as i}from"node:child_process";import{createApp as a}from"@x-otto/coding";import{openBrowser as o}from"@x-otto/shared";import{SDK_VERSION as s}from"@x-otto/env";import{promisify as c}from"node:util";const l=c(i);function u(e){let t=e.trim().replace(/\.git$/,``),n=t.match(/^git@[^:]+:(.+)$/);if(n)return n[1];let r=t.match(/^https?:\/\/[^/]+\/(.+)$/);if(r)return r[1]}async function d(e){try{let{stdout:t}=await l(`git`,[`remote`,`get-url`,`origin`],{cwd:e,timeout:5e3});return u(t)}catch{return}}async function f(e){return e.configuredRepo?e.configuredRepo:await d(e.cwd)||e.cliRepoArg}function p(i){return[`- otto version: ${s} (build ${e}, ${t}@dev)`,`- Node: ${process.version}`,`- OS: ${n()} ${process.arch} ${r()}`,`- Model: ${i??`(none configured)`}`].join(`
|
|
3
|
-
`)}function m(e){let t,n,r,i,a,o=!1,s=(t,n)=>{let r=e[t+1];if(r===void 0||r.startsWith(`--`))throw Error(`${n} requires a value`);return r};for(let c=0;c<e.length;c++){let l=e[c];l===`--title`?(t=s(c,`--title`),c++):l===`--reproduce`?(n=s(c,`--reproduce`),c++):l===`--expected`?(r=s(c,`--expected`),c++):l===`--actual`?(i=s(c,`--actual`),c++):l===`--repo`?(a=s(c,`--repo`),c++):l===`--browser`&&(o=!0)}return{title:t,reproduce:n,expected:r,actual:i,repo:a,browser:o}}function h(e){e(`Usage: otto feedback --title <title> [options]
|
|
4
|
-
|
|
5
|
-
Report an issue or send feedback via GitHub (non-interactive — for scripting/CI).
|
|
6
|
-
For an interactive prompt-driven flow, use the "/feedback" command inside otto.
|
|
7
|
-
|
|
8
|
-
Options:
|
|
9
|
-
--title <title> Short issue title (required)
|
|
10
|
-
--reproduce <text> Steps to reproduce (optional)
|
|
11
|
-
--expected <text> Expected behavior (optional)
|
|
12
|
-
--actual <text> Actual behavior (optional)
|
|
13
|
-
--repo <owner/repo> Target repository (defaults to feedback_repo config, then git remote origin)
|
|
14
|
-
--browser Skip the gh CLI channel and always use the browser fallback
|
|
15
|
-
--help Show this help
|
|
16
|
-
`)}async function g(e,t={}){let n=t.stdout??(e=>process.stdout.write(e)),r=t.stderr??(e=>process.stderr.write(e));if(e[0]===`help`||e[0]===`--help`)return h(n),0;let i;try{i=m(e)}catch(e){return r(`otto feedback: ${e instanceof Error?e.message:String(e)}\n`),1}if(!i.title)return r(`otto feedback: --title is required (see --help)
|
|
17
|
-
`),1;let s=t.app??a({logger:{name:`otto-feedback`,level:`error`,destination:`stderr`},workspaceDir:t.projectDir,projectConfigPath:t.configPath});try{await s.start()}catch(e){return r(`otto feedback: failed to start app: ${e instanceof Error?e.message:String(e)}\n`),1}try{let e=s.feedbackRegistry.get();if(!e)return r(`otto feedback: feedback plugin is not available (disabled or not installed)
|
|
18
|
-
`),1;let a=await f({configuredRepo:s.settings.get(`feedback_repo`),cliRepoArg:i.repo,cwd:t.projectDir??process.cwd()});if(!a)return r(`otto feedback: could not resolve a target repository — pass --repo or set "feedback_repo" in .otto/config.json
|
|
19
|
-
`),1;let c=p(void 0),l={title:i.title,reproduce:i.reproduce,expected:i.expected,actual:i.actual,envInfo:c,repo:a};if(!i.browser&&e.submitIssueViaGh){let t=await e.submitIssueViaGh(l);if(t.ok)return n(`Issue created: ${t.url}\n`),0;r(`otto feedback: gh channel failed (${t.kind}) — falling back to browser\n`)}let u=e.buildIssueUrl?.(l);return u?(o(u),n(`Opened: ${u}\n`),0):(r(`otto feedback: feedback plugin does not support buildIssueUrl
|
|
20
|
-
`),1)}finally{await s.stop()}}export{f as n,g as r,p as t};
|
|
21
|
-
//# sourceMappingURL=feedback-7qQsiabD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"feedback-7qQsiabD.js","names":["BUILD_ID"],"sources":["../src/commands/feedback.ts"],"sourcesContent":["/**\n * /feedback 命令的 TUI 接线层(提交/展示)。\n */\nimport { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { platform, release } from 'node:os'\nimport { SDK_VERSION } from '@x-otto/env'\nimport { BUILD_ID, BUILD_BRANCH, BUILD_HASH } from '../build-id'\nimport { createApp } from '@x-otto/coding'\nimport type { App } from '@x-otto/coding'\nimport { openBrowser } from '@x-otto/shared'\n\nconst execFileAsync = promisify(execFile)\n\n/** git remote origin URL → `owner/repo`(支持 `git@github.com:owner/repo.git` 与 `https://github.com/owner/repo.git` 两种形态)。 */\nfunction parseOwnerRepoFromGitUrl(url: string): string | undefined {\n const trimmed = url.trim().replace(/\\.git$/, '')\n const sshMatch = trimmed.match(/^git@[^:]+:(.+)$/)\n if (sshMatch) return sshMatch[1]\n const httpsMatch = trimmed.match(/^https?:\\/\\/[^/]+\\/(.+)$/)\n if (httpsMatch) return httpsMatch[1]\n return undefined\n}\n\n/** `git remote get-url origin` 解析当前工作目录的 owner/repo;非 git 仓库/无 origin 时返回 undefined(fail-soft,不抛出)。 */\nasync function resolveGitRemoteRepo(cwd: string): Promise<string | undefined> {\n try {\n const { stdout } = await execFileAsync('git', ['remote', 'get-url', 'origin'], { cwd, timeout: 5000 })\n return parseOwnerRepoFromGitUrl(stdout)\n } catch {\n return undefined\n }\n}\n\n/**\n * RFC-207 §3.4:目标仓库解析优先级——`feedback_repo` 配置 > git remote 解析 >\n * `--repo` 命令行参数。全部缺失时返回 `undefined`。\n */\nexport async function resolveFeedbackRepo(opts: {\n configuredRepo?: string\n cliRepoArg?: string\n cwd: string\n}): Promise<string | undefined> {\n if (opts.configuredRepo) return opts.configuredRepo\n const fromGit = await resolveGitRemoteRepo(opts.cwd)\n if (fromGit) return fromGit\n return opts.cliRepoArg\n}\n\n/**\n * RFC-207 §3.1 环境信息行(issue body 的\"环境信息\"小节,脱敏——只含版本/运行时/OS/\n * 模型,不含用户路径/环境变量等敏感信息)。\n */\nexport function buildFeedbackEnvInfo(modelId: string | undefined): string {\n return [\n `- otto version: ${SDK_VERSION} (build ${BUILD_ID}, ${BUILD_HASH}@${BUILD_BRANCH})`,\n `- Node: ${process.version}`,\n `- OS: ${platform()} ${process.arch} ${release()}`,\n `- Model: ${modelId ?? '(none configured)'}`,\n ].join('\\n')\n}\n\nexport interface FeedbackCommandDeps {\n stdout?: (s: string) => void\n stderr?: (s: string) => void\n projectDir?: string\n configPath?: string\n /** 测试注入:替代生产装配的 app(真实 App 类型——需要 feedbackRegistry/submitIssueViaGh,\n * 非 ServiceHostPort/AppContext 窄接口能覆盖,同 serve.ts 的 AppContext 注入模式不同)。 */\n app?: App\n}\n\nfunction parseFeedbackArgs(args: string[]): {\n title?: string\n reproduce?: string\n expected?: string\n actual?: string\n repo?: string\n browser: boolean\n} {\n let title: string | undefined\n let reproduce: string | undefined\n let expected: string | undefined\n let actual: string | undefined\n let repo: string | undefined\n let browser = false\n\n const takeValue = (i: number, name: string): string => {\n const raw = args[i + 1]\n if (raw === undefined || raw.startsWith('--')) {\n throw new Error(`${name} requires a value`)\n }\n return raw\n }\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]\n if (arg === '--title') { title = takeValue(i, '--title'); i++ }\n else if (arg === '--reproduce') { reproduce = takeValue(i, '--reproduce'); i++ }\n else if (arg === '--expected') { expected = takeValue(i, '--expected'); i++ }\n else if (arg === '--actual') { actual = takeValue(i, '--actual'); i++ }\n else if (arg === '--repo') { repo = takeValue(i, '--repo'); i++ }\n else if (arg === '--browser') { browser = true }\n }\n\n return { title, reproduce, expected, actual, repo, browser }\n}\n\nfunction printFeedbackHelp(stdout: (s: string) => void): void {\n stdout(`Usage: otto feedback --title <title> [options]\n\nReport an issue or send feedback via GitHub (non-interactive — for scripting/CI).\nFor an interactive prompt-driven flow, use the \"/feedback\" command inside otto.\n\nOptions:\n --title <title> Short issue title (required)\n --reproduce <text> Steps to reproduce (optional)\n --expected <text> Expected behavior (optional)\n --actual <text> Actual behavior (optional)\n --repo <owner/repo> Target repository (defaults to feedback_repo config, then git remote origin)\n --browser Skip the gh CLI channel and always use the browser fallback\n --help Show this help\n`)\n}\n\n/**\n * `otto feedback` 非交互顶层子命令(RFC-207 §3.2/§3.4)——CI/脚本化调用场景,\n * `--title`/`--body` 系参数直接传参跳过 TUI openInput 链。复用与 `/feedback` 斜杠\n * 命令相同的核心逻辑(`resolveFeedbackRepo`/`buildFeedbackEnvInfo`)+ 同一个\n * `FeedbackRegistry`/插件实现(`app.feedbackRegistry`),两个入口不重复业务逻辑。\n */\nexport async function runFeedbackCommand(args: string[], deps: FeedbackCommandDeps = {}): Promise<number> {\n const stdout = deps.stdout ?? ((s) => process.stdout.write(s))\n const stderr = deps.stderr ?? ((s) => process.stderr.write(s))\n\n if (args[0] === 'help' || args[0] === '--help') {\n printFeedbackHelp(stdout)\n return 0\n }\n\n let parsed: ReturnType<typeof parseFeedbackArgs>\n try {\n parsed = parseFeedbackArgs(args)\n } catch (error) {\n stderr(`otto feedback: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n }\n\n if (!parsed.title) {\n stderr('otto feedback: --title is required (see --help)\\n')\n return 1\n }\n\n const app =\n deps.app ??\n createApp({\n logger: { name: 'otto-feedback', level: 'error', destination: 'stderr' },\n workspaceDir: deps.projectDir,\n projectConfigPath: deps.configPath,\n })\n\n try {\n await app.start()\n } catch (error) {\n stderr(`otto feedback: failed to start app: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n }\n\n try {\n const provider = app.feedbackRegistry.get()\n if (!provider) {\n stderr('otto feedback: feedback plugin is not available (disabled or not installed)\\n')\n return 1\n }\n\n const configuredRepo = app.settings.get('feedback_repo') as string | undefined\n const repo = await resolveFeedbackRepo({ configuredRepo, cliRepoArg: parsed.repo, cwd: deps.projectDir ?? process.cwd() })\n if (!repo) {\n stderr('otto feedback: could not resolve a target repository — pass --repo or set \"feedback_repo\" in .otto/config.json\\n')\n return 1\n }\n\n const envInfo = buildFeedbackEnvInfo(undefined)\n const input = {\n title: parsed.title,\n reproduce: parsed.reproduce,\n expected: parsed.expected,\n actual: parsed.actual,\n envInfo,\n repo,\n }\n\n if (!parsed.browser && provider.submitIssueViaGh) {\n const result = await provider.submitIssueViaGh(input)\n if (result.ok) {\n stdout(`Issue created: ${result.url}\\n`)\n return 0\n }\n stderr(`otto feedback: gh channel failed (${result.kind}) — falling back to browser\\n`)\n }\n\n const url = provider.buildIssueUrl?.(input)\n if (!url) {\n stderr('otto feedback: feedback plugin does not support buildIssueUrl\\n')\n return 1\n }\n openBrowser(url)\n stdout(`Opened: ${url}\\n`)\n return 0\n } finally {\n await app.stop()\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"feishu-setup-DIcd3PMK.js","names":[],"sources":["../src/commands/handlers/feishu-setup.ts"],"sourcesContent":["/**\n * /feishu-setup 命令:飞书 webhook 绑定交互向导。\n */\nimport { spawn } from 'node:child_process'\nimport { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { findWorkspaceRoot } from '@x-otto/coding'\nimport { discoverPluginsWithBundled } from '../../discover-with-bundled'\nimport type { CliContext } from '../../command-dispatcher'\n\n/** 按优先级计算 feishu-use 配置文件候选路径(project > repo > user),返回最高优先级路径。 */\nfunction resolveConfigPath(cwd: string): string {\n const repoRoot = findWorkspaceRoot(cwd)\n if (repoRoot && join(repoRoot) !== join(cwd)) {\n return join(cwd, '.otto', 'feishu-use.json')\n }\n return join(cwd, '.otto', 'feishu-use.json')\n}\n\nexport interface FeishuSetupResult {\n /** app_id 已写入配置 */\n appId: string\n /** bot/v3/info 返回的 bot 身份;验证失败时为 undefined */\n botOpenId?: string\n botName?: string\n /** 实际写入的配置文件路径 */\n configPath: string\n /** setup 是否完整成功(凭据已写入 + bot 身份已验证) */\n success: boolean\n}\n\n/** 往指定路径写入 feishu-use 配置文件(mode 0o600),覆盖已有文件。 */\nfunction writeFeishuConfigFile(configPath: string, overrides: Record<string, unknown>): string {\n const base = {\n app_id: 'REPLACE_WITH_YOUR_FEISHU_APP_ID',\n app_secret: 'REPLACE_WITH_YOUR_FEISHU_APP_SECRET',\n allowed_chats: [] as string[],\n workspace_dir: join(configPath, '..', '..', 'feishu-use-workspace'),\n max_reply_chunk_length: 4000,\n }\n const merged = { ...base }\n for (const [k, v] of Object.entries(overrides)) {\n if (v !== undefined && v !== '') (merged as Record<string, unknown>)[k] = v\n }\n mkdirSync(join(configPath, '..'), { recursive: true })\n writeFileSync(configPath, JSON.stringify(merged, null, 2) + '\\n', { mode: 0o600 })\n return configPath\n}\n\n/** 从配置文件读取当前值;文件不存在时返回 undefined。 */\nfunction readConfigSafe(configPath: string): Record<string, unknown> | undefined {\n try {\n if (!existsSync(configPath)) return undefined\n return JSON.parse(readFileSync(configPath, 'utf8')) as Record<string, unknown>\n } catch {\n return undefined\n }\n}\n\n/** openSelect 的 Promise 包装:用户选择 → resolve(value),Esc/取消 → resolve(undefined)。 */\nfunction promiseSelect(\n ctx: CliContext,\n opts: { title: string; options: Array<{ label: string; value: string; detail?: string }> },\n): Promise<string | undefined> {\n return new Promise((resolve) => {\n ctx.tui.modal.openModal({\n kind: 'select',\n ...opts,\n onSelect: (v) => resolve(v),\n onCancel: () => resolve(undefined),\n })\n })\n}\n\n/** openConfirm 的 Promise 包装:确认 → resolve(true),取消 → resolve(false)。 */\nfunction promiseConfirm(ctx: CliContext, title: string, message: string | string[]): Promise<boolean> {\n return new Promise((resolve) => {\n ctx.tui.modal.openModal({\n kind: 'confirm',\n title,\n message,\n onConfirm: () => resolve(true),\n onCancel: () => resolve(false),\n })\n })\n}\n\n/**\n * 启动 `registerApp` 子进程并等待结果。\n *\n * 子进程 stdout 输出 JSON `{ client_id, client_secret }` 表示成功;\n * 超时/崩溃/非零退出码 → resolve(undefined)。\n *\n * `pluginDir` 是 feishu-use 插件安装目录(含 node_modules/@larksuiteoapi/node-sdk)。\n *\n * `onQrCodeUrl`(RFC-134 §8 F1 修复):QR 码 URL 由 SDK `onQRCodeReady` 回调在子进程侧\n * 产生,经 `QRCODE_URL::<json>` 前缀行写入 stderr——本函数解析该前缀行并回调给调用方展示,\n * 修复此前\"stderr 从未被读取、URL 被静默丢弃\"的缺陷(用户选择扫码后永远看不到二维码)。\n */\nfunction runRegisterApp(\n pluginDir: string,\n timeoutMs = 5 * 60 * 1000,\n onQrCodeUrl?: (info: { url: string; expireIn: number }) => void,\n): Promise<{ client_id: string; client_secret: string } | undefined> {\n return new Promise((resolve) => {\n const scriptPath = join(pluginDir, 'scripts', 'register-app.mjs')\n if (!existsSync(scriptPath)) {\n resolve(undefined)\n return\n }\n\n const child = spawn('node', [scriptPath], {\n cwd: pluginDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout: timeoutMs,\n env: { ...process.env },\n })\n\n let stdout = ''\n let stderr = ''\n let qrEmitted = false\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString()\n })\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString()\n if (qrEmitted || !onQrCodeUrl) return\n const line = stderr.split('\\n').find((l) => l.startsWith('QRCODE_URL::'))\n if (!line) return\n try {\n const info = JSON.parse(line.slice('QRCODE_URL::'.length)) as { url: string; expireIn: number }\n if (info.url) {\n qrEmitted = true\n onQrCodeUrl(info)\n }\n } catch {\n /* 前缀行畸形,忽略,等待后续 data 事件或直到子进程结束都拿不到 URL */\n }\n })\n\n child.on('error', () => resolve(undefined))\n child.on('close', (code) => {\n if (code !== 0) return resolve(undefined)\n try {\n const result = JSON.parse(stdout.trim().split('\\n').pop()!) as {\n client_id: string\n client_secret: string\n }\n if (result.client_id && result.client_secret) {\n resolve(result)\n return\n }\n } catch {\n /* fall through */\n }\n resolve(undefined)\n })\n })\n}\n\n/**\n * 用 app_id/app_secret 调用 `bot/v3/info` 验证凭据并获取 bot 身份。\n *\n * 子进程执行插件自带脚本 `scripts/verify-bot.mjs`(与 `runRegisterApp` 调用\n * `register-app.mjs` 同模式,终局架构 review 2026-07-20 下沉)——宿主 CLI 只负责\n * spawn + 传参 + 解析结果,不内联飞书 SDK 的具体调用细节(`bot/v3/info` 端点/Client\n * 构造方式),保持 RFC-134 D2 的架构隔离:`packages/cli` 对插件内部实现零认知。\n * 失败(网络不通/凭据错误/权限不足/脚本缺失)返回 undefined。\n */\nfunction verifyBotIdentity(\n pluginDir: string,\n appId: string,\n appSecret: string,\n): Promise<{ openId: string; name: string } | undefined> {\n return new Promise((resolve) => {\n const scriptPath = join(pluginDir, 'scripts', 'verify-bot.mjs')\n if (!existsSync(scriptPath)) {\n resolve(undefined)\n return\n }\n\n const child = spawn('node', [scriptPath], {\n cwd: pluginDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout: 15_000,\n env: { ...process.env, FEISHU_VERIFY_APP_ID: appId, FEISHU_VERIFY_APP_SECRET: appSecret },\n })\n\n let stdout = ''\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString()\n })\n\n child.on('error', () => resolve(undefined))\n child.on('close', (code) => {\n if (code !== 0) return resolve(undefined)\n try {\n const r = JSON.parse(stdout.trim()) as { openId: string; name: string }\n resolve(r)\n } catch {\n resolve(undefined)\n }\n })\n })\n}\n\n/** 脱敏 App ID:前 8 字符 + *** + 后 4 字符。 */\nfunction maskAppId(appId: string): string {\n if (appId.length <= 12) return appId.slice(0, 4) + '***'\n return appId.slice(0, 8) + '***' + appId.slice(-4)\n}\n\n/**\n * feishu-use 交互式配置向导主入口(RFC-134 M1)。\n *\n * 在 TUI 内调用(需已有 TUI 上下文),通过 async/await 链式编排对话框。\n * 外部调用方负责提供 configPath 和 pluginDir。\n *\n * @param ctx CliContext(需 TUI 可用)\n * @param configPath 配置文件的目标路径(如 `<cwd>/.otto/feishu-use.json`)\n * @param pluginDir feishu-use 插件的安装目录(含 node_modules)\n * @param existingConfig 已有配置(存在时为重配置模式,展示当前值)\n */\nexport async function runFeishuSetupWizard(\n ctx: CliContext,\n configPath: string,\n pluginDir: string,\n existingConfig?: Record<string, unknown>,\n): Promise<FeishuSetupResult | undefined> {\n // ── 重配置模式:展示当前值 + 确认覆盖 ──\n if (existingConfig?.app_id) {\n const currentAppId = String(existingConfig.app_id)\n const lines = [\n `App ID: ${maskAppId(currentAppId)}`,\n existingConfig.allowed_chats\n ? `allowed_chats: ${(existingConfig.allowed_chats as string[]).length} 个`\n : 'allowed_chats: 空(拒绝所有)',\n `配置路径: ${configPath}`,\n ]\n const confirmed = await promiseConfirm(ctx, 'feishu-use 重配置', ['当前配置:', '', ...lines, '', '确定要重新配置吗?当前凭据将被覆盖。'])\n if (!confirmed) return undefined\n }\n\n // ── Step 1: 选择配置方式 ──\n const method = await promiseSelect(ctx, {\n title: 'feishu-use 配置',\n options: [\n { label: '扫码自动创建飞书应用(推荐)', value: 'qr', detail: '飞书 App 扫码确认后自动获取凭据' },\n { label: '手动输入 App ID / App Secret', value: 'manual', detail: '已有飞书自建应用,直接填入凭据' },\n ],\n })\n if (!method) return undefined // user cancelled\n\n let appId = ''\n let appSecret = ''\n let allowedChats: string[] = []\n let trustedChats: string[] = []\n\n // ── Step 2a: QR 码路径 ──\n if (method === 'qr') {\n ctx.tui.info.showToast('plugin-feishu-use', '正在生成飞书应用注册二维码,请稍候…')\n\n const result = await runRegisterApp(pluginDir, 5 * 60 * 1000, (info) => {\n // RFC-134 §8 F1 修复:QR URL 必须展示给用户才能完成扫码,此前写入 stderr 后\n // 从未被读取,用户永远看不到二维码。用 pushMessage(非 toast)展示——toast 会\n // 在几秒后自动消失,而用户需要足够时间打开飞书 App 扫码。\n const expireMinutes = Math.round(info.expireIn / 60)\n ctx.tui.messages.pushMessage({\n role: 'assistant',\n content: [{\n type: 'text',\n text: `feishu-use:请在飞书 App 中打开以下链接扫码确认创建应用(${expireMinutes} 分钟内有效):\\n${info.url}`,\n }],\n })\n })\n if (!result) {\n // 超时或失败 → 回退到手动输入\n const retry = await promiseConfirm(ctx, 'plugin-feishu-use', '扫码注册失败(超时或网络错误)。是否改为手动输入凭据?')\n if (!retry) return undefined\n // 继续执行 manual 路径(故意 fall-through)\n } else {\n appId = result.client_id\n appSecret = result.client_secret\n ctx.tui.info.showToast('plugin-feishu-use', '扫码注册成功!凭据已自动填充。')\n }\n }\n\n // ── Step 2b: 手动输入路径(或 QR 失败后回退) ──\n if (!appId) {\n const hintId = existingConfig?.app_id ? `(当前: ${maskAppId(String(existingConfig.app_id))},留空保留原值)` : ''\n const enteredId = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '飞书 App ID',\n placeholder: `cli_xxxxxxxxxxxx ${hintId}`,\n description: [],\n })\n if (enteredId === null) return undefined\n appId = enteredId || String(existingConfig?.app_id ?? '')\n\n const enteredSecret = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '飞书 App Secret',\n mask: true,\n description: existingConfig?.app_secret ? ['留空保留当前 Secret'] : [],\n })\n if (enteredSecret === null) return undefined\n appSecret = enteredSecret || String(existingConfig?.app_secret ?? '')\n\n if (!appId || !appSecret) {\n ctx.tui.info.showToast('plugin-feishu-use', 'App ID 和 App Secret 不能为空,配置已取消。')\n return undefined\n }\n\n // 可选字段\n const chatsRaw = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '允许的 chat_id(逗号分隔,留空=拒绝所有)',\n placeholder: existingConfig ? `当前: ${(existingConfig.allowed_chats as string[] | undefined)?.join(', ') ?? '空'}` : '',\n description: [],\n })\n if (chatsRaw === null) return undefined\n if (chatsRaw) {\n allowedChats = chatsRaw.split(',').map(s => s.trim()).filter(Boolean)\n } else if (existingConfig?.allowed_chats) {\n allowedChats = existingConfig.allowed_chats as string[]\n }\n\n const trustedRaw = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '可信 chat_id(逗号分隔,留空=仅只读)',\n placeholder: existingConfig ? `当前: ${(existingConfig.trusted_chats as string[] | undefined)?.join(', ') ?? '空'}` : '',\n description: [],\n })\n if (trustedRaw === null) return undefined\n if (trustedRaw) {\n trustedChats = trustedRaw.split(',').map(s => s.trim()).filter(Boolean)\n } else if (existingConfig?.trusted_chats) {\n trustedChats = existingConfig.trusted_chats as string[]\n }\n }\n\n // ── Step 3: 写入配置 ──\n const workspaceDir = existingConfig?.workspace_dir\n ? String(existingConfig.workspace_dir)\n : join(pluginDir, '..', '..', 'feishu-use-workspace')\n writeFeishuConfigFile(configPath, {\n app_id: appId,\n app_secret: appSecret,\n allowed_chats: allowedChats,\n trusted_chats: trustedChats,\n workspace_dir: workspaceDir,\n })\n\n ctx.tui.info.showToast('plugin-feishu-use', `配置已写入 ${configPath}`)\n\n // ── Step 4: 验证 bot 身份 ──\n const bot = await verifyBotIdentity(pluginDir, appId, appSecret)\n if (bot) {\n ctx.tui.info.showToast('plugin-feishu-use', `Bot 身份验证成功:${bot.name} (${bot.openId})`)\n } else {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/feishu setup',\n status: 'error',\n summary: 'bot 身份验证失败(凭据不正确或网络不可达)',\n detail: '请稍后用 `otto feishu config set` 修正凭据,或手动编辑配置文件。',\n })\n }\n\n return {\n appId,\n botOpenId: bot?.openId,\n botName: bot?.name,\n configPath,\n success: !!bot,\n }\n}\n\n/**\n * 给 runFeishuSetupWizard 准备参数的外层封装:计算 configPath 和 pluginDir。\n *\n * @returns setup 结果,或 undefined(用户取消)\n */\nexport async function launchFeishuSetup(\n ctx: CliContext,\n): Promise<FeishuSetupResult | undefined> {\n const ws = ctx.app.workspaceDir ?? process.cwd()\n const configPath = resolveConfigPath(ws)\n\n // 找到 feishu-use 的安装目录(插件目录 = 含 otto-plugin.json 的目录)\n const allPlugins = discoverPluginsWithBundled({ cwd: ws, homedir: homedir() })\n const feishuPlugin = allPlugins.find(p => p.id === 'plugin-feishu-use')\n if (!feishuPlugin) {\n ctx.tui.info.showToast('plugin-feishu-use', 'feishu-use 插件尚未安装,请先在 `/model` 安装入口安装 feishu-use。')\n return undefined\n }\n\n const pluginDir = feishuPlugin.dir\n const existing = readConfigSafe(configPath)\n\n return runFeishuSetupWizard(ctx, configPath, pluginDir, existing)\n}\n"],"mappings":"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"}
|
package/dist/install-DlVJRHRT.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import{InstallManager as e,defaultPostInstallExecutor as t}from"@x-otto/coding";import{formatBytes as n}from"@x-otto/shared";import{i18next as r}from"@x-otto/tui";const i=new Set([`plugin`,`skill`,`mcp`]);async function a(e,n){let i=o(e);if(!i.source){n.tui.info.showToast(r.t(`cmd.titleInstall`),r.t(`cmd.installUsage`));return}let a=i.flags.has(`--global`),c=i.flags.has(`--here`);if(a&&c){n.tui.info.showToast(r.t(`cmd.titleInstall`),r.t(`cmd.installMutuallyExclusive`));return}let l={workspaceDir:c?process.cwd():n.app.workspaceDir,global:a,force:i.flags.has(`--force`),dryRun:i.flags.has(`--dry-run`),as:i.as,type:i.type,env:i.env,subPath:i.subPath,ref:i.ref,postInstallExecutor:t,onProgress:i.dryRun?void 0:e=>{let t=e.total&&e.current?`[${e.current}/${e.total}] `:``;n.tui.info.showToast(`Installing`,`${t}${e.message}`)}};await s(i.source,l,n,!i.dryRun)}function o(e){let t=new Set,n,r,a,o,s,c=!1,l=0;for(;l<e.length;){let u=e[l];if(u===`--as`){l++;let t=e[l]?.toLowerCase();i.has(t??``)&&(n=t)}else if(u===`--type`){l++;let t=e[l]?.toLowerCase();(t===`file`||t===`url`||t===`git`||t===`npm`)&&(r=t)}else if(u===`--sub-path`)l++,o=e[l];else if(u===`--ref`)l++,s=e[l];else if(u===`--env`){l++;let t=e[l];if(t){let e=t.indexOf(`=`);e>0&&(a??={},a[t.slice(0,e)]=t.slice(e+1))}}else if(u.startsWith(`--`))t.add(u);else return c=t.has(`--dry-run`),{source:e.slice(l).filter(e=>!e.startsWith(`--`))[0],flags:t,as:n,type:r,env:a,subPath:o,ref:s,dryRun:c};l++}return c=t.has(`--dry-run`),{source:void 0,flags:t,as:n,type:r,env:a,subPath:o,ref:s,dryRun:c}}async function s(t,n,i,a){let o=new e,s=`${r.t(`cmd.titleInstall`)} — "${t}"`,c=r.t(`cmd.installLoadingHint`),l={...n,dryRun:!0,onProgress:e=>{c=e.message}};i.tui.modal.openModal({kind:`select`,title:s,subtitle:`${r.t(`cmd.titleDryRun`)}...\n${c}`,layout:`buttons`,options:[],onCancel:()=>{i.tui.info.showToast(r.t(`cmd.titleInstall`),r.t(`cmd.selectCancelled`))},async:(async()=>{let e=await o.install(t,l,()=>i.app.settings.get(`mcp_servers`),async e=>{await i.app.settings.persist({mcp_servers:e})});if(!e.success)return{title:s,subtitle:r.t(`cmd.dryRunDetectFailed`,{err:e.error??r.t(`cmd.unknownError`)}),options:[{label:r.t(`cmd.commonClose`),value:`close`,color:`gray`}]};if(!e.plan||e.plan.length===0)return{title:s,subtitle:r.t(`cmd.dryRunPreviewOk`,{name:e.name??r.t(`cmd.unknownLabel`)}),options:[{label:r.t(`cmd.commonClose`),value:`close`,color:`gray`}]};let n=e.plan.filter(e=>!e.startsWith(r.t(`cmd.planSourcePrefix`))),c=e.name??r.t(`cmd.unknownLabel`);if(e.results!==void 0){let i=e.results.length,o=`${r.t(`cmd.titleInstall`)} — "${i} skills (${t})"`;return a?{title:o,subtitle:n.join(`
|
|
3
|
-
`),options:[{label:r.t(`cmd.installActionLabel`),value:`install`,color:`accent`},{label:r.t(`cmd.commonCancel`),value:`cancel`,color:`gray`}]}:{title:o,subtitle:n.join(`
|
|
4
|
-
`),options:[{label:r.t(`cmd.commonClose`),value:`close`,color:`gray`}]}}return a?{title:`${r.t(`cmd.titleInstall`)} — "${c}"`,subtitle:n.join(`
|
|
5
|
-
`),options:[{label:r.t(`cmd.installActionLabel`),value:`install`,color:`accent`},{label:r.t(`cmd.commonCancel`),value:`cancel`,color:`gray`}]}:{title:`${r.t(`cmd.titleDryRun`)} — "${c}"`,subtitle:n.join(`
|
|
6
|
-
`),options:[{label:r.t(`cmd.commonClose`),value:`close`,color:`gray`}]}})(),onSelect:async e=>{e===`cancel`||e===`close`||e===`install`&&await h(t,{...n,dryRun:!1},i)}})}function c(e){return e.bytesTotal!==void 0&&e.bytesDownloaded!==void 0?e.bytesDownloaded/e.bytesTotal:e.current&&e.total?e.current/e.total:0}function l(e){return e.bytesDownloaded===void 0?e.message:e.bytesTotal===void 0?`${e.message} ${n(e.bytesDownloaded)}`:`${e.message} ${n(e.bytesDownloaded)} / ${n(e.bytesTotal)}`}function u(e){return e.replace(/[。.]\s*$/,``)}function d(e,t){let n=(e??[]).filter(e=>!e.success);if(n.length===0)return t;let i=n.slice(0,3),a=i.map(e=>`${e.name??`?`} —— ${u(e.error??t)}`).join(`;`);return n.length>i.length?`${a}${r.t(`install.failureOverflow`)}`:a}function f(e,t){let n=(t??[]).filter(e=>!e.success);if(t?n.length>0&&n.every(e=>e.failureKind===`conflict`):e.failureKind===`conflict`)return{nextStep:{kind:`force`,label:r.t(`install.nextStepForce`)}}}function p(e,t,n){let i=e.results,a=i?i.filter(e=>e.success).length:e.success?1:0,o=i?i.length-a:e.success?0:1;return a===0?{status:`error`,percent:100,phaseStatuses:[`done`,`done`,`error`,`pending`],errorMessage:e.error??d(i,n),...f(e,i)??{}}:o>0?{status:`partial`,percent:100,phaseStatuses:[`done`,`done`,`error`,`done`],errorMessage:r.t(`install.partialCounts`,{ok:a,fail:o})+d(i,n),...f(e,i)??{}}:t?{status:`partial`,percent:100,phaseStatuses:[`done`,`done`,`done`,`error`],errorMessage:r.t(`cmd.installedRefreshErr`,{err:t}),nextStep:{kind:`reload`,label:r.t(`install.nextStepReload`)}}:{status:`success`,percent:100,phaseStatuses:[`done`,`done`,`done`,`done`]}}function m(e,t,n){e.tui.modal.updateProgress(p(t,n,r.t(`cmd.unknownError`)))}async function h(t,n,i){let a=new e,o=r.t(`cmd.titleInstall`),s=[r.t(`install.phaseResolving`),r.t(`install.phaseDetecting`),r.t(`install.phaseInstalling`),r.t(`install.phaseRefreshing`)];i.tui.modal.openModal({kind:`progress`,title:r.t(`install.title`,{source:t}),phases:s});let u={resolving:0,detecting:1,installing:2,refreshing:3},d={...n,onProgress:e=>{let t=u[e.phase]??-1;if(t>=0){let n=s.map((e,n)=>n<t?`done`:n===t?`active`:`pending`),r=c(e),a=l(e);i.tui.modal.updateProgress({phaseIndex:t,phaseStatuses:n,percent:Math.round((t+r)/s.length*90),detail:a,subProgress:e.current!==void 0&&e.total!==void 0?{current:e.current,total:e.total}:void 0})}}},f=await a.install(t,d,()=>i.app.settings.get(`mcp_servers`),async e=>{await i.app.settings.persist({mcp_servers:e})}),p=f.success||(f.results?.some(e=>e.success)??!1),h;if(p){i.tui.modal.updateProgress({phaseIndex:3,phaseStatuses:[`done`,`done`,`done`,`active`],percent:90,detail:r.t(`install.reloadingDetail`)});try{await i.app.reloadCapabilities(),i.tui.plugins.setPluginRegistrations(i.app.getPluginRegistrations())}catch(e){h=e instanceof Error?e.message:String(e)}}if(m(i,f,h),!f.success&&(!f.results||f.results.every(e=>!e.success))&&(i.tui.info.showToast(o,r.t(`cmd.installFailed`,{err:f.error??r.t(`cmd.unknownError`)})),!f.results))return;let _=n.global?r.t(`cmd.scopeGlobal`):r.t(`cmd.scopeProject`);if(f.results){let e=0,t=0;for(let n of f.results)n.success?(e++,i.tui.info.showToast(o,r.t(`cmd.installedTo`,{name:n.name??`extension`,scope:_,seg:n.targetPath?` (${n.targetPath})`:``}))):(t++,i.tui.info.showToast(o,r.t(`cmd.installFailed`,{err:n.error??r.t(`cmd.unknownError`)})));let n=`${e} installed${t>0?`, ${t} failed`:``}`;if(i.tui.info.showToast(o,n),t>0){let e=f.results.filter(e=>!e.success).map(e=>`✗ ${e.name??`?`} —— ${e.error??r.t(`cmd.unknownError`)}`);i.tui.messages.pushMessage({role:`command_echo`,command:`/install`,status:`error`,summary:n,detail:e.join(`
|
|
7
|
-
`)})}f.warnings&&f.warnings.length>0&&i.tui.messages.pushMessage({role:`command_echo`,command:`/install`,status:`warn`,summary:`${f.warnings.length} 项警告`,detail:f.warnings.map(e=>`⚠ ${e}`).join(`
|
|
8
|
-
`)}),g(i,h);return}if(f.postinstall)if(f.postinstall.success)i.tui.info.showToast(o,`"${f.name}" installed (${_}). postinstall: ok`);else{let e=f.postinstall.timeout?`timed out (30s)`:f.postinstall.error??`exit=${f.postinstall.exitCode??`N/A`}`;i.tui.info.showToast(o,`"${f.name}" installed (${_}). postinstall: FAILED (${e})`);let t=[`Plugin "${f.name}" postinstall hook failed:`];t.push(` command: ${f.postinstall.command}`),f.postinstall.exitCode!=null&&t.push(` exit code: ${f.postinstall.exitCode}`),f.postinstall.stderr&&t.push(` stderr: ${f.postinstall.stderr}`),f.postinstall.truncated&&t.push(` (output truncated)`),i.tui.messages.pushMessage({role:`command_echo`,command:`/install`,status:`error`,summary:`"${f.name}" postinstall hook failed`,detail:t.join(`
|
|
9
|
-
`)})}else i.tui.info.showToast(o,r.t(`cmd.installedTo`,{name:f.name??`extension`,scope:_,seg:f.targetPath?` (${f.targetPath})`:``}));f.warnings&&f.warnings.length>0&&i.tui.messages.pushMessage({role:`command_echo`,command:`/install`,status:`warn`,summary:`${f.warnings.length} 项警告`,detail:f.warnings.map(e=>`⚠ ${e}`).join(`
|
|
10
|
-
`)}),g(i,h)}function g(e,t){if(t){e.tui.messages.pushMessage({role:`command_echo`,command:`/install`,status:`error`,summary:r.t(`cmd.installedRefreshErr`,{err:t})});return}e.tui.info.showToast(r.t(`cmd.titleInstall`),r.t(`cmd.capRefreshedInstall`))}export{o as a,a as i,p as n,l as r,c as t};
|
|
11
|
-
//# sourceMappingURL=install-DlVJRHRT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"install-DlVJRHRT.js","names":[],"sources":["../src/commands/handlers/install.ts"],"sourcesContent":["/**\n * handlers/install.ts — /install 命令处理\n *\n * /install [--global] [--force] [--dry-run] [--as <type>] [--type <src>] <source>\n *\n * 安装 skill/plugin/mcp 包到项目 (.otto/) 或全局 (~/.otto/)。\n * 支持 file/url/git/npm 四种来源,自动检测内容类型。\n */\nimport { i18next } from '@x-otto/tui'\nimport { InstallManager, defaultPostInstallExecutor } from '@x-otto/coding'\nimport { formatBytes } from '@x-otto/shared'\nimport type { InstallOptions, InstallProgress, InstallResult } from '@x-otto/coding'\nimport type { McpServersConfig } from '@x-otto/setting'\nimport type { SelectOption } from '@x-otto/tui'\nimport type { CliContext } from '../../command-dispatcher'\n\nconst VALID_AS = new Set(['plugin', 'skill', 'mcp'])\n\nexport async function handleInstall(\n args: string[],\n ctx: CliContext,\n): Promise<void> {\n const parsed = parseArgs(args)\n if (!parsed.source) {\n ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.installUsage'))\n return\n }\n\n const isGlobal = parsed.flags.has('--global')\n const isHere = parsed.flags.has('--here')\n\n if (isGlobal && isHere) {\n ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.installMutuallyExclusive'))\n return\n }\n\n const options: InstallOptions = {\n // --here:锚到当前命令目录,绕过 ctx.app.workspaceDir 的向上工作区根查找。\n workspaceDir: isHere ? process.cwd() : ctx.app.workspaceDir,\n global: isGlobal,\n force: parsed.flags.has('--force'),\n dryRun: parsed.flags.has('--dry-run'),\n as: parsed.as,\n type: parsed.type,\n env: parsed.env,\n subPath: parsed.subPath,\n ref: parsed.ref,\n postInstallExecutor: defaultPostInstallExecutor,\n // RFC-131: 进度回调 — 仅非 dry-run 时注入\n onProgress: !parsed.dryRun ? (p) => {\n const prefix = p.total && p.current ? `[${p.current}/${p.total}] ` : ''\n ctx.tui.info.showToast('Installing', `${prefix}${p.message}`)\n } : undefined,\n }\n\n // 确认优先(P3 安全边界):始终先 dry-run 取 plan 展示——含将运行的 postinstall/setup\n // shell 命令——确认后才真装(项目级插件真装含 auto-trust + 立即跑 postinstall)。\n // --dry-run 仅预览,不提供安装按钮。\n await showConfirmPanel(parsed.source, options, ctx, !parsed.dryRun)\n}\n\n// ── 参数解析 ──\n\ninterface ParsedArgs {\n source: string | undefined\n flags: Set<string>\n as: 'plugin' | 'skill' | 'mcp' | undefined\n type: 'file' | 'url' | 'git' | 'npm' | undefined\n env: Record<string, string> | undefined\n /** RFC-201:monorepo 子目录(--sub-path,registry 条目 subPath 的 CLI 透传面)。 */\n subPath: string | undefined\n /**\n * RFC-221 D2/T221-3:git 来源的 tag/branch 名(`--ref`),透传到\n * `InstallOptions.ref` → `resolveGitSource(gitUrl, ref)`。仅对 git 来源有效,\n * 其余来源类型忽略此参数(不报错,与 subPath 对非 git/归档来源的静默忽略语义一致)。\n * 不支持 commit hash——见 `git-resolver.ts` 头部注释(主流托管方服务端默认拒绝)。\n */\n ref: string | undefined\n dryRun: boolean\n}\n\n/** 导出供单测覆盖 `--ref`/`--sub-path` 等标志解析行为(RFC-221 T221-3),非公开 API。 */\nexport function parseArgs(args: string[]): ParsedArgs {\n const flags = new Set<string>()\n let as: 'plugin' | 'skill' | 'mcp' | undefined\n let type: 'file' | 'url' | 'git' | 'npm' | undefined\n let env: Record<string, string> | undefined\n let subPath: string | undefined\n let ref: string | undefined\n let dryRun = false\n let i = 0\n\n while (i < args.length) {\n const a = args[i]!\n if (a === '--as') {\n i++\n const val = args[i]?.toLowerCase()\n if (VALID_AS.has(val ?? '')) as = val as typeof as\n } else if (a === '--type') {\n i++\n const val = args[i]?.toLowerCase()\n if (val === 'file' || val === 'url' || val === 'git' || val === 'npm') {\n type = val\n }\n } else if (a === '--sub-path') {\n // RFC-201 M0/M1:monorepo 子目录安装(registry 条目 subPath 的 CLI 透传面)\n i++\n subPath = args[i]\n } else if (a === '--ref') {\n // RFC-221 D2/T221-3:git 来源 tag/branch 指定安装\n i++\n ref = args[i]\n } else if (a === '--env') {\n i++\n const kv = args[i]\n if (kv) {\n const eq = kv.indexOf('=')\n if (eq > 0) {\n env = env ?? {}\n env[kv.slice(0, eq)] = kv.slice(eq + 1)\n }\n }\n } else if (a.startsWith('--')) {\n flags.add(a)\n } else {\n // positional (source)\n dryRun = flags.has('--dry-run')\n const rest = args.slice(i).filter((x) => !x.startsWith('--'))\n return { source: rest[0], flags, as, type, env, subPath, ref, dryRun }\n }\n i++\n }\n\n dryRun = flags.has('--dry-run')\n return { source: undefined, flags, as, type, env, subPath, ref, dryRun }\n}\n\n// ── 确认面板 ──\n\n/**\n * 先 dry-run 取安装 plan 并展示确认屏。\n *\n * RFC-226:命令发出到确认框出现之间新增 loading 反馈——dry-run 探测(git clone/npm pack/\n * url 下载 + 内容检测)本身耗时不短,此前全程静默。改造后:① 立即展示 loading 态弹窗\n * (options=[] 空选项态,D2);② dry-run 探测阶段(resolving/detecting)的进度通过局部\n * `onProgress`(不复用 `options.onProgress` toast 版本,D5b/R6)实时刷新 loading 文案;\n * ③ dry-run 完成后原地过渡为确认框或错误态(D3/D5a)。\n *\n * **修复**(dialog 栈堆积):此前每次状态转换(loading→进度更新→确认/错误/预览)都调用\n * `ctx.tui.modal.openModal({ kind: 'select' })`,而同步 select 路径每次 `router.navigate` 都是\n * push 新栈条目,导致对话框层层堆积、用户要按多次 Esc 才能退出。现改用框架层 `async` 字段:\n * loading 态由框架自动展示,dry-run 在 async promise 内完成,resolve 后框架用 `replace: true`\n * 原地过渡为真实内容。最终的确认/错误/预览弹窗由 `onSelect` 回调内驱动(此时 loading 弹窗\n * 已被框架 replace 为最终态,Esc 一次即可退出)。\n *\n * @param proceed true=确认后真装([安装, 取消]);false=纯预览(--dry-run,仅[关闭])。\n */\nasync function showConfirmPanel(\n source: string,\n options: InstallOptions,\n ctx: CliContext,\n proceed: boolean,\n): Promise<void> {\n const manager = new InstallManager()\n const loadingTitle = `${i18next.t('cmd.titleInstall')} — \"${source}\"`\n\n // dry-run 专用局部 onProgress——不复用 options.onProgress(toast 版本,只服务\n // executeInstall 真实安装路径)。探测阶段进度文案由 async promise 内部记录,\n // resolve 时作为 subtitle 回灌(框架 replace 一次性更新)。\n let progressMessage = i18next.t('cmd.installLoadingHint')\n const dryOptions: InstallOptions = {\n ...options,\n dryRun: true,\n onProgress: (p: InstallProgress) => {\n progressMessage = p.message\n },\n }\n\n ctx.tui.modal.openModal({\n kind: 'select',\n title: loadingTitle,\n subtitle: `${i18next.t('cmd.titleDryRun')}...\\n${progressMessage}`,\n layout: 'buttons',\n options: [],\n onCancel: () => {\n ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.selectCancelled'))\n },\n // async:dry-run 探测在 promise 内完成,resolve 后框架用 replace:true 原地过渡。\n async: (async (): Promise<{ title?: string; subtitle?: string; options: SelectOption[] }> => {\n const result = await manager.install(\n source,\n dryOptions,\n () => ctx.app.settings.get('mcp_servers') as Record<string, unknown> | undefined,\n async (servers) => {\n await ctx.app.settings.persist({ mcp_servers: servers as McpServersConfig })\n },\n )\n\n if (!result.success) {\n return {\n title: loadingTitle,\n subtitle: i18next.t('cmd.dryRunDetectFailed', { err: result.error ?? i18next.t('cmd.unknownError') }),\n options: [{ label: i18next.t('cmd.commonClose'), value: 'close', color: 'gray' }],\n }\n }\n\n if (!result.plan || result.plan.length === 0) {\n return {\n title: loadingTitle,\n subtitle: i18next.t('cmd.dryRunPreviewOk', { name: result.name ?? i18next.t('cmd.unknownLabel') }),\n options: [{ label: i18next.t('cmd.commonClose'), value: 'close', color: 'gray' }],\n }\n }\n\n const plan = result.plan.filter((l) => !l.startsWith(i18next.t('cmd.planSourcePrefix')))\n const name = result.name ?? i18next.t('cmd.unknownLabel')\n const isMultiSkill = result.results !== undefined\n\n // RFC-130: 多 skill 仓库 → 聚合面板\n if (isMultiSkill) {\n const count = result.results!.length\n const title = `${i18next.t('cmd.titleInstall')} — \"${count} skills (${source})\"`\n\n if (!proceed) {\n return {\n title,\n subtitle: plan.join('\\n'),\n options: [{ label: i18next.t('cmd.commonClose'), value: 'close', color: 'gray' }],\n }\n }\n\n return {\n title,\n subtitle: plan.join('\\n'),\n options: [\n { label: i18next.t('cmd.installActionLabel'), value: 'install', color: 'accent' },\n { label: i18next.t('cmd.commonCancel'), value: 'cancel', color: 'gray' },\n ],\n }\n }\n\n // --dry-run:纯预览,不提供安装入口(修正旧语义——旧 dry-run 的\"安装\"按钮会真装)。\n if (!proceed) {\n return {\n title: `${i18next.t('cmd.titleDryRun')} — \"${name}\"`,\n subtitle: plan.join('\\n'),\n options: [{ label: i18next.t('cmd.commonClose'), value: 'close', color: 'gray' }],\n }\n }\n\n return {\n title: `${i18next.t('cmd.titleInstall')} — \"${name}\"`,\n subtitle: plan.join('\\n'),\n options: [\n { label: i18next.t('cmd.installActionLabel'), value: 'install', color: 'accent' },\n { label: i18next.t('cmd.commonCancel'), value: 'cancel', color: 'gray' },\n ],\n }\n })(),\n onSelect: async (v: string) => {\n if (v === 'cancel' || v === 'close') return\n if (v === 'install') {\n await executeInstall(source, { ...options, dryRun: false }, ctx)\n }\n },\n })\n}\n\n// ── 执行安装 ──\n\n/**\n * RFC-224 D2/D3:安装进度 fraction 计算——字节级进度(bytesDownloaded/bytesTotal)\n * 优先于多 skill 计数(current/total),两者互斥语义(见 InstallProgress JSDoc)。\n * 导出供单测覆盖(`install-progress-bytes.test.ts`),不作为公开 API。\n */\nexport function computeProgressFraction(p: {\n bytesDownloaded?: number\n bytesTotal?: number\n current?: number\n total?: number\n}): number {\n if (p.bytesTotal !== undefined && p.bytesDownloaded !== undefined) {\n return p.bytesDownloaded / p.bytesTotal\n }\n if (p.current && p.total) {\n return p.current / p.total\n }\n return 0\n}\n\n/**\n * RFC-224 D2/R4:安装进度 detail 文案格式化——bytesDownloaded 存在时渲染字节数\n * (`formatBytes`),Content-Length 缺失(bytesTotal 为 undefined)时降级为纯字节\n * 计数,不编造总数;否则保持原始 message 不变。导出供单测覆盖。\n */\nexport function formatProgressDetail(p: {\n message: string\n bytesDownloaded?: number\n bytesTotal?: number\n}): string {\n if (p.bytesDownloaded === undefined) return p.message\n if (p.bytesTotal !== undefined) {\n return `${p.message} ${formatBytes(p.bytesDownloaded)} / ${formatBytes(p.bytesTotal)}`\n }\n return `${p.message} ${formatBytes(p.bytesDownloaded)}`\n}\n\n/** `Refreshing…` 在四阶段清单中的下标(RFC-286 D4:该阶段由 CLI 持有)。 */\nconst REFRESHING_PHASE_INDEX = 3\n\n/** partial 面板最多直接列出的失败项条数,超出部分转由 command_echo 承载(RFC-286 D6)。 */\nconst PARTIAL_DETAIL_MAX_ITEMS = 3\n\n/**\n * RFC-286 D6:把失败子项渲染成面板可读的一行摘要。\n *\n * 面板是窄栏终端 UI,超过 `PARTIAL_DETAIL_MAX_ITEMS` 条时只列前几条并指向消息流\n * ——完整清单由 `executeInstall` 的 command_echo 承载(R5:不依赖 toast 单槽通道)。\n */\n/** 拼接多条子错误前去掉句末句号,避免出现「。;」这类双标点(RFC-290 T290-11 收尾)。 */\nfunction stripTrailingPeriod(text: string): string {\n return text.replace(/[。.]\\s*$/, '')\n}\n\nfunction describeFailures(\n subs: Array<{ success: boolean; name?: string; error?: string }> | undefined,\n unknownErrorLabel: string,\n): string {\n const failed = (subs ?? []).filter((r) => !r.success)\n if (failed.length === 0) return unknownErrorLabel\n const shown = failed.slice(0, PARTIAL_DETAIL_MAX_ITEMS)\n const details = shown\n .map((r) => `${r.name ?? '?'} —— ${stripTrailingPeriod(r.error ?? unknownErrorLabel)}`)\n .join(';')\n return failed.length > shown.length ? `${details}${i18next.t('install.failureOverflow')}` : details\n}\n\n/**\n * RFC-290 D2:冲突类失败 → `--force` 建议。\n *\n * **只读结构化的 `failureKind`,绝不匹配 `error` 文案**——错误文案是本地化的人读文本,\n * 子串嗅探(如判断是否含\"已存在\")会在换 locale 或改措辞时静默失效。\n * 单结果读顶层字段,多结果要求**全部**失败项同为 conflict(混合原因不给建议,R4)。\n */\nfunction resolveConflictNextStep(\n result: { failureKind?: 'conflict' },\n subs: Array<{ success: boolean; failureKind?: 'conflict' }> | undefined,\n): { nextStep: { kind: 'force'; label: string } } | undefined {\n const failed = (subs ?? []).filter((r) => !r.success)\n const isConflict = subs\n ? failed.length > 0 && failed.every((r) => r.failureKind === 'conflict')\n : result.failureKind === 'conflict'\n if (!isConflict) return undefined\n return { nextStep: { kind: 'force', label: i18next.t('install.nextStepForce') } }\n}\n\n/**\n * RFC-286 D2/D3/D6:安装终态裁决——把 `InstallResult` + reload 结果映射为进度对话框终态。\n *\n * 映射表(RFC-286 D2 唯一定义处):\n * - 全部成功且 reload 成功 → `success`(2s 自动关闭)\n * - 部分成功(ok>0 && fail>0)→ `partial`(不自动关闭)\n * - 全部失败 → `error`(不自动关闭)\n * - 产物已装、仅 reload 失败 → `partial`(degraded success,提示 /reload)\n *\n * 三种非 success 终态都显式下发 `percent` 与 `phaseStatuses`(R4),失败阶段标 `'error'`\n * ——该取值此前在全仓无任何生产者,`ProgressDialog` 的行内错误渲染是死代码,本函数激活它。\n *\n * 导出供单测覆盖(`install-terminal-state.test.ts`),不作为公开 API。\n */\nexport function computeTerminalProgress(\n result: {\n success: boolean\n error?: string\n failureKind?: 'conflict'\n results?: Array<{ success: boolean; name?: string; error?: string; failureKind?: 'conflict' }>\n },\n reloadError: string | undefined,\n unknownErrorLabel: string,\n): {\n status: 'success' | 'partial' | 'error'\n percent: number\n phaseStatuses: Array<'pending' | 'active' | 'done' | 'error'>\n errorMessage?: string\n nextStep?: { kind: 'force' | 'reload'; label: string }\n} {\n const subs = result.results\n const okCount = subs ? subs.filter((r) => r.success).length : result.success ? 1 : 0\n const failCount = subs ? subs.length - okCount : result.success ? 0 : 1\n\n // 全部失败 → error。失败阶段停在 `installing`(写盘阶段),而非 refreshing——\n // 安装根本没成功,不该显示\"刷新\"阶段被走到过。\n //\n // R3:错误文案按「顶层 error → 子项 error 还原 → 兜底文案」逐级降级。兜底只在\n // 上游连子项原因都没给时才允许出现,绝不用它掩盖上游缺口(本次事故的根因模式)。\n if (okCount === 0) {\n return {\n status: 'error',\n percent: 100,\n phaseStatuses: ['done', 'done', 'error', 'pending'],\n errorMessage: result.error ?? describeFailures(subs, unknownErrorLabel),\n ...(resolveConflictNextStep(result, subs) ?? {}),\n }\n }\n\n // 有成功项但也有失败项 → partial\n if (failCount > 0) {\n return {\n status: 'partial',\n percent: 100,\n phaseStatuses: ['done', 'done', 'error', 'done'],\n errorMessage:\n i18next.t('install.partialCounts', { ok: okCount, fail: failCount }) +\n describeFailures(subs, unknownErrorLabel),\n ...(resolveConflictNextStep(result, subs) ?? {}),\n }\n }\n\n // 全部装好但能力刷新失败 → degraded success(产物已落盘,不能报 error)\n if (reloadError) {\n return {\n status: 'partial',\n percent: 100,\n phaseStatuses: ['done', 'done', 'done', 'error'],\n errorMessage: i18next.t('cmd.installedRefreshErr', { err: reloadError }),\n nextStep: { kind: 'reload', label: i18next.t('install.nextStepReload') },\n }\n }\n\n return { status: 'success', percent: 100, phaseStatuses: ['done', 'done', 'done', 'done'] }\n}\n\nfunction applyTerminalProgress(\n ctx: CliContext,\n result: InstallResult,\n reloadError: string | undefined,\n): void {\n ctx.tui.modal.updateProgress(computeTerminalProgress(result, reloadError, i18next.t('cmd.unknownError')))\n}\n\nasync function executeInstall(\n source: string,\n options: InstallOptions,\n ctx: CliContext,\n): Promise<void> {\n const manager = new InstallManager()\n const label = i18next.t('cmd.titleInstall')\n\n // 打开进度条 dialog(聚焦阻断输入)\n // RFC-290 D5:阶段名与标题走 i18n——此前是硬编码英文字面量,中文环境下面板会突兀\n // 蹦出英文,与其余 20+ 弹窗不一致。\n const phases = [\n i18next.t('install.phaseResolving'),\n i18next.t('install.phaseDetecting'),\n i18next.t('install.phaseInstalling'),\n i18next.t('install.phaseRefreshing'),\n ]\n ctx.tui.modal.openModal({ kind: 'progress', title: i18next.t('install.title', { source }), phases })\n\n // 安装进度 → dialog 更新\n const phaseMap: Record<string, number> = { resolving: 0, detecting: 1, installing: 2, refreshing: 3 }\n const progressOptions: InstallOptions = {\n ...options,\n onProgress: (p) => {\n const idx = phaseMap[p.phase] ?? -1\n if (idx >= 0) {\n const statuses = phases.map((_, i) => {\n if (i < idx) return 'done' as const\n if (i === idx) return 'active' as const\n return 'pending' as const\n })\n const fraction = computeProgressFraction(p)\n const detail = formatProgressDetail(p)\n ctx.tui.modal.updateProgress({\n phaseIndex: idx,\n phaseStatuses: statuses,\n percent: Math.round(((idx + fraction) / phases.length) * 90),\n detail,\n subProgress: p.current !== undefined && p.total !== undefined ? { current: p.current, total: p.total } : undefined,\n })\n }\n },\n }\n\n const result = await manager.install(\n source,\n progressOptions,\n () => ctx.app.settings.get('mcp_servers') as Record<string, unknown> | undefined,\n async (servers) => {\n await ctx.app.settings.persist({ mcp_servers: servers as McpServersConfig })\n },\n )\n\n // ── 安装写盘结束 → 真实能力刷新(RFC-286 D4/D5) ──\n //\n // `refreshing` 阶段由本处 fire——`InstallManager` 不再自行 fire(它没有 App 句柄,\n // 其 refreshing 与真实 reload 无关,属伪进度)。谁执行谁上报:面板上的\n // \"Refreshing…\" 从此对应下方 `reloadCapabilities()` 的真实耗时。\n //\n // 时序变更:终态裁决移到 reload **之后**(此前在 reload 前就置 success 并开始\n // 2s 自动关闭倒计时)。这样 reload 失败才能降级为 partial 呈现给用户。\n const installOk = result.success || (result.results?.some((r) => r.success) ?? false)\n let reloadError: string | undefined\n if (installOk) {\n ctx.tui.modal.updateProgress({\n phaseIndex: REFRESHING_PHASE_INDEX,\n phaseStatuses: ['done', 'done', 'done', 'active'],\n percent: 90,\n detail: i18next.t('install.reloadingDetail'),\n })\n try {\n await ctx.app.reloadCapabilities()\n ctx.tui.plugins.setPluginRegistrations(ctx.app.getPluginRegistrations())\n } catch (err) {\n reloadError = err instanceof Error ? err.message : String(err)\n }\n }\n\n // 安装结束 → dialog 终态(RFC-286 D2/D3/R4:终态必须同时下发 percent 与\n // phaseStatuses,不得让面板继承中途快照——此前只传 status/errorMessage,\n // 导致失败面板冻结在 68% 且阶段行仍是 active,看起来像还在刷新)。\n applyTerminalProgress(ctx, result, reloadError)\n\n if (!result.success) {\n // 多 skill 部分失败时继续(D5:独立安装不回滚)\n // 全部失败时展示第一个错误\n if (!result.results || result.results.every((r) => !r.success)) {\n ctx.tui.info.showToast(label, i18next.t('cmd.installFailed', { err: result.error ?? i18next.t('cmd.unknownError') }))\n if (!result.results) return\n // 多 skill 全部失败但仍然有 results→继续执行 toast 汇总\n }\n }\n\n const scopeLabel = options.global ? i18next.t('cmd.scopeGlobal') : i18next.t('cmd.scopeProject')\n\n // RFC-130: 多 skill → 逐条 toast + 汇总\n if (result.results) {\n let ok = 0\n let fail = 0\n for (const r of result.results) {\n if (r.success) {\n ok++\n ctx.tui.info.showToast(\n label,\n i18next.t('cmd.installedTo', { name: r.name ?? 'extension', scope: scopeLabel, seg: r.targetPath ? ` (${r.targetPath})` : '' }),\n )\n } else {\n fail++\n ctx.tui.info.showToast(label, i18next.t('cmd.installFailed', { err: r.error ?? i18next.t('cmd.unknownError') }))\n }\n }\n const summary = `${ok} installed${fail > 0 ? `, ${fail} failed` : ''}`\n ctx.tui.info.showToast(label, summary)\n\n // RFC-286 D6/R5:失败清单必须落到消息流。上面的逐条 toast 是单槽通道\n // (`main.ts` 的 showToast 每次都覆盖上一条),多个失败项之间会互相顶掉,\n // 面板终态又只列前 3 条——没有这条 command_echo,超出部分将彻底不可见。\n if (fail > 0) {\n const failedLines = result.results\n .filter((r) => !r.success)\n .map((r) => `✗ ${r.name ?? '?'} —— ${r.error ?? i18next.t('cmd.unknownError')}`)\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/install',\n status: 'error',\n summary,\n detail: failedLines.join('\\n'),\n })\n }\n\n // 聚合 warnings\n if (result.warnings && result.warnings.length > 0) {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/install',\n status: 'warn',\n summary: `${result.warnings.length} 项警告`,\n detail: result.warnings.map((w) => `⚠ ${w}`).join('\\n'),\n })\n }\n\n // 能力刷新已在终态裁决前统一执行(RFC-286 D4,见上方 `refreshing` 阶段),\n // 失败已降级为 partial 面板终态;此处只补一条 command_echo 供回溯(R5:\n // 关键失败信息不依赖 toast 单槽通道)。\n reportReloadOutcome(ctx, reloadError)\n return\n }\n\n // RFC-080:postinstall 钩子结果反馈\n if (result.postinstall) {\n if (result.postinstall.success) {\n ctx.tui.info.showToast(label, `\"${result.name}\" installed (${scopeLabel}). postinstall: ok`)\n } else {\n const reason = result.postinstall.timeout\n ? 'timed out (30s)'\n : result.postinstall.error ?? `exit=${result.postinstall.exitCode ?? 'N/A'}`\n ctx.tui.info.showToast(label, `\"${result.name}\" installed (${scopeLabel}). postinstall: FAILED (${reason})`)\n\n const lines = [`Plugin \"${result.name}\" postinstall hook failed:`]\n lines.push(` command: ${result.postinstall.command}`)\n if (result.postinstall.exitCode != null) lines.push(` exit code: ${result.postinstall.exitCode}`)\n if (result.postinstall.stderr) lines.push(` stderr: ${result.postinstall.stderr}`)\n if (result.postinstall.truncated) lines.push(' (output truncated)')\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/install',\n status: 'error',\n summary: `\"${result.name}\" postinstall hook failed`,\n detail: lines.join('\\n'),\n })\n }\n } else {\n ctx.tui.info.showToast(\n label,\n i18next.t('cmd.installedTo', { name: result.name ?? 'extension', scope: scopeLabel, seg: result.targetPath ? ` (${result.targetPath})` : '' }),\n )\n }\n\n // warnings(RFC-080:信任持久化失败等非致命警告)\n if (result.warnings && result.warnings.length > 0) {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/install',\n status: 'warn',\n summary: `${result.warnings.length} 项警告`,\n detail: result.warnings.map((w) => `⚠ ${w}`).join('\\n'),\n })\n }\n\n // 能力刷新已在终态裁决前统一执行(RFC-286 D4)。\n reportReloadOutcome(ctx, reloadError)\n}\n\n/**\n * RFC-286 D4/R5:能力刷新结果的消息通道反馈。\n *\n * 刷新本身已在终态裁决前执行完毕(面板 `Refreshing…` 阶段对应其真实耗时),失败时面板\n * 已降级为 `partial`。此处只负责消息流留痕:成功走 toast(非关键信息,可被覆盖);\n * 失败走 command_echo(关键信息,不依赖 toast 单槽通道)。\n */\nfunction reportReloadOutcome(ctx: CliContext, reloadError: string | undefined): void {\n if (reloadError) {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/install',\n status: 'error',\n summary: i18next.t('cmd.installedRefreshErr', { err: reloadError }),\n })\n return\n }\n ctx.tui.info.showToast(i18next.t('cmd.titleInstall'), i18next.t('cmd.capRefreshedInstall'))\n}\n"],"mappings":"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"}
|
package/dist/install-WCcQ9k9M.js
DELETED