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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +65 -65
  2. package/bin/otto.js +6 -0
  3. package/dist/{auth-command-CXAB3_hs.js → auth-command-D1T16ed-.js} +3 -3
  4. package/dist/auth-command-D1T16ed-.js.map +1 -0
  5. package/dist/build-id-D7Wf4tQw.js +3 -0
  6. package/dist/build-id-D7Wf4tQw.js.map +1 -0
  7. package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/meta.json +1 -1
  8. package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/plugin.cjs +2085 -18164
  9. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/anthropic-messages.ts +2 -2
  10. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-completions.ts +12 -5
  11. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-responses.ts +4 -1
  12. package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/meta.json +1 -1
  13. package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/plugin.cjs +78 -52294
  14. package/dist/bundled-extensions-C3n8jg20.js +3 -0
  15. package/dist/bundled-extensions-C3n8jg20.js.map +1 -0
  16. package/dist/command-dispatcher-ZyrfHgIk.js +21 -0
  17. package/dist/command-dispatcher-ZyrfHgIk.js.map +1 -0
  18. package/dist/create-host-app-BXQ8bU-B.js +3 -0
  19. package/dist/create-host-app-BXQ8bU-B.js.map +1 -0
  20. package/dist/create-host-app-DXII2WTM.js +2 -0
  21. package/dist/discover-with-bundled-B5d4q___.js +2 -0
  22. package/dist/discover-with-bundled-CF8qFP41.js +3 -0
  23. package/dist/discover-with-bundled-CF8qFP41.js.map +1 -0
  24. package/dist/doctor-CuNvDmyA.js +36 -0
  25. package/dist/doctor-CuNvDmyA.js.map +1 -0
  26. package/dist/{extension-BKXIr86F.js → extension-DLFCUBJr.js} +41 -33
  27. package/dist/extension-DLFCUBJr.js.map +1 -0
  28. package/dist/extension-I87MkZYp.js +2 -0
  29. package/dist/feedback-B-d-GV2J.js +2 -0
  30. package/dist/feedback-DuTIx3-K.js +21 -0
  31. package/dist/feedback-DuTIx3-K.js.map +1 -0
  32. package/dist/{feishu-setup-D6vnuNLw.js → feishu-setup-B6ETf-8Y.js} +2 -2
  33. package/dist/{feishu-setup-D6vnuNLw.js.map → feishu-setup-B6ETf-8Y.js.map} +1 -1
  34. package/dist/ftue-doctor-recovery-DeAf8jVo.js +3 -0
  35. package/dist/ftue-doctor-recovery-DeAf8jVo.js.map +1 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +10 -12
  38. package/dist/index.js.map +1 -1
  39. package/dist/observe-CZ96Vre9.js +83 -0
  40. package/dist/observe-CZ96Vre9.js.map +1 -0
  41. package/dist/parse-cli-CqocMnXY.js +3 -0
  42. package/dist/parse-cli-CqocMnXY.js.map +1 -0
  43. package/dist/persistence-CTXa-OyC.js +85 -0
  44. package/dist/persistence-CTXa-OyC.js.map +1 -0
  45. package/dist/plugin-assembly-check-C2TephMb.js +3 -0
  46. package/dist/plugin-assembly-check-C2TephMb.js.map +1 -0
  47. package/dist/registry-BOX7DaxS.js +2 -0
  48. package/dist/{registry-D0NONjLv.js → registry-BdCcwyGy.js} +2 -2
  49. package/dist/registry-BdCcwyGy.js.map +1 -0
  50. package/dist/{run-attach-Ci5jYyCa.js → run-attach-CvGCWF_i.js} +2 -2
  51. package/dist/{run-attach-Ci5jYyCa.js.map → run-attach-CvGCWF_i.js.map} +1 -1
  52. package/dist/{serve-pVR6UIT9.js → serve-Dbn3THez.js} +3 -3
  53. package/dist/serve-Dbn3THez.js.map +1 -0
  54. package/dist/{service-client-BLNeugM9.js → service-client-Cr4sxDVM.js} +1 -1
  55. package/dist/{service-client-BLNeugM9.js.map → service-client-Cr4sxDVM.js.map} +1 -1
  56. package/dist/{version-C0qM-qJM.js → version-Cq2kcrXL.js} +2 -2
  57. package/dist/{version-C0qM-qJM.js.map → version-Cq2kcrXL.js.map} +1 -1
  58. package/package.json +5 -5
  59. package/dist/auth-command-CXAB3_hs.js.map +0 -1
  60. package/dist/build-id-BX4QHzM0.js +0 -3
  61. package/dist/build-id-BX4QHzM0.js.map +0 -1
  62. package/dist/command-dispatcher-Vb_AJSAR.js +0 -13
  63. package/dist/command-dispatcher-Vb_AJSAR.js.map +0 -1
  64. package/dist/debug-CiosFwme.js +0 -34
  65. package/dist/debug-CiosFwme.js.map +0 -1
  66. package/dist/discover-with-bundled-CznLjkkw.js +0 -3
  67. package/dist/discover-with-bundled-CznLjkkw.js.map +0 -1
  68. package/dist/doctor-C84-hhEr.js +0 -30
  69. package/dist/doctor-C84-hhEr.js.map +0 -1
  70. package/dist/extension-BKXIr86F.js.map +0 -1
  71. package/dist/extension-CnON6Xum.js +0 -2
  72. package/dist/extension-plugin-Bmqlc6as.js +0 -2
  73. package/dist/extension-plugin-C2Yhv77x.js +0 -7
  74. package/dist/extension-plugin-C2Yhv77x.js.map +0 -1
  75. package/dist/feedback-BES_6ebi.js +0 -21
  76. package/dist/feedback-BES_6ebi.js.map +0 -1
  77. package/dist/feedback-BRbLUn9u.js +0 -2
  78. package/dist/model-auth-DUPFPOjY.js +0 -2
  79. package/dist/model-auth-qY5zeBF7.js +0 -3
  80. package/dist/model-auth-qY5zeBF7.js.map +0 -1
  81. package/dist/observe-CVg-MRM2.js +0 -32
  82. package/dist/observe-CVg-MRM2.js.map +0 -1
  83. package/dist/parse-cli-BCfFgFAC.js +0 -3
  84. package/dist/parse-cli-BCfFgFAC.js.map +0 -1
  85. package/dist/persistenced-CCwHu1Ww.js +0 -36
  86. package/dist/persistenced-CCwHu1Ww.js.map +0 -1
  87. package/dist/registry-B3nfN62q.js +0 -2
  88. package/dist/registry-D0NONjLv.js.map +0 -1
  89. package/dist/remote-persistence-server-B_hONoVR.js +0 -35
  90. package/dist/remote-persistence-server-B_hONoVR.js.map +0 -1
  91. package/dist/serve-pVR6UIT9.js.map +0 -1
  92. package/dist/time-travel-ZozuPPI-.js +0 -21
  93. package/dist/time-travel-ZozuPPI-.js.map +0 -1
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import{n as e,t}from"./build-id-D7Wf4tQw.js";import{t as n}from"./create-host-app-BXQ8bU-B.js";import{platform as r,release as i}from"node:os";import{execFile as a}from"node:child_process";import{openBrowser as o}from"@x-otto/shared";import{SDK_VERSION as s}from"@x-otto/env";import{promisify as c}from"node:util";const l=c(a);function u(e){let t=e.trim().replace(/\.git$/,``),n=t.match(/^git@[^:]+:(.+)$/);if(n)return n[1];let r=t.match(/^https?:\/\/[^/]+\/(.+)$/);if(r)return r[1]}async function d(e){try{let{stdout:t}=await l(`git`,[`remote`,`get-url`,`origin`],{cwd:e,timeout:5e3});return u(t)}catch{return}}async function f(e){return e.configuredRepo?e.configuredRepo:await d(e.cwd)||e.cliRepoArg}function p(n){return[`- otto version: ${s} (build ${e}, ${t}@dev)`,`- Node: ${process.version}`,`- OS: ${r()} ${process.arch} ${i()}`,`- Model: ${n??`(none configured)`}`].join(`
3
+ `)}function m(e){let t,n,r,i,a,o=!1,s=(t,n)=>{let r=e[t+1];if(r===void 0||r.startsWith(`--`))throw Error(`${n} requires a value`);return r};for(let c=0;c<e.length;c++){let l=e[c];l===`--title`?(t=s(c,`--title`),c++):l===`--reproduce`?(n=s(c,`--reproduce`),c++):l===`--expected`?(r=s(c,`--expected`),c++):l===`--actual`?(i=s(c,`--actual`),c++):l===`--repo`?(a=s(c,`--repo`),c++):l===`--browser`&&(o=!0)}return{title:t,reproduce:n,expected:r,actual:i,repo:a,browser:o}}function h(e){e(`Usage: otto feedback --title <title> [options]
4
+
5
+ Report an issue or send feedback via GitHub (non-interactive — for scripting/CI).
6
+ For an interactive prompt-driven flow, use the "/feedback" command inside otto.
7
+
8
+ Options:
9
+ --title <title> Short issue title (required)
10
+ --reproduce <text> Steps to reproduce (optional)
11
+ --expected <text> Expected behavior (optional)
12
+ --actual <text> Actual behavior (optional)
13
+ --repo <owner/repo> Target repository (defaults to feedback_repo config, then git remote origin)
14
+ --browser Skip the gh CLI channel and always use the browser fallback
15
+ --help Show this help
16
+ `)}async function g(e,t={}){let r=t.stdout??(e=>process.stdout.write(e)),i=t.stderr??(e=>process.stderr.write(e));if(e[0]===`help`||e[0]===`--help`)return h(r),0;let a;try{a=m(e)}catch(e){return i(`otto feedback: ${e instanceof Error?e.message:String(e)}\n`),1}if(!a.title)return i(`otto feedback: --title is required (see --help)
17
+ `),1;let s=t.app??n({logger:{name:`otto-feedback`,level:`error`,destination:`stderr`},workspaceDir:t.projectDir,projectConfigPath:t.configPath});try{await s.start()}catch(e){return i(`otto feedback: failed to start app: ${e instanceof Error?e.message:String(e)}\n`),1}try{let e=s.feedbackRegistry.get();if(!e)return i(`otto feedback: feedback plugin is not available (disabled or not installed)
18
+ `),1;let n=await f({configuredRepo:s.settings.get(`feedback_repo`),cliRepoArg:a.repo,cwd:t.projectDir??process.cwd()});if(!n)return i(`otto feedback: could not resolve a target repository — pass --repo or set "feedback_repo" in .otto/config.json
19
+ `),1;let c=p(void 0),l={title:a.title,reproduce:a.reproduce,expected:a.expected,actual:a.actual,envInfo:c,repo:n};if(!a.browser&&e.submitIssueViaGh){let t=await e.submitIssueViaGh(l);if(t.ok)return r(`Issue created: ${t.url}\n`),0;i(`otto feedback: gh channel failed (${t.kind}) — falling back to browser\n`)}let u=e.buildIssueUrl?.(l);return u?(o(u),r(`Opened: ${u}\n`),0):(i(`otto feedback: feedback plugin does not support buildIssueUrl
20
+ `),1)}finally{await s.stop()}}export{f as n,g as r,p as t};
21
+ //# sourceMappingURL=feedback-DuTIx3-K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback-DuTIx3-K.js","names":["BUILD_ID"],"sources":["../src/commands/feedback.ts"],"sourcesContent":["/**\n * /feedback 命令的 TUI 接线层(提交/展示)。\n */\nimport { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { platform, release } from 'node:os'\nimport { SDK_VERSION } from '@x-otto/env'\nimport { BUILD_ID, BUILD_BRANCH, BUILD_HASH } from '../build-id'\nimport { createHostApp } from '../create-host-app'\nimport type { App } from '@x-otto/coding'\nimport { openBrowser } from '@x-otto/shared'\n\nconst execFileAsync = promisify(execFile)\n\n/** git remote origin URL → `owner/repo`(支持 `git@github.com:owner/repo.git` 与 `https://github.com/owner/repo.git` 两种形态)。 */\nfunction parseOwnerRepoFromGitUrl(url: string): string | undefined {\n const trimmed = url.trim().replace(/\\.git$/, '')\n const sshMatch = trimmed.match(/^git@[^:]+:(.+)$/)\n if (sshMatch) return sshMatch[1]\n const httpsMatch = trimmed.match(/^https?:\\/\\/[^/]+\\/(.+)$/)\n if (httpsMatch) return httpsMatch[1]\n return undefined\n}\n\n/** `git remote get-url origin` 解析当前工作目录的 owner/repo;非 git 仓库/无 origin 时返回 undefined(fail-soft,不抛出)。 */\nasync function resolveGitRemoteRepo(cwd: string): Promise<string | undefined> {\n try {\n const { stdout } = await execFileAsync('git', ['remote', 'get-url', 'origin'], { cwd, timeout: 5000 })\n return parseOwnerRepoFromGitUrl(stdout)\n } catch {\n return undefined\n }\n}\n\n/**\n * RFC-207 §3.4:目标仓库解析优先级——`feedback_repo` 配置 > git remote 解析 >\n * `--repo` 命令行参数。全部缺失时返回 `undefined`。\n */\nexport async function resolveFeedbackRepo(opts: {\n configuredRepo?: string\n cliRepoArg?: string\n cwd: string\n}): Promise<string | undefined> {\n if (opts.configuredRepo) return opts.configuredRepo\n const fromGit = await resolveGitRemoteRepo(opts.cwd)\n if (fromGit) return fromGit\n return opts.cliRepoArg\n}\n\n/**\n * RFC-207 §3.1 环境信息行(issue body 的\"环境信息\"小节,脱敏——只含版本/运行时/OS/\n * 模型,不含用户路径/环境变量等敏感信息)。\n */\nexport function buildFeedbackEnvInfo(modelId: string | undefined): string {\n return [\n `- otto version: ${SDK_VERSION} (build ${BUILD_ID}, ${BUILD_HASH}@${BUILD_BRANCH})`,\n `- Node: ${process.version}`,\n `- OS: ${platform()} ${process.arch} ${release()}`,\n `- Model: ${modelId ?? '(none configured)'}`,\n ].join('\\n')\n}\n\nexport interface FeedbackCommandDeps {\n stdout?: (s: string) => void\n stderr?: (s: string) => void\n projectDir?: string\n configPath?: string\n /** 测试注入:替代生产装配的 app(真实 App 类型——需要 feedbackRegistry/submitIssueViaGh,\n * 非 ServiceHostPort/AppContext 窄接口能覆盖,同 serve.ts 的 AppContext 注入模式不同)。 */\n app?: App\n}\n\nfunction parseFeedbackArgs(args: string[]): {\n title?: string\n reproduce?: string\n expected?: string\n actual?: string\n repo?: string\n browser: boolean\n} {\n let title: string | undefined\n let reproduce: string | undefined\n let expected: string | undefined\n let actual: string | undefined\n let repo: string | undefined\n let browser = false\n\n const takeValue = (i: number, name: string): string => {\n const raw = args[i + 1]\n if (raw === undefined || raw.startsWith('--')) {\n throw new Error(`${name} requires a value`)\n }\n return raw\n }\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]\n if (arg === '--title') { title = takeValue(i, '--title'); i++ }\n else if (arg === '--reproduce') { reproduce = takeValue(i, '--reproduce'); i++ }\n else if (arg === '--expected') { expected = takeValue(i, '--expected'); i++ }\n else if (arg === '--actual') { actual = takeValue(i, '--actual'); i++ }\n else if (arg === '--repo') { repo = takeValue(i, '--repo'); i++ }\n else if (arg === '--browser') { browser = true }\n }\n\n return { title, reproduce, expected, actual, repo, browser }\n}\n\nfunction printFeedbackHelp(stdout: (s: string) => void): void {\n stdout(`Usage: otto feedback --title <title> [options]\n\nReport an issue or send feedback via GitHub (non-interactive — for scripting/CI).\nFor an interactive prompt-driven flow, use the \"/feedback\" command inside otto.\n\nOptions:\n --title <title> Short issue title (required)\n --reproduce <text> Steps to reproduce (optional)\n --expected <text> Expected behavior (optional)\n --actual <text> Actual behavior (optional)\n --repo <owner/repo> Target repository (defaults to feedback_repo config, then git remote origin)\n --browser Skip the gh CLI channel and always use the browser fallback\n --help Show this help\n`)\n}\n\n/**\n * `otto feedback` 非交互顶层子命令(RFC-207 §3.2/§3.4)——CI/脚本化调用场景,\n * `--title`/`--body` 系参数直接传参跳过 TUI openInput 链。复用与 `/feedback` 斜杠\n * 命令相同的核心逻辑(`resolveFeedbackRepo`/`buildFeedbackEnvInfo`)+ 同一个\n * `FeedbackRegistry`/插件实现(`app.feedbackRegistry`),两个入口不重复业务逻辑。\n */\nexport async function runFeedbackCommand(args: string[], deps: FeedbackCommandDeps = {}): Promise<number> {\n const stdout = deps.stdout ?? ((s) => process.stdout.write(s))\n const stderr = deps.stderr ?? ((s) => process.stderr.write(s))\n\n if (args[0] === 'help' || args[0] === '--help') {\n printFeedbackHelp(stdout)\n return 0\n }\n\n let parsed: ReturnType<typeof parseFeedbackArgs>\n try {\n parsed = parseFeedbackArgs(args)\n } catch (error) {\n stderr(`otto feedback: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n }\n\n if (!parsed.title) {\n stderr('otto feedback: --title is required (see --help)\\n')\n return 1\n }\n\n const app =\n deps.app ??\n createHostApp({\n logger: { name: 'otto-feedback', level: 'error', destination: 'stderr' },\n workspaceDir: deps.projectDir,\n projectConfigPath: deps.configPath,\n })\n\n try {\n await app.start()\n } catch (error) {\n stderr(`otto feedback: failed to start app: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n }\n\n try {\n const provider = app.feedbackRegistry.get()\n if (!provider) {\n stderr('otto feedback: feedback plugin is not available (disabled or not installed)\\n')\n return 1\n }\n\n const configuredRepo = app.settings.get('feedback_repo') as string | undefined\n const repo = await resolveFeedbackRepo({ configuredRepo, cliRepoArg: parsed.repo, cwd: deps.projectDir ?? process.cwd() })\n if (!repo) {\n stderr('otto feedback: could not resolve a target repository — pass --repo or set \"feedback_repo\" in .otto/config.json\\n')\n return 1\n }\n\n const envInfo = buildFeedbackEnvInfo(undefined)\n const input = {\n title: parsed.title,\n reproduce: parsed.reproduce,\n expected: parsed.expected,\n actual: parsed.actual,\n envInfo,\n repo,\n }\n\n if (!parsed.browser && provider.submitIssueViaGh) {\n const result = await provider.submitIssueViaGh(input)\n if (result.ok) {\n stdout(`Issue created: ${result.url}\\n`)\n return 0\n }\n stderr(`otto feedback: gh channel failed (${result.kind}) — falling back to browser\\n`)\n }\n\n const url = provider.buildIssueUrl?.(input)\n if (!url) {\n stderr('otto feedback: feedback plugin does not support buildIssueUrl\\n')\n return 1\n }\n openBrowser(url)\n stdout(`Opened: ${url}\\n`)\n return 0\n } finally {\n await app.stop()\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import{t as e}from"./discover-with-bundled-CznLjkkw.js";import{homedir as t}from"node:os";import{spawn as n}from"node:child_process";import{findWorkspaceRoot as r}from"@x-otto/coding";import{join as i}from"node:path";import{existsSync as a,mkdirSync as o,readFileSync as s,writeFileSync as c}from"node:fs";function l(e){let t=r(e);return t&&(i(t),i(e)),i(e,`.otto`,`feishu-use.json`)}function u(e,t){let n={app_id:`REPLACE_WITH_YOUR_FEISHU_APP_ID`,app_secret:`REPLACE_WITH_YOUR_FEISHU_APP_SECRET`,allowed_chats:[],workspace_dir:i(e,`..`,`..`,`feishu-use-workspace`),max_reply_chunk_length:4e3};for(let[e,r]of Object.entries(t))r!==void 0&&r!==``&&(n[e]=r);return o(i(e,`..`),{recursive:!0}),c(e,JSON.stringify(n,null,2)+`
2
+ import{t as e}from"./discover-with-bundled-CF8qFP41.js";import{homedir as t}from"node:os";import{spawn as n}from"node:child_process";import{findWorkspaceRoot as r}from"@x-otto/coding";import{join as i}from"node:path";import{existsSync as a,mkdirSync as o,readFileSync as s,writeFileSync as c}from"node:fs";function l(e){let t=r(e);return t&&(i(t),i(e)),i(e,`.otto`,`feishu-use.json`)}function u(e,t){let n={app_id:`REPLACE_WITH_YOUR_FEISHU_APP_ID`,app_secret:`REPLACE_WITH_YOUR_FEISHU_APP_SECRET`,allowed_chats:[],workspace_dir:i(e,`..`,`..`,`feishu-use-workspace`),max_reply_chunk_length:4e3};for(let[e,r]of Object.entries(t))r!==void 0&&r!==``&&(n[e]=r);return o(i(e,`..`),{recursive:!0}),c(e,JSON.stringify(n,null,2)+`
3
3
  `,{mode:384}),e}function d(e){try{return a(e)?JSON.parse(s(e,`utf8`)):void 0}catch{return}}function f(e,t){return new Promise(n=>{e.tui.modal.openModal({kind:`select`,...t,onSelect:e=>n(e),onCancel:()=>n(void 0)})})}function p(e,t,n){return new Promise(r=>{e.tui.modal.openModal({kind:`confirm`,title:t,message:n,onConfirm:()=>r(!0),onCancel:()=>r(!1)})})}function m(e,t=300*1e3,r){return new Promise(o=>{let s=i(e,`scripts`,`register-app.mjs`);if(!a(s)){o(void 0);return}let c=n(`node`,[s],{cwd:e,stdio:[`ignore`,`pipe`,`pipe`],timeout:t,env:{...process.env}}),l=``,u=``,d=!1;c.stdout.on(`data`,e=>{l+=e.toString()}),c.stderr.on(`data`,e=>{if(u+=e.toString(),d||!r)return;let t=u.split(`
4
4
  `).find(e=>e.startsWith(`QRCODE_URL::`));if(t)try{let e=JSON.parse(t.slice(12));e.url&&(d=!0,r(e))}catch{}}),c.on(`error`,()=>o(void 0)),c.on(`close`,e=>{if(e!==0)return o(void 0);try{let e=JSON.parse(l.trim().split(`
5
5
  `).pop());if(e.client_id&&e.client_secret){o(e);return}}catch{}o(void 0)})})}function h(e,t,r){return new Promise(o=>{let s=i(e,`scripts`,`verify-bot.mjs`);if(!a(s)){o(void 0);return}let c=n(`node`,[s],{cwd:e,stdio:[`ignore`,`pipe`,`pipe`],timeout:15e3,env:{...process.env,FEISHU_VERIFY_APP_ID:t,FEISHU_VERIFY_APP_SECRET:r}}),l=``;c.stdout.on(`data`,e=>{l+=e.toString()}),c.on(`error`,()=>o(void 0)),c.on(`close`,e=>{if(e!==0)return o(void 0);try{o(JSON.parse(l.trim()))}catch{o(void 0)}})})}function g(e){return e.length<=12?e.slice(0,4)+`***`:e.slice(0,8)+`***`+e.slice(-4)}async function _(e,t,n,r){if(r?.app_id&&!await p(e,`feishu-use 重配置`,[`当前配置:`,``,...[`App ID: ${g(String(r.app_id))}`,r.allowed_chats?`allowed_chats: ${r.allowed_chats.length} 个`:`allowed_chats: 空(拒绝所有)`,`配置路径: ${t}`],``,`确定要重新配置吗?当前凭据将被覆盖。`]))return;let a=await f(e,{title:`feishu-use 配置`,options:[{label:`扫码自动创建飞书应用(推荐)`,value:`qr`,detail:`飞书 App 扫码确认后自动获取凭据`},{label:`手动输入 App ID / App Secret`,value:`manual`,detail:`已有飞书自建应用,直接填入凭据`}]});if(!a)return;let o=``,s=``,c=[],l=[];if(a===`qr`){e.tui.info.showToast(`plugin-feishu-use`,`正在生成飞书应用注册二维码,请稍候…`);let t=await m(n,300*1e3,t=>{let n=Math.round(t.expireIn/60);e.tui.messages.pushMessage({role:`assistant`,content:[{type:`text`,text:`feishu-use:请在飞书 App 中打开以下链接扫码确认创建应用(${n} 分钟内有效):\n${t.url}`}]})});if(t)o=t.client_id,s=t.client_secret,e.tui.info.showToast(`plugin-feishu-use`,`扫码注册成功!凭据已自动填充。`);else if(!await p(e,`plugin-feishu-use`,`扫码注册失败(超时或网络错误)。是否改为手动输入凭据?`))return}if(!o){let t=r?.app_id?`(当前: ${g(String(r.app_id))},留空保留原值)`:``,n=await e.tui.modal.openModal({kind:`input`,title:`飞书 App ID`,placeholder:`cli_xxxxxxxxxxxx ${t}`,description:[]});if(n===null)return;o=n||String(r?.app_id??``);let i=await e.tui.modal.openModal({kind:`input`,title:`飞书 App Secret`,mask:!0,description:r?.app_secret?[`留空保留当前 Secret`]:[]});if(i===null)return;if(s=i||String(r?.app_secret??``),!o||!s){e.tui.info.showToast(`plugin-feishu-use`,`App ID 和 App Secret 不能为空,配置已取消。`);return}let a=await e.tui.modal.openModal({kind:`input`,title:`允许的 chat_id(逗号分隔,留空=拒绝所有)`,placeholder:r?`当前: ${r.allowed_chats?.join(`, `)??`空`}`:``,description:[]});if(a===null)return;a?c=a.split(`,`).map(e=>e.trim()).filter(Boolean):r?.allowed_chats&&(c=r.allowed_chats);let u=await e.tui.modal.openModal({kind:`input`,title:`可信 chat_id(逗号分隔,留空=仅只读)`,placeholder:r?`当前: ${r.trusted_chats?.join(`, `)??`空`}`:``,description:[]});if(u===null)return;u?l=u.split(`,`).map(e=>e.trim()).filter(Boolean):r?.trusted_chats&&(l=r.trusted_chats)}let d=r?.workspace_dir?String(r.workspace_dir):i(n,`..`,`..`,`feishu-use-workspace`);u(t,{app_id:o,app_secret:s,allowed_chats:c,trusted_chats:l,workspace_dir:d}),e.tui.info.showToast(`plugin-feishu-use`,`配置已写入 ${t}`);let _=await h(n,o,s);return _?e.tui.info.showToast(`plugin-feishu-use`,`Bot 身份验证成功:${_.name} (${_.openId})`):e.tui.messages.pushMessage({role:`command_echo`,command:`/feishu setup`,status:`error`,summary:`bot 身份验证失败(凭据不正确或网络不可达)`,detail:"请稍后用 `otto feishu config set` 修正凭据,或手动编辑配置文件。"}),{appId:o,botOpenId:_?.openId,botName:_?.name,configPath:t,success:!!_}}async function v(n){let r=n.app.workspaceDir??process.cwd(),i=l(r),a=e({cwd:r,homedir:t()}).find(e=>e.id===`plugin-feishu-use`);if(!a){n.tui.info.showToast(`plugin-feishu-use`,"feishu-use 插件尚未安装,请先在 `/model` 安装入口安装 feishu-use。");return}let o=a.dir;return _(n,i,o,d(i))}export{v as launchFeishuSetup};
6
- //# sourceMappingURL=feishu-setup-D6vnuNLw.js.map
6
+ //# sourceMappingURL=feishu-setup-B6ETf-8Y.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"feishu-setup-D6vnuNLw.js","names":[],"sources":["../src/commands/handlers/feishu-setup.ts"],"sourcesContent":["/**\n * /feishu-setup 命令:飞书 webhook 绑定交互向导。\n */\nimport { spawn } from 'node:child_process'\nimport { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { findWorkspaceRoot } from '@x-otto/coding'\nimport { discoverPluginsWithBundled } from '../../discover-with-bundled'\nimport type { CliContext } from '../../command-dispatcher'\n\n/** 按优先级计算 feishu-use 配置文件候选路径(project > repo > user),返回最高优先级路径。 */\nfunction resolveConfigPath(cwd: string): string {\n const repoRoot = findWorkspaceRoot(cwd)\n if (repoRoot && join(repoRoot) !== join(cwd)) {\n return join(cwd, '.otto', 'feishu-use.json')\n }\n return join(cwd, '.otto', 'feishu-use.json')\n}\n\nexport interface FeishuSetupResult {\n /** app_id 已写入配置 */\n appId: string\n /** bot/v3/info 返回的 bot 身份;验证失败时为 undefined */\n botOpenId?: string\n botName?: string\n /** 实际写入的配置文件路径 */\n configPath: string\n /** setup 是否完整成功(凭据已写入 + bot 身份已验证) */\n success: boolean\n}\n\n/** 往指定路径写入 feishu-use 配置文件(mode 0o600),覆盖已有文件。 */\nfunction writeFeishuConfigFile(configPath: string, overrides: Record<string, unknown>): string {\n const base = {\n app_id: 'REPLACE_WITH_YOUR_FEISHU_APP_ID',\n app_secret: 'REPLACE_WITH_YOUR_FEISHU_APP_SECRET',\n allowed_chats: [] as string[],\n workspace_dir: join(configPath, '..', '..', 'feishu-use-workspace'),\n max_reply_chunk_length: 4000,\n }\n const merged = { ...base }\n for (const [k, v] of Object.entries(overrides)) {\n if (v !== undefined && v !== '') (merged as Record<string, unknown>)[k] = v\n }\n mkdirSync(join(configPath, '..'), { recursive: true })\n writeFileSync(configPath, JSON.stringify(merged, null, 2) + '\\n', { mode: 0o600 })\n return configPath\n}\n\n/** 从配置文件读取当前值;文件不存在时返回 undefined。 */\nfunction readConfigSafe(configPath: string): Record<string, unknown> | undefined {\n try {\n if (!existsSync(configPath)) return undefined\n return JSON.parse(readFileSync(configPath, 'utf8')) as Record<string, unknown>\n } catch {\n return undefined\n }\n}\n\n/** openSelect 的 Promise 包装:用户选择 → resolve(value),Esc/取消 → resolve(undefined)。 */\nfunction promiseSelect(\n ctx: CliContext,\n opts: { title: string; options: Array<{ label: string; value: string; detail?: string }> },\n): Promise<string | undefined> {\n return new Promise((resolve) => {\n ctx.tui.modal.openModal({\n kind: 'select',\n ...opts,\n onSelect: (v) => resolve(v),\n onCancel: () => resolve(undefined),\n })\n })\n}\n\n/** openConfirm 的 Promise 包装:确认 → resolve(true),取消 → resolve(false)。 */\nfunction promiseConfirm(ctx: CliContext, title: string, message: string | string[]): Promise<boolean> {\n return new Promise((resolve) => {\n ctx.tui.modal.openModal({\n kind: 'confirm',\n title,\n message,\n onConfirm: () => resolve(true),\n onCancel: () => resolve(false),\n })\n })\n}\n\n/**\n * 启动 `registerApp` 子进程并等待结果。\n *\n * 子进程 stdout 输出 JSON `{ client_id, client_secret }` 表示成功;\n * 超时/崩溃/非零退出码 → resolve(undefined)。\n *\n * `pluginDir` 是 feishu-use 插件安装目录(含 node_modules/@larksuiteoapi/node-sdk)。\n *\n * `onQrCodeUrl`(RFC-134 §8 F1 修复):QR 码 URL 由 SDK `onQRCodeReady` 回调在子进程侧\n * 产生,经 `QRCODE_URL::<json>` 前缀行写入 stderr——本函数解析该前缀行并回调给调用方展示,\n * 修复此前\"stderr 从未被读取、URL 被静默丢弃\"的缺陷(用户选择扫码后永远看不到二维码)。\n */\nfunction runRegisterApp(\n pluginDir: string,\n timeoutMs = 5 * 60 * 1000,\n onQrCodeUrl?: (info: { url: string; expireIn: number }) => void,\n): Promise<{ client_id: string; client_secret: string } | undefined> {\n return new Promise((resolve) => {\n const scriptPath = join(pluginDir, 'scripts', 'register-app.mjs')\n if (!existsSync(scriptPath)) {\n resolve(undefined)\n return\n }\n\n const child = spawn('node', [scriptPath], {\n cwd: pluginDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout: timeoutMs,\n env: { ...process.env },\n })\n\n let stdout = ''\n let stderr = ''\n let qrEmitted = false\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString()\n })\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString()\n if (qrEmitted || !onQrCodeUrl) return\n const line = stderr.split('\\n').find((l) => l.startsWith('QRCODE_URL::'))\n if (!line) return\n try {\n const info = JSON.parse(line.slice('QRCODE_URL::'.length)) as { url: string; expireIn: number }\n if (info.url) {\n qrEmitted = true\n onQrCodeUrl(info)\n }\n } catch {\n /* 前缀行畸形,忽略,等待后续 data 事件或直到子进程结束都拿不到 URL */\n }\n })\n\n child.on('error', () => resolve(undefined))\n child.on('close', (code) => {\n if (code !== 0) return resolve(undefined)\n try {\n const result = JSON.parse(stdout.trim().split('\\n').pop()!) as {\n client_id: string\n client_secret: string\n }\n if (result.client_id && result.client_secret) {\n resolve(result)\n return\n }\n } catch {\n /* fall through */\n }\n resolve(undefined)\n })\n })\n}\n\n/**\n * 用 app_id/app_secret 调用 `bot/v3/info` 验证凭据并获取 bot 身份。\n *\n * 子进程执行插件自带脚本 `scripts/verify-bot.mjs`(与 `runRegisterApp` 调用\n * `register-app.mjs` 同模式,终局架构 review 2026-07-20 下沉)——宿主 CLI 只负责\n * spawn + 传参 + 解析结果,不内联飞书 SDK 的具体调用细节(`bot/v3/info` 端点/Client\n * 构造方式),保持 RFC-134 D2 的架构隔离:`packages/cli` 对插件内部实现零认知。\n * 失败(网络不通/凭据错误/权限不足/脚本缺失)返回 undefined。\n */\nfunction verifyBotIdentity(\n pluginDir: string,\n appId: string,\n appSecret: string,\n): Promise<{ openId: string; name: string } | undefined> {\n return new Promise((resolve) => {\n const scriptPath = join(pluginDir, 'scripts', 'verify-bot.mjs')\n if (!existsSync(scriptPath)) {\n resolve(undefined)\n return\n }\n\n const child = spawn('node', [scriptPath], {\n cwd: pluginDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout: 15_000,\n env: { ...process.env, FEISHU_VERIFY_APP_ID: appId, FEISHU_VERIFY_APP_SECRET: appSecret },\n })\n\n let stdout = ''\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString()\n })\n\n child.on('error', () => resolve(undefined))\n child.on('close', (code) => {\n if (code !== 0) return resolve(undefined)\n try {\n const r = JSON.parse(stdout.trim()) as { openId: string; name: string }\n resolve(r)\n } catch {\n resolve(undefined)\n }\n })\n })\n}\n\n/** 脱敏 App ID:前 8 字符 + *** + 后 4 字符。 */\nfunction maskAppId(appId: string): string {\n if (appId.length <= 12) return appId.slice(0, 4) + '***'\n return appId.slice(0, 8) + '***' + appId.slice(-4)\n}\n\n/**\n * feishu-use 交互式配置向导主入口(RFC-134 M1)。\n *\n * 在 TUI 内调用(需已有 TUI 上下文),通过 async/await 链式编排对话框。\n * 外部调用方负责提供 configPath 和 pluginDir。\n *\n * @param ctx CliContext(需 TUI 可用)\n * @param configPath 配置文件的目标路径(如 `<cwd>/.otto/feishu-use.json`)\n * @param pluginDir feishu-use 插件的安装目录(含 node_modules)\n * @param existingConfig 已有配置(存在时为重配置模式,展示当前值)\n */\nexport async function runFeishuSetupWizard(\n ctx: CliContext,\n configPath: string,\n pluginDir: string,\n existingConfig?: Record<string, unknown>,\n): Promise<FeishuSetupResult | undefined> {\n // ── 重配置模式:展示当前值 + 确认覆盖 ──\n if (existingConfig?.app_id) {\n const currentAppId = String(existingConfig.app_id)\n const lines = [\n `App ID: ${maskAppId(currentAppId)}`,\n existingConfig.allowed_chats\n ? `allowed_chats: ${(existingConfig.allowed_chats as string[]).length} 个`\n : 'allowed_chats: 空(拒绝所有)',\n `配置路径: ${configPath}`,\n ]\n const confirmed = await promiseConfirm(ctx, 'feishu-use 重配置', ['当前配置:', '', ...lines, '', '确定要重新配置吗?当前凭据将被覆盖。'])\n if (!confirmed) return undefined\n }\n\n // ── Step 1: 选择配置方式 ──\n const method = await promiseSelect(ctx, {\n title: 'feishu-use 配置',\n options: [\n { label: '扫码自动创建飞书应用(推荐)', value: 'qr', detail: '飞书 App 扫码确认后自动获取凭据' },\n { label: '手动输入 App ID / App Secret', value: 'manual', detail: '已有飞书自建应用,直接填入凭据' },\n ],\n })\n if (!method) return undefined // user cancelled\n\n let appId = ''\n let appSecret = ''\n let allowedChats: string[] = []\n let trustedChats: string[] = []\n\n // ── Step 2a: QR 码路径 ──\n if (method === 'qr') {\n ctx.tui.info.showToast('plugin-feishu-use', '正在生成飞书应用注册二维码,请稍候…')\n\n const result = await runRegisterApp(pluginDir, 5 * 60 * 1000, (info) => {\n // RFC-134 §8 F1 修复:QR URL 必须展示给用户才能完成扫码,此前写入 stderr 后\n // 从未被读取,用户永远看不到二维码。用 pushMessage(非 toast)展示——toast 会\n // 在几秒后自动消失,而用户需要足够时间打开飞书 App 扫码。\n const expireMinutes = Math.round(info.expireIn / 60)\n ctx.tui.messages.pushMessage({\n role: 'assistant',\n content: [{\n type: 'text',\n text: `feishu-use:请在飞书 App 中打开以下链接扫码确认创建应用(${expireMinutes} 分钟内有效):\\n${info.url}`,\n }],\n })\n })\n if (!result) {\n // 超时或失败 → 回退到手动输入\n const retry = await promiseConfirm(ctx, 'plugin-feishu-use', '扫码注册失败(超时或网络错误)。是否改为手动输入凭据?')\n if (!retry) return undefined\n // 继续执行 manual 路径(故意 fall-through)\n } else {\n appId = result.client_id\n appSecret = result.client_secret\n ctx.tui.info.showToast('plugin-feishu-use', '扫码注册成功!凭据已自动填充。')\n }\n }\n\n // ── Step 2b: 手动输入路径(或 QR 失败后回退) ──\n if (!appId) {\n const hintId = existingConfig?.app_id ? `(当前: ${maskAppId(String(existingConfig.app_id))},留空保留原值)` : ''\n const enteredId = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '飞书 App ID',\n placeholder: `cli_xxxxxxxxxxxx ${hintId}`,\n description: [],\n })\n if (enteredId === null) return undefined\n appId = enteredId || String(existingConfig?.app_id ?? '')\n\n const enteredSecret = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '飞书 App Secret',\n mask: true,\n description: existingConfig?.app_secret ? ['留空保留当前 Secret'] : [],\n })\n if (enteredSecret === null) return undefined\n appSecret = enteredSecret || String(existingConfig?.app_secret ?? '')\n\n if (!appId || !appSecret) {\n ctx.tui.info.showToast('plugin-feishu-use', 'App ID 和 App Secret 不能为空,配置已取消。')\n return undefined\n }\n\n // 可选字段\n const chatsRaw = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '允许的 chat_id(逗号分隔,留空=拒绝所有)',\n placeholder: existingConfig ? `当前: ${(existingConfig.allowed_chats as string[] | undefined)?.join(', ') ?? '空'}` : '',\n description: [],\n })\n if (chatsRaw === null) return undefined\n if (chatsRaw) {\n allowedChats = chatsRaw.split(',').map(s => s.trim()).filter(Boolean)\n } else if (existingConfig?.allowed_chats) {\n allowedChats = existingConfig.allowed_chats as string[]\n }\n\n const trustedRaw = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '可信 chat_id(逗号分隔,留空=仅只读)',\n placeholder: existingConfig ? `当前: ${(existingConfig.trusted_chats as string[] | undefined)?.join(', ') ?? '空'}` : '',\n description: [],\n })\n if (trustedRaw === null) return undefined\n if (trustedRaw) {\n trustedChats = trustedRaw.split(',').map(s => s.trim()).filter(Boolean)\n } else if (existingConfig?.trusted_chats) {\n trustedChats = existingConfig.trusted_chats as string[]\n }\n }\n\n // ── Step 3: 写入配置 ──\n const workspaceDir = existingConfig?.workspace_dir\n ? String(existingConfig.workspace_dir)\n : join(pluginDir, '..', '..', 'feishu-use-workspace')\n writeFeishuConfigFile(configPath, {\n app_id: appId,\n app_secret: appSecret,\n allowed_chats: allowedChats,\n trusted_chats: trustedChats,\n workspace_dir: workspaceDir,\n })\n\n ctx.tui.info.showToast('plugin-feishu-use', `配置已写入 ${configPath}`)\n\n // ── Step 4: 验证 bot 身份 ──\n const bot = await verifyBotIdentity(pluginDir, appId, appSecret)\n if (bot) {\n ctx.tui.info.showToast('plugin-feishu-use', `Bot 身份验证成功:${bot.name} (${bot.openId})`)\n } else {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/feishu setup',\n status: 'error',\n summary: 'bot 身份验证失败(凭据不正确或网络不可达)',\n detail: '请稍后用 `otto feishu config set` 修正凭据,或手动编辑配置文件。',\n })\n }\n\n return {\n appId,\n botOpenId: bot?.openId,\n botName: bot?.name,\n configPath,\n success: !!bot,\n }\n}\n\n/**\n * 给 runFeishuSetupWizard 准备参数的外层封装:计算 configPath 和 pluginDir。\n *\n * @returns setup 结果,或 undefined(用户取消)\n */\nexport async function launchFeishuSetup(\n ctx: CliContext,\n): Promise<FeishuSetupResult | undefined> {\n const ws = ctx.app.workspaceDir ?? process.cwd()\n const configPath = resolveConfigPath(ws)\n\n // 找到 feishu-use 的安装目录(插件目录 = 含 otto-plugin.json 的目录)\n const allPlugins = discoverPluginsWithBundled({ cwd: ws, homedir: homedir() })\n const feishuPlugin = allPlugins.find(p => p.id === 'plugin-feishu-use')\n if (!feishuPlugin) {\n ctx.tui.info.showToast('plugin-feishu-use', 'feishu-use 插件尚未安装,请先在 `/model` 安装入口安装 feishu-use。')\n return undefined\n }\n\n const pluginDir = feishuPlugin.dir\n const existing = readConfigSafe(configPath)\n\n return runFeishuSetupWizard(ctx, configPath, pluginDir, existing)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"feishu-setup-B6ETf-8Y.js","names":[],"sources":["../src/commands/handlers/feishu-setup.ts"],"sourcesContent":["/**\n * /feishu-setup 命令:飞书 webhook 绑定交互向导。\n */\nimport { spawn } from 'node:child_process'\nimport { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { findWorkspaceRoot } from '@x-otto/coding'\nimport { discoverPluginsWithBundled } from '../../discover-with-bundled'\nimport type { CliContext } from '../../command-dispatcher'\n\n/** 按优先级计算 feishu-use 配置文件候选路径(project > repo > user),返回最高优先级路径。 */\nfunction resolveConfigPath(cwd: string): string {\n const repoRoot = findWorkspaceRoot(cwd)\n if (repoRoot && join(repoRoot) !== join(cwd)) {\n return join(cwd, '.otto', 'feishu-use.json')\n }\n return join(cwd, '.otto', 'feishu-use.json')\n}\n\nexport interface FeishuSetupResult {\n /** app_id 已写入配置 */\n appId: string\n /** bot/v3/info 返回的 bot 身份;验证失败时为 undefined */\n botOpenId?: string\n botName?: string\n /** 实际写入的配置文件路径 */\n configPath: string\n /** setup 是否完整成功(凭据已写入 + bot 身份已验证) */\n success: boolean\n}\n\n/** 往指定路径写入 feishu-use 配置文件(mode 0o600),覆盖已有文件。 */\nfunction writeFeishuConfigFile(configPath: string, overrides: Record<string, unknown>): string {\n const base = {\n app_id: 'REPLACE_WITH_YOUR_FEISHU_APP_ID',\n app_secret: 'REPLACE_WITH_YOUR_FEISHU_APP_SECRET',\n allowed_chats: [] as string[],\n workspace_dir: join(configPath, '..', '..', 'feishu-use-workspace'),\n max_reply_chunk_length: 4000,\n }\n const merged = { ...base }\n for (const [k, v] of Object.entries(overrides)) {\n if (v !== undefined && v !== '') (merged as Record<string, unknown>)[k] = v\n }\n mkdirSync(join(configPath, '..'), { recursive: true })\n writeFileSync(configPath, JSON.stringify(merged, null, 2) + '\\n', { mode: 0o600 })\n return configPath\n}\n\n/** 从配置文件读取当前值;文件不存在时返回 undefined。 */\nfunction readConfigSafe(configPath: string): Record<string, unknown> | undefined {\n try {\n if (!existsSync(configPath)) return undefined\n return JSON.parse(readFileSync(configPath, 'utf8')) as Record<string, unknown>\n } catch {\n return undefined\n }\n}\n\n/** openSelect 的 Promise 包装:用户选择 → resolve(value),Esc/取消 → resolve(undefined)。 */\nfunction promiseSelect(\n ctx: CliContext,\n opts: { title: string; options: Array<{ label: string; value: string; detail?: string }> },\n): Promise<string | undefined> {\n return new Promise((resolve) => {\n ctx.tui.modal.openModal({\n kind: 'select',\n ...opts,\n onSelect: (v) => resolve(v),\n onCancel: () => resolve(undefined),\n })\n })\n}\n\n/** openConfirm 的 Promise 包装:确认 → resolve(true),取消 → resolve(false)。 */\nfunction promiseConfirm(ctx: CliContext, title: string, message: string | string[]): Promise<boolean> {\n return new Promise((resolve) => {\n ctx.tui.modal.openModal({\n kind: 'confirm',\n title,\n message,\n onConfirm: () => resolve(true),\n onCancel: () => resolve(false),\n })\n })\n}\n\n/**\n * 启动 `registerApp` 子进程并等待结果。\n *\n * 子进程 stdout 输出 JSON `{ client_id, client_secret }` 表示成功;\n * 超时/崩溃/非零退出码 → resolve(undefined)。\n *\n * `pluginDir` 是 feishu-use 插件安装目录(含 node_modules/@larksuiteoapi/node-sdk)。\n *\n * `onQrCodeUrl`(RFC-134 §8 F1 修复):QR 码 URL 由 SDK `onQRCodeReady` 回调在子进程侧\n * 产生,经 `QRCODE_URL::<json>` 前缀行写入 stderr——本函数解析该前缀行并回调给调用方展示,\n * 修复此前\"stderr 从未被读取、URL 被静默丢弃\"的缺陷(用户选择扫码后永远看不到二维码)。\n */\nfunction runRegisterApp(\n pluginDir: string,\n timeoutMs = 5 * 60 * 1000,\n onQrCodeUrl?: (info: { url: string; expireIn: number }) => void,\n): Promise<{ client_id: string; client_secret: string } | undefined> {\n return new Promise((resolve) => {\n const scriptPath = join(pluginDir, 'scripts', 'register-app.mjs')\n if (!existsSync(scriptPath)) {\n resolve(undefined)\n return\n }\n\n const child = spawn('node', [scriptPath], {\n cwd: pluginDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout: timeoutMs,\n env: { ...process.env },\n })\n\n let stdout = ''\n let stderr = ''\n let qrEmitted = false\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString()\n })\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString()\n if (qrEmitted || !onQrCodeUrl) return\n const line = stderr.split('\\n').find((l) => l.startsWith('QRCODE_URL::'))\n if (!line) return\n try {\n const info = JSON.parse(line.slice('QRCODE_URL::'.length)) as { url: string; expireIn: number }\n if (info.url) {\n qrEmitted = true\n onQrCodeUrl(info)\n }\n } catch {\n /* 前缀行畸形,忽略,等待后续 data 事件或直到子进程结束都拿不到 URL */\n }\n })\n\n child.on('error', () => resolve(undefined))\n child.on('close', (code) => {\n if (code !== 0) return resolve(undefined)\n try {\n const result = JSON.parse(stdout.trim().split('\\n').pop()!) as {\n client_id: string\n client_secret: string\n }\n if (result.client_id && result.client_secret) {\n resolve(result)\n return\n }\n } catch {\n /* fall through */\n }\n resolve(undefined)\n })\n })\n}\n\n/**\n * 用 app_id/app_secret 调用 `bot/v3/info` 验证凭据并获取 bot 身份。\n *\n * 子进程执行插件自带脚本 `scripts/verify-bot.mjs`(与 `runRegisterApp` 调用\n * `register-app.mjs` 同模式,终局架构 review 2026-07-20 下沉)——宿主 CLI 只负责\n * spawn + 传参 + 解析结果,不内联飞书 SDK 的具体调用细节(`bot/v3/info` 端点/Client\n * 构造方式),保持 RFC-134 D2 的架构隔离:`packages/cli` 对插件内部实现零认知。\n * 失败(网络不通/凭据错误/权限不足/脚本缺失)返回 undefined。\n */\nfunction verifyBotIdentity(\n pluginDir: string,\n appId: string,\n appSecret: string,\n): Promise<{ openId: string; name: string } | undefined> {\n return new Promise((resolve) => {\n const scriptPath = join(pluginDir, 'scripts', 'verify-bot.mjs')\n if (!existsSync(scriptPath)) {\n resolve(undefined)\n return\n }\n\n const child = spawn('node', [scriptPath], {\n cwd: pluginDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout: 15_000,\n env: { ...process.env, FEISHU_VERIFY_APP_ID: appId, FEISHU_VERIFY_APP_SECRET: appSecret },\n })\n\n let stdout = ''\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString()\n })\n\n child.on('error', () => resolve(undefined))\n child.on('close', (code) => {\n if (code !== 0) return resolve(undefined)\n try {\n const r = JSON.parse(stdout.trim()) as { openId: string; name: string }\n resolve(r)\n } catch {\n resolve(undefined)\n }\n })\n })\n}\n\n/** 脱敏 App ID:前 8 字符 + *** + 后 4 字符。 */\nfunction maskAppId(appId: string): string {\n if (appId.length <= 12) return appId.slice(0, 4) + '***'\n return appId.slice(0, 8) + '***' + appId.slice(-4)\n}\n\n/**\n * feishu-use 交互式配置向导主入口(RFC-134 M1)。\n *\n * 在 TUI 内调用(需已有 TUI 上下文),通过 async/await 链式编排对话框。\n * 外部调用方负责提供 configPath 和 pluginDir。\n *\n * @param ctx CliContext(需 TUI 可用)\n * @param configPath 配置文件的目标路径(如 `<cwd>/.otto/feishu-use.json`)\n * @param pluginDir feishu-use 插件的安装目录(含 node_modules)\n * @param existingConfig 已有配置(存在时为重配置模式,展示当前值)\n */\nexport async function runFeishuSetupWizard(\n ctx: CliContext,\n configPath: string,\n pluginDir: string,\n existingConfig?: Record<string, unknown>,\n): Promise<FeishuSetupResult | undefined> {\n // ── 重配置模式:展示当前值 + 确认覆盖 ──\n if (existingConfig?.app_id) {\n const currentAppId = String(existingConfig.app_id)\n const lines = [\n `App ID: ${maskAppId(currentAppId)}`,\n existingConfig.allowed_chats\n ? `allowed_chats: ${(existingConfig.allowed_chats as string[]).length} 个`\n : 'allowed_chats: 空(拒绝所有)',\n `配置路径: ${configPath}`,\n ]\n const confirmed = await promiseConfirm(ctx, 'feishu-use 重配置', ['当前配置:', '', ...lines, '', '确定要重新配置吗?当前凭据将被覆盖。'])\n if (!confirmed) return undefined\n }\n\n // ── Step 1: 选择配置方式 ──\n const method = await promiseSelect(ctx, {\n title: 'feishu-use 配置',\n options: [\n { label: '扫码自动创建飞书应用(推荐)', value: 'qr', detail: '飞书 App 扫码确认后自动获取凭据' },\n { label: '手动输入 App ID / App Secret', value: 'manual', detail: '已有飞书自建应用,直接填入凭据' },\n ],\n })\n if (!method) return undefined // user cancelled\n\n let appId = ''\n let appSecret = ''\n let allowedChats: string[] = []\n let trustedChats: string[] = []\n\n // ── Step 2a: QR 码路径 ──\n if (method === 'qr') {\n ctx.tui.info.showToast('plugin-feishu-use', '正在生成飞书应用注册二维码,请稍候…')\n\n const result = await runRegisterApp(pluginDir, 5 * 60 * 1000, (info) => {\n // RFC-134 §8 F1 修复:QR URL 必须展示给用户才能完成扫码,此前写入 stderr 后\n // 从未被读取,用户永远看不到二维码。用 pushMessage(非 toast)展示——toast 会\n // 在几秒后自动消失,而用户需要足够时间打开飞书 App 扫码。\n const expireMinutes = Math.round(info.expireIn / 60)\n ctx.tui.messages.pushMessage({\n role: 'assistant',\n content: [{\n type: 'text',\n text: `feishu-use:请在飞书 App 中打开以下链接扫码确认创建应用(${expireMinutes} 分钟内有效):\\n${info.url}`,\n }],\n })\n })\n if (!result) {\n // 超时或失败 → 回退到手动输入\n const retry = await promiseConfirm(ctx, 'plugin-feishu-use', '扫码注册失败(超时或网络错误)。是否改为手动输入凭据?')\n if (!retry) return undefined\n // 继续执行 manual 路径(故意 fall-through)\n } else {\n appId = result.client_id\n appSecret = result.client_secret\n ctx.tui.info.showToast('plugin-feishu-use', '扫码注册成功!凭据已自动填充。')\n }\n }\n\n // ── Step 2b: 手动输入路径(或 QR 失败后回退) ──\n if (!appId) {\n const hintId = existingConfig?.app_id ? `(当前: ${maskAppId(String(existingConfig.app_id))},留空保留原值)` : ''\n const enteredId = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '飞书 App ID',\n placeholder: `cli_xxxxxxxxxxxx ${hintId}`,\n description: [],\n })\n if (enteredId === null) return undefined\n appId = enteredId || String(existingConfig?.app_id ?? '')\n\n const enteredSecret = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '飞书 App Secret',\n mask: true,\n description: existingConfig?.app_secret ? ['留空保留当前 Secret'] : [],\n })\n if (enteredSecret === null) return undefined\n appSecret = enteredSecret || String(existingConfig?.app_secret ?? '')\n\n if (!appId || !appSecret) {\n ctx.tui.info.showToast('plugin-feishu-use', 'App ID 和 App Secret 不能为空,配置已取消。')\n return undefined\n }\n\n // 可选字段\n const chatsRaw = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '允许的 chat_id(逗号分隔,留空=拒绝所有)',\n placeholder: existingConfig ? `当前: ${(existingConfig.allowed_chats as string[] | undefined)?.join(', ') ?? '空'}` : '',\n description: [],\n })\n if (chatsRaw === null) return undefined\n if (chatsRaw) {\n allowedChats = chatsRaw.split(',').map(s => s.trim()).filter(Boolean)\n } else if (existingConfig?.allowed_chats) {\n allowedChats = existingConfig.allowed_chats as string[]\n }\n\n const trustedRaw = await ctx.tui.modal.openModal({\n kind: 'input',\n title: '可信 chat_id(逗号分隔,留空=仅只读)',\n placeholder: existingConfig ? `当前: ${(existingConfig.trusted_chats as string[] | undefined)?.join(', ') ?? '空'}` : '',\n description: [],\n })\n if (trustedRaw === null) return undefined\n if (trustedRaw) {\n trustedChats = trustedRaw.split(',').map(s => s.trim()).filter(Boolean)\n } else if (existingConfig?.trusted_chats) {\n trustedChats = existingConfig.trusted_chats as string[]\n }\n }\n\n // ── Step 3: 写入配置 ──\n const workspaceDir = existingConfig?.workspace_dir\n ? String(existingConfig.workspace_dir)\n : join(pluginDir, '..', '..', 'feishu-use-workspace')\n writeFeishuConfigFile(configPath, {\n app_id: appId,\n app_secret: appSecret,\n allowed_chats: allowedChats,\n trusted_chats: trustedChats,\n workspace_dir: workspaceDir,\n })\n\n ctx.tui.info.showToast('plugin-feishu-use', `配置已写入 ${configPath}`)\n\n // ── Step 4: 验证 bot 身份 ──\n const bot = await verifyBotIdentity(pluginDir, appId, appSecret)\n if (bot) {\n ctx.tui.info.showToast('plugin-feishu-use', `Bot 身份验证成功:${bot.name} (${bot.openId})`)\n } else {\n ctx.tui.messages.pushMessage({\n role: 'command_echo',\n command: '/feishu setup',\n status: 'error',\n summary: 'bot 身份验证失败(凭据不正确或网络不可达)',\n detail: '请稍后用 `otto feishu config set` 修正凭据,或手动编辑配置文件。',\n })\n }\n\n return {\n appId,\n botOpenId: bot?.openId,\n botName: bot?.name,\n configPath,\n success: !!bot,\n }\n}\n\n/**\n * 给 runFeishuSetupWizard 准备参数的外层封装:计算 configPath 和 pluginDir。\n *\n * @returns setup 结果,或 undefined(用户取消)\n */\nexport async function launchFeishuSetup(\n ctx: CliContext,\n): Promise<FeishuSetupResult | undefined> {\n const ws = ctx.app.workspaceDir ?? process.cwd()\n const configPath = resolveConfigPath(ws)\n\n // 找到 feishu-use 的安装目录(插件目录 = 含 otto-plugin.json 的目录)\n const allPlugins = discoverPluginsWithBundled({ cwd: ws, homedir: homedir() })\n const feishuPlugin = allPlugins.find(p => p.id === 'plugin-feishu-use')\n if (!feishuPlugin) {\n ctx.tui.info.showToast('plugin-feishu-use', 'feishu-use 插件尚未安装,请先在 `/model` 安装入口安装 feishu-use。')\n return undefined\n }\n\n const pluginDir = feishuPlugin.dir\n const existing = readConfigSafe(configPath)\n\n return runFeishuSetupWizard(ctx, configPath, pluginDir, existing)\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ const e={"no-provider-plugin":{summary:`No provider plugin detected.`,recoveryAction:"Run `otto` and follow the setup prompt, or `otto extension install <provider>` directly."},"no-credential":{summary:`Provider plugin found, but no credential is registered.`,recoveryAction:"Run `otto` and connect your account, or `otto auth login <provider>`."},"looks-configured":{summary:`Provider and credential look configured (not a readiness guarantee — model usability is verified at runtime).`,recoveryAction:"Run `otto` in a project to start."}};function t(t){let n;return n=t.providerPluginDiscovered?t.hasAnyCredential?`looks-configured`:`no-credential`:`no-provider-plugin`,{status:n,...e[n]}}export{t as deriveDoctorFtueGuidance};
3
+ //# sourceMappingURL=ftue-doctor-recovery-DeAf8jVo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ftue-doctor-recovery-DeAf8jVo.js","names":[],"sources":["../src/commands/ftue/ftue-doctor-recovery.ts"],"sourcesContent":["/**\n * ftue-doctor-recovery.ts — RFC-326 M6-01:`otto doctor` 的 FTUE 环境自检 + recovery\n * action 映射。\n *\n * 与 `ftue-snapshot.ts`(TUI 运行时权威判定,5 阶段 `provider→authentication→model→\n * ready→first-prompt`)的关系:**本模块不是它的替代品,是它的轻量诊断变体**。\n *\n * 为什么不能直接复用 `deriveFtueSnapshot`:该函数的 `model` 阶段判据依赖\n * `usableModelIds`——必须先构造真实 `ModelRegistry` 并跑完整 `createModelUsabilityGate`\n * (逐模型探查凭据+订阅风险),这正是 `doctor.ts` 头部已声明的设计约束(RFC-150 D3:\n * doctor 是轻量诊断命令,不承担重依赖/不构造完整 App)。构造完整 App 来回答\"配置好了\n * 没有\"违反了这条约束本身。\n *\n * 因此本模块只收集三个**廉价**事实(无需 App/ModelRegistry):\n * 1. 是否发现任一声明 `provider` capability 的插件(`discoverPluginsWithBundled` 已在\n * doctor 的 `collectPlugins()` 跑过,直接复用其产出,零额外扫描);\n * 2. 是否有任一已登记凭据(`AuthStore.listProviders()`,只读本地 auth.json + env,\n * 不发网络请求、不做 OAuth 刷新,见 `getAuthSource` 头注释);\n * 3. 是否已发出过第一条 prompt(`first_prompt_submitted_at` 是否已写入,读 settings 一个\n * 字段)。\n *\n * 代价:**判据比 TUI 运行时权威判定更宽松**——\"有凭据条目\"不等于\"凭据仍然有效\",\n * 更不等于\"至少一个模型可用\"(那需要逐模型探查订阅 429 风险等,doctor 明确不做)。\n * 这是刻意的取舍:doctor 给出的是\"大方向卡在哪一步\"的诊断提示,不是可发 prompt 的\n * 权威裁决——用户装了 provider、连了凭据后,doctor 会显示\"看起来已配置\",但真正的\n * ready 判定仍然只由 TUI 运行时的 `FtueController` 给出。字段命名与文案措辞需体现\n * 这种不确定性(\"looks configured\" 而非 \"ready\")。\n */\n\n/** doctor 能廉价收集到的三个 FTUE 事实——均为只读本地探查,零网络请求。 */\nexport interface DoctorFtueFacts {\n /** 是否发现任一声明 `provider` capability 的插件(builtin/project/repository/user 任一层)。 */\n providerPluginDiscovered: boolean\n /** 是否有任一已登记凭据(`AuthStore.listProviders().length > 0`)。 */\n hasAnyCredential: boolean\n /** 是否已发出过第一条 prompt(`first_prompt_submitted_at` 已写入)。 */\n firstPromptSubmitted: boolean\n}\n\n/** 廉价诊断阶段——刻意用与 `FtueStage` 不同的类型名,避免调用方误当作权威判定复用。 */\nexport type DoctorFtueStatus =\n | 'no-provider-plugin'\n | 'no-credential'\n | 'looks-configured'\n\nexport interface DoctorFtueGuidance {\n status: DoctorFtueStatus\n /** 面向终端用户的一行诊断文案(英文,doctor 全量输出是纯文本、无 i18n 依赖)。 */\n summary: string\n /** 推荐的下一步命令(纯文本,可直接复制执行)。 */\n recoveryAction: string\n}\n\nconst GUIDANCE: Record<DoctorFtueStatus, Omit<DoctorFtueGuidance, 'status'>> = {\n 'no-provider-plugin': {\n summary: 'No provider plugin detected.',\n recoveryAction: 'Run `otto` and follow the setup prompt, or `otto extension install <provider>` directly.',\n },\n 'no-credential': {\n summary: 'Provider plugin found, but no credential is registered.',\n recoveryAction: 'Run `otto` and connect your account, or `otto auth login <provider>`.',\n },\n 'looks-configured': {\n summary: 'Provider and credential look configured (not a readiness guarantee — model usability is verified at runtime).',\n recoveryAction: 'Run `otto` in a project to start.',\n },\n}\n\n/**\n * 纯函数:由三个廉价事实推导诊断状态 + 建议动作。\n *\n * 判定顺序刻意与 `deriveStage`(ftue-snapshot.ts)的前两级保持一致(provider → 凭据),\n * 但到此为止——本模块不判定\"model\"/\"first-prompt\"两级,因为那需要真实可用性探查。\n * `firstPromptSubmitted` 入参目前只影响 `looks-configured` 分支的文案精细度预留,不\n * 参与状态判定本身(doctor 无法验证\"发过 prompt 时是否真的成功\",展示\"看起来已配置\"\n * 已是本模块能给出的诚实上限)。\n */\nexport function deriveDoctorFtueGuidance(facts: DoctorFtueFacts): DoctorFtueGuidance {\n let status: DoctorFtueStatus\n if (!facts.providerPluginDiscovered) {\n status = 'no-provider-plugin'\n } else if (!facts.hasAnyCredential) {\n status = 'no-credential'\n } else {\n status = 'looks-configured'\n }\n return { status, ...GUIDANCE[status] }\n}\n"],"mappings":";AAqDA,MAAM,EAAyE,CAC7E,qBAAsB,CACpB,QAAS,+BACT,eAAgB,2FACjB,CACD,gBAAiB,CACf,QAAS,0DACT,eAAgB,wEACjB,CACD,mBAAoB,CAClB,QAAS,gHACT,eAAgB,oCACjB,CACF,CAWD,SAAgB,EAAyB,EAA4C,CACnF,IAAI,EAQJ,MAPA,CACE,EADG,EAAM,yBAEC,EAAM,iBAGP,mBAFA,gBAFA,qBAMJ,CAAE,SAAQ,GAAG,EAAS,GAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli.ts"],"mappings":";;iBAyRsB,MAAA,CAAA,GAAU,OAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli.ts"],"mappings":";;iBA6RsB,MAAA,CAAA,GAAU,OAAA"}