@geminixiang/mikan 1.0.0-beta.39 → 1.0.0-beta.40

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 (397) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +50 -23
  3. package/dist/adapter.d.ts +1 -1
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js.map +1 -1
  6. package/dist/adapters/discord/bot.d.ts +2 -1
  7. package/dist/adapters/discord/bot.d.ts.map +1 -1
  8. package/dist/adapters/discord/bot.js +3 -3
  9. package/dist/adapters/discord/bot.js.map +1 -1
  10. package/dist/adapters/discord/context.d.ts +2 -1
  11. package/dist/adapters/discord/context.d.ts.map +1 -1
  12. package/dist/adapters/discord/context.js +1 -1
  13. package/dist/adapters/discord/context.js.map +1 -1
  14. package/dist/adapters/github/bot.d.ts +2 -1
  15. package/dist/adapters/github/bot.d.ts.map +1 -1
  16. package/dist/adapters/github/bot.js +11 -7
  17. package/dist/adapters/github/bot.js.map +1 -1
  18. package/dist/adapters/github/client.js +2 -2
  19. package/dist/adapters/github/client.js.map +1 -1
  20. package/dist/adapters/github/context.d.ts +4 -2
  21. package/dist/adapters/github/context.d.ts.map +1 -1
  22. package/dist/adapters/github/context.js +2 -2
  23. package/dist/adapters/github/context.js.map +1 -1
  24. package/dist/adapters/github/gcp-auth.js +2 -2
  25. package/dist/adapters/github/gcp-auth.js.map +1 -1
  26. package/dist/adapters/github/github-ops.d.ts +3 -2
  27. package/dist/adapters/github/github-ops.d.ts.map +1 -1
  28. package/dist/adapters/github/github-ops.js +1 -1
  29. package/dist/adapters/github/github-ops.js.map +1 -1
  30. package/dist/adapters/github/ids.d.ts.map +1 -1
  31. package/dist/adapters/github/ids.js +7 -3
  32. package/dist/adapters/github/ids.js.map +1 -1
  33. package/dist/adapters/github/repo.js +3 -3
  34. package/dist/adapters/github/repo.js.map +1 -1
  35. package/dist/adapters/github/types.d.ts +2 -1
  36. package/dist/adapters/github/types.d.ts.map +1 -1
  37. package/dist/adapters/github/types.js.map +1 -1
  38. package/dist/adapters/progressive-renderer.js +1 -1
  39. package/dist/adapters/progressive-renderer.js.map +1 -1
  40. package/dist/adapters/shared.js +4 -4
  41. package/dist/adapters/shared.js.map +1 -1
  42. package/dist/adapters/slack/blocks.d.ts.map +1 -1
  43. package/dist/adapters/slack/blocks.js +3 -1
  44. package/dist/adapters/slack/blocks.js.map +1 -1
  45. package/dist/adapters/slack/bot.d.ts +12 -2
  46. package/dist/adapters/slack/bot.d.ts.map +1 -1
  47. package/dist/adapters/slack/bot.js +70 -9
  48. package/dist/adapters/slack/bot.js.map +1 -1
  49. package/dist/adapters/slack/context.d.ts +2 -1
  50. package/dist/adapters/slack/context.d.ts.map +1 -1
  51. package/dist/adapters/slack/context.js +1 -0
  52. package/dist/adapters/slack/context.js.map +1 -1
  53. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  54. package/dist/adapters/slack/response-lifecycle.js +4 -4
  55. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  56. package/dist/adapters/slack/session.d.ts.map +1 -1
  57. package/dist/adapters/slack/session.js +2 -3
  58. package/dist/adapters/slack/session.js.map +1 -1
  59. package/dist/adapters/slack/tools/attach.js +1 -1
  60. package/dist/adapters/slack/tools/attach.js.map +1 -1
  61. package/dist/adapters/telegram/bot.d.ts +2 -1
  62. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  63. package/dist/adapters/telegram/bot.js +6 -6
  64. package/dist/adapters/telegram/bot.js.map +1 -1
  65. package/dist/adapters/telegram/context.d.ts +2 -1
  66. package/dist/adapters/telegram/context.d.ts.map +1 -1
  67. package/dist/adapters/telegram/context.js.map +1 -1
  68. package/dist/adapters/telegram/html.d.ts.map +1 -1
  69. package/dist/adapters/telegram/html.js +10 -7
  70. package/dist/adapters/telegram/html.js.map +1 -1
  71. package/dist/adapters/types.d.ts +4 -2
  72. package/dist/adapters/types.d.ts.map +1 -1
  73. package/dist/adapters/types.js.map +1 -1
  74. package/dist/agent-events.d.ts +3 -2
  75. package/dist/agent-events.d.ts.map +1 -1
  76. package/dist/agent-events.js +19 -1
  77. package/dist/agent-events.js.map +1 -1
  78. package/dist/agent.d.ts.map +1 -1
  79. package/dist/agent.js +34 -9
  80. package/dist/agent.js.map +1 -1
  81. package/dist/cli/arg-grammar.d.ts.map +1 -1
  82. package/dist/cli/arg-grammar.js +5 -3
  83. package/dist/cli/arg-grammar.js.map +1 -1
  84. package/dist/cli/boot.d.ts.map +1 -1
  85. package/dist/cli/boot.js +5 -3
  86. package/dist/cli/boot.js.map +1 -1
  87. package/dist/cli/download.d.ts.map +1 -1
  88. package/dist/cli/download.js +2 -0
  89. package/dist/cli/download.js.map +1 -1
  90. package/dist/cli/ext-dev.d.ts.map +1 -1
  91. package/dist/cli/ext-dev.js +4 -1
  92. package/dist/cli/ext-dev.js.map +1 -1
  93. package/dist/cli/ext.d.ts.map +1 -1
  94. package/dist/cli/ext.js +123 -23
  95. package/dist/cli/ext.js.map +1 -1
  96. package/dist/cli/office.d.ts.map +1 -1
  97. package/dist/cli/office.js +2 -0
  98. package/dist/cli/office.js.map +1 -1
  99. package/dist/cli/types.d.ts +0 -6
  100. package/dist/cli/types.d.ts.map +1 -1
  101. package/dist/cli/types.js.map +1 -1
  102. package/dist/commands/admin.js +1 -1
  103. package/dist/commands/admin.js.map +1 -1
  104. package/dist/commands/auto-reply.js +2 -2
  105. package/dist/commands/auto-reply.js.map +1 -1
  106. package/dist/commands/extensions.js +1 -1
  107. package/dist/commands/extensions.js.map +1 -1
  108. package/dist/commands/login.js +1 -1
  109. package/dist/commands/login.js.map +1 -1
  110. package/dist/commands/manifest.d.ts +7 -0
  111. package/dist/commands/manifest.d.ts.map +1 -1
  112. package/dist/commands/manifest.js +28 -0
  113. package/dist/commands/manifest.js.map +1 -1
  114. package/dist/commands/model.d.ts.map +1 -1
  115. package/dist/commands/model.js +4 -1
  116. package/dist/commands/model.js.map +1 -1
  117. package/dist/commands/new.js +1 -1
  118. package/dist/commands/new.js.map +1 -1
  119. package/dist/commands/sandbox.js +3 -3
  120. package/dist/commands/sandbox.js.map +1 -1
  121. package/dist/commands/session-view.d.ts.map +1 -1
  122. package/dist/commands/session-view.js +5 -2
  123. package/dist/commands/session-view.js.map +1 -1
  124. package/dist/commands/types.d.ts +1 -1
  125. package/dist/commands/types.d.ts.map +1 -1
  126. package/dist/commands/types.js.map +1 -1
  127. package/dist/config.js +4 -4
  128. package/dist/config.js.map +1 -1
  129. package/dist/env-manifest.d.ts +2 -0
  130. package/dist/env-manifest.d.ts.map +1 -1
  131. package/dist/env-manifest.js +11 -1
  132. package/dist/env-manifest.js.map +1 -1
  133. package/dist/events.d.ts.map +1 -1
  134. package/dist/events.js +7 -6
  135. package/dist/events.js.map +1 -1
  136. package/dist/execution-resolver.d.ts.map +1 -1
  137. package/dist/execution-resolver.js +3 -2
  138. package/dist/execution-resolver.js.map +1 -1
  139. package/dist/extension-schedules.d.ts +31 -0
  140. package/dist/extension-schedules.d.ts.map +1 -0
  141. package/dist/extension-schedules.js +229 -0
  142. package/dist/extension-schedules.js.map +1 -0
  143. package/dist/harness/auth.js +5 -5
  144. package/dist/harness/auth.js.map +1 -1
  145. package/dist/harness/event-format.d.ts +2 -0
  146. package/dist/harness/event-format.d.ts.map +1 -1
  147. package/dist/harness/event-format.js +2 -1
  148. package/dist/harness/event-format.js.map +1 -1
  149. package/dist/harness/extensions/loader.d.ts +2 -2
  150. package/dist/harness/extensions/loader.d.ts.map +1 -1
  151. package/dist/harness/extensions/loader.js +135 -33
  152. package/dist/harness/extensions/loader.js.map +1 -1
  153. package/dist/harness/extensions/registry.d.ts +33 -1
  154. package/dist/harness/extensions/registry.d.ts.map +1 -1
  155. package/dist/harness/extensions/registry.js +79 -2
  156. package/dist/harness/extensions/registry.js.map +1 -1
  157. package/dist/harness/extensions/types.d.ts +165 -21
  158. package/dist/harness/extensions/types.d.ts.map +1 -1
  159. package/dist/harness/extensions/types.js.map +1 -1
  160. package/dist/harness/index.d.ts +2 -2
  161. package/dist/harness/index.d.ts.map +1 -1
  162. package/dist/harness/index.js +1 -1
  163. package/dist/harness/index.js.map +1 -1
  164. package/dist/harness/models.d.ts.map +1 -1
  165. package/dist/harness/models.js +36 -24
  166. package/dist/harness/models.js.map +1 -1
  167. package/dist/harness/runner.js +2 -2
  168. package/dist/harness/runner.js.map +1 -1
  169. package/dist/harness/session-store.d.ts.map +1 -1
  170. package/dist/harness/session-store.js +9 -7
  171. package/dist/harness/session-store.js.map +1 -1
  172. package/dist/harness/skills.js +3 -3
  173. package/dist/harness/skills.js.map +1 -1
  174. package/dist/harness/subagent-profiles.js +2 -2
  175. package/dist/harness/subagent-profiles.js.map +1 -1
  176. package/dist/harness/subagent-runner.js +3 -3
  177. package/dist/harness/subagent-runner.js.map +1 -1
  178. package/dist/index.d.ts +8 -8
  179. package/dist/index.d.ts.map +1 -1
  180. package/dist/index.js +5 -5
  181. package/dist/index.js.map +1 -1
  182. package/dist/main.js +58 -9
  183. package/dist/main.js.map +1 -1
  184. package/dist/observability/instrument.js +1 -1
  185. package/dist/observability/instrument.js.map +1 -1
  186. package/dist/observability/sentry.js +1 -1
  187. package/dist/observability/sentry.js.map +1 -1
  188. package/dist/office/index.d.ts +1 -1
  189. package/dist/office/index.d.ts.map +1 -1
  190. package/dist/office/index.js +1 -1
  191. package/dist/office/index.js.map +1 -1
  192. package/dist/office/layout.d.ts +7 -0
  193. package/dist/office/layout.d.ts.map +1 -1
  194. package/dist/office/layout.js +10 -1
  195. package/dist/office/layout.js.map +1 -1
  196. package/dist/office/registry.d.ts.map +1 -1
  197. package/dist/office/registry.js +7 -5
  198. package/dist/office/registry.js.map +1 -1
  199. package/dist/packages/materialize.js +10 -3
  200. package/dist/packages/materialize.js.map +1 -1
  201. package/dist/packages/source.d.ts +6 -0
  202. package/dist/packages/source.d.ts.map +1 -1
  203. package/dist/packages/source.js +14 -2
  204. package/dist/packages/source.js.map +1 -1
  205. package/dist/packages/types.d.ts +5 -3
  206. package/dist/packages/types.d.ts.map +1 -1
  207. package/dist/packages/types.js.map +1 -1
  208. package/dist/provisioner.js +5 -5
  209. package/dist/provisioner.js.map +1 -1
  210. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  211. package/dist/runtime/conversation-runtime.js +44 -2
  212. package/dist/runtime/conversation-runtime.js.map +1 -1
  213. package/dist/runtime/types.d.ts +10 -2
  214. package/dist/runtime/types.d.ts.map +1 -1
  215. package/dist/runtime/types.js.map +1 -1
  216. package/dist/sandbox/cloudflare.js +1 -1
  217. package/dist/sandbox/cloudflare.js.map +1 -1
  218. package/dist/sandbox/container.js +1 -2
  219. package/dist/sandbox/container.js.map +1 -1
  220. package/dist/sandbox/firecracker.js +1 -1
  221. package/dist/sandbox/firecracker.js.map +1 -1
  222. package/dist/sandbox/gondolin.d.ts.map +1 -1
  223. package/dist/sandbox/gondolin.js +1 -2
  224. package/dist/sandbox/gondolin.js.map +1 -1
  225. package/dist/sandbox/host.d.ts.map +1 -1
  226. package/dist/sandbox/host.js +4 -5
  227. package/dist/sandbox/host.js.map +1 -1
  228. package/dist/sandbox/index.js +1 -1
  229. package/dist/sandbox/index.js.map +1 -1
  230. package/dist/sandbox/utils.d.ts +2 -0
  231. package/dist/sandbox/utils.d.ts.map +1 -1
  232. package/dist/sandbox/utils.js +30 -1
  233. package/dist/sandbox/utils.js.map +1 -1
  234. package/dist/sessions/chat-history-sync.d.ts.map +1 -1
  235. package/dist/sessions/chat-history-sync.js +9 -8
  236. package/dist/sessions/chat-history-sync.js.map +1 -1
  237. package/dist/sessions/conversation-log.d.ts.map +1 -1
  238. package/dist/sessions/conversation-log.js +5 -2
  239. package/dist/sessions/conversation-log.js.map +1 -1
  240. package/dist/sessions/history-line.d.ts +9 -0
  241. package/dist/sessions/history-line.d.ts.map +1 -1
  242. package/dist/sessions/history-line.js +22 -1
  243. package/dist/sessions/history-line.js.map +1 -1
  244. package/dist/sessions/session-key.d.ts +5 -12
  245. package/dist/sessions/session-key.d.ts.map +1 -1
  246. package/dist/sessions/session-key.js +23 -0
  247. package/dist/sessions/session-key.js.map +1 -1
  248. package/dist/sessions/store.d.ts +2 -6
  249. package/dist/sessions/store.d.ts.map +1 -1
  250. package/dist/sessions/store.js +16 -16
  251. package/dist/sessions/store.js.map +1 -1
  252. package/dist/sessions/types.d.ts +2 -1
  253. package/dist/sessions/types.d.ts.map +1 -1
  254. package/dist/sessions/types.js.map +1 -1
  255. package/dist/tools/bash.js +1 -1
  256. package/dist/tools/bash.js.map +1 -1
  257. package/dist/tools/edit.d.ts.map +1 -1
  258. package/dist/tools/edit.js +4 -1
  259. package/dist/tools/edit.js.map +1 -1
  260. package/dist/tools/event.js +1 -1
  261. package/dist/tools/event.js.map +1 -1
  262. package/dist/tools/generate-image.js +3 -3
  263. package/dist/tools/generate-image.js.map +1 -1
  264. package/dist/tools/read.js +4 -4
  265. package/dist/tools/read.js.map +1 -1
  266. package/dist/tools/subagent.d.ts.map +1 -1
  267. package/dist/tools/subagent.js +9 -2
  268. package/dist/tools/subagent.js.map +1 -1
  269. package/dist/tools/types.d.ts +0 -26
  270. package/dist/tools/types.d.ts.map +1 -1
  271. package/dist/tools/types.js.map +1 -1
  272. package/dist/trigger.d.ts +2 -1
  273. package/dist/trigger.d.ts.map +1 -1
  274. package/dist/trigger.js.map +1 -1
  275. package/dist/types.d.ts +108 -16
  276. package/dist/types.d.ts.map +1 -1
  277. package/dist/types.js +2 -2
  278. package/dist/types.js.map +1 -1
  279. package/dist/utils/file-guards.d.ts +9 -0
  280. package/dist/utils/file-guards.d.ts.map +1 -1
  281. package/dist/utils/file-guards.js +44 -1
  282. package/dist/utils/file-guards.js.map +1 -1
  283. package/dist/vault/disabled.d.ts.map +1 -1
  284. package/dist/vault/disabled.js +3 -0
  285. package/dist/vault/disabled.js.map +1 -1
  286. package/dist/vault/index.d.ts +29 -0
  287. package/dist/vault/index.d.ts.map +1 -1
  288. package/dist/vault/index.js +68 -4
  289. package/dist/vault/index.js.map +1 -1
  290. package/dist/vault/types.d.ts +2 -0
  291. package/dist/vault/types.d.ts.map +1 -1
  292. package/dist/vault/types.js.map +1 -1
  293. package/dist/web/admin/portal.d.ts +1 -1
  294. package/dist/web/admin/portal.d.ts.map +1 -1
  295. package/dist/web/admin/portal.js +194 -22
  296. package/dist/web/admin/portal.js.map +1 -1
  297. package/dist/web/admin/provider-models.js +1 -1
  298. package/dist/web/admin/provider-models.js.map +1 -1
  299. package/dist/web/login/oauth.js +1 -1
  300. package/dist/web/login/oauth.js.map +1 -1
  301. package/dist/web/login/portal.d.ts +1 -1
  302. package/dist/web/login/portal.d.ts.map +1 -1
  303. package/dist/web/login/portal.js +13 -36
  304. package/dist/web/login/portal.js.map +1 -1
  305. package/dist/web/portal-shell.d.ts +17 -0
  306. package/dist/web/portal-shell.d.ts.map +1 -1
  307. package/dist/web/portal-shell.js +66 -1
  308. package/dist/web/portal-shell.js.map +1 -1
  309. package/dist/web/server.d.ts +3 -2
  310. package/dist/web/server.d.ts.map +1 -1
  311. package/dist/web/server.js +3 -3
  312. package/dist/web/server.js.map +1 -1
  313. package/dist/web/session-view/portal.d.ts +1 -1
  314. package/dist/web/session-view/portal.d.ts.map +1 -1
  315. package/dist/web/session-view/portal.js +10 -12
  316. package/dist/web/session-view/portal.js.map +1 -1
  317. package/dist/web/session-view/service.d.ts.map +1 -1
  318. package/dist/web/session-view/service.js +11 -6
  319. package/dist/web/session-view/service.js.map +1 -1
  320. package/dist/web/session-view/types.d.ts +0 -4
  321. package/dist/web/session-view/types.d.ts.map +1 -1
  322. package/dist/web/session-view/types.js.map +1 -1
  323. package/dist/web/token-store.js +1 -1
  324. package/dist/web/token-store.js.map +1 -1
  325. package/package.json +8 -17
  326. package/dist/cli/ext-git.d.ts +0 -17
  327. package/dist/cli/ext-git.d.ts.map +0 -1
  328. package/dist/cli/ext-git.js +0 -107
  329. package/dist/cli/ext-git.js.map +0 -1
  330. package/dist/commands/parse.d.ts +0 -7
  331. package/dist/commands/parse.d.ts.map +0 -1
  332. package/dist/commands/parse.js +0 -14
  333. package/dist/commands/parse.js.map +0 -1
  334. package/dist/commands/text.d.ts +0 -2
  335. package/dist/commands/text.d.ts.map +0 -1
  336. package/dist/commands/text.js +0 -16
  337. package/dist/commands/text.js.map +0 -1
  338. package/dist/harness/extensions/blockkit.d.ts +0 -19
  339. package/dist/harness/extensions/blockkit.d.ts.map +0 -1
  340. package/dist/harness/extensions/blockkit.js +0 -42
  341. package/dist/harness/extensions/blockkit.js.map +0 -1
  342. package/dist/sandbox/path-context.d.ts +0 -3
  343. package/dist/sandbox/path-context.d.ts.map +0 -1
  344. package/dist/sandbox/path-context.js +0 -30
  345. package/dist/sandbox/path-context.js.map +0 -1
  346. package/dist/sessions/metadata.d.ts +0 -3
  347. package/dist/sessions/metadata.d.ts.map +0 -1
  348. package/dist/sessions/metadata.js +0 -11
  349. package/dist/sessions/metadata.js.map +0 -1
  350. package/dist/sessions/policy.d.ts +0 -6
  351. package/dist/sessions/policy.d.ts.map +0 -1
  352. package/dist/sessions/policy.js +0 -25
  353. package/dist/sessions/policy.js.map +0 -1
  354. package/dist/tools/truncate.d.ts +0 -33
  355. package/dist/tools/truncate.d.ts.map +0 -1
  356. package/dist/tools/truncate.js +0 -184
  357. package/dist/tools/truncate.js.map +0 -1
  358. package/dist/utils/date.d.ts +0 -10
  359. package/dist/utils/date.d.ts.map +0 -1
  360. package/dist/utils/date.js +0 -23
  361. package/dist/utils/date.js.map +0 -1
  362. package/dist/utils/env.d.ts +0 -3
  363. package/dist/utils/env.d.ts.map +0 -1
  364. package/dist/utils/env.js +0 -12
  365. package/dist/utils/env.js.map +0 -1
  366. package/dist/utils/fs-atomic.d.ts +0 -10
  367. package/dist/utils/fs-atomic.d.ts.map +0 -1
  368. package/dist/utils/fs-atomic.js +0 -45
  369. package/dist/utils/fs-atomic.js.map +0 -1
  370. package/dist/utils/html.d.ts +0 -2
  371. package/dist/utils/html.d.ts.map +0 -1
  372. package/dist/utils/html.js +0 -4
  373. package/dist/utils/html.js.map +0 -1
  374. package/dist/utils/http-body.d.ts +0 -10
  375. package/dist/utils/http-body.d.ts.map +0 -1
  376. package/dist/utils/http-body.js +0 -34
  377. package/dist/utils/http-body.js.map +0 -1
  378. package/dist/vault/injection.d.ts +0 -12
  379. package/dist/vault/injection.d.ts.map +0 -1
  380. package/dist/vault/injection.js +0 -30
  381. package/dist/vault/injection.js.map +0 -1
  382. package/dist/vault/policy.d.ts +0 -20
  383. package/dist/vault/policy.d.ts.map +0 -1
  384. package/dist/vault/policy.js +0 -20
  385. package/dist/vault/policy.js.map +0 -1
  386. package/dist/web/agent-events/portal.d.ts +0 -3
  387. package/dist/web/agent-events/portal.d.ts.map +0 -1
  388. package/dist/web/agent-events/portal.js +0 -19
  389. package/dist/web/agent-events/portal.js.map +0 -1
  390. package/dist/web/request.d.ts +0 -3
  391. package/dist/web/request.d.ts.map +0 -1
  392. package/dist/web/request.js +0 -13
  393. package/dist/web/request.js.map +0 -1
  394. package/dist/web/session-view/command.d.ts +0 -4
  395. package/dist/web/session-view/command.d.ts.map +0 -1
  396. package/dist/web/session-view/command.js +0 -10
  397. package/dist/web/session-view/command.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ext.js","sourceRoot":"","sources":["../../src/cli/ext.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `mikan ext` — manage extensions from the CLI.\n *\n * mikan ext install <source> [--global | --conversation <id>] [--state-dir <dir>]\n * mikan ext validate <path>\n * mikan ext list [--conversation <id>] [--state-dir <dir>]\n * mikan ext remove <slug> (--global | --conversation <id>) [--state-dir <dir>]\n *\n * `<source>` is a local path or a git URL (https://…, git@…, or github:owner/repo)\n * with an optional `#subpath` for extensions inside a repo. Reinstalling over an\n * existing extension updates it (data is preserved). Extensions install into the\n * host-only state dir (never the workspace); see src/harness/extensions/LAYOUT.md.\n */\nimport { cpSync, existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { basename, join, resolve } from \"node:path\";\nimport {\n defaultExtensionDirs,\n listInstalledExtensions,\n validateExtension,\n} from \"../harness/index.js\";\nimport { resolveStateDir, takeValueFlag } from \"./arg-grammar.js\";\nimport { runExtDevCommand } from \"./ext-dev.js\";\nimport { parseGitSource, resolveGitSource } from \"./ext-git.js\";\nimport { officeStateDir } from \"../office/index.js\";\nimport { resolveOwnedOfficeAddress } from \"../office/index.js\";\n\ninterface ExtArgs {\n action?: string;\n target?: string;\n stateDir: string;\n scope: \"global\" | \"conversation\" | undefined;\n conversationId?: string;\n}\n\nfunction parseExtArgs(argv: string[]): ExtArgs {\n // Shares the daemon's state-dir precedence: --state-dir > env > ~/.mikan.\n const stateDir = resolveStateDir(argv);\n let scope: ExtArgs[\"scope\"];\n let conversationId: string | undefined;\n const positional: string[] = [];\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n let taken;\n if (arg === \"--global\") scope = \"global\";\n else if ((taken = takeValueFlag(argv, i, \"--conversation\"))) {\n scope = \"conversation\";\n conversationId = taken.value || undefined;\n i = taken.lastIndex;\n } else if ((taken = takeValueFlag(argv, i, \"--state-dir\"))) {\n i = taken.lastIndex; // value already folded in by resolveStateDir\n } else positional.push(arg);\n }\n\n return { action: positional[0], target: positional[1], stateDir, scope, conversationId };\n}\n\n/** Directory that extension CODE for the chosen scope lives in. */\nfunction scopeExtensionsDir(args: ExtArgs): string {\n if (args.scope === \"global\") return join(args.stateDir, \"global\", \"extensions\");\n if (args.scope === \"conversation\" && args.conversationId) {\n // defaultExtensionDirs returns [global, conversation]; take the conversation one.\n return defaultExtensionDirs(\n resolveOwnedOfficeAddress(args.conversationId, args.stateDir),\n args.stateDir,\n )[1];\n }\n throw new Error(\"Specify a scope: --global or --conversation <id>\");\n}\n\nconst USAGE = `Usage:\n mikan ext dev <path> [--workspace <dir>] [--state-dir <dir>]\n Run an extension in a local stdin/stdout conversation — no Slack, no\n install. Edit, send /pi-new, test again.\n mikan ext install <source> (--global | --conversation <id>) [--state-dir <dir>]\n <source>: a local path, or a git URL / github:owner/repo with optional #subpath\n e.g. github:geminixiang/mikan#deploy/examples/extensions/agent-pm\n Reinstalling over an existing extension updates it (data preserved).\n mikan ext validate <path>\n mikan ext list [--conversation <id>] [--state-dir <dir>]\n mikan ext remove <slug> (--global | --conversation <id>) [--state-dir <dir>]`;\n\nfunction noop(): void {}\n\nexport async function runExtCommand(argv: string[]): Promise<number> {\n const args = parseExtArgs(argv);\n\n switch (args.action) {\n case \"dev\":\n // Parsed by the dev command itself: it takes different flags.\n return runExtDevCommand(argv.slice(1));\n case \"validate\":\n return validateAction(args);\n case \"install\":\n return installAction(args);\n case \"list\":\n return listAction(args);\n case \"remove\":\n return removeAction(args);\n default:\n console.error(USAGE);\n return 1;\n }\n}\n\nasync function validateAction(args: ExtArgs): Promise<number> {\n if (!args.target) {\n console.error(\"ext validate: missing <path>\");\n return 1;\n }\n const result = await validateExtension(resolve(args.target));\n printValidation(result);\n return result.ok ? 0 : 1;\n}\n\nasync function installAction(args: ExtArgs): Promise<number> {\n if (!args.target) {\n console.error(\"ext install: missing <path>\");\n return 1;\n }\n let destDir: string;\n try {\n destDir = scopeExtensionsDir(args);\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n // Resolve the source: a git URL (optionally with #subpath) is cloned to a\n // temp dir; a local path is used directly.\n const gitSpec = parseGitSource(args.target);\n let source: string;\n let cleanup = noop;\n if (gitSpec) {\n try {\n const resolved = resolveGitSource(gitSpec);\n source = resolved.dir;\n cleanup = resolved.cleanup;\n } catch (err) {\n console.error(`Failed to fetch ${gitSpec.url}: ${err instanceof Error ? err.message : err}`);\n return 1;\n }\n } else {\n source = resolve(args.target);\n }\n\n try {\n return await installResolved(args, source, destDir);\n } finally {\n cleanup();\n }\n}\n\nasync function installResolved(args: ExtArgs, source: string, destDir: string): Promise<number> {\n const result = await validateExtension(source);\n printValidation(result);\n if (!result.ok) {\n console.error(\"Refusing to install: fix the errors above.\");\n return 1;\n }\n\n // Install into a named subdirectory so the slug never degenerates to the\n // scope name. A bare file installs as <name>.<ext>; a directory as <name>/.\n const destName = basename(source);\n const dest = join(destDir, destName);\n mkdirSync(destDir, { recursive: true });\n // Reinstalling over an existing install is how updates work: replace it.\n const replaced = existsSync(dest);\n if (replaced) rmSync(dest, { recursive: true, force: true });\n cpSync(source, dest, { recursive: true });\n\n const scopeLabel =\n args.scope === \"global\" ? \"all conversations\" : `conversation ${args.conversationId}`;\n const dataDir =\n args.scope === \"global\"\n ? join(args.stateDir, \"global\", \"extension-data\", result.slug)\n : join(\n officeStateDir(\n args.stateDir,\n resolveOwnedOfficeAddress(args.conversationId!, args.stateDir),\n ),\n \"extension-data\",\n result.slug,\n );\n const verb = replaced ? \"Reinstalled\" : \"Installed\";\n console.log(`\\n${verb} ${result.name} (slug: ${result.slug}) for ${scopeLabel}.`);\n console.log(` code: ${dest}`);\n console.log(` data: ${dataDir} (created on first use, preserved on reinstall)`);\n console.log(\"Run /pi-new in the conversation to activate.\");\n return 0;\n}\n\nfunction listAction(args: ExtArgs): number {\n // List the requested scope, or both when no conversation is given.\n const dirs = args.conversationId\n ? defaultExtensionDirs(\n resolveOwnedOfficeAddress(args.conversationId, args.stateDir),\n args.stateDir,\n )\n : [join(args.stateDir, \"global\", \"extensions\")];\n const installed = listInstalledExtensions(dirs);\n if (installed.length === 0) {\n console.log(\"No extensions installed.\");\n return 0;\n }\n for (const info of installed) {\n const scope = basename(resolve(info.dir, \"..\")) === \"global\" ? \"global\" : \"conversation\";\n const version = info.version ? `@${info.version}` : \"\";\n console.log(`${info.name}${version} [${scope}] slug=${info.slug}`);\n if (info.description) console.log(` ${info.description}`);\n if (info.skillNames.length > 0) console.log(` skills: ${info.skillNames.join(\", \")}`);\n }\n return 0;\n}\n\nfunction removeAction(args: ExtArgs): number {\n if (!args.target) {\n console.error(\"ext remove: missing <slug>\");\n return 1;\n }\n let destDir: string;\n try {\n destDir = scopeExtensionsDir(args);\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n return 1;\n }\n // Match by installed slug so the caller doesn't need the exact dir name.\n const match = listInstalledExtensions([destDir]).find((info) => info.slug === args.target);\n if (!match) {\n console.error(`No extension with slug '${args.target}' in ${destDir}`);\n return 1;\n }\n // match.path is the entrypoint. Dir-form: its parent is the extension dir to\n // remove. Bare-file: the parent is the scan dir, so remove the file itself.\n const parent = resolve(match.path, \"..\");\n const removeTarget = parent === resolve(destDir) ? match.path : parent;\n rmSync(removeTarget, { recursive: true, force: true });\n console.log(`Removed ${match.name} (slug: ${match.slug}). Data left in place.`);\n console.log(\"Run /pi-new in the conversation to apply.\");\n return 0;\n}\n\nfunction printValidation(result: {\n ok: boolean;\n name: string;\n slug: string;\n version?: string;\n entrypoint?: string;\n skillNames: string[];\n errors: string[];\n warnings: string[];\n}): void {\n console.log(`${result.name}${result.version ? `@${result.version}` : \"\"} (slug: ${result.slug})`);\n if (result.entrypoint) console.log(` entrypoint: ${result.entrypoint}`);\n if (result.skillNames.length > 0) console.log(` skills: ${result.skillNames.join(\", \")}`);\n for (const warning of result.warnings) console.log(` warning: ${warning}`);\n for (const error of result.errors) console.error(` error: ${error}`);\n console.log(result.ok ? \" ✓ valid\" : \" ✗ invalid\");\n}\n"]}
1
+ {"version":3,"file":"ext.js","sourceRoot":"","sources":["../../src/cli/ext.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `mikan ext` — manage extensions from the CLI.\n *\n * mikan ext install <source> [--global | --conversation <id>] [--state-dir <dir>]\n * mikan ext validate <path>\n * mikan ext list [--conversation <id>] [--state-dir <dir>]\n * mikan ext remove <slug> (--global | --conversation <id>) [--state-dir <dir>]\n *\n * `<source>` is a local path or a git URL (https://…, git@…, or github:owner/repo)\n * with an optional `#subpath` for extensions inside a repo. Reinstalling over an\n * existing extension updates it (data is preserved). Extensions install into the\n * host-only state dir (never the workspace); see src/harness/extensions/LAYOUT.md.\n */\nimport { cpSync, existsSync, mkdirSync, readdirSync, rmSync } from \"node:fs\";\nimport { basename, join, resolve } from \"node:path\";\nimport {\n defaultExtensionDirs,\n listInstalledExtensions,\n validateExtension,\n} from \"../harness/index.js\";\nimport { resolveStateDir, takeValueFlag } from \"./arg-grammar.js\";\nimport { runExtDevCommand } from \"./ext-dev.js\";\nimport { materializeSource } from \"../packages/materialize.js\";\nimport { isGitSourceString } from \"../packages/source.js\";\nimport { officeStateDir } from \"../office/index.js\";\nimport { resolveOwnedOfficeAddress } from \"../office/index.js\";\n\ninterface ExtArgs {\n action?: string;\n target?: string;\n stateDir: string;\n scope: \"global\" | \"conversation\" | undefined;\n conversationId?: string;\n /** Workspace root; lets `remove --purge` sweep the events bus. */\n workspaceDir?: string;\n /** With `remove`: also sweep schedules, secrets, and data for the slug. */\n purge: boolean;\n}\n\nfunction parseExtArgs(argv: string[]): ExtArgs {\n // Shares the daemon's state-dir precedence: --state-dir > env > ~/.mikan.\n const stateDir = resolveStateDir(argv);\n let scope: ExtArgs[\"scope\"];\n let conversationId: string | undefined;\n let workspaceDir: string | undefined;\n let purge = false;\n const positional: string[] = [];\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === undefined) continue;\n let taken;\n if (arg === \"--global\") scope = \"global\";\n else if (arg === \"--purge\") purge = true;\n else if ((taken = takeValueFlag(argv, i, \"--conversation\"))) {\n scope = \"conversation\";\n conversationId = taken.value || undefined;\n i = taken.lastIndex;\n } else if ((taken = takeValueFlag(argv, i, \"--workspace\"))) {\n workspaceDir = taken.value ? resolve(taken.value) : undefined;\n i = taken.lastIndex;\n } else if ((taken = takeValueFlag(argv, i, \"--state-dir\"))) {\n i = taken.lastIndex; // value already folded in by resolveStateDir\n } else positional.push(arg);\n }\n\n return {\n action: positional[0] ?? \"\",\n target: positional[1],\n stateDir,\n scope,\n conversationId,\n workspaceDir,\n purge,\n };\n}\n\n/** Directory that extension CODE for the chosen scope lives in. */\nfunction scopeExtensionsDir(args: ExtArgs): string {\n if (args.scope === \"global\") return join(args.stateDir, \"global\", \"extensions\");\n if (args.scope === \"conversation\" && args.conversationId) {\n // defaultExtensionDirs returns [global, conversation]; take the conversation one.\n const conversationDir = defaultExtensionDirs(\n resolveOwnedOfficeAddress(args.conversationId, args.stateDir),\n args.stateDir,\n )[1];\n if (conversationDir === undefined) {\n throw new Error(\"defaultExtensionDirs returned no conversation directory\");\n }\n return conversationDir;\n }\n throw new Error(\"Specify a scope: --global or --conversation <id>\");\n}\n\nconst USAGE = `Usage:\n mikan ext dev <path> [--workspace <dir>] [--state-dir <dir>]\n Run an extension in a local stdin/stdout conversation — no Slack, no\n install. Edit, send /pi-new, test again.\n mikan ext install <source> (--global | --conversation <id>) [--state-dir <dir>]\n <source>: a local path, or a git URL / github:owner/repo with optional @ref and #subpath\n e.g. github:geminixiang/mikan#deploy/examples/extensions/agent-pm\n Reinstalling over an existing extension updates it (data preserved).\n mikan ext validate <path>\n mikan ext list [--conversation <id>] [--state-dir <dir>]\n mikan ext remove <slug> (--global | --conversation <id>) [--purge] [--workspace <dir>] [--state-dir <dir>]\n Removes the extension's code. --purge also sweeps its schedules, secrets\n vault, and data dirs; add --workspace to sweep its event files too.`;\n\nexport async function runExtCommand(argv: string[]): Promise<number> {\n const args = parseExtArgs(argv);\n\n switch (args.action) {\n case \"dev\":\n // Parsed by the dev command itself: it takes different flags.\n return runExtDevCommand(argv.slice(1));\n case \"validate\":\n return validateAction(args);\n case \"install\":\n return installAction(args);\n case \"list\":\n return listAction(args);\n case \"remove\":\n return removeAction(args);\n default:\n console.error(USAGE);\n return 1;\n }\n}\n\nasync function validateAction(args: ExtArgs): Promise<number> {\n if (!args.target) {\n console.error(\"ext validate: missing <path>\");\n return 1;\n }\n const result = await validateExtension(resolve(args.target));\n printValidation(result);\n return result.ok ? 0 : 1;\n}\n\nasync function installAction(args: ExtArgs): Promise<number> {\n if (!args.target) {\n console.error(\"ext install: missing <path>\");\n return 1;\n }\n let destDir: string;\n try {\n destDir = scopeExtensionsDir(args);\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n // Resolve the source: a git source materializes through the packages layer\n // (cached clone under the scope's git/ dir, @ref supported); a local path is\n // used directly. `refresh` re-fetches so reinstall always picks up updates.\n let source: string;\n if (isGitSourceString(args.target)) {\n try {\n source = materializeSource(args.target, {\n scope: args.scope!,\n address:\n args.scope === \"conversation\"\n ? resolveOwnedOfficeAddress(args.conversationId!, args.stateDir)\n : undefined,\n stateDir: args.stateDir,\n mode: \"refresh\",\n }).dir;\n } catch (err) {\n console.error(`Failed to fetch ${args.target}: ${err instanceof Error ? err.message : err}`);\n return 1;\n }\n } else {\n source = resolve(args.target);\n }\n\n return await installResolved(args, source, destDir);\n}\n\nasync function installResolved(args: ExtArgs, source: string, destDir: string): Promise<number> {\n const result = await validateExtension(source);\n printValidation(result);\n if (!result.ok) {\n console.error(\"Refusing to install: fix the errors above.\");\n return 1;\n }\n\n // Install into a named subdirectory so the slug never degenerates to the\n // scope name. A bare file installs as <name>.<ext>; a directory as <name>/.\n const destName = basename(source);\n const dest = join(destDir, destName);\n mkdirSync(destDir, { recursive: true });\n // Reinstalling over an existing install is how updates work: replace it.\n const replaced = existsSync(dest);\n if (replaced) rmSync(dest, { recursive: true, force: true });\n cpSync(source, dest, { recursive: true });\n\n const scopeLabel =\n args.scope === \"global\" ? \"all conversations\" : `conversation ${args.conversationId}`;\n const dataDir =\n args.scope === \"global\"\n ? join(args.stateDir, \"global\", \"extension-data\", result.slug)\n : join(\n officeStateDir(\n args.stateDir,\n resolveOwnedOfficeAddress(args.conversationId!, args.stateDir),\n ),\n \"extension-data\",\n result.slug,\n );\n const verb = replaced ? \"Reinstalled\" : \"Installed\";\n console.log(`\\n${verb} ${result.name} (slug: ${result.slug}) for ${scopeLabel}.`);\n console.log(` code: ${dest}`);\n console.log(` data: ${dataDir} (created on first use, preserved on reinstall)`);\n console.log(\"Run /pi-new in the conversation to activate.\");\n return 0;\n}\n\nfunction listAction(args: ExtArgs): number {\n // List the requested scope, or both when no conversation is given.\n const dirs = args.conversationId\n ? defaultExtensionDirs(\n resolveOwnedOfficeAddress(args.conversationId, args.stateDir),\n args.stateDir,\n )\n : [join(args.stateDir, \"global\", \"extensions\")];\n const installed = listInstalledExtensions(dirs);\n if (installed.length === 0) {\n console.log(\"No extensions installed.\");\n return 0;\n }\n for (const info of installed) {\n const scope = basename(resolve(info.dir, \"..\")) === \"global\" ? \"global\" : \"conversation\";\n const version = info.version ? `@${info.version}` : \"\";\n console.log(`${info.name}${version} [${scope}] slug=${info.slug}`);\n if (info.description) console.log(` ${info.description}`);\n if (info.skillNames.length > 0) console.log(` skills: ${info.skillNames.join(\", \")}`);\n if (info.secrets.length > 0) {\n const keys = info.secrets.map((s) => `${s.key}${s.required ? \" (required)\" : \"\"}`);\n console.log(` secrets: ${keys.join(\", \")}`);\n }\n }\n return 0;\n}\n\n/**\n * Everything a slug leaves behind outside its code dir: callback-schedule\n * files, the secrets vault, data dirs, and (when the workspace is known)\n * `ext.<slug>.*` / `extrun.<slug>.*` files on the events bus.\n */\ninterface ExtensionResidue {\n callbackScheduleFiles: string[];\n vaultDir?: string;\n dataDirs: string[];\n eventFiles: string[];\n}\n\n/** Files in `dir` (non-recursive) whose names satisfy `matches`; [] when absent. */\nfunction matchingFiles(dir: string, matches: (filename: string) => boolean): string[] {\n if (!existsSync(dir)) return [];\n return readdirSync(dir)\n .filter(matches)\n .map((filename) => join(dir, filename));\n}\n\nfunction findExtensionResidue(\n slug: string,\n stateDir: string,\n workspaceDir?: string,\n): ExtensionResidue {\n const callbackScheduleFiles: string[] = [];\n const dataDirs: string[] = [];\n const conversationsDir = join(stateDir, \"conversations\");\n const officeDirs = existsSync(conversationsDir) ? readdirSync(conversationsDir) : [];\n for (const officeDir of officeDirs) {\n callbackScheduleFiles.push(\n ...matchingFiles(\n join(conversationsDir, officeDir, \"extension-schedules\"),\n (filename) => filename.startsWith(`${slug}.`) && filename.endsWith(\".json\"),\n ),\n );\n const dataDir = join(conversationsDir, officeDir, \"extension-data\", slug);\n if (existsSync(dataDir)) dataDirs.push(dataDir);\n }\n const globalDataDir = join(stateDir, \"global\", \"extension-data\", slug);\n if (existsSync(globalDataDir)) dataDirs.push(globalDataDir);\n\n const eventFiles = workspaceDir\n ? matchingFiles(\n join(workspaceDir, \"events\"),\n (filename) =>\n (filename.startsWith(`ext.${slug}.`) || filename.startsWith(`extrun.${slug}.`)) &&\n filename.endsWith(\".json\"),\n )\n : [];\n\n const vaultDir = join(stateDir, \"vaults\", \"extensions\", slug);\n return {\n callbackScheduleFiles,\n ...(existsSync(vaultDir) ? { vaultDir } : {}),\n dataDirs,\n eventFiles,\n };\n}\n\nfunction removeAction(args: ExtArgs): number {\n if (!args.target) {\n console.error(\"ext remove: missing <slug>\");\n return 1;\n }\n let destDir: string;\n try {\n destDir = scopeExtensionsDir(args);\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n return 1;\n }\n // Match by installed slug so the caller doesn't need the exact dir name.\n const match = listInstalledExtensions([destDir]).find((info) => info.slug === args.target);\n if (!match) {\n console.error(`No extension with slug '${args.target}' in ${destDir}`);\n return 1;\n }\n // match.path is the entrypoint. Dir-form: its parent is the extension dir to\n // remove. Bare-file: the parent is the scan dir, so remove the file itself.\n const parent = resolve(match.path, \"..\");\n const removeTarget = parent === resolve(destDir) ? match.path : parent;\n rmSync(removeTarget, { recursive: true, force: true });\n console.log(`Removed ${match.name} (slug: ${match.slug}).`);\n\n // Never sweep implicitly: the same slug may still be active through another\n // scope or a PACKAGES declaration this command cannot see. --purge is the\n // admin's statement that nothing else owns it.\n const residue = findExtensionResidue(match.slug, args.stateDir, args.workspaceDir);\n if (args.purge) {\n for (const file of [...residue.callbackScheduleFiles, ...residue.eventFiles]) {\n rmSync(file, { force: true });\n }\n for (const dataDir of residue.dataDirs) rmSync(dataDir, { recursive: true, force: true });\n if (residue.vaultDir) rmSync(residue.vaultDir, { recursive: true, force: true });\n console.log(\n `Purged: ${residue.callbackScheduleFiles.length} schedule file(s), ` +\n `${residue.eventFiles.length} event file(s), ${residue.dataDirs.length} data dir(s)` +\n `${residue.vaultDir ? \", secrets vault\" : \"\"}.`,\n );\n if (!args.workspaceDir) {\n console.log(\n \"Note: events-bus files were not swept — pass --workspace <dir> to include them.\",\n );\n }\n if (residue.callbackScheduleFiles.length > 0) {\n console.log(\"A running daemon keeps deleted schedules armed until restart.\");\n }\n } else {\n const leftovers = [\n residue.callbackScheduleFiles.length > 0\n ? `${residue.callbackScheduleFiles.length} schedule file(s)`\n : undefined,\n residue.vaultDir ? \"secrets vault\" : undefined,\n residue.dataDirs.length > 0 ? `${residue.dataDirs.length} data dir(s)` : undefined,\n residue.eventFiles.length > 0 ? `${residue.eventFiles.length} event file(s)` : undefined,\n ].filter(Boolean);\n if (leftovers.length > 0) {\n console.log(`Left in place: ${leftovers.join(\", \")}. Re-run with --purge to sweep.`);\n }\n }\n console.log(\"Run /pi-new in the conversation to apply.\");\n return 0;\n}\n\nfunction printValidation(result: {\n ok: boolean;\n name: string;\n slug: string;\n version?: string;\n entrypoint?: string;\n skillNames: string[];\n secrets: Array<{ key: string; required?: boolean }>;\n errors: string[];\n warnings: string[];\n}): void {\n console.log(`${result.name}${result.version ? `@${result.version}` : \"\"} (slug: ${result.slug})`);\n if (result.entrypoint) console.log(` entrypoint: ${result.entrypoint}`);\n if (result.skillNames.length > 0) console.log(` skills: ${result.skillNames.join(\", \")}`);\n if (result.secrets.length > 0) {\n const keys = result.secrets.map((s) => `${s.key}${s.required ? \" (required)\" : \"\"}`);\n console.log(` secrets: ${keys.join(\", \")}`);\n }\n for (const warning of result.warnings) console.log(` warning: ${warning}`);\n for (const error of result.errors) console.error(` error: ${error}`);\n console.log(result.ok ? \" ✓ valid\" : \" ✗ invalid\");\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"office.d.ts","sourceRoot":"","sources":["../../src/cli/office.ts"],"names":[],"mappings":"AAoBA,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CA2BvD"}
1
+ {"version":3,"file":"office.d.ts","sourceRoot":"","sources":["../../src/cli/office.ts"],"names":[],"mappings":"AAoBA,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CA4BvD"}
@@ -22,6 +22,8 @@ export function runOfficeCommand(argv) {
22
22
  const positional = [];
23
23
  for (let i = 0; i < argv.length; i++) {
24
24
  const arg = argv[i];
25
+ if (arg === undefined)
26
+ continue;
25
27
  let taken;
26
28
  if ((taken = takeValueFlag(argv, i, "--workspace"))) {
27
29
  workspaceArg = taken.value;
@@ -1 +1 @@
1
- {"version":3,"file":"office.js","sourceRoot":"","sources":["../../src/cli/office.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `mikan office` — inspect and claim conversation offices from the CLI.\n *\n * mikan office list [--state-dir <dir>] [--workspace <dir>]\n * mikan office claim <conversationId> <platform> [--state-dir <dir>] [--workspace <dir>]\n *\n * `claim` records which platform owns a legacy raw-id conversation directory\n * when several platforms are enabled and boot cannot infer ownership. The\n * daemon performs the actual move on its next start. Run it with the daemon\n * stopped so the directory is not moving under a live runtime.\n */\nimport { join, resolve } from \"node:path\";\nimport { assertPlatformName } from \"../office/index.js\";\nimport { OfficeRegistry } from \"../office/index.js\";\nimport { resolveStateDir, takeValueFlag } from \"./arg-grammar.js\";\n\nconst USAGE = `Usage:\n mikan office list [--state-dir <dir>] [--workspace <dir>]\n mikan office claim <conversationId> <platform> [--state-dir <dir>] [--workspace <dir>]`;\n\nexport function runOfficeCommand(argv: string[]): number {\n const stateDir = resolveStateDir(argv);\n let workspaceArg: string | undefined;\n const positional: string[] = [];\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n let taken;\n if ((taken = takeValueFlag(argv, i, \"--workspace\"))) {\n workspaceArg = taken.value;\n i = taken.lastIndex;\n } else if ((taken = takeValueFlag(argv, i, \"--state-dir\"))) {\n i = taken.lastIndex; // consumed by resolveStateDir above\n } else if (arg.startsWith(\"-\")) {\n console.error(`Unknown flag: ${arg}\\n${USAGE}`);\n return 1;\n } else {\n positional.push(arg);\n }\n }\n const workspaceRoot = workspaceArg ? resolve(workspaceArg) : join(stateDir, \"workspace\");\n const [action, ...rest] = positional;\n\n if (action === \"list\") return listOffices(stateDir);\n if (action === \"claim\") return claimOffice(stateDir, workspaceRoot, rest);\n console.error(USAGE);\n return 1;\n}\n\nfunction listOffices(stateDir: string): number {\n const state = new OfficeRegistry(stateDir).getState();\n console.log(`Enabled platforms: ${state.enabledPlatforms.join(\", \") || \"(none)\"}`);\n console.log(`Offices (${state.offices.length}):`);\n for (const office of state.offices) {\n console.log(` ${office.platform} ${office.conversationId}`);\n }\n const pending = state.migrations.filter((record) => record.status !== \"committed\");\n if (pending.length > 0) {\n console.log(`Pending migrations (${pending.length}):`);\n for (const record of pending) {\n const error = record.error ? ` (${record.error})` : \"\";\n console.log(` ${record.status} ${record.rawConversationId}${error}`);\n }\n }\n return 0;\n}\n\nfunction claimOffice(stateDir: string, workspaceRoot: string, rest: string[]): number {\n const [rawConversationId, platform] = rest;\n if (!rawConversationId || !platform) {\n console.error(USAGE);\n return 1;\n }\n try {\n const record = new OfficeRegistry(stateDir).prepareLegacyMigration({\n rawConversationId,\n sourceDir: join(workspaceRoot, rawConversationId),\n workspaceRoot,\n ownerPlatform: assertPlatformName(platform),\n });\n console.log(\n `Claimed ${record.rawConversationId} for ${record.ownerPlatform}; ` +\n \"the daemon migrates the directory on its next start.\",\n );\n return 0;\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n return 1;\n }\n}\n"]}
1
+ {"version":3,"file":"office.js","sourceRoot":"","sources":["../../src/cli/office.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `mikan office` — inspect and claim conversation offices from the CLI.\n *\n * mikan office list [--state-dir <dir>] [--workspace <dir>]\n * mikan office claim <conversationId> <platform> [--state-dir <dir>] [--workspace <dir>]\n *\n * `claim` records which platform owns a legacy raw-id conversation directory\n * when several platforms are enabled and boot cannot infer ownership. The\n * daemon performs the actual move on its next start. Run it with the daemon\n * stopped so the directory is not moving under a live runtime.\n */\nimport { join, resolve } from \"node:path\";\nimport { assertPlatformName } from \"../office/index.js\";\nimport { OfficeRegistry } from \"../office/index.js\";\nimport { resolveStateDir, takeValueFlag } from \"./arg-grammar.js\";\n\nconst USAGE = `Usage:\n mikan office list [--state-dir <dir>] [--workspace <dir>]\n mikan office claim <conversationId> <platform> [--state-dir <dir>] [--workspace <dir>]`;\n\nexport function runOfficeCommand(argv: string[]): number {\n const stateDir = resolveStateDir(argv);\n let workspaceArg: string | undefined;\n const positional: string[] = [];\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === undefined) continue;\n let taken;\n if ((taken = takeValueFlag(argv, i, \"--workspace\"))) {\n workspaceArg = taken.value;\n i = taken.lastIndex;\n } else if ((taken = takeValueFlag(argv, i, \"--state-dir\"))) {\n i = taken.lastIndex; // consumed by resolveStateDir above\n } else if (arg.startsWith(\"-\")) {\n console.error(`Unknown flag: ${arg}\\n${USAGE}`);\n return 1;\n } else {\n positional.push(arg);\n }\n }\n const workspaceRoot = workspaceArg ? resolve(workspaceArg) : join(stateDir, \"workspace\");\n const [action, ...rest] = positional;\n\n if (action === \"list\") return listOffices(stateDir);\n if (action === \"claim\") return claimOffice(stateDir, workspaceRoot, rest);\n console.error(USAGE);\n return 1;\n}\n\nfunction listOffices(stateDir: string): number {\n const state = new OfficeRegistry(stateDir).getState();\n console.log(`Enabled platforms: ${state.enabledPlatforms.join(\", \") || \"(none)\"}`);\n console.log(`Offices (${state.offices.length}):`);\n for (const office of state.offices) {\n console.log(` ${office.platform} ${office.conversationId}`);\n }\n const pending = state.migrations.filter((record) => record.status !== \"committed\");\n if (pending.length > 0) {\n console.log(`Pending migrations (${pending.length}):`);\n for (const record of pending) {\n const error = record.error ? ` (${record.error})` : \"\";\n console.log(` ${record.status} ${record.rawConversationId}${error}`);\n }\n }\n return 0;\n}\n\nfunction claimOffice(stateDir: string, workspaceRoot: string, rest: string[]): number {\n const [rawConversationId, platform] = rest;\n if (!rawConversationId || !platform) {\n console.error(USAGE);\n return 1;\n }\n try {\n const record = new OfficeRegistry(stateDir).prepareLegacyMigration({\n rawConversationId,\n sourceDir: join(workspaceRoot, rawConversationId),\n workspaceRoot,\n ownerPlatform: assertPlatformName(platform),\n });\n console.log(\n `Claimed ${record.rawConversationId} for ${record.ownerPlatform}; ` +\n \"the daemon migrates the directory on its next start.\",\n );\n return 0;\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n return 1;\n }\n}\n"]}
@@ -13,10 +13,4 @@ export interface BootPlan {
13
13
  sandbox: SandboxConfig;
14
14
  downloadChannel?: string;
15
15
  }
16
- export interface ResolvedGitSource {
17
- /** Local directory holding the extension (clone root or a subpath within). */
18
- dir: string;
19
- /** Remove the temporary clone. */
20
- cleanup: () => void;
21
- }
22
16
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC;IACrF,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC;IACrF,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SandboxConfig } from \"../sandbox/types.js\";\n\nexport interface BootPlan {\n mode: \"ext\" | \"office\" | \"env\" | \"help\" | \"version\" | \"onboard\" | \"download\" | \"run\";\n /** argv after `ext`, handed to runExtCommand. Only set for mode \"ext\". */\n extArgs?: string[];\n /** argv after `office`, handed to runOfficeCommand. Only set for mode \"office\". */\n officeArgs?: string[];\n stateDir: string;\n /** Resolved working directory; defaults to `<stateDir>/workspace`. */\n workingDir: string;\n /** True when the working directory came from argv (then it is not auto-created). */\n workingDirExplicit: boolean;\n sandbox: SandboxConfig;\n downloadChannel?: string;\n}\n\nexport interface ResolvedGitSource {\n /** Local directory holding the extension (clone root or a subpath within). */\n dir: string;\n /** Remove the temporary clone. */\n cleanup: () => void;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SandboxConfig } from \"../sandbox/types.js\";\n\nexport interface BootPlan {\n mode: \"ext\" | \"office\" | \"env\" | \"help\" | \"version\" | \"onboard\" | \"download\" | \"run\";\n /** argv after `ext`, handed to runExtCommand. Only set for mode \"ext\". */\n extArgs?: string[];\n /** argv after `office`, handed to runOfficeCommand. Only set for mode \"office\". */\n officeArgs?: string[];\n stateDir: string;\n /** Resolved working directory; defaults to `<stateDir>/workspace`. */\n workingDir: string;\n /** True when the working directory came from argv (then it is not auto-created). */\n workingDirExplicit: boolean;\n sandbox: SandboxConfig;\n downloadChannel?: string;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { slashForms } from "./manifest.js";
2
- import { matchCommand } from "./parse.js";
2
+ import { matchCommand } from "./manifest.js";
3
3
  import { formatCommandSummary, replyPrivatelyWithContext } from "./utils.js";
4
4
  const ADMIN_COMMANDS = slashForms("admin");
5
5
  export class AdminCommandHandler {
@@ -1 +1 @@
1
- {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/commands/admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE7E,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;AAE3C,MAAM,OAAO,mBAAmB;IAC9B,KAAK,CAAC,SAAS,CAAC,OAAuB;QACrC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;QAE7F,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;YACpC,MAAM,yBAAyB,CAC7B,OAAO,EACP,oBAAoB,CAAC,OAAO,EAAE;gBAC5B,iCAAiC;gBACjC,0DAA0D;aAC3D,CAAC,EACF,EAAE,KAAK,EAAE,OAAO,EAAE,CACnB,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG;aAC7B,gBAAgB,EAAE;aAClB,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;QAC5D,MAAM,gBAAgB,GAAG,YAAY,EAAE,QAAQ,IAAI,YAAY,EAAE,WAAW,CAAC;QAE7E,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC;YACpD,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,gBAAgB,KAAK,CAAC,KAAK,EAAE,CAAC;QAC3E,MAAM,yBAAyB,CAC7B,OAAO,EACP,oBAAoB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,EAC3D,EAAE,KAAK,EAAE,OAAO,EAAE,CACnB,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;CACF","sourcesContent":["import { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./parse.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { formatCommandSummary, replyPrivatelyWithContext } from \"./utils.js\";\n\nconst ADMIN_COMMANDS = slashForms(\"admin\");\n\nexport class AdminCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!matchCommand(context.commandText, ADMIN_COMMANDS, { stripMention: true })) return false;\n\n if (!context.services.portalBaseUrl) {\n await replyPrivatelyWithContext(\n context,\n formatCommandSummary(\"Admin\", [\n \"Admin portal is not configured.\",\n \"Set `MIKAN_LINK_URL` or `MIKAN_LINK_PORT` on the server.\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const platformUser = context.bot\n .getMessagingInfo()\n .users.find((user) => user.id === context.platformUserId);\n const platformUserName = platformUser?.userName || platformUser?.displayName;\n\n const token = context.services.adminTokenStore.create({\n platform: context.platform,\n platformUserId: context.platformUserId,\n conversationId: context.conversationId,\n ...(platformUserName ? { platformUserName } : {}),\n });\n\n const url = `${context.services.portalBaseUrl}/admin?token=${token.token}`;\n await replyPrivatelyWithContext(\n context,\n formatCommandSummary(\"Admin\", [url, \"Expires: 30 minutes\"]),\n { style: \"muted\" },\n );\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/commands/admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE7E,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;AAE3C,MAAM,OAAO,mBAAmB;IAC9B,KAAK,CAAC,SAAS,CAAC,OAAuB;QACrC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;QAE7F,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;YACpC,MAAM,yBAAyB,CAC7B,OAAO,EACP,oBAAoB,CAAC,OAAO,EAAE;gBAC5B,iCAAiC;gBACjC,0DAA0D;aAC3D,CAAC,EACF,EAAE,KAAK,EAAE,OAAO,EAAE,CACnB,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG;aAC7B,gBAAgB,EAAE;aAClB,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;QAC5D,MAAM,gBAAgB,GAAG,YAAY,EAAE,QAAQ,IAAI,YAAY,EAAE,WAAW,CAAC;QAE7E,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC;YACpD,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,gBAAgB,KAAK,CAAC,KAAK,EAAE,CAAC;QAC3E,MAAM,yBAAyB,CAC7B,OAAO,EACP,oBAAoB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,EAC3D,EAAE,KAAK,EAAE,OAAO,EAAE,CACnB,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;CACF","sourcesContent":["import { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { formatCommandSummary, replyPrivatelyWithContext } from \"./utils.js\";\n\nconst ADMIN_COMMANDS = slashForms(\"admin\");\n\nexport class AdminCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!matchCommand(context.commandText, ADMIN_COMMANDS, { stripMention: true })) return false;\n\n if (!context.services.portalBaseUrl) {\n await replyPrivatelyWithContext(\n context,\n formatCommandSummary(\"Admin\", [\n \"Admin portal is not configured.\",\n \"Set `MIKAN_LINK_URL` or `MIKAN_LINK_PORT` on the server.\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const platformUser = context.bot\n .getMessagingInfo()\n .users.find((user) => user.id === context.platformUserId);\n const platformUserName = platformUser?.userName || platformUser?.displayName;\n\n const token = context.services.adminTokenStore.create({\n platform: context.platform,\n platformUserId: context.platformUserId,\n conversationId: context.conversationId,\n ...(platformUserName ? { platformUserName } : {}),\n });\n\n const url = `${context.services.portalBaseUrl}/admin?token=${token.token}`;\n await replyPrivatelyWithContext(\n context,\n formatCommandSummary(\"Admin\", [url, \"Expires: 30 minutes\"]),\n { style: \"muted\" },\n );\n return true;\n }\n}\n"]}
@@ -1,7 +1,7 @@
1
- import { join } from "path";
1
+ import { join } from "node:path";
2
2
  import { loadConversationAutoReplyConfig, saveConversationAutoReplyConfig, } from "../config.js";
3
3
  import { slashForms } from "./manifest.js";
4
- import { matchCommand } from "./parse.js";
4
+ import { matchCommand } from "./manifest.js";
5
5
  import { formatCommandSummary, replyDiagnosticWithContext } from "./utils.js";
6
6
  const AUTO_REPLY_COMMANDS = slashForms("auto-reply");
7
7
  function parseAutoReplyCommand(text) {
@@ -1 +1 @@
1
- {"version":3,"file":"auto-reply.js","sourceRoot":"","sources":["../../src/commands/auto-reply.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"path\";\nimport {\n type AutoReplyConfig,\n loadConversationAutoReplyConfig,\n saveConversationAutoReplyConfig,\n} from \"../config.js\";\nimport { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./parse.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { formatCommandSummary, replyDiagnosticWithContext } from \"./utils.js\";\n\ntype AutoReplyAction = { type: \"status\" } | { type: \"on\" } | { type: \"off\" } | { type: \"invalid\" };\n\nconst AUTO_REPLY_COMMANDS = slashForms(\"auto-reply\");\n\nfunction parseAutoReplyCommand(text: string): AutoReplyAction | null {\n const matched = matchCommand(text, AUTO_REPLY_COMMANDS, { stripMention: true });\n if (!matched) return null;\n if (matched.args.length === 0) return { type: \"status\" };\n\n const lower = matched.args.join(\" \").toLowerCase();\n if (lower === \"status\") return { type: \"status\" };\n if (lower === \"on\" || lower === \"enable\" || lower === \"enabled\") return { type: \"on\" };\n if (lower === \"off\" || lower === \"disable\" || lower === \"disabled\") return { type: \"off\" };\n\n return { type: \"invalid\" };\n}\n\nfunction formatAutoReplyStatus(config: AutoReplyConfig): string {\n const lines = [`Auto-reply is ${config.enabled ? \"enabled\" : \"disabled\"} for this channel.`];\n if (config.rules.length > 0) {\n lines.push(`Current rules: ${config.rules.join(\"; \")}`);\n }\n return formatCommandSummary(\"Auto Reply\", lines);\n}\n\nfunction applyAction(current: AutoReplyConfig, action: AutoReplyAction): AutoReplyConfig {\n switch (action.type) {\n case \"status\":\n case \"invalid\":\n return current;\n case \"on\":\n return { ...current, enabled: true };\n case \"off\":\n return { ...current, enabled: false };\n }\n}\n\n/**\n * @deprecated Auto-reply is kept for compatibility while its future is undecided.\n */\nexport class AutoReplyCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const action = parseAutoReplyCommand(context.commandText);\n if (!action) return false;\n\n if (context.privateConversation) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Auto Reply\", [\"只能在 group/channel 裡設定。\"]),\n { style: \"muted\" },\n );\n return true;\n }\n\n if (action.type === \"invalid\") {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Auto Reply\", [\"Usage: `/pi-auto-reply on|off|status`\"]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const conversationDir = context.services.workspace.office(context.address).dir;\n const current = loadConversationAutoReplyConfig(conversationDir);\n const next = applyAction(current, action);\n if (action.type === \"on\" || (action.type === \"off\" && current.enabled)) {\n saveConversationAutoReplyConfig(conversationDir, next);\n }\n\n const status = formatAutoReplyStatus(next);\n const text =\n action.type === \"on\"\n ? `${status}\\nEdit rules at: \\`${join(conversationDir, \"auto-reply\")}\\``\n : status;\n await replyDiagnosticWithContext(context.responder, text, {\n style: \"muted\",\n });\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"auto-reply.js","sourceRoot":"","sources":["../../src/commands/auto-reply.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport {\n type AutoReplyConfig,\n loadConversationAutoReplyConfig,\n saveConversationAutoReplyConfig,\n} from \"../config.js\";\nimport { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { formatCommandSummary, replyDiagnosticWithContext } from \"./utils.js\";\n\ntype AutoReplyAction = { type: \"status\" } | { type: \"on\" } | { type: \"off\" } | { type: \"invalid\" };\n\nconst AUTO_REPLY_COMMANDS = slashForms(\"auto-reply\");\n\nfunction parseAutoReplyCommand(text: string): AutoReplyAction | null {\n const matched = matchCommand(text, AUTO_REPLY_COMMANDS, { stripMention: true });\n if (!matched) return null;\n if (matched.args.length === 0) return { type: \"status\" };\n\n const lower = matched.args.join(\" \").toLowerCase();\n if (lower === \"status\") return { type: \"status\" };\n if (lower === \"on\" || lower === \"enable\" || lower === \"enabled\") return { type: \"on\" };\n if (lower === \"off\" || lower === \"disable\" || lower === \"disabled\") return { type: \"off\" };\n\n return { type: \"invalid\" };\n}\n\nfunction formatAutoReplyStatus(config: AutoReplyConfig): string {\n const lines = [`Auto-reply is ${config.enabled ? \"enabled\" : \"disabled\"} for this channel.`];\n if (config.rules.length > 0) {\n lines.push(`Current rules: ${config.rules.join(\"; \")}`);\n }\n return formatCommandSummary(\"Auto Reply\", lines);\n}\n\nfunction applyAction(current: AutoReplyConfig, action: AutoReplyAction): AutoReplyConfig {\n switch (action.type) {\n case \"status\":\n case \"invalid\":\n return current;\n case \"on\":\n return { ...current, enabled: true };\n case \"off\":\n return { ...current, enabled: false };\n }\n}\n\n/**\n * @deprecated Auto-reply is kept for compatibility while its future is undecided.\n */\nexport class AutoReplyCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const action = parseAutoReplyCommand(context.commandText);\n if (!action) return false;\n\n if (context.privateConversation) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Auto Reply\", [\"只能在 group/channel 裡設定。\"]),\n { style: \"muted\" },\n );\n return true;\n }\n\n if (action.type === \"invalid\") {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Auto Reply\", [\"Usage: `/pi-auto-reply on|off|status`\"]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const conversationDir = context.services.workspace.office(context.address).dir;\n const current = loadConversationAutoReplyConfig(conversationDir);\n const next = applyAction(current, action);\n if (action.type === \"on\" || (action.type === \"off\" && current.enabled)) {\n saveConversationAutoReplyConfig(conversationDir, next);\n }\n\n const status = formatAutoReplyStatus(next);\n const text =\n action.type === \"on\"\n ? `${status}\\nEdit rules at: \\`${join(conversationDir, \"auto-reply\")}\\``\n : status;\n await replyDiagnosticWithContext(context.responder, text, {\n style: \"muted\",\n });\n return true;\n }\n}\n"]}
@@ -3,7 +3,7 @@ import { basename, dirname, join } from "node:path";
3
3
  import { defaultExtensionDirs, listInstalledExtensions } from "../harness/index.js";
4
4
  import { effectiveStateDir } from "../cli/arg-grammar.js";
5
5
  import { slashForms } from "./manifest.js";
6
- import { matchCommand } from "./parse.js";
6
+ import { matchCommand } from "./manifest.js";
7
7
  import { replyDiagnosticWithContext } from "./utils.js";
8
8
  const EXTENSIONS_COMMANDS = slashForms("extensions");
9
9
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../src/commands/extensions.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport { defaultExtensionDirs, listInstalledExtensions } from \"../harness/index.js\";\nimport { effectiveStateDir } from \"../cli/arg-grammar.js\";\nimport { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./parse.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nconst EXTENSIONS_COMMANDS = slashForms(\"extensions\");\n\n/**\n * `/pi-extensions` — inventory of extensions installed for this conversation.\n *\n * Discovery-only (nothing is imported or activated): it reports what is on\n * disk under the host-only state dir, for both the `global` scope and this\n * conversation's scope. Doubles as the user-facing answer to \"why can the\n * bot suddenly do X\" and the operator-facing answer to \"what is installed\n * where\".\n */\nexport class ExtensionsCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const matched = matchCommand(context.commandText, EXTENSIONS_COMMANDS, { stripMention: true });\n if (!matched) return false;\n\n const dirs = defaultExtensionDirs(context.address, effectiveStateDir());\n const installed = listInstalledExtensions(dirs);\n\n // A root-level index file means an extension's contents were copied into\n // the scope directory itself; the loader skips it (the slug would\n // degenerate to the scope name). Surface that here, not just in logs.\n const misinstalled = dirs.filter((dir) =>\n [\"index.mjs\", \"index.js\"].some((file) => existsSync(join(dir, file))),\n );\n const misinstallLines = misinstalled.map(\n (dir) =>\n `⚠️ \\`${dir}/index.mjs\\` 位於範圍根目錄,已被忽略 — 請移入具名子目錄(如 \\`${dir}/my-ext/\\`)。`,\n );\n\n if (installed.length === 0 && misinstallLines.length === 0) {\n await replyDiagnosticWithContext(\n context.responder,\n [\n \"_Extensions_\",\n \"沒有安裝任何 extension。\",\n `安裝位置:\\`${dirs[0]}\\`(所有會話)或 \\`${dirs[1]}\\`(僅此會話)。`,\n ].join(\"\\n\"),\n { style: \"muted\" },\n );\n return true;\n }\n\n const lines = [\"_Extensions_\", ...misinstallLines];\n for (const info of installed) {\n // info.dir ends in `<scope>/extensions`; the scope is its parent segment\n // (`global` or a conversation id).\n const scope = basename(dirname(info.dir)) === \"global\" ? \"global\" : \"this conversation\";\n const version = info.version ? `@${info.version}` : \"\";\n const slug = info.slug !== info.name ? ` (slug: ${info.slug})` : \"\";\n lines.push(`• *${info.name}*${version} — ${scope}${slug}`);\n if (info.description) lines.push(` ${info.description}`);\n if (info.skillNames.length > 0) lines.push(` skills: ${info.skillNames.join(\", \")}`);\n }\n lines.push(\"_(安裝/移除後,對話輸入 `/pi-new` 生效)_\");\n\n await replyDiagnosticWithContext(context.responder, lines.join(\"\\n\"), { style: \"muted\" });\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../src/commands/extensions.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport { defaultExtensionDirs, listInstalledExtensions } from \"../harness/index.js\";\nimport { effectiveStateDir } from \"../cli/arg-grammar.js\";\nimport { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nconst EXTENSIONS_COMMANDS = slashForms(\"extensions\");\n\n/**\n * `/pi-extensions` — inventory of extensions installed for this conversation.\n *\n * Discovery-only (nothing is imported or activated): it reports what is on\n * disk under the host-only state dir, for both the `global` scope and this\n * conversation's scope. Doubles as the user-facing answer to \"why can the\n * bot suddenly do X\" and the operator-facing answer to \"what is installed\n * where\".\n */\nexport class ExtensionsCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const matched = matchCommand(context.commandText, EXTENSIONS_COMMANDS, { stripMention: true });\n if (!matched) return false;\n\n const dirs = defaultExtensionDirs(context.address, effectiveStateDir());\n const installed = listInstalledExtensions(dirs);\n\n // A root-level index file means an extension's contents were copied into\n // the scope directory itself; the loader skips it (the slug would\n // degenerate to the scope name). Surface that here, not just in logs.\n const misinstalled = dirs.filter((dir) =>\n [\"index.mjs\", \"index.js\"].some((file) => existsSync(join(dir, file))),\n );\n const misinstallLines = misinstalled.map(\n (dir) =>\n `⚠️ \\`${dir}/index.mjs\\` 位於範圍根目錄,已被忽略 — 請移入具名子目錄(如 \\`${dir}/my-ext/\\`)。`,\n );\n\n if (installed.length === 0 && misinstallLines.length === 0) {\n await replyDiagnosticWithContext(\n context.responder,\n [\n \"_Extensions_\",\n \"沒有安裝任何 extension。\",\n `安裝位置:\\`${dirs[0]}\\`(所有會話)或 \\`${dirs[1]}\\`(僅此會話)。`,\n ].join(\"\\n\"),\n { style: \"muted\" },\n );\n return true;\n }\n\n const lines = [\"_Extensions_\", ...misinstallLines];\n for (const info of installed) {\n // info.dir ends in `<scope>/extensions`; the scope is its parent segment\n // (`global` or a conversation id).\n const scope = basename(dirname(info.dir)) === \"global\" ? \"global\" : \"this conversation\";\n const version = info.version ? `@${info.version}` : \"\";\n const slug = info.slug !== info.name ? ` (slug: ${info.slug})` : \"\";\n lines.push(`• *${info.name}*${version} — ${scope}${slug}`);\n if (info.description) lines.push(` ${info.description}`);\n if (info.skillNames.length > 0) lines.push(` skills: ${info.skillNames.join(\", \")}`);\n }\n lines.push(\"_(安裝/移除後,對話輸入 `/pi-new` 生效)_\");\n\n await replyDiagnosticWithContext(context.responder, lines.join(\"\\n\"), { style: \"muted\" });\n return true;\n }\n}\n"]}
@@ -2,7 +2,7 @@ import * as log from "../log.js";
2
2
  import { credentialAuthorizationKey } from "../sandbox/identity.js";
3
3
  import { sharedVaultKey } from "../vault/index.js";
4
4
  import { slashForms } from "./manifest.js";
5
- import { matchCommand } from "./parse.js";
5
+ import { matchCommand } from "./manifest.js";
6
6
  import { formatCommandSummary, replyDiagnosticWithContext } from "./utils.js";
7
7
  import { createOfficeAddress } from "../office/index.js";
8
8
  const LOGIN_COMMANDS = slashForms("login");
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import * as log from \"../log.js\";\nimport { credentialAuthorizationKey } from \"../sandbox/identity.js\";\nimport { sharedVaultKey } from \"../vault/index.js\";\nimport { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./parse.js\";\nimport type { CommandContext, CommandHandler, ParsedLoginCommand } from \"./types.js\";\nimport { formatCommandSummary, replyDiagnosticWithContext } from \"./utils.js\";\nimport { createOfficeAddress } from \"../office/index.js\";\n\nconst LOGIN_COMMANDS = slashForms(\"login\");\n\nexport function parseLoginCommand(text: string): ParsedLoginCommand | null {\n const matched = matchCommand(text, LOGIN_COMMANDS);\n if (!matched) return null;\n\n const [subcommand, operation, name, ...extra] = matched.args;\n if (!subcommand) return { action: \"setup\" };\n\n if (subcommand.toLowerCase() === \"shared\") {\n const op = operation?.toLowerCase();\n if (op === \"list\" && !name && extra.length === 0) return { action: \"shared_list\" };\n if ((op === \"create\" || op === \"update\" || op === \"delete\") && name && extra.length === 0) {\n return {\n action: `shared_${op}` as \"shared_create\" | \"shared_update\" | \"shared_delete\",\n name,\n };\n }\n return null;\n }\n\n if (subcommand.toLowerCase() === \"copy\" && operation && !name && extra.length === 0) {\n return { action: \"copy_shared\", name: operation };\n }\n\n // Backward-compatible provider arguments open the generic login page.\n if (!operation && extra.length === 0) return { action: \"setup\" };\n return null;\n}\n\nfunction ensureLoginVault(context: CommandContext): string {\n const { services, platformUserId, conversationId, vaultConversationId } = context;\n // The vault target is a conversation on the same platform; a command may\n // aim at another conversation's vault via vaultConversationId.\n return credentialAuthorizationKey(services.sandbox, {\n userId: platformUserId,\n address: createOfficeAddress(context.address.platform, vaultConversationId ?? conversationId),\n });\n}\n\nasync function replyVault(context: CommandContext, lines: string[]): Promise<void> {\n await replyDiagnosticWithContext(context.responder, formatCommandSummary(\"Vault\", lines), {\n style: \"muted\",\n });\n}\n\nasync function refreshCopiedVaultRuntime(\n context: CommandContext,\n vaultId: string,\n): Promise<string | undefined> {\n if (context.services.sandbox.type !== \"image\") return undefined;\n\n const targetAddress = createOfficeAddress(\n context.address.platform,\n context.vaultConversationId ?? context.conversationId,\n );\n const cleared = context.services.runtime?.refreshConversationEnvironment(targetAddress);\n if (cleared === false) {\n return \"A session is currently running, so the sandbox was not restarted. The copied credentials will be applied after the run finishes and the sandbox is recreated.\";\n }\n\n if (!context.services.provisioner) {\n return \"The cached session was refreshed. The sandbox will pick up copied credentials on the next provision.\";\n }\n\n await context.services.provisioner.remove(vaultId);\n return \"The sandbox container was removed and will be recreated with the copied env and file mounts on the next message.\";\n}\n\nexport class LoginCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseLoginCommand(context.commandText);\n if (!parsed) return false;\n\n if (!context.privateConversation) {\n await replyVault(context, [\n \"為了保護你的憑證,`/login` 只能在與機器人的私訊中使用。\",\n \"請先私訊機器人,再重新執行 `/login`。\",\n ]);\n return true;\n }\n\n if (parsed.action === \"shared_list\") {\n const profiles = context.services.vaultManager.listSharedVaults();\n await replyVault(\n context,\n profiles.length > 0\n ? [\"Shared login profiles:\", ...profiles.map((name) => `- ${name}`)]\n : [\"No shared login profiles found.\"],\n );\n return true;\n }\n\n if (parsed.action === \"shared_delete\") {\n try {\n const deleted = context.services.vaultManager.deleteSharedVault(parsed.name);\n await replyVault(context, [\n deleted\n ? `Deleted shared login profile \\`${parsed.name}\\`.`\n : `Shared login profile \\`${parsed.name}\\` does not exist.`,\n ]);\n } catch (error) {\n await replyVault(context, [error instanceof Error ? error.message : String(error)]);\n }\n return true;\n }\n\n if (parsed.action === \"copy_shared\") {\n try {\n const vaultId = ensureLoginVault(context);\n const result = context.services.vaultManager.copySharedVaultTo(parsed.name, vaultId);\n const refreshNote = await refreshCopiedVaultRuntime(context, vaultId);\n await replyVault(context, [\n `Copied shared login profile \\`${parsed.name}\\` into this conversation.`,\n \"Shared values overwrite matching conversation values; conversation-only values are kept.\",\n `Copied: ${result.envKeysCopied} env key(s), ${result.filesCopied} file(s).`,\n ...(refreshNote ? [refreshNote] : []),\n ]);\n } catch (error) {\n await replyVault(context, [error instanceof Error ? error.message : String(error)]);\n }\n return true;\n }\n\n if (!context.services.portalBaseUrl) {\n await replyVault(context, [\n \"Login is not configured.\",\n \"Set `MIKAN_LINK_URL` or `MIKAN_LINK_PORT` on the server.\",\n ]);\n return true;\n }\n\n const isSharedSetup = parsed.action === \"shared_create\" || parsed.action === \"shared_update\";\n let vaultId: string;\n try {\n vaultId = isSharedSetup ? (sharedVaultKey(parsed.name) ?? \"\") : ensureLoginVault(context);\n if (!vaultId) {\n throw new Error(\n isSharedSetup ? `Invalid shared login profile name: ${parsed.name}` : \"Invalid vault id\",\n );\n }\n } catch (error) {\n log.logWarning(\n `[${context.conversationId}] Failed to prepare login vault for ${context.platform}/${context.platformUserId}`,\n error instanceof Error ? error.message : String(error),\n );\n await replyVault(context, [\n \"Login setup failed on the server.\",\n \"請稍後重試,或聯絡管理員檢查 vault 儲存權限。\",\n ]);\n return true;\n }\n\n const token = context.services.linkTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n vaultId,\n \"\",\n );\n const vaultLabel = isSharedSetup\n ? `shared login profile (${parsed.name})`\n : context.services.sandbox.type === \"container\"\n ? `container vault (${vaultId})`\n : \"your vault\";\n await replyVault(context, [\n `${context.services.portalBaseUrl}/link?token=${token.token}`,\n `Target: ${vaultLabel} · Expires: 15 minutes`,\n ]);\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import * as log from \"../log.js\";\nimport { credentialAuthorizationKey } from \"../sandbox/identity.js\";\nimport { sharedVaultKey } from \"../vault/index.js\";\nimport { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler, ParsedLoginCommand } from \"./types.js\";\nimport { formatCommandSummary, replyDiagnosticWithContext } from \"./utils.js\";\nimport { createOfficeAddress } from \"../office/index.js\";\n\nconst LOGIN_COMMANDS = slashForms(\"login\");\n\nexport function parseLoginCommand(text: string): ParsedLoginCommand | null {\n const matched = matchCommand(text, LOGIN_COMMANDS);\n if (!matched) return null;\n\n const [subcommand, operation, name, ...extra] = matched.args;\n if (!subcommand) return { action: \"setup\" };\n\n if (subcommand.toLowerCase() === \"shared\") {\n const op = operation?.toLowerCase();\n if (op === \"list\" && !name && extra.length === 0) return { action: \"shared_list\" };\n if ((op === \"create\" || op === \"update\" || op === \"delete\") && name && extra.length === 0) {\n return {\n action: `shared_${op}` as \"shared_create\" | \"shared_update\" | \"shared_delete\",\n name,\n };\n }\n return null;\n }\n\n if (subcommand.toLowerCase() === \"copy\" && operation && !name && extra.length === 0) {\n return { action: \"copy_shared\", name: operation };\n }\n\n // Backward-compatible provider arguments open the generic login page.\n if (!operation && extra.length === 0) return { action: \"setup\" };\n return null;\n}\n\nfunction ensureLoginVault(context: CommandContext): string {\n const { services, platformUserId, conversationId, vaultConversationId } = context;\n // The vault target is a conversation on the same platform; a command may\n // aim at another conversation's vault via vaultConversationId.\n return credentialAuthorizationKey(services.sandbox, {\n userId: platformUserId,\n address: createOfficeAddress(context.address.platform, vaultConversationId ?? conversationId),\n });\n}\n\nasync function replyVault(context: CommandContext, lines: string[]): Promise<void> {\n await replyDiagnosticWithContext(context.responder, formatCommandSummary(\"Vault\", lines), {\n style: \"muted\",\n });\n}\n\nasync function refreshCopiedVaultRuntime(\n context: CommandContext,\n vaultId: string,\n): Promise<string | undefined> {\n if (context.services.sandbox.type !== \"image\") return undefined;\n\n const targetAddress = createOfficeAddress(\n context.address.platform,\n context.vaultConversationId ?? context.conversationId,\n );\n const cleared = context.services.runtime?.refreshConversationEnvironment(targetAddress);\n if (cleared === false) {\n return \"A session is currently running, so the sandbox was not restarted. The copied credentials will be applied after the run finishes and the sandbox is recreated.\";\n }\n\n if (!context.services.provisioner) {\n return \"The cached session was refreshed. The sandbox will pick up copied credentials on the next provision.\";\n }\n\n await context.services.provisioner.remove(vaultId);\n return \"The sandbox container was removed and will be recreated with the copied env and file mounts on the next message.\";\n}\n\nexport class LoginCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseLoginCommand(context.commandText);\n if (!parsed) return false;\n\n if (!context.privateConversation) {\n await replyVault(context, [\n \"為了保護你的憑證,`/login` 只能在與機器人的私訊中使用。\",\n \"請先私訊機器人,再重新執行 `/login`。\",\n ]);\n return true;\n }\n\n if (parsed.action === \"shared_list\") {\n const profiles = context.services.vaultManager.listSharedVaults();\n await replyVault(\n context,\n profiles.length > 0\n ? [\"Shared login profiles:\", ...profiles.map((name) => `- ${name}`)]\n : [\"No shared login profiles found.\"],\n );\n return true;\n }\n\n if (parsed.action === \"shared_delete\") {\n try {\n const deleted = context.services.vaultManager.deleteSharedVault(parsed.name);\n await replyVault(context, [\n deleted\n ? `Deleted shared login profile \\`${parsed.name}\\`.`\n : `Shared login profile \\`${parsed.name}\\` does not exist.`,\n ]);\n } catch (error) {\n await replyVault(context, [error instanceof Error ? error.message : String(error)]);\n }\n return true;\n }\n\n if (parsed.action === \"copy_shared\") {\n try {\n const vaultId = ensureLoginVault(context);\n const result = context.services.vaultManager.copySharedVaultTo(parsed.name, vaultId);\n const refreshNote = await refreshCopiedVaultRuntime(context, vaultId);\n await replyVault(context, [\n `Copied shared login profile \\`${parsed.name}\\` into this conversation.`,\n \"Shared values overwrite matching conversation values; conversation-only values are kept.\",\n `Copied: ${result.envKeysCopied} env key(s), ${result.filesCopied} file(s).`,\n ...(refreshNote ? [refreshNote] : []),\n ]);\n } catch (error) {\n await replyVault(context, [error instanceof Error ? error.message : String(error)]);\n }\n return true;\n }\n\n if (!context.services.portalBaseUrl) {\n await replyVault(context, [\n \"Login is not configured.\",\n \"Set `MIKAN_LINK_URL` or `MIKAN_LINK_PORT` on the server.\",\n ]);\n return true;\n }\n\n const isSharedSetup = parsed.action === \"shared_create\" || parsed.action === \"shared_update\";\n let vaultId: string;\n try {\n vaultId = isSharedSetup ? (sharedVaultKey(parsed.name) ?? \"\") : ensureLoginVault(context);\n if (!vaultId) {\n throw new Error(\n isSharedSetup ? `Invalid shared login profile name: ${parsed.name}` : \"Invalid vault id\",\n );\n }\n } catch (error) {\n log.logWarning(\n `[${context.conversationId}] Failed to prepare login vault for ${context.platform}/${context.platformUserId}`,\n error instanceof Error ? error.message : String(error),\n );\n await replyVault(context, [\n \"Login setup failed on the server.\",\n \"請稍後重試,或聯絡管理員檢查 vault 儲存權限。\",\n ]);\n return true;\n }\n\n const token = context.services.linkTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n vaultId,\n \"\",\n );\n const vaultLabel = isSharedSetup\n ? `shared login profile (${parsed.name})`\n : context.services.sandbox.type === \"container\"\n ? `container vault (${vaultId})`\n : \"your vault\";\n await replyVault(context, [\n `${context.services.portalBaseUrl}/link?token=${token.token}`,\n `Target: ${vaultLabel} · Expires: 15 minutes`,\n ]);\n return true;\n }\n}\n"]}
@@ -27,4 +27,11 @@ export declare function telegramCommandMenu(): {
27
27
  command: string;
28
28
  description: string;
29
29
  }[];
30
+ export declare function matchCommand<Command extends string>(text: string, aliases: readonly Command[], options?: {
31
+ stripMention?: boolean;
32
+ }): {
33
+ command: Command;
34
+ args: string[];
35
+ } | null;
36
+ export declare function isCommandText(text: string): boolean;
30
37
  //# sourceMappingURL=manifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAExE,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,EAAE,SAAS,oBAAoB,EAgF3D,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAIvE;AAED,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAM1D;AAED,0FAA0F;AAC1F,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAG5D;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,CAKhF"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAExE,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,EAAE,SAAS,oBAAoB,EAgF3D,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAIvE;AAED,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAM1D;AAED,0FAA0F;AAC1F,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAG5D;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,CAKhF;AAOD,wBAAgB,YAAY,CAAC,OAAO,SAAS,MAAM,EACjD,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GACnC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAS7C;AAkBD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD"}
@@ -120,4 +120,32 @@ export function telegramCommandMenu() {
120
120
  description: entry.telegramMenu?.description ?? entry.description,
121
121
  }));
122
122
  }
123
+ function normalizeCommandToken(token, options) {
124
+ const command = options?.stripMention ? token.replace(/@\w+$/i, "") : token;
125
+ return command.toLowerCase();
126
+ }
127
+ export function matchCommand(text, aliases, options) {
128
+ const tokens = text.trim().split(/\s+/).filter(Boolean);
129
+ const first = tokens[0];
130
+ if (first === undefined)
131
+ return null;
132
+ const command = normalizeCommandToken(first, options);
133
+ return aliases.includes(command)
134
+ ? { command: command, args: tokens.slice(1) }
135
+ : null;
136
+ }
137
+ /**
138
+ * Chat-command text recognition, derived from the command manifest so the
139
+ * inventory has a single source of truth. Session resume uses it to keep
140
+ * command messages out of replayed history.
141
+ *
142
+ * The `pi-[\w-]+` alternative is a catch-all for platform-prefixed command
143
+ * names (including ones a platform registers but this build no longer
144
+ * handles), independent of the manifest.
145
+ */
146
+ const COMMAND_NAMES = COMMAND_MANIFEST.flatMap((entry) => [entry.name].concat(entry.aliases ?? []));
147
+ const COMMAND_TEXT_PATTERN = new RegExp(`^\\/(?:pi-[\\w-]+|${COMMAND_NAMES.join("|")})(?:@\\w+)?(?:\\s|$)`, "i");
148
+ export function isCommandText(text) {
149
+ return COMMAND_TEXT_PATTERN.test(text.trim());
150
+ }
123
151
  //# sourceMappingURL=manifest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Platform-agnostic command manifest — the single inventory that platform\n * adapters derive their native slash-command registration and routing from,\n * and that `isCommandText` and the handler grammars mirror.\n *\n * Adding a command means adding a manifest entry plus a handler in this\n * directory; adapters pick up registration and routing from the entry instead\n * of hand-restating the inventory (where a missed spot used to mean a silent\n * no-response).\n *\n * Per CONTEXT.md, slash commands are a minimal chat control surface: detailed\n * or infrequent settings belong in Admin, and bare commands are limited to\n * `session`. `stop` is listed for platform registration/UI only — it is a\n * magic word owned by conversation intake, not a command handler.\n */\n\nimport type { CommandManifestEntry } from \"./types.js\";\n\nexport type { CommandManifestEntry, SlackSlashRoute } from \"./types.js\";\n\n/** Entry order is the Telegram command-menu order. */\nexport const COMMAND_MANIFEST: readonly CommandManifestEntry[] = [\n {\n name: \"login\",\n description: \"Store credentials in your private vault\",\n slackCommand: \"/pi-login\",\n slackRoute: { includeText: true, privateCommand: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"session\",\n description: \"Open the current session in the web viewer\",\n bare: true,\n slackCommand: \"/pi-session\",\n slackRoute: { thread: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"model\",\n description: \"Switch this conversation's LLM model\",\n arg: {\n name: \"model\",\n description: \"provider/model[:thinking], e.g. anthropic/claude-sonnet-4-6:off\",\n required: false,\n },\n slackCommand: \"/pi-model\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"sandbox\",\n description: \"Show sandbox status, boost limits, or set the office door policy\",\n arg: {\n name: \"action\",\n // Discord caps option descriptions at 100 characters (registration\n // fails otherwise); commands.test.ts enforces the budget.\n description:\n \"'boost' applies the configured boost limits; 'door default|isolated|shared|full' sets door policy\",\n required: false,\n },\n slackCommand: \"/pi-sandbox\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: { description: \"Show or boost sandbox limits\" },\n },\n {\n name: \"stop\",\n description: \"Stop the current conversation\",\n magicWord: true,\n discord: true,\n telegramMenu: { description: \"Stop ongoing conversation\" },\n },\n {\n name: \"new\",\n description: \"Reset conversation history and start fresh\",\n slackCommand: \"/pi-new\",\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"admin\",\n description: \"Open the admin portal\",\n slackCommand: \"/pi-admin\",\n slackRoute: { thread: true },\n },\n {\n name: \"extensions\",\n description: \"List extensions installed for this conversation\",\n slackCommand: \"/pi-extensions\",\n slackRoute: { includeText: true },\n },\n {\n name: \"auto-reply\",\n description: \"Enable, disable, or show auto-reply for this channel\",\n aliases: [\"autoreply\"],\n slackCommand: \"/pi-auto-reply\",\n slackRoute: { includeText: true },\n },\n];\n\nexport function commandManifestEntry(name: string): CommandManifestEntry {\n const entry = COMMAND_MANIFEST.find((candidate) => candidate.name === name);\n if (!entry) throw new Error(`Unknown command in manifest: ${name}`);\n return entry;\n}\n\n/** Slash spellings of a command: `/<name>` and `/pi-<name>` for the name and each alias. */\nexport function slashForms(name: string): readonly string[] {\n const entry = commandManifestEntry(name);\n return [entry.name, ...(entry.aliases ?? [])].flatMap((spelling) => [\n `/${spelling}`,\n `/pi-${spelling}`,\n ]);\n}\n\n/** Every accepted spelling: the bare name (when the command is bare) plus slash forms. */\nexport function commandForms(name: string): readonly string[] {\n const entry = commandManifestEntry(name);\n return [...(entry.bare ? [entry.name] : []), ...slashForms(name)];\n}\n\n/** Telegram `setMyCommands` payload (menu registration; routing is separate). */\nexport function telegramCommandMenu(): { command: string; description: string }[] {\n return COMMAND_MANIFEST.filter((entry) => entry.telegramMenu).map((entry) => ({\n command: entry.name,\n description: entry.telegramMenu?.description ?? entry.description,\n }));\n}\n"]}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Platform-agnostic command manifest — the single inventory that platform\n * adapters derive their native slash-command registration and routing from,\n * and that `isCommandText` and the handler grammars mirror.\n *\n * Adding a command means adding a manifest entry plus a handler in this\n * directory; adapters pick up registration and routing from the entry instead\n * of hand-restating the inventory (where a missed spot used to mean a silent\n * no-response).\n *\n * Per CONTEXT.md, slash commands are a minimal chat control surface: detailed\n * or infrequent settings belong in Admin, and bare commands are limited to\n * `session`. `stop` is listed for platform registration/UI only — it is a\n * magic word owned by conversation intake, not a command handler.\n */\n\nimport type { CommandManifestEntry } from \"./types.js\";\n\nexport type { CommandManifestEntry, SlackSlashRoute } from \"./types.js\";\n\n/** Entry order is the Telegram command-menu order. */\nexport const COMMAND_MANIFEST: readonly CommandManifestEntry[] = [\n {\n name: \"login\",\n description: \"Store credentials in your private vault\",\n slackCommand: \"/pi-login\",\n slackRoute: { includeText: true, privateCommand: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"session\",\n description: \"Open the current session in the web viewer\",\n bare: true,\n slackCommand: \"/pi-session\",\n slackRoute: { thread: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"model\",\n description: \"Switch this conversation's LLM model\",\n arg: {\n name: \"model\",\n description: \"provider/model[:thinking], e.g. anthropic/claude-sonnet-4-6:off\",\n required: false,\n },\n slackCommand: \"/pi-model\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"sandbox\",\n description: \"Show sandbox status, boost limits, or set the office door policy\",\n arg: {\n name: \"action\",\n // Discord caps option descriptions at 100 characters (registration\n // fails otherwise); commands.test.ts enforces the budget.\n description:\n \"'boost' applies the configured boost limits; 'door default|isolated|shared|full' sets door policy\",\n required: false,\n },\n slackCommand: \"/pi-sandbox\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: { description: \"Show or boost sandbox limits\" },\n },\n {\n name: \"stop\",\n description: \"Stop the current conversation\",\n magicWord: true,\n discord: true,\n telegramMenu: { description: \"Stop ongoing conversation\" },\n },\n {\n name: \"new\",\n description: \"Reset conversation history and start fresh\",\n slackCommand: \"/pi-new\",\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"admin\",\n description: \"Open the admin portal\",\n slackCommand: \"/pi-admin\",\n slackRoute: { thread: true },\n },\n {\n name: \"extensions\",\n description: \"List extensions installed for this conversation\",\n slackCommand: \"/pi-extensions\",\n slackRoute: { includeText: true },\n },\n {\n name: \"auto-reply\",\n description: \"Enable, disable, or show auto-reply for this channel\",\n aliases: [\"autoreply\"],\n slackCommand: \"/pi-auto-reply\",\n slackRoute: { includeText: true },\n },\n];\n\nexport function commandManifestEntry(name: string): CommandManifestEntry {\n const entry = COMMAND_MANIFEST.find((candidate) => candidate.name === name);\n if (!entry) throw new Error(`Unknown command in manifest: ${name}`);\n return entry;\n}\n\n/** Slash spellings of a command: `/<name>` and `/pi-<name>` for the name and each alias. */\nexport function slashForms(name: string): readonly string[] {\n const entry = commandManifestEntry(name);\n return [entry.name, ...(entry.aliases ?? [])].flatMap((spelling) => [\n `/${spelling}`,\n `/pi-${spelling}`,\n ]);\n}\n\n/** Every accepted spelling: the bare name (when the command is bare) plus slash forms. */\nexport function commandForms(name: string): readonly string[] {\n const entry = commandManifestEntry(name);\n return [...(entry.bare ? [entry.name] : []), ...slashForms(name)];\n}\n\n/** Telegram `setMyCommands` payload (menu registration; routing is separate). */\nexport function telegramCommandMenu(): { command: string; description: string }[] {\n return COMMAND_MANIFEST.filter((entry) => entry.telegramMenu).map((entry) => ({\n command: entry.name,\n description: entry.telegramMenu?.description ?? entry.description,\n }));\n}\n\nfunction normalizeCommandToken(token: string, options?: { stripMention?: boolean }): string {\n const command = options?.stripMention ? token.replace(/@\\w+$/i, \"\") : token;\n return command.toLowerCase();\n}\n\nexport function matchCommand<Command extends string>(\n text: string,\n aliases: readonly Command[],\n options?: { stripMention?: boolean },\n): { command: Command; args: string[] } | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n const first = tokens[0];\n if (first === undefined) return null;\n\n const command = normalizeCommandToken(first, options);\n return aliases.includes(command as Command)\n ? { command: command as Command, args: tokens.slice(1) }\n : null;\n}\n\n/**\n * Chat-command text recognition, derived from the command manifest so the\n * inventory has a single source of truth. Session resume uses it to keep\n * command messages out of replayed history.\n *\n * The `pi-[\\w-]+` alternative is a catch-all for platform-prefixed command\n * names (including ones a platform registers but this build no longer\n * handles), independent of the manifest.\n */\nconst COMMAND_NAMES = COMMAND_MANIFEST.flatMap((entry) => [entry.name].concat(entry.aliases ?? []));\n\nconst COMMAND_TEXT_PATTERN = new RegExp(\n `^\\\\/(?:pi-[\\\\w-]+|${COMMAND_NAMES.join(\"|\")})(?:@\\\\w+)?(?:\\\\s|$)`,\n \"i\",\n);\n\nexport function isCommandText(text: string): boolean {\n return COMMAND_TEXT_PATTERN.test(text.trim());\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAapG,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIrD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CA4BzE;AAgCD,qBAAa,mBAAoB,YAAW,cAAc;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa;IAA1C,YAA6B,aAAa,EAAE,aAAa,EAAI;IACvD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CA6GzD;CACF"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAapG,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIrD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CA+BzE;AAgCD,qBAAa,mBAAoB,YAAW,cAAc;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa;IAA1C,YAA6B,aAAa,EAAE,aAAa,EAAI;IACvD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CA6GzD;CACF"}
@@ -1,7 +1,7 @@
1
1
  import { resolveConversationSettings } from "../config.js";
2
2
  import { applyConversationSettings } from "../settings-mutation.js";
3
3
  import { slashForms } from "./manifest.js";
4
- import { matchCommand } from "./parse.js";
4
+ import { matchCommand } from "./manifest.js";
5
5
  import { formatCommandSummary, replyDiagnosticWithContext } from "./utils.js";
6
6
  const PI_AI_THINKING_LEVELS = [
7
7
  "minimal",
@@ -24,6 +24,9 @@ export function parseModelCommand(text) {
24
24
  return { error: "invalid_spec" };
25
25
  }
26
26
  const spec = matched.args[0];
27
+ if (spec === undefined) {
28
+ return { error: "invalid_spec" };
29
+ }
27
30
  const slash = spec.indexOf("/");
28
31
  if (slash <= 0 || slash === spec.length - 1) {
29
32
  return { error: "invalid_spec" };
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { ThinkingLevel as PiAiThinkingLevel } from \"@earendil-works/pi-ai\";\nimport { resolveConversationSettings } from \"../config.js\";\nimport { applyConversationSettings } from \"../settings-mutation.js\";\nimport { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./parse.js\";\nimport type { CommandContext, CommandHandler, ModelRegistry, ParsedModelCommand } from \"./types.js\";\nimport { formatCommandSummary, replyDiagnosticWithContext } from \"./utils.js\";\n\nconst PI_AI_THINKING_LEVELS = [\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n \"max\",\n] satisfies PiAiThinkingLevel[];\nconst THINKING_LEVELS = new Set<ThinkingLevel>([\"off\", ...PI_AI_THINKING_LEVELS]);\n\nexport type { ParsedModelCommand } from \"./types.js\";\n\nconst MODEL_COMMANDS = slashForms(\"model\");\n\nexport function parseModelCommand(text: string): ParsedModelCommand | null {\n const matched = matchCommand(text, MODEL_COMMANDS);\n if (!matched) return null;\n\n if (matched.args.length === 0) {\n return {};\n }\n\n if (matched.args.length !== 1) {\n return { error: \"invalid_spec\" };\n }\n\n const spec = matched.args[0];\n const slash = spec.indexOf(\"/\");\n if (slash <= 0 || slash === spec.length - 1) {\n return { error: \"invalid_spec\" };\n }\n\n const modelSpec = spec.slice(slash + 1);\n const parsedModel = parseModelThinkingLevel(modelSpec);\n if (parsedModel.error) {\n return { error: parsedModel.error };\n }\n\n return {\n provider: spec.slice(0, slash),\n ...parsedModel,\n };\n}\n\nfunction parseModelThinkingLevel(modelSpec: string): {\n model?: string;\n modelCandidate?: string;\n thinkingLevelCandidate?: string;\n thinkingLevel?: ThinkingLevel;\n error?: \"invalid_spec\";\n} {\n const colon = modelSpec.lastIndexOf(\":\");\n if (colon === 0 || colon === modelSpec.length - 1) {\n return { error: \"invalid_spec\" };\n }\n if (colon < 0) {\n return { model: modelSpec };\n }\n\n const suffix = modelSpec.slice(colon + 1);\n return {\n model: modelSpec.slice(0, colon),\n modelCandidate: modelSpec,\n thinkingLevelCandidate: suffix,\n ...(THINKING_LEVELS.has(suffix as ThinkingLevel)\n ? { thinkingLevel: suffix as ThinkingLevel }\n : {}),\n };\n}\n\nfunction formatModelSpec(provider: string, model: string, thinkingLevel?: ThinkingLevel): string {\n return `${provider}/${model}${thinkingLevel ? `:${thinkingLevel}` : \"\"}`;\n}\n\nexport class ModelCommandHandler implements CommandHandler {\n constructor(private readonly modelRegistry: ModelRegistry) {}\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseModelCommand(context.commandText);\n if (!parsed) return false;\n\n if (parsed.error) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n \"無效的模型參數,請使用 `provider/model[:thinking]`。\",\n \"Example: `/pi-model anthropic/claude-sonnet-4-6:off`\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const office = context.services.workspace.office(context.address);\n if (!parsed.provider || !parsed.model) {\n const current = resolveConversationSettings(office);\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n `Current: \\`${formatModelSpec(current.provider, current.model, current.thinkingLevel)}\\``,\n \"\",\n \"Usage: `/pi-model provider/model[:thinking]`\",\n \"Example: `/pi-model anthropic/claude-sonnet-4-6:off`\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const exactModelId = parsed.modelCandidate ?? parsed.model;\n let selectedModelId = exactModelId;\n let selectedThinkingLevel = parsed.thinkingLevel;\n let registeredModel = this.modelRegistry.find(parsed.provider, exactModelId);\n\n if (!registeredModel && parsed.modelCandidate) {\n if (\n parsed.thinkingLevelCandidate &&\n !THINKING_LEVELS.has(parsed.thinkingLevelCandidate as ThinkingLevel)\n ) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n \"未知的 thinking level,請使用 `off`、`minimal`、`low`、`medium`、`high`、`xhigh` 或 `max`。\",\n \"Example: `/pi-model anthropic/claude-sonnet-4-6:off`\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n selectedModelId = parsed.model;\n registeredModel = this.modelRegistry.find(parsed.provider, selectedModelId);\n }\n\n if (!registeredModel) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n `找不到模型:\\`${formatModelSpec(parsed.provider, selectedModelId, selectedThinkingLevel)}\\``,\n \"請確認 provider/model 名稱,或先在 pi models.json 註冊自訂模型。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n if (parsed.modelCandidate && selectedModelId === parsed.modelCandidate) {\n selectedThinkingLevel = undefined;\n }\n\n if (!context.services.runtime) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n \"Model command is not configured correctly on the server. Please try again later.\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const result = applyConversationSettings(context.services.runtime, office, {\n provider: parsed.provider,\n model: selectedModelId,\n ...(selectedThinkingLevel ? { thinkingLevel: selectedThinkingLevel } : {}),\n });\n if (!result.ok) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n \"目前這個 conversation 有執行中的工作,請等它完成或先 `/stop` 後再切換模型。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n `Switched: \\`${formatModelSpec(parsed.provider, selectedModelId, selectedThinkingLevel)}\\``,\n \"下一則訊息會使用新模型。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { ThinkingLevel as PiAiThinkingLevel } from \"@earendil-works/pi-ai\";\nimport { resolveConversationSettings } from \"../config.js\";\nimport { applyConversationSettings } from \"../settings-mutation.js\";\nimport { slashForms } from \"./manifest.js\";\nimport { matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler, ModelRegistry, ParsedModelCommand } from \"./types.js\";\nimport { formatCommandSummary, replyDiagnosticWithContext } from \"./utils.js\";\n\nconst PI_AI_THINKING_LEVELS = [\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n \"max\",\n] satisfies PiAiThinkingLevel[];\nconst THINKING_LEVELS = new Set<ThinkingLevel>([\"off\", ...PI_AI_THINKING_LEVELS]);\n\nexport type { ParsedModelCommand } from \"./types.js\";\n\nconst MODEL_COMMANDS = slashForms(\"model\");\n\nexport function parseModelCommand(text: string): ParsedModelCommand | null {\n const matched = matchCommand(text, MODEL_COMMANDS);\n if (!matched) return null;\n\n if (matched.args.length === 0) {\n return {};\n }\n\n if (matched.args.length !== 1) {\n return { error: \"invalid_spec\" };\n }\n\n const spec = matched.args[0];\n if (spec === undefined) {\n return { error: \"invalid_spec\" };\n }\n const slash = spec.indexOf(\"/\");\n if (slash <= 0 || slash === spec.length - 1) {\n return { error: \"invalid_spec\" };\n }\n\n const modelSpec = spec.slice(slash + 1);\n const parsedModel = parseModelThinkingLevel(modelSpec);\n if (parsedModel.error) {\n return { error: parsedModel.error };\n }\n\n return {\n provider: spec.slice(0, slash),\n ...parsedModel,\n };\n}\n\nfunction parseModelThinkingLevel(modelSpec: string): {\n model?: string;\n modelCandidate?: string;\n thinkingLevelCandidate?: string;\n thinkingLevel?: ThinkingLevel;\n error?: \"invalid_spec\";\n} {\n const colon = modelSpec.lastIndexOf(\":\");\n if (colon === 0 || colon === modelSpec.length - 1) {\n return { error: \"invalid_spec\" };\n }\n if (colon < 0) {\n return { model: modelSpec };\n }\n\n const suffix = modelSpec.slice(colon + 1);\n return {\n model: modelSpec.slice(0, colon),\n modelCandidate: modelSpec,\n thinkingLevelCandidate: suffix,\n ...(THINKING_LEVELS.has(suffix as ThinkingLevel)\n ? { thinkingLevel: suffix as ThinkingLevel }\n : {}),\n };\n}\n\nfunction formatModelSpec(provider: string, model: string, thinkingLevel?: ThinkingLevel): string {\n return `${provider}/${model}${thinkingLevel ? `:${thinkingLevel}` : \"\"}`;\n}\n\nexport class ModelCommandHandler implements CommandHandler {\n constructor(private readonly modelRegistry: ModelRegistry) {}\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseModelCommand(context.commandText);\n if (!parsed) return false;\n\n if (parsed.error) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n \"無效的模型參數,請使用 `provider/model[:thinking]`。\",\n \"Example: `/pi-model anthropic/claude-sonnet-4-6:off`\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const office = context.services.workspace.office(context.address);\n if (!parsed.provider || !parsed.model) {\n const current = resolveConversationSettings(office);\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n `Current: \\`${formatModelSpec(current.provider, current.model, current.thinkingLevel)}\\``,\n \"\",\n \"Usage: `/pi-model provider/model[:thinking]`\",\n \"Example: `/pi-model anthropic/claude-sonnet-4-6:off`\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const exactModelId = parsed.modelCandidate ?? parsed.model;\n let selectedModelId = exactModelId;\n let selectedThinkingLevel = parsed.thinkingLevel;\n let registeredModel = this.modelRegistry.find(parsed.provider, exactModelId);\n\n if (!registeredModel && parsed.modelCandidate) {\n if (\n parsed.thinkingLevelCandidate &&\n !THINKING_LEVELS.has(parsed.thinkingLevelCandidate as ThinkingLevel)\n ) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n \"未知的 thinking level,請使用 `off`、`minimal`、`low`、`medium`、`high`、`xhigh` 或 `max`。\",\n \"Example: `/pi-model anthropic/claude-sonnet-4-6:off`\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n selectedModelId = parsed.model;\n registeredModel = this.modelRegistry.find(parsed.provider, selectedModelId);\n }\n\n if (!registeredModel) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n `找不到模型:\\`${formatModelSpec(parsed.provider, selectedModelId, selectedThinkingLevel)}\\``,\n \"請確認 provider/model 名稱,或先在 pi models.json 註冊自訂模型。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n if (parsed.modelCandidate && selectedModelId === parsed.modelCandidate) {\n selectedThinkingLevel = undefined;\n }\n\n if (!context.services.runtime) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n \"Model command is not configured correctly on the server. Please try again later.\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const result = applyConversationSettings(context.services.runtime, office, {\n provider: parsed.provider,\n model: selectedModelId,\n ...(selectedThinkingLevel ? { thinkingLevel: selectedThinkingLevel } : {}),\n });\n if (!result.ok) {\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n \"目前這個 conversation 有執行中的工作,請等它完成或先 `/stop` 後再切換模型。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n await replyDiagnosticWithContext(\n context.responder,\n formatCommandSummary(\"Model\", [\n `Switched: \\`${formatModelSpec(parsed.provider, selectedModelId, selectedThinkingLevel)}\\``,\n \"下一則訊息會使用新模型。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { createConversationMessage } from "../adapter.js";
2
2
  import { slashForms } from "./manifest.js";
3
- import { matchCommand } from "./parse.js";
3
+ import { matchCommand } from "./manifest.js";
4
4
  import { formatCommandSummary, replyDiagnosticWithContext } from "./utils.js";
5
5
  const NEW_COMMANDS = slashForms("new");
6
6
  export class NewCommandHandler {