@x-otto/cli 0.0.1-alpha.9 → 0.1.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -65
- package/bin/otto.js +6 -0
- package/dist/{auth-command-CXAB3_hs.js → auth-command-WddVZWgz.js} +3 -3
- package/dist/auth-command-WddVZWgz.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-CGJ2iv_o.js +3 -0
- package/dist/build-id-CGJ2iv_o.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-Du_Sk33M.js +18 -0
- package/dist/command-dispatcher-Du_Sk33M.js.map +1 -0
- package/dist/config-migration-DatflIxs.js.map +1 -1
- package/dist/create-host-app-DwlqiuPB.js +3 -0
- package/dist/create-host-app-DwlqiuPB.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-CPIS47uZ.js +36 -0
- package/dist/doctor-CPIS47uZ.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-BivcU-y_.js +30 -0
- package/dist/feedback-BivcU-y_.js.map +1 -0
- package/dist/feedback-backend-methods-DkhBM2l6.js +3 -0
- package/dist/feedback-backend-methods-DkhBM2l6.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-DVJjhqnt.js +3 -0
- package/dist/interactive-schedule-DVJjhqnt.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-Dq72PTeW.js +5 -0
- package/dist/mcp-server-Dq72PTeW.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-Cp5a7taK.js +3 -0
- package/dist/parse-cli-Cp5a7taK.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-a4W5vFXL.js +21 -0
- package/dist/serve-a4W5vFXL.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-D1Hmbcos.js} +2 -2
- package/dist/{version-DaMJKLwj.js.map → version-D1Hmbcos.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
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{defaultPostInstallExecutor as e,install 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(t,n){let i=o(t);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:e,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(e,n,i,a){let o=`${r.t(`cmd.titleInstall`)} — "${e}"`,s=r.t(`cmd.installLoadingHint`),c={...n,dryRun:!0,onProgress:e=>{s=e.message}};i.tui.modal.openModal({kind:`select`,title:o,subtitle:`${r.t(`cmd.titleDryRun`)}...\n${s}`,layout:`buttons`,options:[],onCancel:()=>{i.tui.info.showToast(r.t(`cmd.titleInstall`),r.t(`cmd.selectCancelled`))},async:(async()=>{let n=await t(e,c,()=>i.app.settings.get(`mcp_servers`),async e=>{await i.app.settings.persist({mcp_servers:e})});if(!n.success)return{title:o,subtitle:r.t(`cmd.dryRunDetectFailed`,{err:n.error??r.t(`cmd.unknownError`)}),options:[{label:r.t(`cmd.commonClose`),value:`close`,color:`gray`}]};if(!n.plan||n.plan.length===0)return{title:o,subtitle:r.t(`cmd.dryRunPreviewOk`,{name:n.name??r.t(`cmd.unknownLabel`)}),options:[{label:r.t(`cmd.commonClose`),value:`close`,color:`gray`}]};let s=n.plan.filter(e=>!e.startsWith(r.t(`cmd.planSourcePrefix`))),l=n.name??r.t(`cmd.unknownLabel`);if(n.results!==void 0){let t=n.results.length,i=`${r.t(`cmd.titleInstall`)} — "${t} skills (${e})"`;return a?{title:i,subtitle:s.join(`
|
|
3
|
+
`),options:[{label:r.t(`cmd.installActionLabel`),value:`install`,color:`accent`},{label:r.t(`cmd.commonCancel`),value:`cancel`,color:`gray`}]}:{title:i,subtitle:s.join(`
|
|
4
|
+
`),options:[{label:r.t(`cmd.commonClose`),value:`close`,color:`gray`}]}}return a?{title:`${r.t(`cmd.titleInstall`)} — "${l}"`,subtitle:s.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`)} — "${l}"`,subtitle:s.join(`
|
|
6
|
+
`),options:[{label:r.t(`cmd.commonClose`),value:`close`,color:`gray`}]}})(),onSelect:async t=>{t===`cancel`||t===`close`||t===`install`&&await h(e,{...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(e,n,i){let a=r.t(`cmd.titleInstall`),o=[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:e}),phases:o});let s={resolving:0,detecting:1,installing:2,refreshing:3},u=await t(e,{...n,onProgress:e=>{let t=s[e.phase]??-1;if(t>=0){let n=o.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)/o.length*90),detail:a,subProgress:e.current!==void 0&&e.total!==void 0?{current:e.current,total:e.total}:void 0})}}},()=>i.app.settings.get(`mcp_servers`),async e=>{await i.app.settings.persist({mcp_servers:e})}),d=u.success||(u.results?.some(e=>e.success)??!1),f;if(d){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){f=e instanceof Error?e.message:String(e)}}if(m(i,u,f),!u.success&&(!u.results||u.results.every(e=>!e.success))&&(i.tui.info.showToast(a,r.t(`cmd.installFailed`,{err:u.error??r.t(`cmd.unknownError`)})),!u.results))return;let p=n.global?r.t(`cmd.scopeGlobal`):r.t(`cmd.scopeProject`);if(u.results){let e=0,t=0;for(let n of u.results)n.success?(e++,i.tui.info.showToast(a,r.t(`cmd.installedTo`,{name:n.name??`extension`,scope:p,seg:n.targetPath?` (${n.targetPath})`:``}))):(t++,i.tui.info.showToast(a,r.t(`cmd.installFailed`,{err:n.error??r.t(`cmd.unknownError`)})));let n=`${e} installed${t>0?`, ${t} failed`:``}`;if(i.tui.info.showToast(a,n),t>0){let e=u.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
|
+
`)})}u.warnings&&u.warnings.length>0&&i.tui.messages.pushMessage({role:`command_echo`,command:`/install`,status:`warn`,summary:`${u.warnings.length} 项警告`,detail:u.warnings.map(e=>`⚠ ${e}`).join(`
|
|
8
|
+
`)}),g(i,f);return}if(u.postinstall)if(u.postinstall.success)i.tui.info.showToast(a,`"${u.name}" installed (${p}). postinstall: ok`);else{let e=u.postinstall.timeout?`timed out (30s)`:u.postinstall.error??`exit=${u.postinstall.exitCode??`N/A`}`;i.tui.info.showToast(a,`"${u.name}" installed (${p}). postinstall: FAILED (${e})`);let t=[`Plugin "${u.name}" postinstall hook failed:`];t.push(` command: ${u.postinstall.command}`),u.postinstall.exitCode!=null&&t.push(` exit code: ${u.postinstall.exitCode}`),u.postinstall.stderr&&t.push(` stderr: ${u.postinstall.stderr}`),u.postinstall.truncated&&t.push(` (output truncated)`),i.tui.messages.pushMessage({role:`command_echo`,command:`/install`,status:`error`,summary:`"${u.name}" postinstall hook failed`,detail:t.join(`
|
|
9
|
+
`)})}else i.tui.info.showToast(a,r.t(`cmd.installedTo`,{name:u.name??`extension`,scope:p,seg:u.targetPath?` (${u.targetPath})`:``}));u.warnings&&u.warnings.length>0&&i.tui.messages.pushMessage({role:`command_echo`,command:`/install`,status:`warn`,summary:`${u.warnings.length} 项警告`,detail:u.warnings.map(e=>`⚠ ${e}`).join(`
|
|
10
|
+
`)}),g(i,f)}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-CEPczuet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-CEPczuet.js","names":["runInstall"],"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 { install as runInstall, 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\n/** RFC-426 M3:/extension install——app 仅 getPluginRegistrations/reloadCapabilities/settings/workspaceDir + tui。 */\nexport type InstallCtx = Pick<CliContext, 'tui'> & {\n app: Pick<CliContext['app'], 'getPluginRegistrations' | 'reloadCapabilities' | 'settings' | 'workspaceDir'>\n}\n\nconst VALID_AS = new Set(['plugin', 'skill', 'mcp'])\n\nexport async function handleInstall(\n args: string[],\n ctx: InstallCtx,\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: InstallCtx,\n proceed: boolean,\n): Promise<void> {\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 runInstall(\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,提示 /extension 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: InstallCtx,\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: InstallCtx,\n): Promise<void> {\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 runInstall(\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 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: InstallCtx, 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":";4JAqBA,MAAM,EAAW,IAAI,IAAI,CAAC,SAAU,QAAS,MAAM,CAAC,CAEpD,eAAsB,EACpB,EACA,EACe,CACf,IAAM,EAAS,EAAU,EAAK,CAC9B,GAAI,CAAC,EAAO,OAAQ,CAClB,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,mBAAmB,CAAC,CACpF,OAGF,IAAM,EAAW,EAAO,MAAM,IAAI,WAAW,CACvC,EAAS,EAAO,MAAM,IAAI,SAAS,CAEzC,GAAI,GAAY,EAAQ,CACtB,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,+BAA+B,CAAC,CAChG,OAGF,IAAM,EAA0B,CAE9B,aAAc,EAAS,QAAQ,KAAK,CAAG,EAAI,IAAI,aAC/C,OAAQ,EACR,MAAO,EAAO,MAAM,IAAI,UAAU,CAClC,OAAQ,EAAO,MAAM,IAAI,YAAY,CACrC,GAAI,EAAO,GACX,KAAM,EAAO,KACb,IAAK,EAAO,IACZ,QAAS,EAAO,QAChB,IAAK,EAAO,IACZ,oBAAqB,EAErB,WAAa,EAAO,OAGhB,IAAA,GAH0B,GAAM,CAClC,IAAM,EAAS,EAAE,OAAS,EAAE,QAAU,IAAI,EAAE,QAAQ,GAAG,EAAE,MAAM,IAAM,GACrE,EAAI,IAAI,KAAK,UAAU,aAAc,GAAG,IAAS,EAAE,UAAU,EAEhE,CAKD,MAAM,EAAiB,EAAO,OAAQ,EAAS,EAAK,CAAC,EAAO,OAAO,CAwBrE,SAAgB,EAAU,EAA4B,CACpD,IAAM,EAAQ,IAAI,IACd,EACA,EACA,EACA,EACA,EACA,EAAS,GACT,EAAI,EAER,KAAO,EAAI,EAAK,QAAQ,CACtB,IAAM,EAAI,EAAK,GACf,GAAI,IAAM,OAAQ,CAChB,IACA,IAAM,EAAM,EAAK,IAAI,aAAa,CAC9B,EAAS,IAAI,GAAO,GAAG,GAAE,EAAK,WACzB,IAAM,SAAU,CACzB,IACA,IAAM,EAAM,EAAK,IAAI,aAAa,EAC9B,IAAQ,QAAU,IAAQ,OAAS,IAAQ,OAAS,IAAQ,SAC9D,EAAO,WAEA,IAAM,aAEf,IACA,EAAU,EAAK,WACN,IAAM,QAEf,IACA,EAAM,EAAK,WACF,IAAM,QAAS,CACxB,IACA,IAAM,EAAK,EAAK,GAChB,GAAI,EAAI,CACN,IAAM,EAAK,EAAG,QAAQ,IAAI,CACtB,EAAK,IACP,IAAa,EAAE,CACf,EAAI,EAAG,MAAM,EAAG,EAAG,EAAI,EAAG,MAAM,EAAK,EAAE,WAGlC,EAAE,WAAW,KAAK,CAC3B,EAAM,IAAI,EAAE,MAKZ,MAFA,GAAS,EAAM,IAAI,YAAY,CAExB,CAAE,OADI,EAAK,MAAM,EAAE,CAAC,OAAQ,GAAM,CAAC,EAAE,WAAW,KAAK,CAAC,CACvC,GAAI,QAAO,KAAI,OAAM,MAAK,UAAS,MAAK,SAAQ,CAExE,IAIF,MADA,GAAS,EAAM,IAAI,YAAY,CACxB,CAAE,OAAQ,IAAA,GAAW,QAAO,KAAI,OAAM,MAAK,UAAS,MAAK,SAAQ,CAuB1E,eAAe,EACb,EACA,EACA,EACA,EACe,CACf,IAAM,EAAe,GAAG,EAAQ,EAAE,mBAAmB,CAAC,MAAM,EAAO,GAK/D,EAAkB,EAAQ,EAAE,yBAAyB,CACnD,EAA6B,CACjC,GAAG,EACH,OAAQ,GACR,WAAa,GAAuB,CAClC,EAAkB,EAAE,SAEvB,CAED,EAAI,IAAI,MAAM,UAAU,CACtB,KAAM,SACN,MAAO,EACP,SAAU,GAAG,EAAQ,EAAE,kBAAkB,CAAC,OAAO,IACjD,OAAQ,UACR,QAAS,EAAE,CACX,aAAgB,CACd,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,sBAAsB,CAAC,EAGzF,OAAQ,SAAqF,CAC3F,IAAM,EAAS,MAAMA,EACnB,EACA,MACM,EAAI,IAAI,SAAS,IAAI,cAAc,CACzC,KAAO,IAAY,CACjB,MAAM,EAAI,IAAI,SAAS,QAAQ,CAAE,YAAa,EAA6B,CAAC,EAE/E,CAED,GAAI,CAAC,EAAO,QACV,MAAO,CACL,MAAO,EACP,SAAU,EAAQ,EAAE,yBAA0B,CAAE,IAAK,EAAO,OAAS,EAAQ,EAAE,mBAAmB,CAAE,CAAC,CACrG,QAAS,CAAC,CAAE,MAAO,EAAQ,EAAE,kBAAkB,CAAE,MAAO,QAAS,MAAO,OAAQ,CAAC,CAClF,CAGH,GAAI,CAAC,EAAO,MAAQ,EAAO,KAAK,SAAW,EACzC,MAAO,CACL,MAAO,EACP,SAAU,EAAQ,EAAE,sBAAuB,CAAE,KAAM,EAAO,MAAQ,EAAQ,EAAE,mBAAmB,CAAE,CAAC,CAClG,QAAS,CAAC,CAAE,MAAO,EAAQ,EAAE,kBAAkB,CAAE,MAAO,QAAS,MAAO,OAAQ,CAAC,CAClF,CAGH,IAAM,EAAO,EAAO,KAAK,OAAQ,GAAM,CAAC,EAAE,WAAW,EAAQ,EAAE,uBAAuB,CAAC,CAAC,CAClF,EAAO,EAAO,MAAQ,EAAQ,EAAE,mBAAmB,CAIzD,GAHqB,EAAO,UAAY,IAAA,GAGtB,CAChB,IAAM,EAAQ,EAAO,QAAS,OACxB,EAAQ,GAAG,EAAQ,EAAE,mBAAmB,CAAC,MAAM,EAAM,WAAW,EAAO,IAU7E,OARK,EAQE,CACL,QACA,SAAU,EAAK,KAAK;EAAK,CACzB,QAAS,CACP,CAAE,MAAO,EAAQ,EAAE,yBAAyB,CAAE,MAAO,UAAW,MAAO,SAAU,CACjF,CAAE,MAAO,EAAQ,EAAE,mBAAmB,CAAE,MAAO,SAAU,MAAO,OAAQ,CACzE,CACF,CAdQ,CACL,QACA,SAAU,EAAK,KAAK;EAAK,CACzB,QAAS,CAAC,CAAE,MAAO,EAAQ,EAAE,kBAAkB,CAAE,MAAO,QAAS,MAAO,OAAQ,CAAC,CAClF,CAsBL,OARK,EAQE,CACL,MAAO,GAAG,EAAQ,EAAE,mBAAmB,CAAC,MAAM,EAAK,GACnD,SAAU,EAAK,KAAK;EAAK,CACzB,QAAS,CACP,CAAE,MAAO,EAAQ,EAAE,yBAAyB,CAAE,MAAO,UAAW,MAAO,SAAU,CACjF,CAAE,MAAO,EAAQ,EAAE,mBAAmB,CAAE,MAAO,SAAU,MAAO,OAAQ,CACzE,CACF,CAdQ,CACL,MAAO,GAAG,EAAQ,EAAE,kBAAkB,CAAC,MAAM,EAAK,GAClD,SAAU,EAAK,KAAK;EAAK,CACzB,QAAS,CAAC,CAAE,MAAO,EAAQ,EAAE,kBAAkB,CAAE,MAAO,QAAS,MAAO,OAAQ,CAAC,CAClF,IAWD,CACJ,SAAU,KAAO,IAAc,CACzB,IAAM,UAAY,IAAM,SACxB,IAAM,WACR,MAAM,EAAe,EAAQ,CAAE,GAAG,EAAS,OAAQ,GAAO,CAAE,EAAI,EAGrE,CAAC,CAUJ,SAAgB,EAAwB,EAK7B,CAOT,OANI,EAAE,aAAe,IAAA,IAAa,EAAE,kBAAoB,IAAA,GAC/C,EAAE,gBAAkB,EAAE,WAE3B,EAAE,SAAW,EAAE,MACV,EAAE,QAAU,EAAE,MAEhB,EAQT,SAAgB,EAAqB,EAI1B,CAKT,OAJI,EAAE,kBAAoB,IAAA,GAAkB,EAAE,QAC1C,EAAE,aAAe,IAAA,GAGd,GAAG,EAAE,QAAQ,GAAG,EAAY,EAAE,gBAAgB,GAF5C,GAAG,EAAE,QAAQ,GAAG,EAAY,EAAE,gBAAgB,CAAC,KAAK,EAAY,EAAE,WAAW,GAkBxF,SAAS,EAAoB,EAAsB,CACjD,OAAO,EAAK,QAAQ,WAAY,GAAG,CAGrC,SAAS,EACP,EACA,EACQ,CACR,IAAM,GAAU,GAAQ,EAAE,EAAE,OAAQ,GAAM,CAAC,EAAE,QAAQ,CACrD,GAAI,EAAO,SAAW,EAAG,OAAO,EAChC,IAAM,EAAQ,EAAO,MAAM,EAAG,EAAyB,CACjD,EAAU,EACb,IAAK,GAAM,GAAG,EAAE,MAAQ,IAAI,MAAM,EAAoB,EAAE,OAAS,EAAkB,GAAG,CACtF,KAAK,IAAI,CACZ,OAAO,EAAO,OAAS,EAAM,OAAS,GAAG,IAAU,EAAQ,EAAE,0BAA0B,GAAK,EAU9F,SAAS,EACP,EACA,EAC4D,CAC5D,IAAM,GAAU,GAAQ,EAAE,EAAE,OAAQ,GAAM,CAAC,EAAE,QAAQ,CAClC,KACf,EAAO,OAAS,GAAK,EAAO,MAAO,GAAM,EAAE,cAAgB,WAAW,CACtE,EAAO,cAAgB,WAE3B,MAAO,CAAE,SAAU,CAAE,KAAM,QAAS,MAAO,EAAQ,EAAE,wBAAwB,CAAE,CAAE,CAiBnF,SAAgB,EACd,EAMA,EACA,EAOA,CACA,IAAM,EAAO,EAAO,QACd,EAAU,EAAO,EAAK,OAAQ,GAAM,EAAE,QAAQ,CAAC,OAAS,EAAO,QAAU,EAAI,EAC7E,EAAY,EAAO,EAAK,OAAS,EAAU,EAAO,QAAU,EAAI,EAyCtE,OAlCI,IAAY,EACP,CACL,OAAQ,QACR,QAAS,IACT,cAAe,CAAC,OAAQ,OAAQ,QAAS,UAAU,CACnD,aAAc,EAAO,OAAS,EAAiB,EAAM,EAAkB,CACvE,GAAI,EAAwB,EAAQ,EAAK,EAAI,EAAE,CAChD,CAIC,EAAY,EACP,CACL,OAAQ,UACR,QAAS,IACT,cAAe,CAAC,OAAQ,OAAQ,QAAS,OAAO,CAChD,aACE,EAAQ,EAAE,wBAAyB,CAAE,GAAI,EAAS,KAAM,EAAW,CAAC,CACpE,EAAiB,EAAM,EAAkB,CAC3C,GAAI,EAAwB,EAAQ,EAAK,EAAI,EAAE,CAChD,CAIC,EACK,CACL,OAAQ,UACR,QAAS,IACT,cAAe,CAAC,OAAQ,OAAQ,OAAQ,QAAQ,CAChD,aAAc,EAAQ,EAAE,0BAA2B,CAAE,IAAK,EAAa,CAAC,CACxE,SAAU,CAAE,KAAM,SAAU,MAAO,EAAQ,EAAE,yBAAyB,CAAE,CACzE,CAGI,CAAE,OAAQ,UAAW,QAAS,IAAK,cAAe,CAAC,OAAQ,OAAQ,OAAQ,OAAO,CAAE,CAG7F,SAAS,EACP,EACA,EACA,EACM,CACN,EAAI,IAAI,MAAM,eAAe,EAAwB,EAAQ,EAAa,EAAQ,EAAE,mBAAmB,CAAC,CAAC,CAG3G,eAAe,EACb,EACA,EACA,EACe,CACf,IAAM,EAAQ,EAAQ,EAAE,mBAAmB,CAKrC,EAAS,CACb,EAAQ,EAAE,yBAAyB,CACnC,EAAQ,EAAE,yBAAyB,CACnC,EAAQ,EAAE,0BAA0B,CACpC,EAAQ,EAAE,0BAA0B,CACrC,CACD,EAAI,IAAI,MAAM,UAAU,CAAE,KAAM,WAAY,MAAO,EAAQ,EAAE,gBAAiB,CAAE,SAAQ,CAAC,CAAE,SAAQ,CAAC,CAGpG,IAAM,EAAmC,CAAE,UAAW,EAAG,UAAW,EAAG,WAAY,EAAG,WAAY,EAAG,CAwB/F,EAAS,MAAMA,EACnB,EAxBsC,CACtC,GAAG,EACH,WAAa,GAAM,CACjB,IAAM,EAAM,EAAS,EAAE,QAAU,GACjC,GAAI,GAAO,EAAG,CACZ,IAAM,EAAW,EAAO,KAAK,EAAG,IAC1B,EAAI,EAAY,OAChB,IAAM,EAAY,SACf,UACP,CACI,EAAW,EAAwB,EAAE,CACrC,EAAS,EAAqB,EAAE,CACtC,EAAI,IAAI,MAAM,eAAe,CAC3B,WAAY,EACZ,cAAe,EACf,QAAS,KAAK,OAAQ,EAAM,GAAY,EAAO,OAAU,GAAG,CAC5D,SACA,YAAa,EAAE,UAAY,IAAA,IAAa,EAAE,QAAU,IAAA,GAAY,CAAE,QAAS,EAAE,QAAS,MAAO,EAAE,MAAO,CAAG,IAAA,GAC1G,CAAC,GAGP,KAKO,EAAI,IAAI,SAAS,IAAI,cAAc,CACzC,KAAO,IAAY,CACjB,MAAM,EAAI,IAAI,SAAS,QAAQ,CAAE,YAAa,EAA6B,CAAC,EAE/E,CAUK,EAAY,EAAO,UAAY,EAAO,SAAS,KAAM,GAAM,EAAE,QAAQ,EAAI,IAC3E,EACJ,GAAI,EAAW,CACb,EAAI,IAAI,MAAM,eAAe,CAC3B,WAAY,EACZ,cAAe,CAAC,OAAQ,OAAQ,OAAQ,SAAS,CACjD,QAAS,GACT,OAAQ,EAAQ,EAAE,0BAA0B,CAC7C,CAAC,CACF,GAAI,CACF,MAAM,EAAI,IAAI,oBAAoB,CAClC,EAAI,IAAI,QAAQ,uBAAuB,EAAI,IAAI,wBAAwB,CAAC,OACjE,EAAK,CACZ,EAAc,aAAe,MAAQ,EAAI,QAAU,OAAO,EAAI,EASlE,GAFA,EAAsB,EAAK,EAAQ,EAAY,CAE3C,CAAC,EAAO,UAGN,CAAC,EAAO,SAAW,EAAO,QAAQ,MAAO,GAAM,CAAC,EAAE,QAAQ,IAC5D,EAAI,IAAI,KAAK,UAAU,EAAO,EAAQ,EAAE,oBAAqB,CAAE,IAAK,EAAO,OAAS,EAAQ,EAAE,mBAAmB,CAAE,CAAC,CAAC,CACjH,CAAC,EAAO,SAAS,OAKzB,IAAM,EAAa,EAAQ,OAAS,EAAQ,EAAE,kBAAkB,CAAG,EAAQ,EAAE,mBAAmB,CAGhG,GAAI,EAAO,QAAS,CAClB,IAAI,EAAK,EACL,EAAO,EACX,IAAK,IAAM,KAAK,EAAO,QACjB,EAAE,SACJ,IACA,EAAI,IAAI,KAAK,UACX,EACA,EAAQ,EAAE,kBAAmB,CAAE,KAAM,EAAE,MAAQ,YAAa,MAAO,EAAY,IAAK,EAAE,WAAa,KAAK,EAAE,WAAW,GAAK,GAAI,CAAC,CAChI,GAED,IACA,EAAI,IAAI,KAAK,UAAU,EAAO,EAAQ,EAAE,oBAAqB,CAAE,IAAK,EAAE,OAAS,EAAQ,EAAE,mBAAmB,CAAE,CAAC,CAAC,EAGpH,IAAM,EAAU,GAAG,EAAG,YAAY,EAAO,EAAI,KAAK,EAAK,SAAW,KAMlE,GALA,EAAI,IAAI,KAAK,UAAU,EAAO,EAAQ,CAKlC,EAAO,EAAG,CACZ,IAAM,EAAc,EAAO,QACxB,OAAQ,GAAM,CAAC,EAAE,QAAQ,CACzB,IAAK,GAAM,KAAK,EAAE,MAAQ,IAAI,MAAM,EAAE,OAAS,EAAQ,EAAE,mBAAmB,GAAG,CAClF,EAAI,IAAI,SAAS,YAAY,CAC3B,KAAM,eACN,QAAS,WACT,OAAQ,QACR,UACA,OAAQ,EAAY,KAAK;EAAK,CAC/B,CAAC,CAGA,EAAO,UAAY,EAAO,SAAS,OAAS,GAC9C,EAAI,IAAI,SAAS,YAAY,CAC3B,KAAM,eACN,QAAS,WACT,OAAQ,OACR,QAAS,GAAG,EAAO,SAAS,OAAO,MACnC,OAAQ,EAAO,SAAS,IAAK,GAAM,KAAK,IAAI,CAAC,KAAK;EAAK,CACxD,CAAC,CAMJ,EAAoB,EAAK,EAAY,CACrC,OAIF,GAAI,EAAO,YACT,GAAI,EAAO,YAAY,QACrB,EAAI,IAAI,KAAK,UAAU,EAAO,IAAI,EAAO,KAAK,eAAe,EAAW,oBAAoB,KACvF,CACL,IAAM,EAAS,EAAO,YAAY,QAC9B,kBACA,EAAO,YAAY,OAAS,QAAQ,EAAO,YAAY,UAAY,QACvE,EAAI,IAAI,KAAK,UAAU,EAAO,IAAI,EAAO,KAAK,eAAe,EAAW,0BAA0B,EAAO,GAAG,CAE5G,IAAM,EAAQ,CAAC,WAAW,EAAO,KAAK,4BAA4B,CAClE,EAAM,KAAK,cAAc,EAAO,YAAY,UAAU,CAClD,EAAO,YAAY,UAAY,MAAM,EAAM,KAAK,gBAAgB,EAAO,YAAY,WAAW,CAC9F,EAAO,YAAY,QAAQ,EAAM,KAAK,aAAa,EAAO,YAAY,SAAS,CAC/E,EAAO,YAAY,WAAW,EAAM,KAAK,uBAAuB,CACpE,EAAI,IAAI,SAAS,YAAY,CAC3B,KAAM,eACN,QAAS,WACT,OAAQ,QACR,QAAS,IAAI,EAAO,KAAK,2BACzB,OAAQ,EAAM,KAAK;EAAK,CACzB,CAAC,MAGJ,EAAI,IAAI,KAAK,UACX,EACA,EAAQ,EAAE,kBAAmB,CAAE,KAAM,EAAO,MAAQ,YAAa,MAAO,EAAY,IAAK,EAAO,WAAa,KAAK,EAAO,WAAW,GAAK,GAAI,CAAC,CAC/I,CAIC,EAAO,UAAY,EAAO,SAAS,OAAS,GAC9C,EAAI,IAAI,SAAS,YAAY,CAC3B,KAAM,eACN,QAAS,WACT,OAAQ,OACR,QAAS,GAAG,EAAO,SAAS,OAAO,MACnC,OAAQ,EAAO,SAAS,IAAK,GAAM,KAAK,IAAI,CAAC,KAAK;EAAK,CACxD,CAAC,CAIJ,EAAoB,EAAK,EAAY,CAUvC,SAAS,EAAoB,EAAiB,EAAuC,CACnF,GAAI,EAAa,CACf,EAAI,IAAI,SAAS,YAAY,CAC3B,KAAM,eACN,QAAS,WACT,OAAQ,QACR,QAAS,EAAQ,EAAE,0BAA2B,CAAE,IAAK,EAAa,CAAC,CACpE,CAAC,CACF,OAEF,EAAI,IAAI,KAAK,UAAU,EAAQ,EAAE,mBAAmB,CAAE,EAAQ,EAAE,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{ot as e}from"./command-dispatcher-Du_Sk33M.js";import{n as t}from"./schedule-C_uHI2M8.js";import{t as n}from"./backend-handler-DUafuNWw.js";import{globalAgentJobRegistry as r}from"@x-otto/runtime";import{DEFAULT_SCHEDULE_CONFIG as i,FireLogger as a,InProcessEventTrigger as o,JobFireTarget as s,ScheduleRegistry as c,SchedulerService as l,SessionFireTarget as u,SubscriberFireTarget as d,createLocalFireOwnership as f,createRemoteFireOwnership as p,createRemoteScheduleStore as m,createScheduleStore as h,parseCron as g}from"@x-otto/schedule";import{z as _}from"zod";function v(e){let t=()=>({tasks:e.list(),runningTaskIds:[...e.getRunningTaskIds()],isFireOwner:e.isFireOwner(),fireOwnerFailureReason:e.fireOwnerFailureReason()});return{...n({"schedule.list":{params:_.object({}),run:async()=>t()},"schedule.add":{params:_.object({name:_.string(),prompt:_.string(),cronExpression:_.string(),recurring:_.boolean(),target:_.enum([`job`,`session`]).optional()}),run:async t=>{try{let n=e.add({name:t.name,prompt:t.prompt,cronExpression:t.cronExpression,recurring:t.recurring,origin:`user`,target:t.target});return{ok:!0,taskId:n.id,name:n.name,target:n.target}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}},"schedule.update":{params:_.object({taskId:_.string(),name:_.string().optional(),prompt:_.string().optional(),cronExpression:_.string().optional(),enabled:_.boolean().optional()}),run:async t=>{let n={};return t.name!==void 0&&(n.name=t.name),t.prompt!==void 0&&(n.prompt=t.prompt),t.cronExpression!==void 0&&(n.cronExpression=t.cronExpression),t.enabled!==void 0&&(n.enabled=t.enabled),{ok:e.update(t.taskId,n)}}},"schedule.runNow":{params:_.object({taskId:_.string()}),run:async t=>(e.runNow(t.taskId),{ok:!0})},"schedule.toggle":{params:_.object({taskId:_.string(),enabled:_.boolean()}),run:async t=>({ok:e.update(t.taskId,{enabled:t.enabled})})},"schedule.cancel":{params:_.object({taskId:_.string()}),run:async t=>e.cancel(t.taskId)},"schedule.remove":{params:_.object({taskId:_.string()}),run:async t=>({ok:e.remove(t.taskId)})},"schedule.readLogs":{params:_.object({taskId:_.string(),limit:_.number().optional()}),run:async t=>({entries:e.readFireLogs(t.taskId,t.limit??20)})},"schedule.parseCron":{params:_.object({expr:_.string()}),run:async e=>{try{return{ok:!0,next:g(e.expr).toISOString()}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}}},`schedule`),getSnapshot:t}}var y=e({createFollowUpCurrentSession:()=>b,setupScheduleService:()=>x});function b(e){return t=>{let n=e.value;if(n.id!==t.boundSessionId||n.isPaused||n.status===`archived`)return!1;try{return n.prompt(t.prompt,{streamingBehavior:`followUp`}).catch(()=>{}),!0}catch{return!1}}}function x(e,n){let{sessionRef:g,tui:_,remoteConfig:y}=n,x=e.workspaceRef?.key??`default`,S=new c,C,w;if(e.storage.remoteSessionConfigured&&y){let e=async()=>({token:y.sessionToken});C=m({sessionUrl:y.sessionUrl,getAuth:e,wsKey:x}),w=p({sessionUrl:y.sessionUrl,wsKey:x,getAuth:e})}else C=h(x),w=f(x);let T=new a(i),E=[];g&&E.push(new d({notify:(t,n)=>e.notifyPluginServiceSchedule({pluginId:t.pluginId,serviceId:t.serviceId},n),fireLogger:T}),new u({followUp:b(g),getSessionId:()=>g.value.id,fireLogger:T})),E.push(new s({jobRegistry:r,startJob:t=>({id:e.agentJobs.start({title:t.title,prompt:t.prompt,sessionId:t.sessionId,origin:t.origin,...t.model?{model:t.model}:{}}).id}),...g?{getSessionId:()=>g.value.id}:{},retryBackoffMs:e.getResilienceConfig().schedule.retryBackoffMs,fireLogger:T,needsInputMaxExtensionFactor:i.needsInputMaxExtensionFactor}));let D=new l({registry:S,store:C,ownership:w,fireTargets:E,triggers:[new o({jobRegistry:r})]});D.start().then(()=>{if(!g){let t=D.list().filter(e=>e.target===`session`||!!e.subscriber).length;if(t>0){let n=`${t} 个任务(session/subscriber 类)在 daemon 无人值守形态不会投递,仅 job 类任务执行`;e.logger?.warn?.({count:t},`[schedule] headless mode: non-job tasks skipped`),new a(i).write(`daemon-scheduler`,`headless aggregate warning: ${n}`)}}}).catch(t=>{e.logger?.warn?.({err:t instanceof Error?t.message:String(t)},`[schedule] scheduler failed to start`)}),t(D),e.setScheduleService(D);let O=v(D);if(e.panelBackendController.registerInProcessBackend(`plugin-schedule`,O.call),!_)return{pollInterval:void 0,scheduler:D};let k=setInterval(()=>{if(!_)return;let e=D.list().map(e=>({id:e.id,name:e.name,prompt:e.prompt,cronExpression:e.cronExpression,enabled:e.enabled,recurring:e.recurring,nextFireAt:e.nextFireAt,lastFiredAt:e.lastFiredAt,lastFireFailed:e.lastFireFailed,lastFireError:e.lastFireError,lastFireCancelled:e.lastFireCancelled}));_.stores.tasks.setScheduleTasks(e)},5e3);return k.unref(),{pollInterval:k,scheduler:D}}export{x as n,y as t};
|
|
3
|
+
//# sourceMappingURL=interactive-schedule-DVJjhqnt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive-schedule-DVJjhqnt.js","names":[],"sources":["../src/commands/schedule-backend-methods.ts","../src/commands/interactive-schedule.ts"],"sourcesContent":["/**\n * backend-methods.ts —— RFC-349:schedule 面板 in-process backend methods。\n *\n * 与 browser-use/mobile-use 的区别:那些插件的 backend methods 跑在 MCP server 子进程里。\n * schedule 的 SchedulerService 依赖 `AgentJobService`/`sessionRef`/`notifySubscriber`——\n * 这些只能存活在 App 主进程,无法搬到子进程。故本文件注册为 **in-process backend handler**\n * (经 `PanelBackendController.registerInProcessBackend`),面板 `usePanelBackend().call()`\n * 走同一条路径,但执行发生在 App 主进程内。\n *\n * RFC-439:分发收敛为类型化注册表(backend-handler.ts)——方法 = zod schema + run,\n * 参数校验/前缀剥离/未知方法 fail-closed 由注册表统一承担,不再手写 switch/双重断言。\n *\n * 面板侧调用经 `toBackendWireMethod` 统一加 `backend/` 前缀,注册表接收时剥离前缀。\n */\nimport { z } from 'zod'\nimport type { SchedulerService } from '@x-otto/schedule'\nimport { parseCron } from '@x-otto/schedule'\nimport type { ScheduledTask } from '@x-otto/schedule'\nimport { createBackendHandler, type BackendMethod } from './backend-handler'\n\n/** 面板状态快照(经 pushState 推送或 call 返回)。 */\nexport interface SchedulePanelState {\n tasks: ScheduledTask[]\n runningTaskIds: string[]\n isFireOwner: boolean\n fireOwnerFailureReason: string | undefined\n}\n\nexport function createScheduleBackendHandler(scheduler: SchedulerService): {\n call: (method: string, params: Record<string, unknown>) => Promise<unknown>\n getSnapshot: () => SchedulePanelState\n} {\n const getSnapshot = (): SchedulePanelState => ({\n tasks: scheduler.list(),\n runningTaskIds: [...scheduler.getRunningTaskIds()],\n isFireOwner: scheduler.isFireOwner(),\n fireOwnerFailureReason: scheduler.fireOwnerFailureReason(),\n })\n\n // RFC-439:satisfies 保留各方法的 zod infer(不用默认泛型宽化致 run 参数塌成 unknown)。\n const methods = {\n 'schedule.list': {\n params: z.object({}),\n run: async () => getSnapshot(),\n },\n 'schedule.add': {\n params: z.object({\n name: z.string(),\n prompt: z.string(),\n cronExpression: z.string(),\n recurring: z.boolean(),\n // RFC-357 D3 (M2): optional session-local target, requires fire ownership.\n target: z.enum(['job', 'session']).optional(),\n }),\n run: async (params: {\n name: string\n prompt: string\n cronExpression: string\n recurring: boolean\n target?: 'job' | 'session'\n }) => {\n // RFC-357 D2: scheduler.add() throws when target:'session' is requested without\n // fire ownership — surface that as a structured error, not an unhandled rejection.\n try {\n const task = scheduler.add({\n name: params.name,\n prompt: params.prompt,\n cronExpression: params.cronExpression,\n recurring: params.recurring,\n origin: 'user' as const,\n target: params.target,\n })\n return { ok: true, taskId: task.id, name: task.name, target: task.target }\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) }\n }\n },\n },\n\n 'schedule.update': {\n params: z.object({\n taskId: z.string(),\n name: z.string().optional(),\n prompt: z.string().optional(),\n cronExpression: z.string().optional(),\n enabled: z.boolean().optional(),\n }),\n run: async (params: {\n taskId: string\n name?: string\n prompt?: string\n cronExpression?: string\n enabled?: boolean\n }) => {\n const patch: { name?: string; prompt?: string; cronExpression?: string; enabled?: boolean } = {}\n if (params.name !== undefined) patch.name = params.name\n if (params.prompt !== undefined) patch.prompt = params.prompt\n if (params.cronExpression !== undefined) patch.cronExpression = params.cronExpression\n if (params.enabled !== undefined) patch.enabled = params.enabled\n const ok = scheduler.update(params.taskId, patch)\n return { ok }\n },\n },\n\n 'schedule.runNow': {\n params: z.object({ taskId: z.string() }),\n run: async (params: { taskId: string }) => {\n scheduler.runNow(params.taskId)\n return { ok: true }\n },\n },\n\n 'schedule.toggle': {\n params: z.object({ taskId: z.string(), enabled: z.boolean() }),\n run: async (params: { taskId: string; enabled: boolean }) => {\n const ok = scheduler.update(params.taskId, { enabled: params.enabled })\n return { ok }\n },\n },\n\n 'schedule.cancel': {\n params: z.object({ taskId: z.string() }),\n run: async (params: { taskId: string }) => scheduler.cancel(params.taskId),\n },\n\n 'schedule.remove': {\n params: z.object({ taskId: z.string() }),\n run: async (params: { taskId: string }) => {\n const removed = scheduler.remove(params.taskId)\n return { ok: removed }\n },\n },\n\n 'schedule.readLogs': {\n params: z.object({ taskId: z.string(), limit: z.number().optional() }),\n run: async (params: { taskId: string; limit?: number }) => {\n const entries = scheduler.readFireLogs(params.taskId, params.limit ?? 20)\n return { entries }\n },\n },\n\n 'schedule.parseCron': {\n params: z.object({ expr: z.string() }),\n run: async (params: { expr: string }) => {\n try {\n const next = parseCron(params.expr)\n return { ok: true, next: next.toISOString() }\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) }\n }\n },\n },\n } satisfies Record<string, BackendMethod>\n\n return { ...createBackendHandler(methods, 'schedule'), getSnapshot }\n}\n","/**\n * 定时任务 TUI 交互管线(状态栏/事件/toast 接线)。\n */\n// RFC-161 M2:SchedulerService 等实现已迁至 @x-otto/schedule,直连新真源\n// (coding 的 re-export shim 仅供尚未迁移的其余消费方使用)。\nimport { SchedulerService, ScheduleRegistry, createScheduleStore, createLocalFireOwnership, createRemoteScheduleStore, createRemoteFireOwnership, FireLogger, DEFAULT_SCHEDULE_CONFIG, JobFireTarget, SessionFireTarget, SubscriberFireTarget, InProcessEventTrigger } from '@x-otto/schedule'\nimport { globalAgentJobRegistry } from '@x-otto/runtime'\nimport { setScheduleService } from './handlers/schedule'\nimport { createScheduleBackendHandler } from './schedule-backend-methods'\n\nimport type { App, AgentSession } from '@x-otto/coding'\nimport type { ScheduleStore, FireOwnership, FireTarget } from '@x-otto/schedule'\nimport type { TuiHandle } from '@x-otto/tui'\n\n/** Remote connection details, threaded from CLIOptions.sessionUrl/sessionToken. */\nexport interface ScheduleRemoteConfig {\n sessionUrl: string\n sessionToken: string\n}\n\n/**\n * RFC-357 D1/D2(M2):`SchedulerDeps.followUpCurrentSession` 的真实实现——把 session-local\n * 任务的 prompt 注入 `boundSessionId` 对应的当前会话。\n *\n * 三重失效校验(均转 `false`,不抛错——调度器侧 `fireSession()` 把异常/false 统一折叠为\n * fire 失败记日志,不重试,同 `notifySubscriber` 缺省行为的旁路信号纪律):\n *\n * 1. **会话身份不匹配**(独立评审 R1):`sessionRef` 是活引用,RFC-234 attach/detach 会\n * 在同一进程内重新赋值它——若 `boundSessionId` 与当前 `sessionRef.value.id` 不等,说明\n * 用户已切到别的会话,绝不能把内容误投进一个不相干的对话。\n * 2. **paused 态**(D1):暂停会话收到注入会被 `prompt()` 判定为 idle 直接起新回合,\n * 这实质上绕过了用户暂停会话的意图——先查 `isPaused` 拒绝。\n * 3. **archived 态 / 会话已销毁**(D1):`AgentSession.status` 是同步 getter,先在此\n * 同步判断 `'archived'` 拒绝(M3 e2e 测试发现的修订——`prompt()` 是 `async` 函数,\n * 其内部对 archived 态的检查会抛出**被拒绝的 Promise** 而非同步异常;`void\n * session.prompt(...)` 外面包一层 `try/catch` 完全捕获不到它,会变成未处理的\n * promise rejection,在真实调度场景下会让 Node 进程崩溃)。为兜底 `ensureNotDisposed()`\n * 等其他潜在的异步失效路径,仍链一个 `.catch()`(fire-and-forget,不 await、不重试,\n * 只是防止任何未预见的异步异常变成未处理 rejection)。\n */\nexport function createFollowUpCurrentSession(\n sessionRef: { readonly value: AgentSession },\n): (task: { prompt: string; boundSessionId: string; name: string; origin: 'user' | 'model' }) => boolean {\n return (task) => {\n const session = sessionRef.value\n\n if (session.id !== task.boundSessionId) return false\n if (session.isPaused) return false\n if (session.status === 'archived') return false\n\n try {\n // 不 await:fire() 是 fire-and-forget 的旁路信号语义(同 fireSubscriber),\n // prompt() 的 busy/idle 路由与错误处理已在 AgentSession 内部完成;这里只需\n // 同步判断\"是否成功发起了注入\"(streamingBehavior:'followUp' 使 prompt()\n // 同步完成入队/发起,不阻塞到回合真正跑完)。`.catch()` 兜底任何异步异常\n // (见上方文档注释——不能只靠 try/catch,那只捕获同步抛出)。\n void session.prompt(task.prompt, { streamingBehavior: 'followUp' }).catch(() => {})\n return true\n } catch {\n // 理论上不会走到这——保留作为同步异常的最后防线。\n return false\n }\n }\n}\n\nexport interface ScheduleServiceSetupOptions {\n /** 交互会话(TUI 装配传;serve/daemon 无人值守装配不传——job-only 模式)。 */\n sessionRef?: { readonly value: AgentSession }\n /** TUI 句柄(仅交互模式;缺失时跳过 5s taskStore 轮询)。 */\n tui?: TuiHandle\n remoteConfig?: ScheduleRemoteConfig\n}\n\nexport function setupScheduleService(\n app: App,\n opts: ScheduleServiceSetupOptions,\n): { pollInterval: ReturnType<typeof setInterval> | undefined; scheduler: SchedulerService } {\n const { sessionRef, tui, remoteConfig } = opts\n // Wire schedule service for /schedule command + cron tools\n const workspaceKey = app.workspaceRef?.key ?? 'default'\n const scheduleRegistry = new ScheduleRegistry()\n\n let scheduleStore: ScheduleStore\n let scheduleOwnership: FireOwnership\n if (app.storage.remoteSessionConfigured && remoteConfig) {\n const getAuth = async (): Promise<{ token: string }> => ({ token: remoteConfig.sessionToken })\n scheduleStore = createRemoteScheduleStore({\n sessionUrl: remoteConfig.sessionUrl,\n getAuth,\n wsKey: workspaceKey,\n })\n scheduleOwnership = createRemoteFireOwnership({\n sessionUrl: remoteConfig.sessionUrl,\n wsKey: workspaceKey,\n getAuth,\n })\n } else {\n scheduleStore = createScheduleStore(workspaceKey)\n scheduleOwnership = createLocalFireOwnership(workspaceKey)\n }\n\n // RFC-406 D7:投递目标从 SchedulerDeps 内联字段拆为 FireTarget handler 列表。\n // 注册顺序即优先级:subscriber > session > job(保持旧 fire() 分支优先级)。\n //\n // RFC-435 R1(独立核对修复②):SubscriberFireTarget **仅在有 sessionRef 的交互形态注册**。\n // 此前无条件注册使 headless(serve/daemon)实际装配 = Subscriber+Job 两件,违背 R1\n // 「daemon 形态只装配 JobFireTarget」与用户 grill 确认的范围收敛(方案 A:只放 job)。\n // 插件 schedules 声明的 subscriber 任务在 headless 形态走 fire log 降级(D3),\n // 不投递——与「daemon 不启用 subscriber 投递」的决策一致(RFC §2 非目标)。\n const fireLogger = new FireLogger(DEFAULT_SCHEDULE_CONFIG)\n const fireTargets: FireTarget[] = []\n\n if (sessionRef) {\n fireTargets.push(\n // RFC-327 D2:订阅者到点通知的窄端口——scheduler 只交出纯数据标识,\n // 由 App 决定投给哪个运行中的 service(并强制\"插件仍激活\"这一 revoke 语义)。\n // 该路径**不开会话授权 epoch**(schedule.tick 无 sessionId),见 App 侧注释。\n new SubscriberFireTarget({\n notify: (subscriber, tick) =>\n app.notifyPluginServiceSchedule(\n { pluginId: subscriber.pluginId, serviceId: subscriber.serviceId },\n tick,\n ),\n fireLogger,\n }),\n // RFC-357 D1/D2(M2):session-local 任务到点后的投递端口 + 创建时绑定的 sessionId。\n new SessionFireTarget({\n followUp: createFollowUpCurrentSession(sessionRef),\n getSessionId: () => sessionRef.value.id,\n fireLogger,\n }),\n )\n }\n\n // RFC-435 D1:job 类任务不依赖交互会话(detached run + worktree 隔离),\n // 交互与无人值守形态都注册。getSessionId 仅在有 sessionRef 时提供——\n // 无人值守(serve/daemon)缺省,job 任务创建时不绑定 session(target 非 session)。\n fireTargets.push(\n new JobFireTarget({\n jobRegistry: globalAgentJobRegistry,\n startJob: (input) => ({\n id: app.agentJobs.start({\n title: input.title,\n prompt: input.prompt,\n sessionId: input.sessionId,\n origin: input.origin,\n // RFC-438:per-job 模型指定透传(task.modelId/thinkingLevel)\n ...(input.model ? { model: input.model } : {}),\n }).id,\n }),\n ...(sessionRef ? { getSessionId: () => sessionRef.value.id } : {}),\n // RFC-230:本函数在 TUI 启动流程内被调用,晚于 app.start()(settings 已 load),\n // getResilienceConfig() 调用安全。真实构造点(非 packages/coding App 内部),\n // 见 RFC-230 §3 D6 Scheduler 小节二轮评审 P2 修正。\n retryBackoffMs: app.getResilienceConfig().schedule.retryBackoffMs,\n fireLogger,\n needsInputMaxExtensionFactor: DEFAULT_SCHEDULE_CONFIG.needsInputMaxExtensionFactor,\n }),\n )\n\n const scheduler = new SchedulerService({\n registry: scheduleRegistry,\n store: scheduleStore,\n ownership: scheduleOwnership,\n fireTargets,\n // RFC-423:进程内事件触发器——监听 AgentJobRegistry 终态(如 job diff applied),\n // 事件满足时把 prompt 注入当前对话(配 target:'session')。三重死循环闸门内建\n // (自产 job 排除 + add() 拒绝 job 投递 + 频率 debounce)。无人值守(无 sessionRef)\n // 时事件触发投递目标无会话——fire 时跳过并记日志(fireLogger),见 RFC-435 D2。\n triggers: [new InProcessEventTrigger({ jobRegistry: globalAgentJobRegistry })],\n })\n // Non-critical background feature — a bootstrap failure here must not take down\n // the whole interactive session (fire-and-forget start() would otherwise surface\n // as an unhandled rejection and crash the process).\n void scheduler.start().then(() => {\n // RFC-435 D3 聚合告警(独立核对修复①):必须在 start() **完成**后检查——start()\n // 是 async(首个 await tryAcquire()),此前同步 list() 拿到的是空 registry,\n // nonJobCount 恒为 0,告警是生产死代码(评审 A.3 坐实)。移到 then() 内,\n // registry 已加载,计数真实。\n if (!sessionRef) {\n const nonJobCount = scheduler.list().filter((t) => t.target === 'session' || !!t.subscriber).length\n if (nonJobCount > 0) {\n const msg = `${nonJobCount} 个任务(session/subscriber 类)在 daemon 无人值守形态不会投递,仅 job 类任务执行`\n app.logger?.warn?.({ count: nonJobCount }, '[schedule] headless mode: non-job tasks skipped')\n new FireLogger(DEFAULT_SCHEDULE_CONFIG).write('daemon-scheduler', `headless aggregate warning: ${msg}`)\n }\n }\n }).catch((err) => {\n // RFC-435 R9:失败日志走 logger 而非 console(serve 无人值守场景 console 无观察者)。\n app.logger?.warn?.({ err: err instanceof Error ? err.message : String(err) }, '[schedule] scheduler failed to start')\n })\n setScheduleService(scheduler)\n app.setScheduleService(scheduler) // A1: let cron_* model tools reach this scheduler\n\n // RFC-349 M1:注册 in-process backend handler——面板经 usePanelBackend().call() 访问 scheduler\n const scheduleBackend = createScheduleBackendHandler(scheduler)\n app.panelBackendController.registerInProcessBackend('plugin-schedule', scheduleBackend.call)\n\n // Push schedule tasks to TUI store every 5 seconds (status bar / task list).\n // RFC-349 M2: panel data is now fetched via backend.call('schedule.list') —\n // this interval only updates the TUI taskStore for status bar display.\n // RFC-435 D1:serve/daemon 无 tui → 不建轮询(无人值守无状态栏消费方)。\n if (!tui) return { pollInterval: undefined, scheduler }\n\n const pollInterval = setInterval(() => {\n if (!tui) return\n const tasks = scheduler.list()\n const taskSummaries = tasks.map((t) => ({\n id: t.id,\n name: t.name,\n prompt: t.prompt,\n cronExpression: t.cronExpression,\n enabled: t.enabled,\n recurring: t.recurring,\n nextFireAt: t.nextFireAt,\n lastFiredAt: t.lastFiredAt,\n lastFireFailed: t.lastFireFailed,\n lastFireError: t.lastFireError,\n lastFireCancelled: t.lastFireCancelled,\n }))\n tui.stores.tasks.setScheduleTasks(taskSummaries)\n }, 5000)\n pollInterval.unref()\n\n return { pollInterval, scheduler }\n}\n"],"mappings":"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"}
|
|
@@ -9,4 +9,4 @@ import{OTTO_PROJECT_CONFIG_RELPATH as e}from"@x-otto/env";import{SettingsManager
|
|
|
9
9
|
logout <name> Remove stored credentials
|
|
10
10
|
list List authenticated MCP servers
|
|
11
11
|
`),1)}export{s as runMcpCommand};
|
|
12
|
-
//# sourceMappingURL=mcp-
|
|
12
|
+
//# sourceMappingURL=mcp-DjReRjuu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-
|
|
1
|
+
{"version":3,"file":"mcp-DjReRjuu.js","names":[],"sources":["../src/commands/mcp.ts"],"sourcesContent":["import { OTTO_PROJECT_CONFIG_RELPATH } from '@x-otto/env'\nimport { createFileTokenStore, login } from '@x-otto/mcp'\nimport { SettingsManager } from '@x-otto/setting'\n\nimport type { McpServerConfig } from '@x-otto/mcp'\n\nexport interface McpDeps {\n stderr?: (s: string) => void\n}\n\nexport async function runMcpList(): Promise<void> {\n const store = createFileTokenStore()\n const servers = await store.list()\n\n if (servers.length === 0) {\n process.stdout.write('No authenticated MCP servers.\\n')\n return\n }\n\n process.stdout.write('Authenticated MCP servers:\\n')\n for (const name of servers) {\n const creds = await store.load(name)\n const status = creds?.expiresAt\n ? creds.expiresAt > Date.now()\n ? 'valid'\n : 'expired'\n : 'no expiry'\n process.stdout.write(` ${name} (${status})\\n`)\n }\n}\n\nexport async function runMcpLogout(serverName: string): Promise<void> {\n const store = createFileTokenStore()\n await store.delete(serverName)\n process.stdout.write(`Logged out of '${serverName}'.\\n`)\n}\n\nexport async function runMcpLogin(serverName: string): Promise<void> {\n const settings = new SettingsManager()\n await settings.load()\n const servers = settings.get('mcp_servers') as Record<string, McpServerConfig> | undefined\n\n if (!servers?.[serverName]) {\n process.stderr.write(\n `MCP server '${serverName}' not found in settings. Add it to ${OTTO_PROJECT_CONFIG_RELPATH}:\\n` +\n ` \"mcp_servers\": { \"${serverName}\": { \"type\": \"http\", \"url\": \"https://...\" } }\\n`,\n )\n process.exit(1)\n }\n\n const config = servers[serverName]\n if (config.type === 'stdio' || (!config.url && config.command)) {\n process.stderr.write(\n `MCP server '${serverName}' is a stdio server. OAuth login only works with HTTP-based MCP servers.\\n`,\n )\n process.exit(1)\n }\n if (!config.url) {\n process.stderr.write(`MCP server '${serverName}' has no URL configured.\\n`)\n process.exit(1)\n }\n\n process.stdout.write(`Logging into MCP server '${serverName}' (${config.url})...\\n`)\n process.stdout.write('A browser window will open for authentication.\\n')\n\n try {\n const result = await login({\n serverUrl: config.url,\n serverName,\n })\n\n process.stdout.write(`✓ Login successful.\\n`)\n if (result.registered) {\n process.stdout.write(' (Dynamic Client Registration completed)\\n')\n }\n } catch (err) {\n process.stderr.write(`Login failed: ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n/**\n * `otto mcp <action> [name]` sub-command dispatcher.\n * Extracted from the inline if-chain in cli.ts.\n */\nexport async function runMcpCommand(args: string[], deps: McpDeps = {}): Promise<number> {\n const stderr = deps.stderr ?? ((s) => process.stderr.write(s))\n const action = args[0]\n const serverName = args[1]\n\n if (action === 'login' && serverName) {\n await runMcpLogin(serverName)\n return 0\n }\n if (action === 'logout' && serverName) {\n await runMcpLogout(serverName)\n return 0\n }\n if (action === 'list') {\n await runMcpList()\n return 0\n }\n\n stderr(\n 'Usage: otto mcp <login|logout|list> [server-name]\\n' +\n ' login <name> Authenticate with an MCP server (OAuth 2.1)\\n' +\n ' logout <name> Remove stored credentials\\n' +\n ' list List authenticated MCP servers\\n',\n )\n return 1\n}\n"],"mappings":";0KAUA,eAAsB,GAA4B,CAChD,IAAM,EAAQ,GAAsB,CAC9B,EAAU,MAAM,EAAM,MAAM,CAElC,GAAI,EAAQ,SAAW,EAAG,CACxB,QAAQ,OAAO,MAAM;EAAkC,CACvD,OAGF,QAAQ,OAAO,MAAM;EAA+B,CACpD,IAAK,IAAM,KAAQ,EAAS,CAC1B,IAAM,EAAQ,MAAM,EAAM,KAAK,EAAK,CAC9B,EAAS,GAAO,UAClB,EAAM,UAAY,KAAK,KAAK,CAC1B,QACA,UACF,YACJ,QAAQ,OAAO,MAAM,KAAK,EAAK,IAAI,EAAO,KAAK,EAInD,eAAsB,EAAa,EAAmC,CAEpE,MADc,GAAsB,CACxB,OAAO,EAAW,CAC9B,QAAQ,OAAO,MAAM,kBAAkB,EAAW,MAAM,CAG1D,eAAsB,EAAY,EAAmC,CACnE,IAAM,EAAW,IAAI,EACrB,MAAM,EAAS,MAAM,CACrB,IAAM,EAAU,EAAS,IAAI,cAAc,CAEtC,IAAU,KACb,QAAQ,OAAO,MACb,eAAe,EAAW,qCAAqC,EAA4B,yBAClE,EAAW,iDACrC,CACD,QAAQ,KAAK,EAAE,EAGjB,IAAM,EAAS,EAAQ,IACnB,EAAO,OAAS,SAAY,CAAC,EAAO,KAAO,EAAO,WACpD,QAAQ,OAAO,MACb,eAAe,EAAW,4EAC3B,CACD,QAAQ,KAAK,EAAE,EAEZ,EAAO,MACV,QAAQ,OAAO,MAAM,eAAe,EAAW,4BAA4B,CAC3E,QAAQ,KAAK,EAAE,EAGjB,QAAQ,OAAO,MAAM,4BAA4B,EAAW,KAAK,EAAO,IAAI,QAAQ,CACpF,QAAQ,OAAO,MAAM;EAAmD,CAExE,GAAI,CACF,IAAM,EAAS,MAAM,EAAM,CACzB,UAAW,EAAO,IAClB,aACD,CAAC,CAEF,QAAQ,OAAO,MAAM;EAAwB,CACzC,EAAO,YACT,QAAQ,OAAO,MAAM;EAA8C,OAE9D,EAAK,CACZ,QAAQ,OAAO,MAAM,iBAAkB,EAAc,QAAQ,IAAI,CACjE,QAAQ,KAAK,EAAE,EAQnB,eAAsB,EAAc,EAAgB,EAAgB,EAAE,CAAmB,CACvF,IAAM,EAAS,EAAK,SAAY,GAAM,QAAQ,OAAO,MAAM,EAAE,EACvD,EAAS,EAAK,GACd,EAAa,EAAK,GAqBxB,OAnBI,IAAW,SAAW,GACxB,MAAM,EAAY,EAAW,CACtB,GAEL,IAAW,UAAY,GACzB,MAAM,EAAa,EAAW,CACvB,GAEL,IAAW,QACb,MAAM,GAAY,CACX,IAGT,EACE;;;;EAID,CACM"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{randomUUID as e}from"node:crypto";const t=-32603;function n(e,t,n){return`${JSON.stringify({jsonrpc:`2.0`,id:e,error:{code:t,message:n}})}\n`}function r(e,t){return`${JSON.stringify({jsonrpc:`2.0`,id:e,result:t})}\n`}function i(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function a(e,a,o){return async s=>{let{id:c,method:l,params:u}=s;switch(l){case`initialize`:return r(c,{protocolVersion:`2024-11-05`,capabilities:{tools:{}},serverInfo:{name:`otto`,version:`0.0.1`}});case`notifications/initialized`:return``;case`tools/list`:return r(c,{tools:e.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}))});case`tools/call`:{let s=typeof u?.name==`string`?u.name:``;if(!(o&&o.length>0?o:e.map(e=>e.name)).includes(s))return n(c,-32602,`unknown tool: ${s}`);if(!a)return n(c,t,`tool "${s}" execution not wired`);let l=i(u?.arguments)?u.arguments:{},d=typeof u?.sessionId==`string`?u.sessionId:void 0;try{let e=await a(s,l,d);return r(c,{content:[{type:`text`,text:e.text}],isError:e.isError})}catch(e){return n(c,t,e instanceof Error?e.message:String(e))}}default:return n(c,-32601,`unknown method: ${l??``}`)}}}function o(e){let t=e.write??(e=>process.stdout.write(e)),r=a(e.tools??[],e.callTool,e.allToolNames),i=e=>{let i=e.trim();if(!i)return;let a;try{a=JSON.parse(i)}catch{Promise.resolve().then(()=>t(n(void 0,-32700,`parse error`)));return}a.method&&r(a).then(e=>{e&&t(e)})},o=e.readLine;if(o)o(i);else{let e=``;process.stdin.setEncoding(`utf8`),process.stdin.on(`data`,t=>{for(e+=t;;){let t=e.indexOf(`
|
|
3
|
+
`);if(t===-1)break;let n=e.slice(0,t);e=e.slice(t+1),i(n)}}),process.stdin.resume()}let s=e.signal;if(s)s.addEventListener(`abort`,()=>{e.onShutdown?e.onShutdown().catch(()=>void 0).finally(()=>process.exit(0)):Promise.resolve().then(()=>process.exit(0))});else{let t=()=>{e.onShutdown?e.onShutdown().catch(()=>void 0).finally(()=>process.exit(0)):Promise.resolve().then(()=>process.exit(0))};process.on(`SIGTERM`,t),process.on(`SIGINT`,t)}return{handle:i}}async function s(e,t={}){return o(t),0}async function c(e,t={}){let{createHostApp:n}=await import(`./create-host-app-DwlqiuPB.js`).then(e=>e.n),r=n({logger:{name:`otto-mcp-server`,level:`error`,destination:`stderr`},...t.projectDir?{workspaceDir:t.projectDir}:{},...t.modelId?{modelId:t.modelId}:{}});await r.start();let i=l(r.tools,r.permissionRegistry),a=new AbortController;return process.on(`SIGTERM`,()=>a.abort()),process.on(`SIGINT`,()=>a.abort()),s(e,{...i,signal:a.signal,onShutdown:()=>r.stop()})}function l(t,n){return{tools:t.filter(e=>e.readonly===!0&&e.exposure!==`hidden`&&e.exposure!==`deferred`).map(e=>({name:e.name,description:typeof e.description==`function`?e.name:e.description,inputSchema:(typeof e.parameters?.toJSONSchema==`function`?e.parameters.toJSONSchema():void 0)??{type:`object`}})),allToolNames:t.map(e=>e.name),callTool:async(r,i,a)=>{let o=t.find(e=>e.name===r),{createToolExecutor:s}=await import(`@x-otto/agent`);if(o&&o.readonly!==!0){if(!n)return{text:`Tool "${r}" is a write tool but no permission registry is wired; denied (fail-closed).`,isError:!0};let e=n.evaluate({timing:`tool.execute.before`,toolName:r,args:i,agentName:`mcp-server`,sessionId:a??`mcp-server`,metadata:{}});if(e.effect===`ask`)return{text:`Tool "${r}" requires interactive approval, which is not available over MCP (fail-closed). Run it in an interactive otto session instead.`,isError:!0};if(e.effect===`deny`)return{text:`Tool "${r}" denied by permission policy${e.reason?`: ${e.reason}`:``}.`,isError:!0}}let c=await s(t,{sessionId:a??`mcp-server`,agentName:`mcp-server`}).execute({type:`tool_call`,id:e(),name:r,arguments:i},new AbortController().signal);return{text:c.content.map(e=>e.type===`text`?e.text:`[${e.type}]`).join(`
|
|
4
|
+
`)||`(no output)`,isError:c.isError===!0}}}}export{c as runMcpServerCommandFromCli};
|
|
5
|
+
//# sourceMappingURL=mcp-server-Dq72PTeW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server-Dq72PTeW.js","names":[],"sources":["../src/commands/mcp-server.ts"],"sourcesContent":["/**\n * mcp-server.ts —— RFC-384:`otto mcp-server` 独立进程入口。\n *\n * otto 作为 MCP server 供其他 agent/IDE 调用(grill 定案方案 A:独立进程)。\n * M1:协议骨架(stdio JSON-RPC 循环 + initialize/tools/list/tools/call + MCP 错误语义)。\n * M2:readonly 工具执行面(buildMcpServerDepsFromApp——能力面投影 + 无审批 ToolExecutor,\n * D3 定案规避 HITL;请求级 sessionId 透传 D2 定案)。\n *\n * 分帧:换行分隔 JSON(MCP stdio 官方分帧);日志全部走 stderr(stdout 是协议通道)。\n */\nimport { randomUUID } from 'node:crypto'\n\n/** MCP 协议常量(官方 spec):JSON-RPC 错误码。 */\nconst PARSE_ERROR = -32700\nconst METHOD_NOT_FOUND = -32601\nconst INVALID_PARAMS = -32602\nconst INTERNAL_ERROR = -32603\n\nexport interface McpToolDefinition {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n}\n\n/** tools/call 结果文本化(MCP content 块形态)。 */\nexport interface McpCallResult {\n text: string\n isError: boolean\n}\n\nexport interface McpServerDeps {\n /** 暴露的工具目录(M2:装配层从 App 会话能力面投影注入;缺省空集)。 */\n tools?: McpToolDefinition[]\n /**\n * RFC-384 M4:全量已注册工具名(协议层 known-tool 判定用——write 工具不在 tools/list\n * 投影里但可被显式调用,存在性判定需覆盖全量而非仅投影)。\n */\n allToolNames?: string[]\n /**\n * RFC-384 M2:工具执行面——装配层注入(readonly 工具经 tool-executor 执行,\n * 无审批路径)。缺省 = 全部返回「not wired」协议错误。\n */\n callTool?: (\n name: string,\n args: Record<string, unknown>,\n sessionId: string | undefined,\n ) => Promise<McpCallResult>\n /** 测试注入:协议通道(缺省 = 真实 stdin/stdout)。 */\n readLine?: (handler: (line: string) => void) => void\n write?: (line: string) => void\n /** 优雅退出信号(测试注入)。 */\n signal?: AbortSignal\n /** RFC-384 M2:退出前清理回调(生产装配 = app.stop(),插件 dispose/落盘收尾)。 */\n onShutdown?: () => Promise<void>\n}\n\ninterface JsonRpcRequest {\n jsonrpc?: string\n id?: string | number\n method?: string\n params?: Record<string, unknown>\n}\n\nfunction rpcError(id: string | number | undefined, code: number, message: string): string {\n return `${JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } })}\\n`\n}\n\nfunction rpcResult(id: string | number | undefined, result: unknown): string {\n return `${JSON.stringify({ jsonrpc: '2.0', id, result })}\\n`\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\n/** M4 工具执行面:tools/call 经注入 callTool 执行(缺省 not wired)。 */\nexport function createMcpHandlers(\n tools: McpToolDefinition[],\n callTool?: McpServerDeps['callTool'],\n allToolNames?: string[],\n): (req: JsonRpcRequest) => Promise<string> {\n return async (req) => {\n const { id, method, params } = req\n switch (method) {\n case 'initialize': {\n return rpcResult(id, {\n protocolVersion: '2024-11-05',\n capabilities: { tools: {} },\n serverInfo: { name: 'otto', version: '0.0.1' },\n })\n }\n case 'notifications/initialized':\n // 通知无响应\n return ''\n case 'tools/list': {\n return rpcResult(id, {\n tools: tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n })\n }\n case 'tools/call': {\n const name = typeof params?.name === 'string' ? params.name : ''\n // RFC-384 M4:known-tool 判定覆盖全量注册名(write 工具不在 list 投影但可显式调用)\n const knownNames = allToolNames && allToolNames.length > 0 ? allToolNames : tools.map((t) => t.name)\n if (!knownNames.includes(name)) {\n return rpcError(id, INVALID_PARAMS, `unknown tool: ${name}`)\n }\n if (!callTool) {\n return rpcError(id, INTERNAL_ERROR, `tool \"${name}\" execution not wired`)\n }\n const args = isRecord(params?.arguments) ? params!.arguments : {}\n // RFC-384 D2:请求级 sessionId(MCP params 顶层扩展键,官方客户端不受影响)。\n const sessionId = typeof params?.sessionId === 'string' ? params.sessionId : undefined\n try {\n const result = await callTool(name, args as Record<string, unknown>, sessionId)\n return rpcResult(id, {\n content: [{ type: 'text', text: result.text }],\n isError: result.isError,\n })\n } catch (err) {\n return rpcError(id, INTERNAL_ERROR, err instanceof Error ? err.message : String(err))\n }\n }\n default:\n return rpcError(id, METHOD_NOT_FOUND, `unknown method: ${method ?? ''}`)\n }\n }\n}\n\n/** stdio 逐行循环(换行分帧);非法 JSON 返回 parse error。 */\nexport function startMcpStdioLoop(\n deps: McpServerDeps,\n): { handle: (line: string) => void } {\n const write = deps.write ?? ((s) => process.stdout.write(s))\n const handleReq = createMcpHandlers(deps.tools ?? [], deps.callTool, deps.allToolNames)\n\n const handle = (line: string): void => {\n const trimmed = line.trim()\n if (!trimmed) return\n let req: JsonRpcRequest\n try {\n req = JSON.parse(trimmed) as JsonRpcRequest\n } catch {\n // 统一走微任务:与正常响应同序(逐行输入 → 逐行输出顺序一致)。\n void Promise.resolve().then(() => write(rpcError(undefined, PARSE_ERROR, 'parse error')))\n return\n }\n if (!req.method) return\n void handleReq(req).then((out) => {\n if (out) write(out)\n })\n }\n\n const readLine = deps.readLine\n if (readLine) {\n readLine(handle)\n } else {\n let buf = ''\n process.stdin.setEncoding('utf8')\n process.stdin.on('data', (chunk: string) => {\n buf += chunk\n for (;;) {\n const nl = buf.indexOf('\\n')\n if (nl === -1) break\n const line = buf.slice(0, nl)\n buf = buf.slice(nl + 1)\n handle(line)\n }\n })\n process.stdin.resume()\n }\n\n const signal = deps.signal\n if (signal) {\n signal.addEventListener('abort', () => {\n void (deps.onShutdown\n ? deps.onShutdown().catch(() => undefined).finally(() => process.exit(0))\n : Promise.resolve().then(() => process.exit(0)))\n })\n } else {\n const exit = (): void => {\n void (deps.onShutdown\n ? deps.onShutdown().catch(() => undefined).finally(() => process.exit(0))\n : Promise.resolve().then(() => process.exit(0)))\n }\n process.on('SIGTERM', exit)\n process.on('SIGINT', exit)\n }\n\n return { handle }\n}\n\n/** `otto mcp-server` 命令入口。 */\nexport async function runMcpServerCommand(\n _args: string[],\n deps: McpServerDeps = {},\n): Promise<number> {\n startMcpStdioLoop(deps)\n return 0\n}\n\n/**\n * RFC-410 M1 T4:CLI 生产装配入口——从顶层命令目录调用,编排下沉至此(原内联在\n * `cli.ts` 的 SUB_COMMANDS Map 条目里,注册表条目应只做路由)。\n *\n * headless App 起工具注册表(settings/插件/权限装载),readonly 工具投影进 MCP server 面;\n * SIGTERM/SIGINT → 优雅退出信号;退出时 app.stop() 收尾。\n */\nexport async function runMcpServerCommandFromCli(\n args: string[],\n opts: { projectDir?: string; modelId?: string } = {},\n): Promise<number> {\n const { createHostApp } = await import('../create-host-app')\n const app = createHostApp({\n logger: { name: 'otto-mcp-server', level: 'error', destination: 'stderr' },\n ...(opts.projectDir ? { workspaceDir: opts.projectDir } : {}),\n ...(opts.modelId ? { modelId: opts.modelId } : {}),\n })\n await app.start()\n const deps = buildMcpServerDepsFromApp(app.tools, app.permissionRegistry)\n const shutdown = new AbortController()\n process.on('SIGTERM', () => shutdown.abort())\n process.on('SIGINT', () => shutdown.abort())\n return runMcpServerCommand(args, {\n ...deps,\n signal: shutdown.signal,\n onShutdown: () => app.stop(),\n })\n}\n\n/**\n * RFC-384 M2/M4:从宿主 App 构建 server 面——readonly 工具投影 + 权限裁决执行面。\n *\n * 投影规则(D4 定案):exposure 非 hidden/deferred 且 readonly===true 的工具进\n * tools/list(write 工具不在投影但可显式调用,known-tool 判定覆盖全量)。\n *\n * 执行面(M4 权限裁决):\n * - readonly 工具 → 直通执行(D3 定案无 HITL 需求);\n * - write 工具 → 先经权限引擎 evaluate(permissionRegistry 注入时):\n * allow → 执行;ask → fail-closed 拒绝(MCP 通道无交互审批原语,返回明确错误);\n * deny → fail-closed 拒绝(带 policy 原因);\n * - permission 未注入 → write 工具 fail-closed 拒绝(不默认放行)。\n *\n * inputSchema:zod 4 原生 toJSONSchema(M3)。\n */\nexport function buildMcpServerDepsFromApp(\n appTools: import('@x-otto/interchange').AgentToolDefinition[],\n permission?: {\n evaluate(context: {\n timing: string\n toolName: string\n args: Record<string, unknown>\n agentName: string\n sessionId: string\n metadata: Record<string, unknown>\n }): { effect: 'allow' | 'ask' | 'deny'; reason?: string }\n },\n): Required<Pick<McpServerDeps, 'tools' | 'callTool' | 'allToolNames'>> {\n const readonlyTools = appTools.filter(\n (t) => t.readonly === true && t.exposure !== 'hidden' && t.exposure !== 'deferred',\n )\n\n const tools: McpToolDefinition[] = readonlyTools.map((t) => ({\n name: t.name,\n description: typeof t.description === 'function' ? t.name : t.description,\n // RFC-384 M3:zod 4 原生 toJSONSchema(含 description/enum/required/optional 语义)——\n // 协议垫片(interchange ZodType.toJSONSchema)可选调用,缺失时回退最小骨架\n // (执行层 zod safeParse 仍兜底校验)。\n inputSchema:\n (typeof t.parameters?.toJSONSchema === 'function'\n ? (t.parameters.toJSONSchema() as Record<string, unknown>)\n : undefined) ?? { type: 'object' },\n }))\n\n const allToolNames = appTools.map((t) => t.name)\n\n return {\n tools,\n allToolNames,\n callTool: async (name, args, sessionId) => {\n const tool = appTools.find((t) => t.name === name)\n const { createToolExecutor } = await import('@x-otto/agent')\n\n // RFC-384 M4:write 工具执行前权限裁决(fail-closed)。\n if (tool && tool.readonly !== true) {\n if (!permission) {\n return {\n text: `Tool \"${name}\" is a write tool but no permission registry is wired; ` +\n `denied (fail-closed).`,\n isError: true,\n }\n }\n const decision = permission.evaluate({\n timing: 'tool.execute.before',\n toolName: name,\n args,\n agentName: 'mcp-server',\n sessionId: sessionId ?? 'mcp-server',\n metadata: {},\n })\n if (decision.effect === 'ask') {\n return {\n text: `Tool \"${name}\" requires interactive approval, which is not available ` +\n `over MCP (fail-closed). Run it in an interactive otto session instead.`,\n isError: true,\n }\n }\n if (decision.effect === 'deny') {\n return {\n text: `Tool \"${name}\" denied by permission policy${decision.reason ? `: ${decision.reason}` : ''}.`,\n isError: true,\n }\n }\n // allow → 继续执行\n }\n\n const executor = createToolExecutor(appTools, {\n sessionId: sessionId ?? 'mcp-server',\n agentName: 'mcp-server',\n })\n const result = await executor.execute(\n { type: 'tool_call', id: randomUUID(), name, arguments: args },\n new AbortController().signal,\n )\n const text = result.content\n .map((c) => (c.type === 'text' ? c.text : `[${c.type}]`))\n .join('\\n')\n return { text: text || '(no output)', isError: result.isError === true }\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -11,4 +11,4 @@ import{runConfigMigrationCommand as e}from"./config-migration-DatflIxs.js";impor
|
|
|
11
11
|
export [--no-sessions] [-o <path>] 导出配置+插件清单+会话数据为迁移包
|
|
12
12
|
import <archive> [--no-sessions] 导入迁移包(目标已有会话数据时跳过会话部分)
|
|
13
13
|
`),n.sub?1:0}}export{E as runMigrateCommand};
|
|
14
|
-
//# sourceMappingURL=migrate-
|
|
14
|
+
//# sourceMappingURL=migrate-BBi3Txu2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate-
|
|
1
|
+
{"version":3,"file":"migrate-BBi3Txu2.js","names":[],"sources":["../src/commands/migrate.ts"],"sourcesContent":["/**\n * /migrate 命令:旧版配置文件 → 新版格式在线迁移向导。\n */\nimport { existsSync, mkdirSync, writeFileSync, readdirSync, statSync, cpSync } from 'node:fs'\nimport { hostname } from 'node:os'\nimport { join, resolve } from 'node:path'\nimport { OTTO_HOME, OTTO_SESSIONS_DIR } from '@x-otto/env'\nimport {\n createArchive,\n extractArchive,\n validateExtractedPaths,\n checkArchiveBomb,\n createTempDir,\n cleanTempDir,\n} from '@x-otto/coding'\nimport { buildExportPayload, SettingsManager } from '@x-otto/setting'\nimport { runConfigMigrationCommand } from './config-migration'\n\nexport interface MigrateIO {\n stdout: (s: string) => void\n stderr: (s: string) => void\n}\n\ninterface ParsedArgs {\n sub: string | undefined\n positional: string[]\n flags: Set<string>\n values: Map<string, string>\n}\n\nfunction parseArgs(args: string[]): ParsedArgs {\n const [sub, ...rest] = args\n const positional: string[] = []\n const flags = new Set<string>()\n const values = new Map<string, string>()\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]!\n if (a === '-o' || a === '--output') {\n const next = rest[++i]\n if (next) values.set('output', next)\n continue\n }\n if (a.startsWith('--')) {\n flags.add(a)\n continue\n }\n positional.push(a)\n }\n return { sub, positional, flags, values }\n}\n\nfunction timestampNow(): string {\n return new Date().toISOString().replace(/[:.]/g, '-')\n}\n\n/**\n * `OTTO_SESSIONS_DIR` 下机器绑定/纯诊断子目录,`migrate export` 打包时排除\n * (对抗审核 M4 完成审核发现的 RFC 漂移修复:初版实现只排除 `locks/`,用\"排除法\"\n * 会把新增子目录默认打包进迁移包,是脆弱的开放式假设)。\n *\n * - `locks`:写租约信息,随进程/机器绑定,新机器上无意义(`OTTO_SESSION_LOCKS_DIR`)。\n * - `trace`:append-log 诊断轨迹,非会话内容本身,实测可堆积至 25GB+(见\n * `packages/coding/src/app/trace-orphan-sweeper.ts` 头注释记录的真实生产数据),\n * 打包会让迁移包体积失控且无迁移价值(新机器上这些轨迹对应的是旧机器的执行历史)。\n *\n * `panel-state/` **不**在排除清单——它是真实用户数据(面板草稿/todoList 等持久化\n * 状态,见 `packages/runtime/src/create-agent-runtime.ts` `DiskPanelStatePersistence`),\n * 随会话搬迁是用户期望的行为,全量打包/仅打包\"sessions.db + 固定白名单\"两种方案都\n * 会静默丢失该数据(前者不排除、后者是未预料到 panel-state 存在的过度收紧)。\n * `sessions.db-wal`/`sessions.db-shm`(SQLite WAL 模式伴生文件)也需随 `sessions.db`\n * 一起打包——它们不是独立目录而是文件,`readdirSync` 顶层遍历天然包含,无需特殊处理。\n */\nconst MACHINE_BOUND_SESSION_SUBDIRS = new Set(['locks', 'trace'])\n\n/** 目标 sessions.db 是否已存在且非空(RFC-219 重要事项规则 7 判定依据)。 */\nfunction targetSessionsExist(): boolean {\n const dbPath = join(OTTO_SESSIONS_DIR, 'sessions.db')\n if (!existsSync(dbPath)) return false\n try {\n return statSync(dbPath).size > 0\n } catch {\n return false\n }\n}\n\n// ── otto migrate export ──\n\nasync function runMigrateExport(parsed: ParsedArgs, io: MigrateIO): Promise<number> {\n const noSessions = parsed.flags.has('--no-sessions')\n const stagingDir = createTempDir()\n\n try {\n const settings = new SettingsManager({ projectConfigPath: join(OTTO_HOME, 'config.json') })\n await settings.load()\n const payload = buildExportPayload(settings.config, 'global', hostname())\n mkdirSync(stagingDir, { recursive: true })\n writeFileSync(join(stagingDir, 'config.json'), JSON.stringify(payload, null, 2), 'utf-8')\n\n if (!noSessions && existsSync(OTTO_SESSIONS_DIR)) {\n const sessionsStaging = join(stagingDir, 'sessions')\n mkdirSync(sessionsStaging, { recursive: true })\n const entries = readdirSync(OTTO_SESSIONS_DIR)\n for (const entry of entries) {\n // 对抗审核 M4 完成审核修复(RFC 漂移):排除机器绑定/纯诊断子目录,非仅 locks。\n if (MACHINE_BOUND_SESSION_SUBDIRS.has(entry)) continue\n cpSync(join(OTTO_SESSIONS_DIR, entry), join(sessionsStaging, entry), { recursive: true })\n }\n }\n\n const outputPath = parsed.values.get('output')\n ? resolve(parsed.values.get('output')!)\n : join(process.cwd(), `otto-migrate-${timestampNow()}.tar.gz`)\n mkdirSync(join(outputPath, '..'), { recursive: true })\n\n await createArchive(stagingDir, outputPath)\n\n io.stdout(`迁移包已导出: ${outputPath}\\n`)\n if (noSessions) {\n io.stdout('(--no-sessions:未包含会话数据)\\n')\n } else {\n // RFC-338 Q2(用户定稿方案 A):粘贴原文与草稿随包属既定行为,但**必须显式声明**——\n // 这些内容可能含密钥/日志/隐私代码,用户在分享迁移包前有权知情。\n // 该提示有测试守卫(migrate.test.ts),防止后续改动静默移除。\n io.stdout('(包含会话数据:对话历史、草稿与粘贴内容原文——分享前请确认其中无敏感信息)\\n')\n }\n return 0\n } finally {\n cleanTempDir(stagingDir)\n }\n}\n\n// ── otto migrate import ──\n\nasync function runMigrateImport(parsed: ParsedArgs, io: MigrateIO): Promise<number> {\n const archivePath = parsed.positional[0]\n if (!archivePath) {\n io.stderr('otto migrate import <archive> [--no-sessions]\\n')\n return 1\n }\n const resolvedArchive = resolve(archivePath)\n if (!existsSync(resolvedArchive)) {\n io.stderr(`迁移包不存在:${resolvedArchive}\\n`)\n return 1\n }\n\n const extractDir = createTempDir()\n try {\n await extractArchive(resolvedArchive, extractDir)\n const { totalBytes } = validateExtractedPaths(extractDir)\n checkArchiveBomb(resolvedArchive, totalBytes)\n\n const configPath = join(extractDir, 'config.json')\n if (!existsSync(configPath)) {\n io.stderr('迁移包缺少 config.json(非合法 otto migrate 产物)\\n')\n return 1\n }\n\n // 复用 M3 config import 完整逻辑(差异计算+合并+安全门+备份+插件安装),\n // 不重新实现——RFC-219 M3→M4 接线设计,见本文件头注释。\n const configExitCode = await runConfigMigrationCommand(\n ['import', configPath, '--yes'],\n { stdout: io.stdout, stderr: io.stderr },\n )\n if (configExitCode !== 0) {\n return configExitCode\n }\n\n // 会话导入 v1 严格边界(重要事项规则 7):目标已有非空 sessions.db → 跳过,不合并。\n const noSessions = parsed.flags.has('--no-sessions')\n const sessionsStaging = join(extractDir, 'sessions')\n if (noSessions) {\n io.stdout('(--no-sessions:跳过会话导入)\\n')\n } else if (!existsSync(sessionsStaging)) {\n io.stdout('迁移包不含会话数据,跳过\\n')\n } else if (targetSessionsExist()) {\n io.stdout('目标已有会话数据,跳过会话导入(不合并/不覆盖,避免数据冲突)\\n')\n } else {\n mkdirSync(OTTO_SESSIONS_DIR, { recursive: true })\n cpSync(sessionsStaging, OTTO_SESSIONS_DIR, { recursive: true })\n io.stdout('✓ 会话数据已导入\\n')\n }\n\n return 0\n } catch (err) {\n io.stderr(`迁移导入失败:${err instanceof Error ? err.message : String(err)}\\n`)\n return 1\n } finally {\n cleanTempDir(extractDir)\n }\n}\n\n// ── 入口 ──\n\nexport async function runMigrateCommand(args: string[], io: MigrateIO): Promise<number> {\n const parsed = parseArgs(args)\n switch (parsed.sub) {\n case 'export':\n return runMigrateExport(parsed, io)\n case 'import':\n return runMigrateImport(parsed, io)\n default:\n io.stdout(\n 'otto migrate <命令>\\n' +\n ' export [--no-sessions] [-o <path>] 导出配置+插件清单+会话数据为迁移包\\n' +\n ' import <archive> [--no-sessions] 导入迁移包(目标已有会话数据时跳过会话部分)\\n',\n )\n return parsed.sub ? 1 : 0\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as e,i as t,r as n}from"./service-client-cfUUP4EE.js";import{SERVICE_URL as r}from"@x-otto/env";import{BREAKPOINT_POINTS as i}from"@x-otto/agent";const a=i.map(e=>e.point);async function o(r,i={}){let o=i.stdout??(e=>process.stdout.write(e)),c=i.stderr??(e=>process.stderr.write(e)),f,p;try{({baseUrl:f,rest:p}=e(r))}catch(e){return t(`debug`,e,c)}let[m,...h]=p;if(!m||m===`help`)return o(`otto debug — 调试面板(断点管理 / 状态查看)
|
|
3
|
+
|
|
4
|
+
用法:
|
|
5
|
+
otto debug breakpoints list 列出所有断点及启用状态
|
|
6
|
+
otto debug breakpoints set <point> 设置断点(默认启用)
|
|
7
|
+
otto debug breakpoints remove <point> 移除断点
|
|
8
|
+
otto debug breakpoints enable <point> 启用断点
|
|
9
|
+
otto debug breakpoints disable <point> 禁用断点
|
|
10
|
+
otto debug breakpoints enable-all 全部启用
|
|
11
|
+
otto debug breakpoints disable-all 全部禁用
|
|
12
|
+
otto debug points 列出所有可用断点位
|
|
13
|
+
otto debug state 调试器状态(断点统计 + 挂起的暂停)
|
|
14
|
+
|
|
15
|
+
选项:
|
|
16
|
+
--remote <url> service 地址(默认 OTTO_SERVICE_URL 或 http:
|
|
17
|
+
|
|
18
|
+
断点位(BREAKPOINT_POINTS):
|
|
19
|
+
${a.map(e=>` ${e}`).join(`
|
|
20
|
+
`)}
|
|
21
|
+
|
|
22
|
+
注:需要 \`otto serve --inspect\` 启动的 service(调试器默认关闭)。
|
|
23
|
+
resume/inject 等运行时命令经 REST:POST /api/debugger/resume | /inject。
|
|
24
|
+
`),0;let g=n(f,i.fetch);try{switch(m){case`breakpoints`:{let e=h[0];switch(e){case`list`:return await s(g,o);case`set`:return await l(g,h[1],!0,o,c);case`remove`:return await l(g,h[1],!1,o,c,`removed`);case`enable`:return await l(g,h[1],!0,o,c,`enabled`);case`disable`:return await l(g,h[1],!1,o,c,`disabled`);case`enable-all`:return await g.post(`/api/debugger/breakpoints/enable-all`),o(`All breakpoints enabled.
|
|
25
|
+
`),0;case`disable-all`:return await g.post(`/api/debugger/breakpoints/disable-all`),o(`All breakpoints disabled.
|
|
26
|
+
`),0;default:return c(`otto debug breakpoints: unknown sub-action '${e}'.\n`),1}}case`points`:return await u(g,o);case`state`:return await d(g,o);default:return c(`otto debug: unknown action '${m}'. Use breakpoints | points | state.\n`),1}}catch(e){return t(`debug`,e,c)}}async function s(e,t){let{breakpoints:n}=await e.get(`/api/debugger/breakpoints`);if(n.length===0)return t(`No breakpoints set.
|
|
27
|
+
`),0;t(`${`point`.padEnd(36)} enabled\n${`-`.repeat(46)}\n`);for(let e of n)t(`${e.point.padEnd(36)} ${e.enabled?`✓`:`✗`}\n`);return 0}function c(e){return!!e&&a.includes(e)}async function l(e,t,n,r,i,a){return c(t)?(await e.put(`/api/debugger/breakpoints/${t}`,{enabled:n}),r(`Breakpoint ${a??`set: ${t} (${n?`enabled`:`disabled`})`}${a?`: ${t}`:``}\n`),0):(i(`otto debug: invalid or missing breakpoint point.
|
|
28
|
+
Use 'otto debug points' to list valid points.
|
|
29
|
+
`),1)}async function u(e,t){let{breakpoints:n}=await e.get(`/api/debugger/breakpoints`),r=new Map(n.map(e=>[e.point,e]));t(`Available breakpoint points:
|
|
30
|
+
`);for(let e of i){let n=r.get(e.point),i=n?.set?n.enabled?`enabled`:`disabled`:`not set`;t(` ${e.point.padEnd(36)} ${i}\n`)}return 0}async function d(e,t){let n=await e.get(`/api/debugger/state`);t(`Debugger state
|
|
31
|
+
breakpoints: ${n.breakpoints.enabled}/${n.breakpoints.total} enabled
|
|
32
|
+
paused: ${n.paused?`yes`:`no`}
|
|
33
|
+
`);for(let e of n.pendingPauses)t(` ⏸ ${e.point} (turn ${e.turn}) pauseId=${e.pauseId}\n`);return 0}async function f(i,a={}){let o=a.stdout??(e=>process.stdout.write(e)),s=a.stderr??(e=>process.stderr.write(e)),c,l;try{({baseUrl:c,rest:l}=e(i))}catch(e){return t(`time-travel`,e,s)}let[u,d,...f]=l;if(!u||u===`help`)return o(`otto time-travel — 回放+回滚面板
|
|
34
|
+
|
|
35
|
+
用法:
|
|
36
|
+
otto time-travel checkpoints <session-id> 列出所有 checkpoint
|
|
37
|
+
otto time-travel checkpoint <session-id> [--label X] 创建新 checkpoint
|
|
38
|
+
otto time-travel scrubber <session-id> <seq> 显示 traceback 视图
|
|
39
|
+
otto time-travel restore <session-id> <seq> [--mode M] [--rollback]
|
|
40
|
+
恢复/回滚 (mode: conversation|code|both)
|
|
41
|
+
otto time-travel fork <session-id> <seq> 从 checkpoint 分叉新会话
|
|
42
|
+
otto time-travel replay <session-id> 启动确定性重放
|
|
43
|
+
|
|
44
|
+
选项:
|
|
45
|
+
--remote <url> service 地址(默认 OTTO_SERVICE_URL 或 ${r})
|
|
46
|
+
`),0;if(!d)return s(`otto time-travel: session-id is required.
|
|
47
|
+
`),1;let y=n(c,a.fetch);try{switch(u){case`checkpoints`:return await p(y,d,o);case`checkpoint`:return await m(y,d,f,o);case`scrubber`:return await h(y,d,f[0],o,s);case`restore`:return await g(y,d,f,o,s);case`fork`:return await _(y,d,f[0],o,s);case`replay`:return await v(y,d,o);default:return s(`otto time-travel: unknown action '${u}'.\n`),1}}catch(e){return t(`time-travel`,e,s)}}async function p(e,t,n){let{checkpoints:r}=await e.get(`/api/sessions/${t}/checkpoints`);if(r.length===0)return n(`Session ${t}: no checkpoints found.\n`),0;n(`Checkpoints — ${t} (${r.length})\n`),n(`${`seq`.padStart(4)} ${`traceSeq`.padStart(9)} ${`label`.padEnd(24)} created\n`),n(`${`-`.repeat(70)}\n`);for(let e of r){let t=(e.label??``).padEnd(24),r=new Date(e.createdAt).toISOString(),i=e.traceSeq===void 0?` -`:String(e.traceSeq).padStart(9);n(`${String(e.seq).padStart(4)} ${i} ${t} ${r}\n`)}return 0}async function m(e,t,n,r){let i=n.indexOf(`--label`),a=i>=0?n[i+1]:void 0,{checkpoint:o}=await e.post(`/api/sessions/${t}/checkpoints`,{label:a});return r(`Checkpoint created: seq=${o.seq}${o.label?` label="${o.label}"`:``} traceSeq=${o.traceSeq}\n`),0}async function h(e,t,n,r,i){let a=Number(n);if(!Number.isInteger(a))return i(`otto time-travel scrubber: seq (integer) is required.
|
|
48
|
+
`),1;let o=await e.get(`/api/sessions/${t}/traceback?to=${a}`);r(`Traceback — ${t} @ seq=${o.toSeq}\n`),o.baselineCheckpointSeq===null?r(` Baseline: none (folded from seq 0)
|
|
49
|
+
`):r(` Baseline: checkpoint seq=${o.baselineCheckpointSeq} (fromTraceSeq=${o.fromTraceSeq})\n`),r(` Events folded: ${o.events.length}\n`),r(` Messages reconstructed: ${o.messages.length}\n\n`);for(let e of o.messages)r(` ${e.role.padEnd(12)} ${typeof e.content==`string`?e.content.slice(0,120):JSON.stringify(e.content).slice(0,120)}\n`);return 0}async function g(e,t,n,r,i){let a=Number(n[0]);if(!Number.isInteger(a))return i(`otto time-travel restore: seq (integer) is required.
|
|
50
|
+
`),1;let o=n.indexOf(`--mode`),s=o>=0?n[o+1]:`both`;if(s!==`conversation`&&s!==`code`&&s!==`both`)return i(`otto time-travel restore: invalid mode '${s}'. Use conversation | code | both.\n`),1;let c=n.includes(`--rollback`);return await e.post(`/api/sessions/${t}/checkpoints/${a}/restore`,{mode:s,truncate:c}),r(`Restored to checkpoint seq=${a} mode=${s} rollback=${c}\n`),0}async function _(e,t,n,r,i){let a=Number(n);return Number.isInteger(a)?(r(`Forked session: ${(await e.post(`/api/sessions/${t}/checkpoints/${a}/fork`,{})).forkedSessionId} (from ${t} @ seq=${a})\n`),0):(i(`otto time-travel fork: seq (integer) is required.
|
|
51
|
+
`),1)}async function v(e,t,n){return n(`Replay session created: ${(await e.post(`/api/sessions/${t}/replay`,{})).replaySessionId} (source: ${t})\n`),0}async function y(i,a={}){let s=a.stdout??(e=>process.stdout.write(e)),c=a.stderr??(e=>process.stderr.write(e)),[l,...u]=i;if(l===`debug`)return o(u,a);if(l===`replay`)return f(u,a);let d=i.indexOf(`--json`),p=d>=0,m=p?i.filter((e,t)=>t!==d):i,h,g;try{({baseUrl:h,rest:g}=e(m))}catch(e){return t(`observe`,e,c)}let[_,v]=g;if(!_||_===`help`)return s(`otto observe — 观测面板(trace 时间线 / 摘要 / span 树)
|
|
52
|
+
|
|
53
|
+
用法:
|
|
54
|
+
otto observe summary <session-id> trace 聚合统计
|
|
55
|
+
otto observe timeline <session-id> 文本时间线
|
|
56
|
+
otto observe spans <session-id> span 树视图
|
|
57
|
+
otto observe debug <action> [...] 调试面板(breakpoints / points / state)
|
|
58
|
+
otto observe replay <action> <id> 时间旅行(checkpoints / scrubber / restore / fork / replay)
|
|
59
|
+
|
|
60
|
+
选项:
|
|
61
|
+
--remote <url> service 地址(默认 OTTO_SERVICE_URL 或 ${r})
|
|
62
|
+
--json 输出结构化 JSON(需 service 运行)
|
|
63
|
+
`),0;if(!v)return c(`otto observe: session-id is required.
|
|
64
|
+
`),1;let y=n(h,a.fetch);try{if(p)return await w(y,v,s,c);switch(_){case`summary`:return await x(y,v,s);case`timeline`:return await S(y,v,s);case`spans`:return await C(y,v,s);default:return c(`otto observe: unknown action '${_}'. Use summary | timeline | spans.\n`),1}}catch(e){return t(`observe`,e,c)}}function b(e,t){return e.get(`/api/sessions/${t}/trace/spans`)}async function x(e,t,n){let r=await e.get(`/api/sessions/${t}/trace/summary`);if(r.totalEvents===0)return n(`Session ${t}: no trace events found.\n`),0;let{totalEvents:i,kinds:a,turns:o,toolCalls:s,firstTs:c,lastTs:l}=r;return n(`Trace Summary — ${t}
|
|
65
|
+
──────────────────────────────────────────
|
|
66
|
+
Total events: ${i}
|
|
67
|
+
Turns: ${o}
|
|
68
|
+
Tool calls: ${s}
|
|
69
|
+
Time span: ${c!==void 0&&l!==void 0?`${l-c}ms`:`N/A`}
|
|
70
|
+
First event: ${c?new Date(c).toISOString():`N/A`}
|
|
71
|
+
Last event: ${l?new Date(l).toISOString():`N/A`}
|
|
72
|
+
|
|
73
|
+
By kind:
|
|
74
|
+
${Object.entries(a).map(([e,t])=>` ${e.padEnd(14)} ${t}`).join(`
|
|
75
|
+
`)}
|
|
76
|
+
`),0}async function S(e,t,n){let{spans:r}=await b(e,t);if(r.length===0)return n(`Session ${t}: no trace events found.\n`),0;let i=r.map(e=>{let t=new Date(e.ts).toISOString().slice(11,23),n=e.turn===void 0?` -`:`T${String(e.turn).padStart(2)}`,r=(e.node??``).padEnd(26);return`${String(e.seq).padStart(5)} ${t} ${n} ${e.kind.padEnd(12)} ${r}${e.digest??``}`});return n(`Trace Timeline — ${t} (${i.length} events)
|
|
77
|
+
${`seq`.padStart(5)} time turn ${`kind`.padEnd(12)} ${`node`.padEnd(26)} digest
|
|
78
|
+
${`-`.repeat(100)}
|
|
79
|
+
${i.join(`
|
|
80
|
+
`)}
|
|
81
|
+
`),0}async function C(e,t,n){let{spans:r}=await b(e,t);if(r.length===0)return n(`Session ${t}: no trace events found.\n`),0;let i=new Map,a=-1;for(let e of r){let t=e.turn??0;t>a&&(a=t);let n=i.get(t)??[];n.push(e),i.set(t,n)}n(`Trace Spans — ${t} (${a+1} turns)
|
|
82
|
+
`);for(let e=0;e<=a;e++){let t=i.get(e);if(t){n(`▼ Turn ${e} (${t.length} events)\n`);for(let e of t){let t=e.node?`[${e.node}] `:``;n(` ├─ ${e.kind.padEnd(12)} ${t}${e.digest??``}\n`)}}}return 0}async function w(e,t,n,r){try{let r=await e.get(`/api/sessions/${t}/trace/timeline`);return n(`${JSON.stringify(r,null,2)}\n`),0}catch(e){return r(`observe --json: ${e instanceof Error?e.message:String(e)}\n`),1}}export{y as runObserveCommand};
|
|
83
|
+
//# sourceMappingURL=observe-BqLCpmFD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observe-BqLCpmFD.js","names":[],"sources":["../src/commands/debug.ts","../src/commands/time-travel.ts","../src/commands/observe.ts"],"sourcesContent":["/**\n * `otto debug` 子命令 —— 调试面板:断点管理 / 状态查看。\n *\n * 设计依据:design/observability-platform.md §4/§5 + design/m17-platform-activation.md §1/§3。\n * 数据源 = service REST(/api/debugger 路由;此前各自起 in-memory 进程,\n * 断点状态随进程消失、对运行中引擎无效)。默认连 `otto serve --inspect`\n * 实例,`--remote <url>` 覆盖。断点缺省 disabled,设置即对运行中会话生效。\n */\nimport { BREAKPOINT_POINTS } from '@x-otto/agent'\nimport {\n createServiceClient,\n reportServiceError,\n resolveRemote,\n type ServiceClient,\n} from './service-client'\n\nconst POINT_NAMES: readonly string[] = BREAKPOINT_POINTS.map((d) => d.point)\n\nexport interface DebugDeps {\n stdout?: (s: string) => void\n stderr?: (s: string) => void\n /** fetch 注入(测试用)。 */\n fetch?: typeof globalThis.fetch\n}\n\ninterface BreakpointEntry {\n point: string\n enabled: boolean\n set?: boolean\n}\n\nexport async function runDebugCommand(\n args: string[],\n deps: DebugDeps = {},\n): Promise<number> {\n const stdout = deps.stdout ?? ((s) => process.stdout.write(s))\n const stderr = deps.stderr ?? ((s) => process.stderr.write(s))\n\n let baseUrl: string\n let rest: string[]\n try {\n ;({ baseUrl, rest } = resolveRemote(args))\n } catch (error) {\n return reportServiceError('debug', error, stderr)\n }\n\n const [action, ...sub] = rest\n\n if (!action || action === 'help') {\n stdout(`otto debug — 调试面板(断点管理 / 状态查看)\n\n用法:\n otto debug breakpoints list 列出所有断点及启用状态\n otto debug breakpoints set <point> 设置断点(默认启用)\n otto debug breakpoints remove <point> 移除断点\n otto debug breakpoints enable <point> 启用断点\n otto debug breakpoints disable <point> 禁用断点\n otto debug breakpoints enable-all 全部启用\n otto debug breakpoints disable-all 全部禁用\n otto debug points 列出所有可用断点位\n otto debug state 调试器状态(断点统计 + 挂起的暂停)\n\n选项:\n --remote <url> service 地址(默认 OTTO_SERVICE_URL 或 http:\n\n断点位(BREAKPOINT_POINTS):\n${POINT_NAMES.map((p) => ` ${p}`).join('\\n')}\n\n注:需要 \\`otto serve --inspect\\` 启动的 service(调试器默认关闭)。\nresume/inject 等运行时命令经 REST:POST /api/debugger/resume | /inject。\n`)\n return 0\n }\n\n const client = createServiceClient(baseUrl, deps.fetch)\n\n try {\n switch (action) {\n case 'breakpoints': {\n const op = sub[0]\n switch (op) {\n case 'list':\n return await listBreakpoints(client, stdout)\n case 'set':\n return await setBreakpoint(client, sub[1], true, stdout, stderr)\n case 'remove':\n return await setBreakpoint(client, sub[1], false, stdout, stderr, 'removed')\n case 'enable':\n return await setBreakpoint(client, sub[1], true, stdout, stderr, 'enabled')\n case 'disable':\n return await setBreakpoint(client, sub[1], false, stdout, stderr, 'disabled')\n case 'enable-all':\n await client.post('/api/debugger/breakpoints/enable-all')\n stdout('All breakpoints enabled.\\n')\n return 0\n case 'disable-all':\n await client.post('/api/debugger/breakpoints/disable-all')\n stdout('All breakpoints disabled.\\n')\n return 0\n default:\n stderr(`otto debug breakpoints: unknown sub-action '${op}'.\\n`)\n return 1\n }\n }\n case 'points':\n return await listPoints(client, stdout)\n case 'state':\n return await showState(client, stdout)\n default:\n stderr(`otto debug: unknown action '${action}'. Use breakpoints | points | state.\\n`)\n return 1\n }\n } catch (error) {\n return reportServiceError('debug', error, stderr)\n }\n}\n\nasync function listBreakpoints(client: ServiceClient, stdout: (s: string) => void): Promise<number> {\n const { breakpoints } = await client.get<{ breakpoints: BreakpointEntry[] }>(\n '/api/debugger/breakpoints',\n )\n if (breakpoints.length === 0) {\n stdout('No breakpoints set.\\n')\n return 0\n }\n stdout(`${'point'.padEnd(36)} enabled\\n${'-'.repeat(46)}\\n`)\n for (const bp of breakpoints) {\n stdout(`${bp.point.padEnd(36)} ${bp.enabled ? '✓' : '✗'}\\n`)\n }\n return 0\n}\n\nfunction isValidPoint(point: string | undefined): point is string {\n return !!point && POINT_NAMES.includes(point)\n}\n\nasync function setBreakpoint(\n client: ServiceClient,\n point: string | undefined,\n enabled: boolean,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n verb?: string,\n): Promise<number> {\n if (!isValidPoint(point)) {\n stderr(\n `otto debug: invalid or missing breakpoint point.\\nUse 'otto debug points' to list valid points.\\n`,\n )\n return 1\n }\n await client.put(`/api/debugger/breakpoints/${point}`, { enabled })\n stdout(`Breakpoint ${verb ?? `set: ${point} (${enabled ? 'enabled' : 'disabled'})`}${verb ? `: ${point}` : ''}\\n`)\n return 0\n}\n\nasync function listPoints(client: ServiceClient, stdout: (s: string) => void): Promise<number> {\n const { breakpoints } = await client.get<{ breakpoints: BreakpointEntry[] }>(\n '/api/debugger/breakpoints',\n )\n const byPoint = new Map(breakpoints.map((bp) => [bp.point, bp]))\n\n stdout('Available breakpoint points:\\n')\n for (const def of BREAKPOINT_POINTS) {\n const bp = byPoint.get(def.point)\n const status = bp?.set ? (bp.enabled ? 'enabled' : 'disabled') : 'not set'\n stdout(` ${def.point.padEnd(36)} ${status}\\n`)\n }\n return 0\n}\n\nasync function showState(client: ServiceClient, stdout: (s: string) => void): Promise<number> {\n const state = await client.get<{\n breakpoints: { total: number; enabled: number }\n paused: boolean\n pendingPauses: { pauseId: string; point: string; turn: number }[]\n }>('/api/debugger/state')\n\n stdout(`Debugger state\n breakpoints: ${state.breakpoints.enabled}/${state.breakpoints.total} enabled\n paused: ${state.paused ? 'yes' : 'no'}\n`)\n for (const p of state.pendingPauses) {\n stdout(` ⏸ ${p.point} (turn ${p.turn}) pauseId=${p.pauseId}\\n`)\n }\n return 0\n}\n","/**\n * `otto time-travel` 子命令 —— 回放+回滚面板。\n *\n * 设计依据:design/observability-platform.md §4/§5 + design/m17-platform-activation.md §3。\n * 数据源 = service REST(timetravel 路由)。默认连 `otto serve` 实例,`--remote <url>` 覆盖。\n */\nimport { SERVICE_URL } from '@x-otto/env'\n\nimport {\n createServiceClient,\n reportServiceError,\n resolveRemote,\n type ServiceClient,\n} from './service-client'\n\nexport interface TimeTravelDeps {\n stdout?: (s: string) => void\n stderr?: (s: string) => void\n /** fetch 注入(测试用)。 */\n fetch?: typeof globalThis.fetch\n}\n\ninterface CheckpointInfo {\n seq: number\n label?: string\n createdAt: number\n traceSeq?: number\n}\n\ninterface TracebackView {\n toSeq: number\n baselineCheckpointSeq: number | null\n fromTraceSeq: number\n events: unknown[]\n messages: { role: string; content: unknown }[]\n}\n\nexport async function runTimeTravelCommand(\n args: string[],\n deps: TimeTravelDeps = {},\n): Promise<number> {\n const stdout = deps.stdout ?? ((s) => process.stdout.write(s))\n const stderr = deps.stderr ?? ((s) => process.stderr.write(s))\n\n let baseUrl: string\n let rest: string[]\n try {\n ;({ baseUrl, rest } = resolveRemote(args))\n } catch (error) {\n return reportServiceError('time-travel', error, stderr)\n }\n\n const [action, sessionId, ...extra] = rest\n\n if (!action || action === 'help') {\n stdout(`otto time-travel — 回放+回滚面板\n\n用法:\n otto time-travel checkpoints <session-id> 列出所有 checkpoint\n otto time-travel checkpoint <session-id> [--label X] 创建新 checkpoint\n otto time-travel scrubber <session-id> <seq> 显示 traceback 视图\n otto time-travel restore <session-id> <seq> [--mode M] [--rollback]\n 恢复/回滚 (mode: conversation|code|both)\n otto time-travel fork <session-id> <seq> 从 checkpoint 分叉新会话\n otto time-travel replay <session-id> 启动确定性重放\n\n选项:\n --remote <url> service 地址(默认 OTTO_SERVICE_URL 或 ${SERVICE_URL})\n`)\n return 0\n }\n\n if (!sessionId) {\n stderr('otto time-travel: session-id is required.\\n')\n return 1\n }\n\n const client = createServiceClient(baseUrl, deps.fetch)\n\n try {\n switch (action) {\n case 'checkpoints':\n return await listCheckpoints(client, sessionId, stdout)\n case 'checkpoint':\n return await createCheckpoint(client, sessionId, extra, stdout)\n case 'scrubber':\n return await showScrubber(client, sessionId, extra[0], stdout, stderr)\n case 'restore':\n return await restoreCheckpoint(client, sessionId, extra, stdout, stderr)\n case 'fork':\n return await forkCheckpoint(client, sessionId, extra[0], stdout, stderr)\n case 'replay':\n return await startReplay(client, sessionId, stdout)\n default:\n stderr(`otto time-travel: unknown action '${action}'.\\n`)\n return 1\n }\n } catch (error) {\n return reportServiceError('time-travel', error, stderr)\n }\n}\n\nasync function listCheckpoints(\n client: ServiceClient,\n sessionId: string,\n stdout: (s: string) => void,\n): Promise<number> {\n const { checkpoints } = await client.get<{ checkpoints: CheckpointInfo[] }>(\n `/api/sessions/${sessionId}/checkpoints`,\n )\n if (checkpoints.length === 0) {\n stdout(`Session ${sessionId}: no checkpoints found.\\n`)\n return 0\n }\n\n stdout(`Checkpoints — ${sessionId} (${checkpoints.length})\\n`)\n stdout(`${'seq'.padStart(4)} ${'traceSeq'.padStart(9)} ${'label'.padEnd(24)} ${'created'}\\n`)\n stdout(`${'-'.repeat(70)}\\n`)\n for (const cp of checkpoints) {\n const label = (cp.label ?? '').padEnd(24)\n const created = new Date(cp.createdAt).toISOString()\n const traceSeq = cp.traceSeq !== undefined ? String(cp.traceSeq).padStart(9) : ' -'\n stdout(`${String(cp.seq).padStart(4)} ${traceSeq} ${label} ${created}\\n`)\n }\n return 0\n}\n\nasync function createCheckpoint(\n client: ServiceClient,\n sessionId: string,\n rest: string[],\n stdout: (s: string) => void,\n): Promise<number> {\n const labelIdx = rest.indexOf('--label')\n const label = labelIdx >= 0 ? rest[labelIdx + 1] : undefined\n\n const { checkpoint } = await client.post<{ checkpoint: CheckpointInfo }>(\n `/api/sessions/${sessionId}/checkpoints`,\n { label },\n )\n stdout(\n `Checkpoint created: seq=${checkpoint.seq}${checkpoint.label ? ` label=\"${checkpoint.label}\"` : ''} traceSeq=${checkpoint.traceSeq}\\n`,\n )\n return 0\n}\n\nasync function showScrubber(\n client: ServiceClient,\n sessionId: string,\n seqRaw: string | undefined,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n): Promise<number> {\n const seq = Number(seqRaw)\n if (!Number.isInteger(seq)) {\n stderr('otto time-travel scrubber: seq (integer) is required.\\n')\n return 1\n }\n\n const view = await client.get<TracebackView>(`/api/sessions/${sessionId}/traceback?to=${seq}`)\n\n stdout(`Traceback — ${sessionId} @ seq=${view.toSeq}\\n`)\n if (view.baselineCheckpointSeq !== null) {\n stdout(` Baseline: checkpoint seq=${view.baselineCheckpointSeq} (fromTraceSeq=${view.fromTraceSeq})\\n`)\n } else {\n stdout(` Baseline: none (folded from seq 0)\\n`)\n }\n stdout(` Events folded: ${view.events.length}\\n`)\n stdout(` Messages reconstructed: ${view.messages.length}\\n\\n`)\n\n for (const msg of view.messages) {\n const role = msg.role.padEnd(12)\n const content =\n typeof msg.content === 'string'\n ? msg.content.slice(0, 120)\n : JSON.stringify(msg.content).slice(0, 120)\n stdout(` ${role} ${content}\\n`)\n }\n return 0\n}\n\nasync function restoreCheckpoint(\n client: ServiceClient,\n sessionId: string,\n rest: string[],\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n): Promise<number> {\n const seq = Number(rest[0])\n if (!Number.isInteger(seq)) {\n stderr('otto time-travel restore: seq (integer) is required.\\n')\n return 1\n }\n\n const modeIdx = rest.indexOf('--mode')\n const modeRaw = modeIdx >= 0 ? rest[modeIdx + 1] : 'both'\n if (modeRaw !== 'conversation' && modeRaw !== 'code' && modeRaw !== 'both') {\n stderr(`otto time-travel restore: invalid mode '${modeRaw}'. Use conversation | code | both.\\n`)\n return 1\n }\n const rollback = rest.includes('--rollback')\n\n await client.post(`/api/sessions/${sessionId}/checkpoints/${seq}/restore`, {\n mode: modeRaw,\n truncate: rollback,\n })\n stdout(`Restored to checkpoint seq=${seq} mode=${modeRaw} rollback=${rollback}\\n`)\n return 0\n}\n\nasync function forkCheckpoint(\n client: ServiceClient,\n sessionId: string,\n seqRaw: string | undefined,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n): Promise<number> {\n const seq = Number(seqRaw)\n if (!Number.isInteger(seq)) {\n stderr('otto time-travel fork: seq (integer) is required.\\n')\n return 1\n }\n\n const result = await client.post<{ forkedSessionId: string }>(\n `/api/sessions/${sessionId}/checkpoints/${seq}/fork`,\n {},\n )\n stdout(`Forked session: ${result.forkedSessionId} (from ${sessionId} @ seq=${seq})\\n`)\n return 0\n}\n\nasync function startReplay(\n client: ServiceClient,\n sessionId: string,\n stdout: (s: string) => void,\n): Promise<number> {\n const result = await client.post<{ replaySessionId: string }>(\n `/api/sessions/${sessionId}/replay`,\n {},\n )\n stdout(`Replay session created: ${result.replaySessionId} (source: ${sessionId})\\n`)\n return 0\n}\n","/**\n * `otto observe` 子命令 —— 观测面板:trace 时间线 / 摘要 / span 树。\n *\n * 设计依据:design/observability-platform.md §4/§5 + design/m17-platform-activation.md §3。\n * 数据源 = service REST。\n * 默认连 `http://127.0.0.1:${SERVICE_PORT}`(`otto serve` 启动),`--remote <url>` 覆盖;\n * service 不可达 → 明确报错而非静默空数据。聚合算法在 @x-otto/agent,两端同源。\n */\nimport { SERVICE_URL } from '@x-otto/env'\n\nimport {\n createServiceClient,\n reportServiceError,\n resolveRemote,\n type ServiceClient,\n} from './service-client'\nimport { runDebugCommand } from './debug'\nimport { runTimeTravelCommand } from './time-travel'\nimport type { TraceSpan, TraceSummary } from '@x-otto/agent'\nimport type { TraceTimelineView } from '@x-otto/trace-view'\n\nexport interface ObserveDeps {\n /** 输出(便于测试注入)。 */\n stdout?: (s: string) => void\n stderr?: (s: string) => void\n /** fetch 注入(测试用)。 */\n fetch?: typeof globalThis.fetch\n}\n\nexport async function runObserveCommand(\n args: string[],\n deps: ObserveDeps = {},\n): Promise<number> {\n const stdout = deps.stdout ?? ((s) => process.stdout.write(s))\n const stderr = deps.stderr ?? ((s) => process.stderr.write(s))\n\n // RFC-369 M4:observe 吸收 debug/time-travel 为子命令。\n // `otto observe debug ...` → runDebugCommand\n // `otto observe replay ...` → runTimeTravelCommand\n const [subCommand, ...subRest] = args\n if (subCommand === 'debug') {\n return runDebugCommand(subRest, deps)\n }\n if (subCommand === 'replay') {\n return runTimeTravelCommand(subRest, deps)\n }\n\n // 提取 --json 标志(在 resolveRemote 之前,避免被当作 remote URL)\n const jsonIdx = args.indexOf('--json')\n const json = jsonIdx >= 0\n const cleanArgs = json ? args.filter((_, i) => i !== jsonIdx) : args\n\n let baseUrl: string\n let rest: string[]\n try {\n ;({ baseUrl, rest } = resolveRemote(cleanArgs))\n } catch (error) {\n return reportServiceError('observe', error, stderr)\n }\n\n const [action, sessionId] = rest\n\n if (!action || action === 'help') {\n stdout(`otto observe — 观测面板(trace 时间线 / 摘要 / span 树)\n\n用法:\n otto observe summary <session-id> trace 聚合统计\n otto observe timeline <session-id> 文本时间线\n otto observe spans <session-id> span 树视图\n otto observe debug <action> [...] 调试面板(breakpoints / points / state)\n otto observe replay <action> <id> 时间旅行(checkpoints / scrubber / restore / fork / replay)\n\n选项:\n --remote <url> service 地址(默认 OTTO_SERVICE_URL 或 ${SERVICE_URL})\n --json 输出结构化 JSON(需 service 运行)\n`)\n return 0\n }\n\n if (!sessionId) {\n stderr('otto observe: session-id is required.\\n')\n return 1\n }\n\n const client = createServiceClient(baseUrl, deps.fetch)\n\n try {\n // --json 模式:调用 /trace/timeline 输出结构化 JSON\n if (json) {\n return await observeJson(client, sessionId, stdout, stderr)\n }\n switch (action) {\n case 'summary':\n return await observeSummary(client, sessionId, stdout)\n case 'timeline':\n return await observeTimeline(client, sessionId, stdout)\n case 'spans':\n return await observeSpans(client, sessionId, stdout)\n default:\n stderr(`otto observe: unknown action '${action}'. Use summary | timeline | spans.\\n`)\n return 1\n }\n } catch (error) {\n return reportServiceError('observe', error, stderr)\n }\n}\n\nfunction fetchSpans(client: ServiceClient, sessionId: string): Promise<{ spans: TraceSpan[] }> {\n return client.get<{ spans: TraceSpan[] }>(`/api/sessions/${sessionId}/trace/spans`)\n}\n\nasync function observeSummary(\n client: ServiceClient,\n sessionId: string,\n stdout: (s: string) => void,\n): Promise<number> {\n const summary = await client.get<TraceSummary>(`/api/sessions/${sessionId}/trace/summary`)\n\n if (summary.totalEvents === 0) {\n stdout(`Session ${sessionId}: no trace events found.\\n`)\n return 0\n }\n\n const { totalEvents, kinds, turns, toolCalls, firstTs, lastTs } = summary\n const duration = firstTs !== undefined && lastTs !== undefined ? `${lastTs - firstTs}ms` : 'N/A'\n\n stdout(`Trace Summary — ${sessionId}\n──────────────────────────────────────────\n Total events: ${totalEvents}\n Turns: ${turns}\n Tool calls: ${toolCalls}\n Time span: ${duration}\n First event: ${firstTs ? new Date(firstTs).toISOString() : 'N/A'}\n Last event: ${lastTs ? new Date(lastTs).toISOString() : 'N/A'}\n\n By kind:\n${Object.entries(kinds).map(([k, v]) => ` ${k.padEnd(14)} ${v}`).join('\\n')}\n`)\n return 0\n}\n\nasync function observeTimeline(\n client: ServiceClient,\n sessionId: string,\n stdout: (s: string) => void,\n): Promise<number> {\n const { spans } = await fetchSpans(client, sessionId)\n if (spans.length === 0) {\n stdout(`Session ${sessionId}: no trace events found.\\n`)\n return 0\n }\n\n const lines = spans.map((span) => {\n const ts = new Date(span.ts).toISOString().slice(11, 23)\n const turn = span.turn !== undefined ? `T${String(span.turn).padStart(2)}` : ' -'\n const node = (span.node ?? '').padEnd(26)\n return `${String(span.seq).padStart(5)} ${ts} ${turn} ${span.kind.padEnd(12)} ${node}${span.digest ?? ''}`\n })\n\n stdout(`Trace Timeline — ${sessionId} (${lines.length} events)\n${'seq'.padStart(5)} ${'time'} ${'turn'} ${'kind'.padEnd(12)} ${'node'.padEnd(26)} digest\n${'-'.repeat(100)}\n${lines.join('\\n')}\n`)\n return 0\n}\n\nasync function observeSpans(\n client: ServiceClient,\n sessionId: string,\n stdout: (s: string) => void,\n): Promise<number> {\n const { spans } = await fetchSpans(client, sessionId)\n if (spans.length === 0) {\n stdout(`Session ${sessionId}: no trace events found.\\n`)\n return 0\n }\n\n const turnSpans = new Map<number, TraceSpan[]>()\n let maxTurn = -1\n for (const span of spans) {\n const turn = span.turn ?? 0\n if (turn > maxTurn) maxTurn = turn\n const group = turnSpans.get(turn) ?? []\n group.push(span)\n turnSpans.set(turn, group)\n }\n\n stdout(`Trace Spans — ${sessionId} (${maxTurn + 1} turns)\n`)\n for (let t = 0; t <= maxTurn; t++) {\n const group = turnSpans.get(t)\n if (!group) continue\n stdout(`▼ Turn ${t} (${group.length} events)\\n`)\n for (const span of group) {\n const node = span.node ? `[${span.node}] ` : ''\n stdout(` ├─ ${span.kind.padEnd(12)} ${node}${span.digest ?? ''}\\n`)\n }\n }\n return 0\n}\n\nasync function observeJson(\n client: ServiceClient,\n sessionId: string,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n): Promise<number> {\n try {\n const timeline = await client.get<TraceTimelineView>(\n `/api/sessions/${sessionId}/trace/timeline`,\n )\n stdout(`${JSON.stringify(timeline, null, 2)}\\n`)\n return 0\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n stderr(`observe --json: ${msg}\\n`)\n return 1\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{ot as e}from"./command-dispatcher-Du_Sk33M.js";import{t}from"./version-D1Hmbcos.js";import{SERVICE_PORT as n}from"@x-otto/env";import{resolve as r}from"node:path";const i=[{name:`run`,helpLines:[` otto run <prompt> 执行一次性任务`]},{name:`serve`,helpLines:[` otto serve [--port N] [--inspect] [--static <dir>] [--token <t>] 启动 HTTP/WS service(--static 同源托管前端 SPA,--token 启用鉴权)`],run:async e=>{let{runServeCommand:t}=await import(`./serve-a4W5vFXL.js`);return t(e.args,{projectDir:e.options.projectDir,configPath:e.options.configPath,modelId:e.options.model,verbose:e.options.verbose})}},{name:`doctor`,helpLines:[` otto doctor [--summary|--json] 环境健康检查(环境/配置/存储状态/连通性)`],run:async e=>{let{runDoctorCommand:t}=await import(`./doctor-CPIS47uZ.js`);return t(e.args,{stdout:e.stdout,stderr:e.stderr})}},{name:`auth`,helpLines:[` otto auth login <provider> 登录(cursor / anthropic / openai-codex / github-copilot)`,` otto auth status 查看各 provider 凭据来源`,` otto auth logout <provider> 注销并清除凭据`],run:async e=>{let{runAuthCommand:t}=await import(`./auth-command-WddVZWgz.js`);return t(e.args,{bootstrap:{workspaceDir:e.options.projectDir,projectConfigPath:e.options.configPath,verbose:e.options.verbose},stdout:e.stdout,stderr:e.stderr})}},{name:`config`,helpLines:[` otto config export [--project] [-o <path>] 导出配置(RFC-219)`,` otto config import <file> [--dry-run] [--scope global|project] [--no-plugins] [--plugins-only] [--yes] 导入配置`,` otto config restore [--list] 撤销最近一次导入`],run:async e=>{let{runConfigMigrationCommand:t}=await import(`./config-migration-DatflIxs.js`);return t(e.args,{stdout:e.stdout,stderr:e.stderr})}},{name:`migrate`,helpLines:[` otto migrate export [--no-sessions] [-o <path>] 导出配置+插件清单+会话数据为迁移包(换电脑用)`,` otto migrate import <archive> [--no-sessions] 导入迁移包(目标已有会话数据时跳过会话部分)`],run:async e=>{let{runMigrateCommand:t}=await import(`./migrate-BBi3Txu2.js`);return t(e.args,{stdout:e.stdout,stderr:e.stderr})}},{name:`observe`,helpLines:[` otto observe <action> <id> 观测面板(summary / timeline / spans)`,` otto observe debug <action> [...] 调试面板(breakpoints / points / state)`,` otto observe replay <action> <id> 时间旅行(checkpoints / scrubber / restore / fork / replay)`],run:async e=>{let{runObserveCommand:t}=await import(`./observe-BqLCpmFD.js`);return t(e.args)}},{name:`debug`,helpLines:[` otto debug <action> [...] (alias: otto observe debug)`],run:async e=>{let{runObserveCommand:t}=await import(`./observe-BqLCpmFD.js`);return t([`debug`,...e.args])}},{name:`time-travel`,helpLines:[` otto time-travel <action> <id> (alias: otto observe replay)`],run:async e=>{let{runObserveCommand:t}=await import(`./observe-BqLCpmFD.js`);return t([`replay`,...e.args])}},{name:`mcp`,helpLines:[` otto mcp login <name> MCP server OAuth 登录`,` otto mcp logout <name> 登出 MCP server`,` otto mcp list 列出已认证的 MCP server`],run:async e=>{let{runMcpCommand:t}=await import(`./mcp-DjReRjuu.js`);return t(e.args)}},{name:`extension`,helpLines:[` otto extension create <name> 生成插件骨架(命令 + 面板 + manifest,开箱即用)`,` otto extension install <name> [--yes] 安装首方/自定义插件(anthropic-provider / deepseek-provider / browser-use / …)`,` otto extension update [name] 检查并重装已过期的已安装 skill/plugin`],run:async e=>{let{runExtensionCommand:t}=await import(`./extension-CoJqqktf.js`);return t(e.args,{stdout:e.stdout,stderr:e.stderr})}},{name:`persistence`,helpLines:[` otto persistence local [...flags] 启动独立持久化服务(storage + events + sync)`,` otto persistence remote [...flags] 启动最小化远程持久化服务(5 REST 端点)`],run:async e=>{let{runPersistenceCommand:t}=await import(`./persistence-BvLRnpfi.js`);return t(e.args)}},{name:`persistenced`,helpLines:[` otto persistenced [...flags] (alias: otto persistence local)`],run:async e=>{let{runPersistenceCommand:t}=await import(`./persistence-BvLRnpfi.js`);return t([`local`,...e.args])}},{name:`remote-persistence-server`,helpLines:[` otto remote-persistence-server [...flags] (alias: otto persistence remote)`],run:async e=>{let{runPersistenceCommand:t}=await import(`./persistence-BvLRnpfi.js`);return t([`remote`,...e.args])}},{name:`ps`,helpLines:[` otto ps [--sweep] [--include-weak] [--json] 进程群列表与孤儿清扫(SIGTERM only,RFC-196)`],run:async e=>{let{runPsCommand:t}=await import(`./ps-BVKv6rAo.js`);return t(e.args,{stdout:e.stdout,stderr:e.stderr})}},{name:`export-trajectory`,helpLines:[` otto export-trajectory --session <id> --out <path> [--since-seq <n>] 导出会话轨迹 JSONL(RFC-385;默认关)`],run:async e=>{let{runExportTrajectoryCommand:t}=await import(`./export-trajectory-D-BcH0Wt.js`);return t(e.args,{stdout:e.stdout,stderr:e.stderr})}},{name:`daemon`,run:async e=>{let{runDaemonCommand:t}=await import(`./daemon-COESl-zU.js`);return t(e.args,{projectDir:e.options.projectDir,stdout:e.stdout,stderr:e.stderr})}},{name:`mcp-server`,run:async e=>{let{runMcpServerCommandFromCli:t}=await import(`./mcp-server-Dq72PTeW.js`);return t(e.args,{projectDir:e.options.projectDir,modelId:e.options.model})}},{name:`feedback`,run:async e=>{let{runFeedbackCommand:t}=await import(`./feedback-BivcU-y_.js`);return t(e.args,{stdout:e.stdout,stderr:e.stderr,projectDir:e.options.projectDir,configPath:e.options.configPath})}}],a=new Set(i.map(e=>e.name)),o=new Map(i.filter(e=>e.run).map(e=>[e.name,e.run]));function s(e,t){let n=o.get(e);return n?n(t):void 0}function c(){return i.flatMap(e=>e.helpLines??[])}var l=e({SUB_COMMAND_NAMES:()=>a,getHelpLines:()=>m,parseCLI:()=>p});function u(e){return e.startsWith(`--`)||/^-[^\s-]/.test(e)}const d=new Map([{flags:[`--print`,`-p`],type:`mode`,value:`print`},{flags:[`--interactive`,`-i`],type:`mode`,value:`interactive`},{flags:[`--json`],type:`mode`,value:`json`},{flags:[`--model`,`-m`],type:`string`,key:`model`},{flags:[`--config`,`-c`],type:`string`,key:`configPath`},{flags:[`--project`,`-d`],type:`path`,key:`projectDir`},{flags:[`--quiet`,`-q`],type:`boolean`,key:`quiet`},{flags:[`--verbose`],type:`boolean`,key:`verbose`},{flags:[`--max-tool-turns`],type:`number`,key:`maxToolTurns`},{flags:[`--max-tool-turn-extensions`],type:`number`,key:`maxToolTurnExtensions`},{flags:[`--prompt-token-budget`],type:`number`,key:`promptTokenBudget`},{flags:[`--prompt-time-budget`],type:`number`,key:`promptTimeBudgetMin`},{flags:[`--session-cost-budget`],type:`number`,key:`sessionCostBudget`},{flags:[`--no-stall-detection`],type:`boolean`,key:`noStallDetection`},{flags:[`--stall-window-turns`],type:`number`,key:`stallWindowTurns`},{flags:[`--stall-repeat-threshold`],type:`number`,key:`stallRepeatThreshold`},{flags:[`--continue`],type:`string`,key:`continueSession`},{flags:[`--attach`],type:`string`,key:`attachSession`},{flags:[`--force-write-lease`],type:`boolean`,key:`forceWriteLease`},{flags:[`--memory-restart-grace`],type:`boolean`,key:`memoryRestartGrace`},{flags:[`--resume-prompt`],type:`string`,key:`resumePrompt`},{flags:[`--session-url`],type:`string`,key:`sessionUrl`},{flags:[`--session-token`],type:`string`,key:`sessionToken`},{flags:[`--id`],type:`string`,key:`workspaceId`},{flags:[`--shadow`],type:`boolean`,key:`shadow`},{flags:[`--help`,`-h`],type:`boolean`,key:`showHelp`},{flags:[`--version`,`-v`],type:`action`,handler:h}].flatMap(e=>e.flags.map(t=>[t,e])));var f=class{i;constructor(e){this.args=e,this.i=0}current(){return this.args[this.i]}next(){return this.args[++this.i]}advance(){this.i++}skip(e){this.i+=e}pos(){return this.i}};function p(e){let t=e.slice(2),n=new f(t),i={mode:`interactive`,projectDir:process.cwd(),verbose:!1,quiet:!1},o=n.current(),s,c=[],l,p;o&&a.has(o)?(s=o,c=t.slice(1),n.skip(1),s===`run`&&(i.prompt=t.slice(1).filter(e=>!u(e)).join(` `),i.mode=`print`)):o&&!u(o)&&(l=o,p=t.slice(1));let m=i.prompt?[i.prompt]:[];for(;n.current()!==void 0;){let e=n.current();if(e===`--inspect`){i.inspect=!0,n.advance();continue}if(e?.startsWith(`--inspect=`)){let t=Number(e.slice(10));i.inspect=Number.isInteger(t)&&t>0?t:!0,n.advance();continue}if(e?.startsWith(`--restart-generation=`)){let t=Number(e.slice(21));Number.isInteger(t)&&t>0&&(i.restartGeneration=t),n.advance();continue}let t=d.get(e);if(t){switch(n.advance(),t.type){case`boolean`:i[t.key]=!0;break;case`mode`:i.mode=t.value;break;case`string`:i[t.key]=n.current(),n.advance();break;case`number`:{let e=n.current();if(e===void 0||e.startsWith(`-`))break;let r=Number(e),a=t.key===`maxToolTurnExtensions`;Number.isFinite(r)&&(a?r>=0:r>0)&&(i[t.key]=r),n.advance();continue}case`path`:i[t.key]=r(n.current()??process.cwd()),n.advance();break;case`action`:t.handler();break}continue}e&&!u(e)&&!s&&(i.prompt===void 0&&i.mode===`interactive`&&(i.mode=`print`),m.push(e)),n.advance()}return m.length>0&&(i.prompt=m.join(` `)),{options:i,subCommand:s,subCommandArgs:c,unresolvedCommandName:l,unresolvedCommandArgs:p}}function m(){return[``,`otto - AI 助理命令行工具`,``,`用法:`,` otto [提示词] 使用提示词启动(print 模式)`,` otto 进入交互式 TUI 模式`,` otto --json "query" JSON 输出模式`,``,`选项:`,` -i, --interactive Interactive 模式(默认)`,` -p, --print Print 模式(非交互)`,` --json JSON 输出模式`,` -m, --model <id> 指定模型`,` -c, --config <path> 配置文件路径`,` -d, --project <dir> 项目目录`,` --verbose 输出详细日志`,` -q, --quiet 只输出 error 日志`,` --shadow 影子模式:otto 自有状态(凭据/会话/记忆/教训/配置/trace/日志)本次会话零磁盘写入(不保证 provider 服务端日志与 shell/OS 层痕迹;RFC-345)`,` --max-tool-turns <n> 单个用户回合最大工具回合数(默认 120)`,` --max-tool-turn-extensions <n> 触顶后压缩续跑的扩展次数(默认主会话 3 / 子 agent 0)`,` --prompt-token-budget <n> 单回合输出 token 预算(配置后轮次上限 ×4 退位安全阀;RFC-104)`,` --prompt-time-budget <min> 单回合墙钟时间预算(分钟)`,` --session-cost-budget <usd> 会话累计成本软预算(美元,超过一次性提醒不阻断;RFC-204)`,` --no-stall-detection 关闭早期停滞检测(默认开启;RFC-203)`,` --stall-window-turns <n> 停滞检测滑动窗口大小(需与 --stall-repeat-threshold 同时提供,默认 8)`,` --stall-repeat-threshold <n> 停滞检测同签名重复阈值(需与 --stall-window-turns 同时提供,默认 4)`,` --continue <id> 继续已有会话`," --attach <id> attach 到 `otto serve` 上运行的会话(只读预览 + Chat.query 写)",` --force-write-lease attach 时强抢写权(kill -9 造成 stuck lease 的正式解锁口;服务端 audit 记录)`,` --id <workspace> 显式 workspace id(一目录多 workspace DevPod 模式)`,` --session-url <url> 远端会话持久化地址(如 http://host:3001/sessions)`,` --session-token <token> 远端会话持久化 Bearer token`,` --inspect[=port] 启用 Node.js inspector(默认: 9229)`,` -h, --help 显示帮助`,` --version, -v 显示版本`,``,`命令:`,...c(),``,`面板命令默认连 \`otto serve\` 实例(http://127.0.0.1:${n},OTTO_SERVICE_URL/--remote 可覆盖)。`]}function h(){process.stdout.write(`otto ${t()}\n`),process.exit(0)}export{s as i,l as n,a as r,p as t};
|
|
3
|
+
//# sourceMappingURL=parse-cli-Cp5a7taK.js.map
|