@xopcai/xopc 0.0.29 → 0.0.31

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 (632) hide show
  1. package/dist/extensions/telegram/src/adapters/config-surface.js +3 -1
  2. package/dist/extensions/telegram/src/adapters/config-surface.js.map +1 -1
  3. package/dist/extensions/telegram/src/adapters/onboard-cli.js +23 -21
  4. package/dist/extensions/telegram/src/adapters/onboard-cli.js.map +1 -1
  5. package/dist/extensions/telegram/src/adapters/setup-wizard.js +3 -4
  6. package/dist/extensions/telegram/src/adapters/setup-wizard.js.map +1 -1
  7. package/dist/extensions/telegram/src/config-schema.d.ts +0 -1
  8. package/dist/extensions/telegram/src/config-schema.js +17 -4
  9. package/dist/extensions/telegram/src/config-schema.js.map +1 -1
  10. package/dist/extensions/telegram/src/plugin.js +4 -17
  11. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  12. package/dist/extensions/telegram/xopc.extension.json +1 -1
  13. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  14. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  15. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  16. package/dist/gateway/static/root/assets/agents-3u63Fw2Y.js +216 -0
  17. package/dist/gateway/static/root/assets/agents-3u63Fw2Y.js.map +1 -0
  18. package/dist/gateway/static/root/assets/{apps-page-Bmq19MS-.js → apps-page-CWegY6Kp.js} +2 -2
  19. package/dist/gateway/static/root/assets/{apps-page-Bmq19MS-.js.map → apps-page-CWegY6Kp.js.map} +1 -1
  20. package/dist/gateway/static/root/assets/channels-settings-CiyeXcTK.js +9 -0
  21. package/dist/gateway/static/root/assets/channels-settings-CiyeXcTK.js.map +1 -0
  22. package/dist/gateway/static/root/assets/cron-api-_j_79Zf5.js +3 -0
  23. package/dist/gateway/static/root/assets/cron-api-_j_79Zf5.js.map +1 -0
  24. package/dist/gateway/static/root/assets/cron-page-S86YNTtI.js +2 -0
  25. package/dist/gateway/static/root/assets/cron-page-S86YNTtI.js.map +1 -0
  26. package/dist/gateway/static/root/assets/dist-D0jxbvuz.js +2 -0
  27. package/dist/gateway/static/root/assets/{dist--p2HQ2QF.js.map → dist-D0jxbvuz.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-debug-page-DwHCB_6T.js → extension-debug-page-DB630cW8.js} +2 -2
  29. package/dist/gateway/static/root/assets/{extension-debug-page-DwHCB_6T.js.map → extension-debug-page-DB630cW8.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-page-BsYwQIex.js → extension-page-CnoPUBul.js} +2 -2
  31. package/dist/gateway/static/root/assets/{extension-page-BsYwQIex.js.map → extension-page-CnoPUBul.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-settings-page-nsisEgjB.js → extension-settings-page-BsiOkvBe.js} +2 -2
  33. package/dist/gateway/static/root/assets/{extension-settings-page-nsisEgjB.js.map → extension-settings-page-BsiOkvBe.js.map} +1 -1
  34. package/dist/gateway/static/root/assets/{index-CR8zUHGR.js → index-DHLmAIQl.js} +63 -63
  35. package/dist/gateway/static/root/assets/{index-CR8zUHGR.js.map → index-DHLmAIQl.js.map} +1 -1
  36. package/dist/gateway/static/root/assets/index-DoPwy4aU.css +1 -0
  37. package/dist/gateway/static/root/assets/logs-page-Bndhenn2.js +2 -0
  38. package/dist/gateway/static/root/assets/logs-page-Bndhenn2.js.map +1 -0
  39. package/dist/gateway/static/root/assets/sessions-page-Q201-_lP.js +2 -0
  40. package/dist/gateway/static/root/assets/{sessions-page-Be5kIGl_.js.map → sessions-page-Q201-_lP.js.map} +1 -1
  41. package/dist/gateway/static/root/assets/settings-page-Cw75fpc6.js +2 -0
  42. package/dist/gateway/static/root/assets/settings-page-Cw75fpc6.js.map +1 -0
  43. package/dist/gateway/static/root/assets/skills-page-CVwEzD_J.js +3 -0
  44. package/dist/gateway/static/root/assets/skills-page-CVwEzD_J.js.map +1 -0
  45. package/dist/gateway/static/root/index.html +2 -2
  46. package/dist/package.js +1 -1
  47. package/dist/src/agent/agent-manager.d.ts +5 -7
  48. package/dist/src/agent/agent-manager.js +21 -19
  49. package/dist/src/agent/agent-manager.js.map +1 -1
  50. package/dist/src/agent/ipc/inbox.js +4 -2
  51. package/dist/src/agent/ipc/inbox.js.map +1 -1
  52. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  53. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  54. package/dist/src/agent/orchestration/agent-orchestrator.js +1 -1
  55. package/dist/src/agent/prompt/service-prompt-builder.js +0 -2
  56. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  57. package/dist/src/agent/prompt/system-prompt.d.ts +0 -2
  58. package/dist/src/agent/prompt/system-prompt.js +3 -17
  59. package/dist/src/agent/prompt/system-prompt.js.map +1 -1
  60. package/dist/src/agent/service/btw-query.d.ts +16 -0
  61. package/dist/src/agent/service/btw-query.js +88 -0
  62. package/dist/src/agent/service/btw-query.js.map +1 -0
  63. package/dist/src/agent/service/build-direct-message-content.d.ts +30 -0
  64. package/dist/src/agent/service/build-direct-message-content.js +75 -0
  65. package/dist/src/agent/service/build-direct-message-content.js.map +1 -0
  66. package/dist/src/agent/service/parse-outbound-session-key.d.ts +8 -0
  67. package/dist/src/agent/service/parse-outbound-session-key.js +41 -0
  68. package/dist/src/agent/service/parse-outbound-session-key.js.map +1 -0
  69. package/dist/src/agent/service/process-direct-one-shot.d.ts +34 -0
  70. package/dist/src/agent/service/process-direct-one-shot.js +67 -0
  71. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -0
  72. package/dist/src/agent/service/process-direct-streaming.js +12 -1
  73. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  74. package/dist/src/agent/service/reconcile-dreaming-cron.d.ts +6 -0
  75. package/dist/src/agent/service/reconcile-dreaming-cron.js +70 -0
  76. package/dist/src/agent/service/reconcile-dreaming-cron.js.map +1 -0
  77. package/dist/src/agent/service/session-context-report.d.ts +19 -0
  78. package/dist/src/agent/service/session-context-report.js +47 -0
  79. package/dist/src/agent/service/session-context-report.js.map +1 -0
  80. package/dist/src/agent/service/webchat-tts.d.ts +28 -0
  81. package/dist/src/agent/service/webchat-tts.js +73 -0
  82. package/dist/src/agent/service/webchat-tts.js.map +1 -0
  83. package/dist/src/agent/service.d.ts +8 -12
  84. package/dist/src/agent/service.js +74 -379
  85. package/dist/src/agent/service.js.map +1 -1
  86. package/dist/src/agent/skills/config.js +4 -3
  87. package/dist/src/agent/skills/config.js.map +1 -1
  88. package/dist/src/agent/skills/format-skills-prompt.d.ts +0 -2
  89. package/dist/src/agent/skills/format-skills-prompt.js +3 -24
  90. package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
  91. package/dist/src/agent/skills/hub-lock.js +4 -5
  92. package/dist/src/agent/skills/hub-lock.js.map +1 -1
  93. package/dist/src/agent/skills/index.js +9 -21
  94. package/dist/src/agent/skills/index.js.map +1 -1
  95. package/dist/src/agent/skills/marketplace/adapter.types.d.ts +17 -0
  96. package/dist/src/agent/skills/marketplace/adapter.types.js +1 -0
  97. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.d.ts +2 -0
  98. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js +292 -0
  99. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js.map +1 -0
  100. package/dist/src/agent/skills/marketplace/adapters/skillhub/ecosystem-client.d.ts +73 -0
  101. package/dist/src/agent/skills/marketplace/adapters/skillhub/ecosystem-client.js +200 -0
  102. package/dist/src/agent/skills/marketplace/adapters/skillhub/ecosystem-client.js.map +1 -0
  103. package/dist/src/agent/skills/marketplace/adapters/skillhub/registry-client.d.ts +137 -0
  104. package/dist/src/agent/skills/marketplace/adapters/skillhub/registry-client.js +217 -0
  105. package/dist/src/agent/skills/marketplace/adapters/skillhub/registry-client.js.map +1 -0
  106. package/dist/src/agent/skills/marketplace/adapters/skillhub/skillhub-fetch-cache.d.ts +12 -0
  107. package/dist/src/agent/skills/marketplace/adapters/skillhub/skillhub-fetch-cache.js +90 -0
  108. package/dist/src/agent/skills/marketplace/adapters/skillhub/skillhub-fetch-cache.js.map +1 -0
  109. package/dist/src/agent/skills/marketplace/adapters/store/adapter.d.ts +2 -0
  110. package/dist/src/agent/skills/marketplace/adapters/store/adapter.js +35 -0
  111. package/dist/src/agent/skills/marketplace/adapters/store/adapter.js.map +1 -0
  112. package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.d.ts +124 -0
  113. package/dist/src/agent/skills/{skills-store-client.js → marketplace/adapters/store/store-api-client.js} +4 -3
  114. package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js.map +1 -0
  115. package/dist/src/agent/skills/marketplace/resolve-adapter.d.ts +6 -0
  116. package/dist/src/agent/skills/marketplace/resolve-adapter.js +26 -0
  117. package/dist/src/agent/skills/marketplace/resolve-adapter.js.map +1 -0
  118. package/dist/src/agent/skills/parse-skill-metadata.d.ts +5 -0
  119. package/dist/src/agent/skills/parse-skill-metadata.js +27 -0
  120. package/dist/src/agent/skills/parse-skill-metadata.js.map +1 -0
  121. package/dist/src/agent/skills/skill-markdown-preview-from-raw.d.ts +6 -0
  122. package/dist/src/agent/skills/skill-markdown-preview-from-raw.js +64 -0
  123. package/dist/src/agent/skills/skill-markdown-preview-from-raw.js.map +1 -0
  124. package/dist/src/agent/skills/skill-view-path.d.ts +17 -1
  125. package/dist/src/agent/skills/skill-view-path.js +66 -3
  126. package/dist/src/agent/skills/skill-view-path.js.map +1 -1
  127. package/dist/src/agent/skills/skills-marketplace.d.ts +21 -0
  128. package/dist/src/agent/skills/skills-marketplace.js +18 -0
  129. package/dist/src/agent/skills/skills-marketplace.js.map +1 -0
  130. package/dist/src/agent/skills/test-framework.d.ts +0 -1
  131. package/dist/src/agent/skills/test-framework.js +2 -20
  132. package/dist/src/agent/skills/test-framework.js.map +1 -1
  133. package/dist/src/agent/skills/types.d.ts +12 -6
  134. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  135. package/dist/src/agent/tools/image-generate-tool.js +2 -2
  136. package/dist/src/auth/credentials.js +8 -6
  137. package/dist/src/auth/credentials.js.map +1 -1
  138. package/dist/src/auth/profiles/store.js +5 -3
  139. package/dist/src/auth/profiles/store.js.map +1 -1
  140. package/dist/src/channels/manager.js +1 -1
  141. package/dist/src/channels/outbound/persist-store.js +4 -4
  142. package/dist/src/channels/outbound/persist-store.js.map +1 -1
  143. package/dist/src/cli/commands/config.js +8 -6
  144. package/dist/src/cli/commands/config.js.map +1 -1
  145. package/dist/src/cli/commands/doctor/checks/channel-config.js +3 -4
  146. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -1
  147. package/dist/src/cli/commands/init.js +4 -2
  148. package/dist/src/cli/commands/init.js.map +1 -1
  149. package/dist/src/config/loader.js +3 -1
  150. package/dist/src/config/loader.js.map +1 -1
  151. package/dist/src/config/models-json.js +4 -5
  152. package/dist/src/config/models-json.js.map +1 -1
  153. package/dist/src/config/schema.d.ts +12 -4
  154. package/dist/src/config/schema.js +31 -5
  155. package/dist/src/config/schema.js.map +1 -1
  156. package/dist/src/cron/persistence.d.ts +1 -3
  157. package/dist/src/cron/persistence.js +6 -17
  158. package/dist/src/cron/persistence.js.map +1 -1
  159. package/dist/src/cron/validation.js +1 -1
  160. package/dist/src/cron/validation.js.map +1 -1
  161. package/dist/src/extensions/activation-context.js +0 -1
  162. package/dist/src/extensions/activation-context.js.map +1 -1
  163. package/dist/src/extensions/lockfile.js +4 -2
  164. package/dist/src/extensions/lockfile.js.map +1 -1
  165. package/dist/src/gateway/auth.d.ts +0 -9
  166. package/dist/src/gateway/auth.js +1 -12
  167. package/dist/src/gateway/auth.js.map +1 -1
  168. package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -1
  169. package/dist/src/gateway/hono/lib/extension-store.js +4 -4
  170. package/dist/src/gateway/hono/lib/extension-store.js.map +1 -1
  171. package/dist/src/gateway/hono/routes/channels.js +5 -3
  172. package/dist/src/gateway/hono/routes/channels.js.map +1 -1
  173. package/dist/src/gateway/hono/routes/commands-skills.js +28 -3
  174. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  175. package/dist/src/gateway/hono/routes/config.js +23 -2
  176. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  177. package/dist/src/gateway/hono/routes/sessions.js +124 -2
  178. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  179. package/dist/src/gateway/hono/sse.js +9 -2
  180. package/dist/src/gateway/hono/sse.js.map +1 -1
  181. package/dist/src/gateway/index.js +2 -2
  182. package/dist/src/gateway/lock.js +1 -1
  183. package/dist/src/gateway/service/run-gateway-agent.d.ts +37 -0
  184. package/dist/src/gateway/service/run-gateway-agent.js +163 -0
  185. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -0
  186. package/dist/src/gateway/service/save-webchat-user-message.d.ts +9 -0
  187. package/dist/src/gateway/service/save-webchat-user-message.js +34 -0
  188. package/dist/src/gateway/service/save-webchat-user-message.js.map +1 -0
  189. package/dist/src/gateway/service/session-chat-ids.d.ts +9 -0
  190. package/dist/src/gateway/service/session-chat-ids.js +33 -0
  191. package/dist/src/gateway/service/session-chat-ids.js.map +1 -0
  192. package/dist/src/gateway/service/sse-hub.d.ts +10 -0
  193. package/dist/src/gateway/service/sse-hub.js +55 -0
  194. package/dist/src/gateway/service/sse-hub.js.map +1 -0
  195. package/dist/src/gateway/service/types.d.ts +9 -0
  196. package/dist/src/gateway/service/types.js +1 -0
  197. package/dist/src/gateway/service.d.ts +42 -33
  198. package/dist/src/gateway/service.js +92 -246
  199. package/dist/src/gateway/service.js.map +1 -1
  200. package/dist/src/infra/update-lock.js +4 -2
  201. package/dist/src/infra/update-lock.js.map +1 -1
  202. package/dist/src/infra/update-startup.js +5 -14
  203. package/dist/src/infra/update-startup.js.map +1 -1
  204. package/dist/src/infra/write-file-atomic.d.ts +18 -0
  205. package/dist/src/infra/write-file-atomic.js +115 -0
  206. package/dist/src/infra/write-file-atomic.js.map +1 -0
  207. package/dist/src/session/abort-cutoff.d.ts +6 -0
  208. package/dist/src/session/abort-cutoff.js +10 -0
  209. package/dist/src/session/abort-cutoff.js.map +1 -0
  210. package/dist/src/session/compaction-checkpoints.d.ts +8 -0
  211. package/dist/src/session/compaction-checkpoints.js +21 -0
  212. package/dist/src/session/compaction-checkpoints.js.map +1 -0
  213. package/dist/src/session/config-store.js +4 -2
  214. package/dist/src/session/config-store.js.map +1 -1
  215. package/dist/src/session/index.d.ts +8 -1
  216. package/dist/src/session/index.js +7 -1
  217. package/dist/src/session/manager.d.ts +26 -1
  218. package/dist/src/session/manager.js +39 -2
  219. package/dist/src/session/manager.js.map +1 -1
  220. package/dist/src/session/patch-metadata.d.ts +12 -0
  221. package/dist/src/session/patch-metadata.js +23 -0
  222. package/dist/src/session/patch-metadata.js.map +1 -0
  223. package/dist/src/session/search-index.d.ts +2 -0
  224. package/dist/src/session/search-index.js +30 -2
  225. package/dist/src/session/search-index.js.map +1 -1
  226. package/dist/src/session/session-context-for-llm.d.ts +32 -0
  227. package/dist/src/session/session-context-for-llm.js +60 -0
  228. package/dist/src/session/session-context-for-llm.js.map +1 -0
  229. package/dist/src/session/store.d.ts +53 -2
  230. package/dist/src/session/store.js +427 -145
  231. package/dist/src/session/store.js.map +1 -1
  232. package/dist/src/session/strip-webchat-early-save.d.ts +5 -0
  233. package/dist/src/session/strip-webchat-early-save.js +17 -0
  234. package/dist/src/session/strip-webchat-early-save.js.map +1 -0
  235. package/dist/src/session/transcript-format.d.ts +46 -0
  236. package/dist/src/session/transcript-format.js +88 -0
  237. package/dist/src/session/transcript-format.js.map +1 -0
  238. package/dist/src/session/types.d.ts +37 -0
  239. package/dist/src/session/types.js.map +1 -1
  240. package/dist/src/utils/logger/log-store.js +4 -3
  241. package/dist/src/utils/logger/log-store.js.map +1 -1
  242. package/dist/src/voice/tts/merge-config.js +0 -1
  243. package/dist/src/voice/tts/merge-config.js.map +1 -1
  244. package/dist/src/voice/tts/service.js +1 -2
  245. package/dist/src/voice/tts/service.js.map +1 -1
  246. package/package.json +1 -1
  247. package/skills/business/company-values/SKILL-zh.md +80 -0
  248. package/skills/business/company-values/SKILL.md +80 -0
  249. package/skills/business/find-community/SKILL-zh.md +50 -0
  250. package/skills/business/find-community/SKILL.md +50 -0
  251. package/skills/business/first-customers/SKILL-zh.md +76 -0
  252. package/skills/business/first-customers/SKILL.md +76 -0
  253. package/skills/business/grow-sustainably/SKILL-zh.md +92 -0
  254. package/skills/business/grow-sustainably/SKILL.md +92 -0
  255. package/skills/business/marketing-plan/SKILL-zh.md +100 -0
  256. package/skills/business/marketing-plan/SKILL.md +100 -0
  257. package/skills/business/minimalist-review/SKILL-zh.md +82 -0
  258. package/skills/business/minimalist-review/SKILL.md +82 -0
  259. package/skills/business/mvp/SKILL-zh.md +81 -0
  260. package/skills/business/mvp/SKILL.md +81 -0
  261. package/skills/business/pricing/SKILL-zh.md +64 -0
  262. package/skills/business/pricing/SKILL.md +64 -0
  263. package/skills/business/processize/SKILL-zh.md +91 -0
  264. package/skills/business/processize/SKILL.md +91 -0
  265. package/skills/business/validate-idea/SKILL-zh.md +68 -0
  266. package/skills/business/validate-idea/SKILL.md +68 -0
  267. package/skills/{skill-creator → creative/algorithmic-art}/LICENSE.txt +1 -1
  268. package/skills/creative/algorithmic-art/SKILL-zh.md +405 -0
  269. package/skills/creative/algorithmic-art/SKILL.md +405 -0
  270. package/skills/creative/algorithmic-art/templates/generator_template.js +223 -0
  271. package/skills/creative/algorithmic-art/templates/viewer.html +599 -0
  272. package/skills/creative/canvas-design/LICENSE.txt +202 -0
  273. package/skills/creative/canvas-design/SKILL-zh.md +130 -0
  274. package/skills/creative/canvas-design/SKILL.md +130 -0
  275. package/skills/creative/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
  276. package/skills/creative/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  277. package/skills/creative/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  278. package/skills/creative/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
  279. package/skills/creative/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  280. package/skills/creative/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
  281. package/skills/creative/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  282. package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  283. package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
  284. package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  285. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  286. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  287. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
  288. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  289. package/skills/creative/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
  290. package/skills/creative/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  291. package/skills/creative/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
  292. package/skills/creative/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  293. package/skills/creative/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  294. package/skills/creative/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
  295. package/skills/creative/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  296. package/skills/creative/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
  297. package/skills/creative/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  298. package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  299. package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
  300. package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  301. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  302. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  303. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  304. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  305. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  306. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  307. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  308. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
  309. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  310. package/skills/creative/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  311. package/skills/creative/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  312. package/skills/creative/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
  313. package/skills/creative/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  314. package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  315. package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
  316. package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  317. package/skills/creative/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  318. package/skills/creative/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  319. package/skills/creative/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
  320. package/skills/creative/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
  321. package/skills/creative/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  322. package/skills/creative/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  323. package/skills/creative/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  324. package/skills/creative/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  325. package/skills/creative/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
  326. package/skills/creative/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  327. package/skills/creative/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  328. package/skills/creative/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
  329. package/skills/creative/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  330. package/skills/creative/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
  331. package/skills/creative/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  332. package/skills/creative/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  333. package/skills/creative/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
  334. package/skills/creative/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  335. package/skills/creative/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  336. package/skills/creative/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
  337. package/skills/creative/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
  338. package/skills/creative/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  339. package/skills/creative/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  340. package/skills/creative/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
  341. package/skills/creative/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  342. package/skills/creative/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
  343. package/skills/creative/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  344. package/skills/creative/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  345. package/skills/creative/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
  346. package/skills/creative/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  347. package/skills/creative/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
  348. package/skills/creative/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  349. package/skills/creative/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  350. package/skills/creative/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  351. package/skills/creative/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  352. package/skills/creative/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
  353. package/skills/creative/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  354. package/skills/creative/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
  355. package/skills/creative/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  356. package/skills/creative/frontend-design/LICENSE.txt +177 -0
  357. package/skills/creative/frontend-design/SKILL-zh.md +42 -0
  358. package/skills/creative/frontend-design/SKILL.md +42 -0
  359. package/skills/creative/theme-factory/LICENSE.txt +202 -0
  360. package/skills/creative/theme-factory/SKILL-zh.md +59 -0
  361. package/skills/creative/theme-factory/SKILL.md +59 -0
  362. package/skills/creative/theme-factory/theme-showcase.pdf +0 -0
  363. package/skills/creative/theme-factory/themes/arctic-frost.md +19 -0
  364. package/skills/creative/theme-factory/themes/botanical-garden.md +19 -0
  365. package/skills/creative/theme-factory/themes/desert-rose.md +19 -0
  366. package/skills/creative/theme-factory/themes/forest-canopy.md +19 -0
  367. package/skills/creative/theme-factory/themes/golden-hour.md +19 -0
  368. package/skills/creative/theme-factory/themes/midnight-galaxy.md +19 -0
  369. package/skills/creative/theme-factory/themes/modern-minimalist.md +19 -0
  370. package/skills/creative/theme-factory/themes/ocean-depths.md +19 -0
  371. package/skills/creative/theme-factory/themes/sunset-boulevard.md +19 -0
  372. package/skills/creative/theme-factory/themes/tech-innovation.md +19 -0
  373. package/skills/creative/web-artifacts-builder/LICENSE.txt +202 -0
  374. package/skills/creative/web-artifacts-builder/SKILL-zh.md +74 -0
  375. package/skills/creative/web-artifacts-builder/SKILL.md +74 -0
  376. package/skills/creative/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  377. package/skills/creative/web-artifacts-builder/scripts/init-artifact.sh +322 -0
  378. package/skills/creative/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  379. package/skills/documents/doc-coauthoring/SKILL-zh.md +375 -0
  380. package/skills/documents/doc-coauthoring/SKILL.md +375 -0
  381. package/skills/documents/docx/SKILL-zh.md +590 -0
  382. package/skills/{docx → documents/docx}/SKILL.md +11 -11
  383. package/skills/{docx → documents/docx}/scripts/comment.py +2 -2
  384. package/skills/{docx → documents/docx}/scripts/office/helpers/simplify_redlines.py +1 -1
  385. package/skills/{docx → documents/docx}/scripts/office/pack.py +2 -2
  386. package/skills/{xlsx → documents/docx}/scripts/office/validate.py +2 -2
  387. package/skills/{xlsx → documents/docx}/scripts/office/validators/redlining.py +1 -1
  388. package/skills/documents/pdf/SKILL-zh.md +314 -0
  389. package/skills/documents/pptx/SKILL-zh.md +232 -0
  390. package/skills/{pptx → documents/pptx}/editing.md +3 -3
  391. package/skills/{pptx → documents/pptx}/scripts/office/helpers/simplify_redlines.py +1 -1
  392. package/skills/{xlsx → documents/pptx}/scripts/office/pack.py +2 -2
  393. package/skills/{pptx → documents/pptx}/scripts/office/validate.py +2 -2
  394. package/skills/{docx → documents/pptx}/scripts/office/validators/redlining.py +1 -1
  395. package/skills/documents/xlsx/SKILL-zh.md +292 -0
  396. package/skills/{xlsx → documents/xlsx}/scripts/office/helpers/simplify_redlines.py +1 -1
  397. package/skills/{pptx → documents/xlsx}/scripts/office/pack.py +2 -2
  398. package/skills/{docx → documents/xlsx}/scripts/office/validate.py +2 -2
  399. package/skills/{pptx → documents/xlsx}/scripts/office/validators/redlining.py +1 -1
  400. package/skills/meta/skill-creator/LICENSE.txt +202 -0
  401. package/skills/meta/skill-creator/SKILL-zh.md +483 -0
  402. package/skills/{skill-creator → meta/skill-creator}/SKILL.md +0 -1
  403. package/skills/tools/find-skills/SKILL-zh.md +440 -0
  404. package/skills/tools/github/SKILL-zh.md +48 -0
  405. package/skills/tools/internal-comms/LICENSE.txt +202 -0
  406. package/skills/tools/internal-comms/SKILL-zh.md +32 -0
  407. package/skills/tools/internal-comms/SKILL.md +32 -0
  408. package/skills/tools/internal-comms/examples/3p-updates.md +47 -0
  409. package/skills/tools/internal-comms/examples/company-newsletter.md +65 -0
  410. package/skills/tools/internal-comms/examples/faq-answers.md +30 -0
  411. package/skills/tools/internal-comms/examples/general-comms.md +16 -0
  412. package/skills/tools/summarize/SKILL-zh.md +47 -0
  413. package/skills/tools/weather/SKILL-zh.md +46 -0
  414. package/skills/tools/webapp-testing/LICENSE.txt +202 -0
  415. package/skills/tools/webapp-testing/SKILL-zh.md +96 -0
  416. package/skills/tools/webapp-testing/SKILL.md +96 -0
  417. package/skills/tools/webapp-testing/examples/console_logging.py +35 -0
  418. package/skills/tools/webapp-testing/examples/element_discovery.py +40 -0
  419. package/skills/tools/webapp-testing/examples/static_html_automation.py +33 -0
  420. package/skills/tools/webapp-testing/scripts/with_server.py +106 -0
  421. package/dist/gateway/static/root/assets/agents-CkgFSiCY.js +0 -216
  422. package/dist/gateway/static/root/assets/agents-CkgFSiCY.js.map +0 -1
  423. package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js +0 -9
  424. package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js.map +0 -1
  425. package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js +0 -2
  426. package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js.map +0 -1
  427. package/dist/gateway/static/root/assets/cron-utils-N1PqD2DB.js +0 -3
  428. package/dist/gateway/static/root/assets/cron-utils-N1PqD2DB.js.map +0 -1
  429. package/dist/gateway/static/root/assets/dist--p2HQ2QF.js +0 -2
  430. package/dist/gateway/static/root/assets/index-Dnfha4O2.css +0 -1
  431. package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js +0 -2
  432. package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js.map +0 -1
  433. package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js +0 -2
  434. package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js +0 -2
  435. package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js.map +0 -1
  436. package/dist/gateway/static/root/assets/skills-page-Clg8deH0.js +0 -3
  437. package/dist/gateway/static/root/assets/skills-page-Clg8deH0.js.map +0 -1
  438. package/dist/src/agent/skills/skills-store-client.d.ts +0 -66
  439. package/dist/src/agent/skills/skills-store-client.js.map +0 -1
  440. package/dist/src/stt/index.d.ts +0 -1
  441. package/dist/src/stt/index.js +0 -8
  442. /package/skills/{docx → documents/docx}/LICENSE.txt +0 -0
  443. /package/skills/{docx → documents/docx}/scripts/__init__.py +0 -0
  444. /package/skills/{docx → documents/docx}/scripts/accept_changes.py +0 -0
  445. /package/skills/{docx → documents/docx}/scripts/office/helpers/__init__.py +0 -0
  446. /package/skills/{docx → documents/docx}/scripts/office/helpers/merge_runs.py +0 -0
  447. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -0
  448. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -0
  449. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -0
  450. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -0
  451. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -0
  452. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -0
  453. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -0
  454. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -0
  455. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -0
  456. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -0
  457. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -0
  458. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -0
  459. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -0
  460. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -0
  461. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -0
  462. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -0
  463. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -0
  464. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -0
  465. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -0
  466. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -0
  467. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -0
  468. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -0
  469. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -0
  470. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -0
  471. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -0
  472. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -0
  473. /package/skills/{docx → documents/docx}/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -0
  474. /package/skills/{docx → documents/docx}/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -0
  475. /package/skills/{docx → documents/docx}/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -0
  476. /package/skills/{docx → documents/docx}/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -0
  477. /package/skills/{docx → documents/docx}/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -0
  478. /package/skills/{docx → documents/docx}/scripts/office/schemas/mce/mc.xsd +0 -0
  479. /package/skills/{docx → documents/docx}/scripts/office/schemas/microsoft/wml-2010.xsd +0 -0
  480. /package/skills/{docx → documents/docx}/scripts/office/schemas/microsoft/wml-2012.xsd +0 -0
  481. /package/skills/{docx → documents/docx}/scripts/office/schemas/microsoft/wml-2018.xsd +0 -0
  482. /package/skills/{docx → documents/docx}/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -0
  483. /package/skills/{docx → documents/docx}/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -0
  484. /package/skills/{docx → documents/docx}/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -0
  485. /package/skills/{docx → documents/docx}/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -0
  486. /package/skills/{docx → documents/docx}/scripts/office/soffice.py +0 -0
  487. /package/skills/{docx → documents/docx}/scripts/office/unpack.py +0 -0
  488. /package/skills/{docx → documents/docx}/scripts/office/validators/__init__.py +0 -0
  489. /package/skills/{docx → documents/docx}/scripts/office/validators/base.py +0 -0
  490. /package/skills/{docx → documents/docx}/scripts/office/validators/docx.py +0 -0
  491. /package/skills/{docx → documents/docx}/scripts/office/validators/pptx.py +0 -0
  492. /package/skills/{docx → documents/docx}/scripts/templates/comments.xml +0 -0
  493. /package/skills/{docx → documents/docx}/scripts/templates/commentsExtended.xml +0 -0
  494. /package/skills/{docx → documents/docx}/scripts/templates/commentsExtensible.xml +0 -0
  495. /package/skills/{docx → documents/docx}/scripts/templates/commentsIds.xml +0 -0
  496. /package/skills/{docx → documents/docx}/scripts/templates/people.xml +0 -0
  497. /package/skills/{pdf → documents/pdf}/LICENSE.txt +0 -0
  498. /package/skills/{pdf → documents/pdf}/SKILL.md +0 -0
  499. /package/skills/{pdf → documents/pdf}/forms.md +0 -0
  500. /package/skills/{pdf → documents/pdf}/reference.md +0 -0
  501. /package/skills/{pdf → documents/pdf}/scripts/check_bounding_boxes.py +0 -0
  502. /package/skills/{pdf → documents/pdf}/scripts/check_fillable_fields.py +0 -0
  503. /package/skills/{pdf → documents/pdf}/scripts/convert_pdf_to_images.py +0 -0
  504. /package/skills/{pdf → documents/pdf}/scripts/create_validation_image.py +0 -0
  505. /package/skills/{pdf → documents/pdf}/scripts/extract_form_field_info.py +0 -0
  506. /package/skills/{pdf → documents/pdf}/scripts/extract_form_structure.py +0 -0
  507. /package/skills/{pdf → documents/pdf}/scripts/fill_fillable_fields.py +0 -0
  508. /package/skills/{pdf → documents/pdf}/scripts/fill_pdf_form_with_annotations.py +0 -0
  509. /package/skills/{pptx → documents/pptx}/LICENSE.txt +0 -0
  510. /package/skills/{pptx → documents/pptx}/SKILL.md +0 -0
  511. /package/skills/{pptx → documents/pptx}/pptxgenjs.md +0 -0
  512. /package/skills/{pptx → documents/pptx}/scripts/__init__.py +0 -0
  513. /package/skills/{pptx → documents/pptx}/scripts/add_slide.py +0 -0
  514. /package/skills/{pptx → documents/pptx}/scripts/clean.py +0 -0
  515. /package/skills/{pptx → documents/pptx}/scripts/office/helpers/__init__.py +0 -0
  516. /package/skills/{pptx → documents/pptx}/scripts/office/helpers/merge_runs.py +0 -0
  517. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -0
  518. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -0
  519. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -0
  520. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -0
  521. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -0
  522. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -0
  523. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -0
  524. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -0
  525. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -0
  526. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -0
  527. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -0
  528. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -0
  529. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -0
  530. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -0
  531. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -0
  532. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -0
  533. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -0
  534. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -0
  535. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -0
  536. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -0
  537. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -0
  538. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -0
  539. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -0
  540. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -0
  541. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -0
  542. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -0
  543. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -0
  544. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -0
  545. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -0
  546. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -0
  547. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -0
  548. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/mce/mc.xsd +0 -0
  549. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/microsoft/wml-2010.xsd +0 -0
  550. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/microsoft/wml-2012.xsd +0 -0
  551. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/microsoft/wml-2018.xsd +0 -0
  552. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -0
  553. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -0
  554. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -0
  555. /package/skills/{pptx → documents/pptx}/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -0
  556. /package/skills/{pptx → documents/pptx}/scripts/office/soffice.py +0 -0
  557. /package/skills/{pptx → documents/pptx}/scripts/office/unpack.py +0 -0
  558. /package/skills/{pptx → documents/pptx}/scripts/office/validators/__init__.py +0 -0
  559. /package/skills/{pptx → documents/pptx}/scripts/office/validators/base.py +0 -0
  560. /package/skills/{pptx → documents/pptx}/scripts/office/validators/docx.py +0 -0
  561. /package/skills/{pptx → documents/pptx}/scripts/office/validators/pptx.py +0 -0
  562. /package/skills/{pptx → documents/pptx}/scripts/thumbnail.py +0 -0
  563. /package/skills/{xlsx → documents/xlsx}/LICENSE.txt +0 -0
  564. /package/skills/{xlsx → documents/xlsx}/SKILL.md +0 -0
  565. /package/skills/{skill-creator/scripts → documents/xlsx/scripts/office/helpers}/__init__.py +0 -0
  566. /package/skills/{xlsx → documents/xlsx}/scripts/office/helpers/merge_runs.py +0 -0
  567. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -0
  568. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -0
  569. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -0
  570. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -0
  571. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -0
  572. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -0
  573. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -0
  574. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -0
  575. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -0
  576. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -0
  577. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -0
  578. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -0
  579. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -0
  580. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -0
  581. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -0
  582. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -0
  583. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -0
  584. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -0
  585. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -0
  586. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -0
  587. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -0
  588. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -0
  589. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -0
  590. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -0
  591. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -0
  592. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -0
  593. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -0
  594. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -0
  595. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -0
  596. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -0
  597. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -0
  598. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/mce/mc.xsd +0 -0
  599. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/microsoft/wml-2010.xsd +0 -0
  600. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/microsoft/wml-2012.xsd +0 -0
  601. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/microsoft/wml-2018.xsd +0 -0
  602. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -0
  603. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -0
  604. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -0
  605. /package/skills/{xlsx → documents/xlsx}/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -0
  606. /package/skills/{xlsx → documents/xlsx}/scripts/office/soffice.py +0 -0
  607. /package/skills/{xlsx → documents/xlsx}/scripts/office/unpack.py +0 -0
  608. /package/skills/{xlsx → documents/xlsx}/scripts/office/validators/__init__.py +0 -0
  609. /package/skills/{xlsx → documents/xlsx}/scripts/office/validators/base.py +0 -0
  610. /package/skills/{xlsx → documents/xlsx}/scripts/office/validators/docx.py +0 -0
  611. /package/skills/{xlsx → documents/xlsx}/scripts/office/validators/pptx.py +0 -0
  612. /package/skills/{xlsx → documents/xlsx}/scripts/recalc.py +0 -0
  613. /package/skills/{skill-creator → meta/skill-creator}/agents/analyzer.md +0 -0
  614. /package/skills/{skill-creator → meta/skill-creator}/agents/comparator.md +0 -0
  615. /package/skills/{skill-creator → meta/skill-creator}/agents/grader.md +0 -0
  616. /package/skills/{skill-creator → meta/skill-creator}/assets/eval_review.html +0 -0
  617. /package/skills/{skill-creator → meta/skill-creator}/eval-viewer/generate_review.py +0 -0
  618. /package/skills/{skill-creator → meta/skill-creator}/eval-viewer/viewer.html +0 -0
  619. /package/skills/{skill-creator → meta/skill-creator}/references/schemas.md +0 -0
  620. /package/skills/{xlsx/scripts/office/helpers → meta/skill-creator/scripts}/__init__.py +0 -0
  621. /package/skills/{skill-creator → meta/skill-creator}/scripts/aggregate_benchmark.py +0 -0
  622. /package/skills/{skill-creator → meta/skill-creator}/scripts/generate_report.py +0 -0
  623. /package/skills/{skill-creator → meta/skill-creator}/scripts/improve_description.py +0 -0
  624. /package/skills/{skill-creator → meta/skill-creator}/scripts/package_skill.py +0 -0
  625. /package/skills/{skill-creator → meta/skill-creator}/scripts/quick_validate.py +0 -0
  626. /package/skills/{skill-creator → meta/skill-creator}/scripts/run_eval.py +0 -0
  627. /package/skills/{skill-creator → meta/skill-creator}/scripts/run_loop.py +0 -0
  628. /package/skills/{skill-creator → meta/skill-creator}/scripts/utils.py +0 -0
  629. /package/skills/{find-skills → tools/find-skills}/SKILL.md +0 -0
  630. /package/skills/{github → tools/github}/SKILL.md +0 -0
  631. /package/skills/{summarize → tools/summarize}/SKILL.md +0 -0
  632. /package/skills/{weather → tools/weather}/SKILL.md +0 -0
@@ -1,5 +1,6 @@
1
+ import { init_write_file_atomic, writeTextAtomicSync } from "../../infra/write-file-atomic.js";
1
2
  import path from "node:path";
2
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { chmodSync, existsSync, mkdirSync, readFileSync } from "node:fs";
3
4
  import { homedir } from "os";
4
5
  //#region src/auth/profiles/store.ts
5
6
  /**
@@ -8,6 +9,7 @@ import { homedir } from "os";
8
9
  * Persistent storage for auth profiles.
9
10
  * Handles loading and saving auth credentials.
10
11
  */
12
+ init_write_file_atomic();
11
13
  const AUTH_STORE_VERSION = 1;
12
14
  const AUTH_STORE_FILENAME = "auth-profiles.json";
13
15
  /** Get the auth profiles file path */
@@ -23,7 +25,7 @@ function ensureAuthStoreFile(authPath) {
23
25
  mode: 448
24
26
  });
25
27
  if (!existsSync(authPath)) {
26
- writeFileSync(authPath, JSON.stringify({
28
+ writeTextAtomicSync(authPath, JSON.stringify({
27
29
  version: AUTH_STORE_VERSION,
28
30
  profiles: {}
29
31
  }, null, 2));
@@ -45,7 +47,7 @@ function saveJsonFile(filePath, data) {
45
47
  recursive: true,
46
48
  mode: 448
47
49
  });
48
- writeFileSync(filePath, JSON.stringify(data, null, 2));
50
+ writeTextAtomicSync(filePath, JSON.stringify(data, null, 2));
49
51
  chmodSync(filePath, 384);
50
52
  }
51
53
  function coerceAuthStore(raw) {
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","names":[],"sources":["../../../../src/auth/profiles/store.ts"],"sourcesContent":["/**\n * Auth Profile Store\n * \n * Persistent storage for auth profiles.\n * Handles loading and saving auth credentials.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { homedir } from 'os';\nimport type { AuthProfileCredential, AuthProfileStore, ProfileUsageStats } from './types.js';\n\nconst AUTH_STORE_VERSION = 1;\nconst AUTH_STORE_FILENAME = 'auth-profiles.json';\n\n/** Get the auth profiles file path */\nexport function resolveAuthStorePath(dataDir?: string): string {\n\tconst dir = dataDir ?? path.join(homedir(), '.xopc');\n\treturn path.join(dir, AUTH_STORE_FILENAME);\n}\n\n/** Ensure auth store file exists */\nexport function ensureAuthStoreFile(authPath: string): void {\n\tconst dir = path.dirname(authPath);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t}\n\tif (!existsSync(authPath)) {\n\t\twriteFileSync(authPath, JSON.stringify({ version: AUTH_STORE_VERSION, profiles: {} }, null, 2));\n\t\tchmodSync(authPath, 0o600);\n\t}\n}\n\nfunction loadJsonFile(filePath: string): unknown {\n\tif (!existsSync(filePath)) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst content = readFileSync(filePath, 'utf-8');\n\t\treturn JSON.parse(content);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction saveJsonFile(filePath: string, data: unknown): void {\n\tconst dir = path.dirname(filePath);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t}\n\twriteFileSync(filePath, JSON.stringify(data, null, 2));\n\tchmodSync(filePath, 0o600);\n}\n\nfunction coerceAuthStore(raw: unknown): AuthProfileStore | null {\n\tif (!raw || typeof raw !== 'object') {\n\t\treturn null;\n\t}\n\tconst record = raw as Record<string, unknown>;\n\tif (!record.profiles || typeof record.profiles !== 'object') {\n\t\treturn null;\n\t}\n\tconst profiles = record.profiles as Record<string, unknown>;\n\tconst normalized: Record<string, AuthProfileCredential> = {};\n\tfor (const [key, value] of Object.entries(profiles)) {\n\t\tif (!value || typeof value !== 'object') {\n\t\t\tcontinue;\n\t\t}\n\t\tconst typed = value as Partial<AuthProfileCredential>;\n\t\tif (typed.type !== 'api_key' && typed.type !== 'oauth' && typed.type !== 'token') {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!typed.provider) {\n\t\t\tcontinue;\n\t\t}\n\t\tnormalized[key] = typed as AuthProfileCredential;\n\t}\n\tconst order =\n\t\trecord.order && typeof record.order === 'object'\n\t\t\t? Object.entries(record.order as Record<string, unknown>).reduce(\n\t\t\t\t(acc, [provider, value]) => {\n\t\t\t\t\tif (!Array.isArray(value)) {\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t}\n\t\t\t\t\tconst list = value\n\t\t\t\t\t\t.map((entry) => (typeof entry === 'string' ? entry.trim() : ''))\n\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\tif (list.length === 0) {\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t}\n\t\t\t\t\tacc[provider] = list;\n\t\t\t\t\treturn acc;\n\t\t\t\t},\n\t\t\t\t{} as Record<string, string[]>,\n\t\t\t)\n\t\t\t: undefined;\n\treturn {\n\t\tversion: Number(record.version ?? AUTH_STORE_VERSION),\n\t\tprofiles: normalized,\n\t\torder,\n\t\tlastGood:\n\t\t\trecord.lastGood && typeof record.lastGood === 'object'\n\t\t\t\t? (record.lastGood as Record<string, string>)\n\t\t\t\t: undefined,\n\t\tusageStats:\n\t\t\trecord.usageStats && typeof record.usageStats === 'object'\n\t\t\t\t? (record.usageStats as Record<string, ProfileUsageStats>)\n\t\t\t\t: undefined,\n\t};\n}\n\nfunction mergeRecord<T>(\n\tbase?: Record<string, T>,\n\toverride?: Record<string, T>,\n): Record<string, T> | undefined {\n\tif (!base && !override) {\n\t\treturn undefined;\n\t}\n\tif (!base) {\n\t\treturn { ...override };\n\t}\n\tif (!override) {\n\t\treturn { ...base };\n\t}\n\treturn { ...base, ...override };\n}\n\nfunction _mergeAuthProfileStores(\n\tbase: AuthProfileStore,\n\toverride: AuthProfileStore,\n): AuthProfileStore {\n\tif (\n\t\tObject.keys(override.profiles).length === 0 &&\n\t\t!override.order &&\n\t\t!override.lastGood &&\n\t\t!override.usageStats\n\t) {\n\t\treturn base;\n\t}\n\treturn {\n\t\tversion: Math.max(base.version, override.version ?? base.version),\n\t\tprofiles: { ...base.profiles, ...override.profiles },\n\t\torder: mergeRecord(base.order, override.order),\n\t\tlastGood: mergeRecord(base.lastGood, override.lastGood),\n\t\tusageStats: mergeRecord(base.usageStats, override.usageStats),\n\t};\n}\n\n/** Load auth profile store from disk */\nexport function loadAuthProfileStore(dataDir?: string): AuthProfileStore {\n\tconst authPath = resolveAuthStorePath(dataDir);\n\tconst raw = loadJsonFile(authPath);\n\tconst asStore = coerceAuthStore(raw);\n\tif (asStore) {\n\t\treturn asStore;\n\t}\n\n\treturn { version: AUTH_STORE_VERSION, profiles: {} };\n}\n\n/** Ensure auth profile store exists */\nexport function ensureAuthProfileStore(dataDir?: string): AuthProfileStore {\n\tconst authPath = resolveAuthStorePath(dataDir);\n\tensureAuthStoreFile(authPath);\n\treturn loadAuthProfileStore(dataDir);\n}\n\n/** Save auth profile store to disk */\nexport function saveAuthProfileStore(store: AuthProfileStore, dataDir?: string): void {\n\tconst authPath = resolveAuthStorePath(dataDir);\n\tconst payload = {\n\t\tversion: AUTH_STORE_VERSION,\n\t\tprofiles: store.profiles,\n\t\torder: store.order ?? undefined,\n\t\tlastGood: store.lastGood ?? undefined,\n\t\tusageStats: store.usageStats ?? undefined,\n\t} satisfies AuthProfileStore;\n\tsaveJsonFile(authPath, payload);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"store.js","names":[],"sources":["../../../../src/auth/profiles/store.ts"],"sourcesContent":["/**\n * Auth Profile Store\n * \n * Persistent storage for auth profiles.\n * Handles loading and saving auth credentials.\n */\n\nimport { chmodSync, existsSync, mkdirSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { writeTextAtomicSync } from '../../infra/write-file-atomic.js';\nimport { homedir } from 'os';\nimport type { AuthProfileCredential, AuthProfileStore, ProfileUsageStats } from './types.js';\n\nconst AUTH_STORE_VERSION = 1;\nconst AUTH_STORE_FILENAME = 'auth-profiles.json';\n\n/** Get the auth profiles file path */\nexport function resolveAuthStorePath(dataDir?: string): string {\n\tconst dir = dataDir ?? path.join(homedir(), '.xopc');\n\treturn path.join(dir, AUTH_STORE_FILENAME);\n}\n\n/** Ensure auth store file exists */\nexport function ensureAuthStoreFile(authPath: string): void {\n\tconst dir = path.dirname(authPath);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t}\n\tif (!existsSync(authPath)) {\n\t\twriteTextAtomicSync(\n\t\t\tauthPath,\n\t\t\tJSON.stringify({ version: AUTH_STORE_VERSION, profiles: {} }, null, 2),\n\t\t);\n\t\tchmodSync(authPath, 0o600);\n\t}\n}\n\nfunction loadJsonFile(filePath: string): unknown {\n\tif (!existsSync(filePath)) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst content = readFileSync(filePath, 'utf-8');\n\t\treturn JSON.parse(content);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction saveJsonFile(filePath: string, data: unknown): void {\n\tconst dir = path.dirname(filePath);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t}\n\twriteTextAtomicSync(filePath, JSON.stringify(data, null, 2));\n\tchmodSync(filePath, 0o600);\n}\n\nfunction coerceAuthStore(raw: unknown): AuthProfileStore | null {\n\tif (!raw || typeof raw !== 'object') {\n\t\treturn null;\n\t}\n\tconst record = raw as Record<string, unknown>;\n\tif (!record.profiles || typeof record.profiles !== 'object') {\n\t\treturn null;\n\t}\n\tconst profiles = record.profiles as Record<string, unknown>;\n\tconst normalized: Record<string, AuthProfileCredential> = {};\n\tfor (const [key, value] of Object.entries(profiles)) {\n\t\tif (!value || typeof value !== 'object') {\n\t\t\tcontinue;\n\t\t}\n\t\tconst typed = value as Partial<AuthProfileCredential>;\n\t\tif (typed.type !== 'api_key' && typed.type !== 'oauth' && typed.type !== 'token') {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!typed.provider) {\n\t\t\tcontinue;\n\t\t}\n\t\tnormalized[key] = typed as AuthProfileCredential;\n\t}\n\tconst order =\n\t\trecord.order && typeof record.order === 'object'\n\t\t\t? Object.entries(record.order as Record<string, unknown>).reduce(\n\t\t\t\t(acc, [provider, value]) => {\n\t\t\t\t\tif (!Array.isArray(value)) {\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t}\n\t\t\t\t\tconst list = value\n\t\t\t\t\t\t.map((entry) => (typeof entry === 'string' ? entry.trim() : ''))\n\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\tif (list.length === 0) {\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t}\n\t\t\t\t\tacc[provider] = list;\n\t\t\t\t\treturn acc;\n\t\t\t\t},\n\t\t\t\t{} as Record<string, string[]>,\n\t\t\t)\n\t\t\t: undefined;\n\treturn {\n\t\tversion: Number(record.version ?? AUTH_STORE_VERSION),\n\t\tprofiles: normalized,\n\t\torder,\n\t\tlastGood:\n\t\t\trecord.lastGood && typeof record.lastGood === 'object'\n\t\t\t\t? (record.lastGood as Record<string, string>)\n\t\t\t\t: undefined,\n\t\tusageStats:\n\t\t\trecord.usageStats && typeof record.usageStats === 'object'\n\t\t\t\t? (record.usageStats as Record<string, ProfileUsageStats>)\n\t\t\t\t: undefined,\n\t};\n}\n\nfunction mergeRecord<T>(\n\tbase?: Record<string, T>,\n\toverride?: Record<string, T>,\n): Record<string, T> | undefined {\n\tif (!base && !override) {\n\t\treturn undefined;\n\t}\n\tif (!base) {\n\t\treturn { ...override };\n\t}\n\tif (!override) {\n\t\treturn { ...base };\n\t}\n\treturn { ...base, ...override };\n}\n\nfunction _mergeAuthProfileStores(\n\tbase: AuthProfileStore,\n\toverride: AuthProfileStore,\n): AuthProfileStore {\n\tif (\n\t\tObject.keys(override.profiles).length === 0 &&\n\t\t!override.order &&\n\t\t!override.lastGood &&\n\t\t!override.usageStats\n\t) {\n\t\treturn base;\n\t}\n\treturn {\n\t\tversion: Math.max(base.version, override.version ?? base.version),\n\t\tprofiles: { ...base.profiles, ...override.profiles },\n\t\torder: mergeRecord(base.order, override.order),\n\t\tlastGood: mergeRecord(base.lastGood, override.lastGood),\n\t\tusageStats: mergeRecord(base.usageStats, override.usageStats),\n\t};\n}\n\n/** Load auth profile store from disk */\nexport function loadAuthProfileStore(dataDir?: string): AuthProfileStore {\n\tconst authPath = resolveAuthStorePath(dataDir);\n\tconst raw = loadJsonFile(authPath);\n\tconst asStore = coerceAuthStore(raw);\n\tif (asStore) {\n\t\treturn asStore;\n\t}\n\n\treturn { version: AUTH_STORE_VERSION, profiles: {} };\n}\n\n/** Ensure auth profile store exists */\nexport function ensureAuthProfileStore(dataDir?: string): AuthProfileStore {\n\tconst authPath = resolveAuthStorePath(dataDir);\n\tensureAuthStoreFile(authPath);\n\treturn loadAuthProfileStore(dataDir);\n}\n\n/** Save auth profile store to disk */\nexport function saveAuthProfileStore(store: AuthProfileStore, dataDir?: string): void {\n\tconst authPath = resolveAuthStorePath(dataDir);\n\tconst payload = {\n\t\tversion: AUTH_STORE_VERSION,\n\t\tprofiles: store.profiles,\n\t\torder: store.order ?? undefined,\n\t\tlastGood: store.lastGood ?? undefined,\n\t\tusageStats: store.usageStats ?? undefined,\n\t} satisfies AuthProfileStore;\n\tsaveJsonFile(authPath, payload);\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
3
  import { mergeTtsConfigFromAppConfig } from "../voice/tts/merge-config.js";
4
- import "./internal-outbound.js";
5
4
  import { maybeApplyTtsToPayload } from "../voice/tts/payload.js";
5
+ import "./internal-outbound.js";
6
6
  import { ChannelHealthMonitor } from "./health-monitor.js";
7
7
  import { deliverOutboundMessage } from "./outbound/deliver.js";
8
8
  import { OutboundPersistStore } from "./outbound/persist-store.js";
@@ -1,10 +1,12 @@
1
+ import { init_write_file_atomic, writeTextAtomicSync } from "../../infra/write-file-atomic.js";
1
2
  import { join } from "path";
2
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
3
+ import { existsSync, mkdirSync, readFileSync } from "fs";
3
4
  import { randomUUID } from "node:crypto";
4
5
  //#region src/channels/outbound/persist-store.ts
5
6
  /**
6
7
  * Durable outbound queue (crash recovery): JSON file under agent internal dir.
7
8
  */
9
+ init_write_file_atomic();
8
10
  var OutboundPersistStore = class {
9
11
  filePath;
10
12
  pending = [];
@@ -24,9 +26,7 @@ var OutboundPersistStore = class {
24
26
  }
25
27
  }
26
28
  flush() {
27
- const tmp = `${this.filePath}.tmp`;
28
- writeFileSync(tmp, JSON.stringify(this.pending), "utf-8");
29
- renameSync(tmp, this.filePath);
29
+ writeTextAtomicSync(this.filePath, JSON.stringify(this.pending));
30
30
  }
31
31
  enqueue(message) {
32
32
  const id = randomUUID();
@@ -1 +1 @@
1
- {"version":3,"file":"persist-store.js","names":[],"sources":["../../../../src/channels/outbound/persist-store.ts"],"sourcesContent":["/**\n * Durable outbound queue (crash recovery): JSON file under agent internal dir.\n */\n\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport { randomUUID } from 'node:crypto';\nimport type { OutboundMessage } from '../transport-types.js';\n\nexport interface PendingOutbound {\n id: string;\n enqueuedAt: number;\n message: OutboundMessage;\n}\n\nexport class OutboundPersistStore {\n private readonly filePath: string;\n private pending: PendingOutbound[] = [];\n\n constructor(agentDir: string) {\n mkdirSync(agentDir, { recursive: true });\n this.filePath = join(agentDir, 'outbound-pending.json');\n this.load();\n }\n\n private load(): void {\n if (!existsSync(this.filePath)) return;\n try {\n const raw = readFileSync(this.filePath, 'utf-8');\n const data = JSON.parse(raw) as PendingOutbound[];\n if (Array.isArray(data)) {\n this.pending = data;\n }\n } catch {\n this.pending = [];\n }\n }\n\n private flush(): void {\n const tmp = `${this.filePath}.tmp`;\n writeFileSync(tmp, JSON.stringify(this.pending), 'utf-8');\n renameSync(tmp, this.filePath);\n }\n\n enqueue(message: OutboundMessage): string {\n const id = randomUUID();\n this.pending.push({ id, enqueuedAt: Date.now(), message });\n this.flush();\n return id;\n }\n\n ack(id: string): void {\n const i = this.pending.findIndex((p) => p.id === id);\n if (i >= 0) {\n this.pending.splice(i, 1);\n this.flush();\n }\n }\n\n peek(): readonly PendingOutbound[] {\n return this.pending;\n }\n}\n"],"mappings":";;;;;;;AAeA,IAAa,uBAAb,MAAkC;CAChC;CACA,UAAqC,EAAE;CAEvC,YAAY,UAAkB;AAC5B,YAAU,UAAU,EAAE,WAAW,MAAM,CAAC;AACxC,OAAK,WAAW,KAAK,UAAU,wBAAwB;AACvD,OAAK,MAAM;;CAGb,OAAqB;AACnB,MAAI,CAAC,WAAW,KAAK,SAAS,CAAE;AAChC,MAAI;GACF,MAAM,MAAM,aAAa,KAAK,UAAU,QAAQ;GAChD,MAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,OAAI,MAAM,QAAQ,KAAK,CACrB,MAAK,UAAU;UAEX;AACN,QAAK,UAAU,EAAE;;;CAIrB,QAAsB;EACpB,MAAM,MAAM,GAAG,KAAK,SAAS;AAC7B,gBAAc,KAAK,KAAK,UAAU,KAAK,QAAQ,EAAE,QAAQ;AACzD,aAAW,KAAK,KAAK,SAAS;;CAGhC,QAAQ,SAAkC;EACxC,MAAM,KAAK,YAAY;AACvB,OAAK,QAAQ,KAAK;GAAE;GAAI,YAAY,KAAK,KAAK;GAAE;GAAS,CAAC;AAC1D,OAAK,OAAO;AACZ,SAAO;;CAGT,IAAI,IAAkB;EACpB,MAAM,IAAI,KAAK,QAAQ,WAAW,MAAM,EAAE,OAAO,GAAG;AACpD,MAAI,KAAK,GAAG;AACV,QAAK,QAAQ,OAAO,GAAG,EAAE;AACzB,QAAK,OAAO;;;CAIhB,OAAmC;AACjC,SAAO,KAAK"}
1
+ {"version":3,"file":"persist-store.js","names":[],"sources":["../../../../src/channels/outbound/persist-store.ts"],"sourcesContent":["/**\n * Durable outbound queue (crash recovery): JSON file under agent internal dir.\n */\n\nimport { existsSync, mkdirSync, readFileSync } from 'fs';\nimport { writeTextAtomicSync } from '../../infra/write-file-atomic.js';\nimport { join } from 'path';\nimport { randomUUID } from 'node:crypto';\nimport type { OutboundMessage } from '../transport-types.js';\n\nexport interface PendingOutbound {\n id: string;\n enqueuedAt: number;\n message: OutboundMessage;\n}\n\nexport class OutboundPersistStore {\n private readonly filePath: string;\n private pending: PendingOutbound[] = [];\n\n constructor(agentDir: string) {\n mkdirSync(agentDir, { recursive: true });\n this.filePath = join(agentDir, 'outbound-pending.json');\n this.load();\n }\n\n private load(): void {\n if (!existsSync(this.filePath)) return;\n try {\n const raw = readFileSync(this.filePath, 'utf-8');\n const data = JSON.parse(raw) as PendingOutbound[];\n if (Array.isArray(data)) {\n this.pending = data;\n }\n } catch {\n this.pending = [];\n }\n }\n\n private flush(): void {\n writeTextAtomicSync(this.filePath, JSON.stringify(this.pending));\n }\n\n enqueue(message: OutboundMessage): string {\n const id = randomUUID();\n this.pending.push({ id, enqueuedAt: Date.now(), message });\n this.flush();\n return id;\n }\n\n ack(id: string): void {\n const i = this.pending.findIndex((p) => p.id === id);\n if (i >= 0) {\n this.pending.splice(i, 1);\n this.flush();\n }\n }\n\n peek(): readonly PendingOutbound[] {\n return this.pending;\n }\n}\n"],"mappings":";;;;;;;;wBAKuE;AAWvE,IAAa,uBAAb,MAAkC;CAChC;CACA,UAAqC,EAAE;CAEvC,YAAY,UAAkB;AAC5B,YAAU,UAAU,EAAE,WAAW,MAAM,CAAC;AACxC,OAAK,WAAW,KAAK,UAAU,wBAAwB;AACvD,OAAK,MAAM;;CAGb,OAAqB;AACnB,MAAI,CAAC,WAAW,KAAK,SAAS,CAAE;AAChC,MAAI;GACF,MAAM,MAAM,aAAa,KAAK,UAAU,QAAQ;GAChD,MAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,OAAI,MAAM,QAAQ,KAAK,CACrB,MAAK,UAAU;UAEX;AACN,QAAK,UAAU,EAAE;;;CAIrB,QAAsB;AACpB,sBAAoB,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ,CAAC;;CAGlE,QAAQ,SAAkC;EACxC,MAAM,KAAK,YAAY;AACvB,OAAK,QAAQ,KAAK;GAAE;GAAI,YAAY,KAAK,KAAK;GAAE;GAAS,CAAC;AAC1D,OAAK,OAAO;AACZ,SAAO;;CAGT,IAAI,IAAkB;EACpB,MAAM,IAAI,KAAK,QAAQ,WAAW,MAAM,EAAE,OAAO,GAAG;AACpD,MAAI,KAAK,GAAG;AACV,QAAK,QAAQ,OAAO,GAAG,EAAE;AACzB,QAAK,OAAO;;;CAIhB,OAAmC;AACjC,SAAO,KAAK"}
@@ -1,11 +1,13 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { init_write_file_atomic, writeTextAtomic } from "../../infra/write-file-atomic.js";
3
4
  import { loadConfig } from "../../config/loader.js";
4
5
  import "../../config/index.js";
5
6
  import { formatExamples, register } from "../registry.js";
6
- import { existsSync, writeFileSync } from "fs";
7
+ import { existsSync } from "fs";
7
8
  import { Command } from "commander";
8
9
  //#region src/cli/commands/config.ts
10
+ init_write_file_atomic();
9
11
  init_logger();
10
12
  const log = createLogger("ConfigCommand");
11
13
  function getNestedValue(obj, path) {
@@ -45,7 +47,7 @@ function createConfigCommand(ctx) {
45
47
  }
46
48
  console.log(typeof value === "object" ? JSON.stringify(value, null, 2) : value);
47
49
  });
48
- cmd.command("set <path> <value>").description("Set a config value by dot path").action((path, value) => {
50
+ cmd.command("set <path> <value>").description("Set a config value by dot path").action(async (path, value) => {
49
51
  if (!existsSync(ctx.configPath)) {
50
52
  log.error("Config file not found. Run: xopc onboard");
51
53
  process.exit(1);
@@ -58,10 +60,10 @@ function createConfigCommand(ctx) {
58
60
  }
59
61
  const config = loadConfig(ctx.configPath);
60
62
  setNestedValue(config, path, parsedValue);
61
- writeFileSync(ctx.configPath, JSON.stringify(config, null, 2));
63
+ await writeTextAtomic(ctx.configPath, JSON.stringify(config, null, 2));
62
64
  log.info({ path }, `Config updated`);
63
65
  });
64
- cmd.command("unset <path>").description("Remove a config value by dot path").action((path) => {
66
+ cmd.command("unset <path>").description("Remove a config value by dot path").action(async (path) => {
65
67
  if (!existsSync(ctx.configPath)) {
66
68
  log.error("Config file not found. Run: xopc onboard");
67
69
  process.exit(1);
@@ -74,7 +76,7 @@ function createConfigCommand(ctx) {
74
76
  }, config);
75
77
  if (target && typeof target === "object" && lastKey in target) {
76
78
  delete target[lastKey];
77
- writeFileSync(ctx.configPath, JSON.stringify(config, null, 2));
79
+ await writeTextAtomic(ctx.configPath, JSON.stringify(config, null, 2));
78
80
  log.info({ path }, `Config removed`);
79
81
  } else {
80
82
  log.error({ path }, `Config path not found`);
@@ -112,7 +114,7 @@ function createConfigCommand(ctx) {
112
114
  mode: "token",
113
115
  token: newToken
114
116
  };
115
- writeFileSync(ctx.configPath, JSON.stringify(config, null, 2));
117
+ await writeTextAtomic(ctx.configPath, JSON.stringify(config, null, 2));
116
118
  log.info("New gateway token generated");
117
119
  console.log(`Token: ${newToken.slice(0, 8)}...${newToken.slice(-8)}`);
118
120
  console.log("\nUse \"xopc config token --show\" to view the full token");
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":[],"sources":["../../../../src/cli/commands/config.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { existsSync, writeFileSync } from 'fs';\nimport { loadConfig } from '../../config/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\n\nconst log = createLogger('ConfigCommand');\n\nfunction getNestedValue(obj: any, path: string): any {\n return path.split('.').reduce((current: any, key: string) => {\n if (current && typeof current === 'object' && key in current) {\n return current[key];\n }\n return undefined;\n }, obj);\n}\n\nfunction setNestedValue(obj: any, path: string, value: any): any {\n const keys = path.split('.');\n const lastKey = keys.pop()!;\n const target = keys.reduce((current: any, key: string) => {\n if (!current[key] || typeof current[key] !== 'object') {\n current[key] = {};\n }\n return current[key];\n }, obj);\n target[lastKey] = value;\n return obj;\n}\n\nfunction createConfigCommand(ctx: CLIContext): Command {\n const cmd = new Command('config')\n .description('View and edit configuration')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc config get agents.defaults.model',\n 'xopc config set agents.defaults.temperature 0.8',\n 'xopc config unset agents.defaults.max_tokens',\n 'xopc config show',\n 'xopc config token # Show gateway token info',\n 'xopc config token --show # Show full token',\n 'xopc config token --generate # Generate new token',\n ])\n );\n\n cmd\n .command('get <path>')\n .description('Get a config value by dot path')\n .action((path: string) => {\n if (!existsSync(ctx.configPath)) {\n log.error('Config file not found. Run: xopc onboard');\n process.exit(1);\n }\n\n const config = loadConfig(ctx.configPath);\n const value = getNestedValue(config, path);\n\n if (value === undefined) {\n log.error({ path }, `Config path not found`);\n process.exit(1);\n }\n\n console.log(typeof value === 'object' ? JSON.stringify(value, null, 2) : value);\n });\n\n cmd\n .command('set <path> <value>')\n .description('Set a config value by dot path')\n .action((path: string, value: string) => {\n if (!existsSync(ctx.configPath)) {\n log.error('Config file not found. Run: xopc onboard');\n process.exit(1);\n }\n\n let parsedValue: any;\n try {\n parsedValue = JSON.parse(value);\n } catch {\n parsedValue = value;\n }\n\n const config = loadConfig(ctx.configPath);\n setNestedValue(config, path, parsedValue);\n\n writeFileSync(ctx.configPath, JSON.stringify(config, null, 2));\n \n log.info({ path }, `Config updated`);\n });\n\n cmd\n .command('unset <path>')\n .description('Remove a config value by dot path')\n .action((path: string) => {\n if (!existsSync(ctx.configPath)) {\n log.error('Config file not found. Run: xopc onboard');\n process.exit(1);\n }\n\n const config = loadConfig(ctx.configPath);\n const keys = path.split('.');\n const lastKey = keys.pop()!;\n const target = keys.reduce((current: any, key: string) => {\n return current && current[key];\n }, config);\n\n if (target && typeof target === 'object' && lastKey in target) {\n delete target[lastKey];\n writeFileSync(ctx.configPath, JSON.stringify(config, null, 2));\n log.info({ path }, `Config removed`);\n } else {\n log.error({ path }, `Config path not found`);\n process.exit(1);\n }\n });\n\n cmd\n .command('show')\n .description('Show full configuration (sensitive values masked)')\n .action(() => {\n if (!existsSync(ctx.configPath)) {\n log.warn('No config file found. Run: xopc onboard');\n return;\n }\n\n const config = loadConfig(ctx.configPath);\n \n const maskedConfig = JSON.stringify(config, (key, value) => {\n if (key === 'api_key' || key === 'token') {\n return value ? '********' : value;\n }\n return value;\n }, 2);\n\n console.log(maskedConfig);\n });\n\n cmd\n .command('token')\n .description('Generate or view the gateway auth token')\n .option('--generate', 'Generate a new token')\n .option('--show', 'Show the current token (unmasked)')\n .action(async (options) => {\n if (!existsSync(ctx.configPath)) {\n log.error('Config file not found. Run: xopc onboard');\n process.exit(1);\n }\n\n const config = loadConfig(ctx.configPath);\n\n if (options.show) {\n const token = config?.gateway?.auth?.token;\n if (token) {\n console.log(token);\n } else {\n console.log('No token configured. Gateway auth mode:', config?.gateway?.auth?.mode || 'not set');\n }\n return;\n }\n\n if (options.generate) {\n const crypto = await import('crypto');\n const newToken = crypto.randomBytes(24).toString('hex');\n\n config.gateway = config.gateway || {};\n config.gateway.auth = {\n mode: 'token',\n token: newToken,\n };\n\n writeFileSync(ctx.configPath, JSON.stringify(config, null, 2));\n log.info('New gateway token generated');\n console.log(`Token: ${newToken.slice(0, 8)}...${newToken.slice(-8)}`);\n console.log('\\nUse \"xopc config token --show\" to view the full token');\n return;\n }\n\n // Default: show masked token info\n const token = config?.gateway?.auth?.token;\n const mode = config?.gateway?.auth?.mode;\n const host = config?.gateway?.host || '0.0.0.0';\n const port = config?.gateway?.port || 18790;\n\n console.log('Gateway Configuration:');\n console.log(` Host: ${host}`);\n console.log(` Port: ${port}`);\n console.log(` Auth Mode: ${mode || 'not set'}`);\n if (token) {\n console.log(` Token: ${token.slice(0, 8)}...${token.slice(-8)}`);\n console.log('\\nUse \"xopc config token --show\" to view the full token');\n console.log('Use \"xopc config token --generate\" to generate a new token');\n } else if (mode === 'token') {\n console.log(' Token: not set (will be auto-generated on first gateway start)');\n }\n });\n\n cmd\n .command('path')\n .description('Show configuration file path')\n .action(() => {\n console.log(ctx.configPath);\n });\n\n return cmd;\n}\n\nregister({\n id: 'config',\n name: 'config',\n description: 'View and edit configuration',\n factory: createConfigCommand,\n metadata: {\n category: 'utility',\n examples: [\n 'xopc config get agents.defaults.model',\n 'xopc config set agents.defaults.temperature 0.8',\n 'xopc config show',\n 'xopc config token',\n 'xopc config token --show',\n 'xopc config token --generate',\n ],\n },\n});\n"],"mappings":";;;;;;;;aAGqD;AAGrD,MAAM,MAAM,aAAa,gBAAgB;AAEzC,SAAS,eAAe,KAAU,MAAmB;AACnD,QAAO,KAAK,MAAM,IAAI,CAAC,QAAQ,SAAc,QAAgB;AAC3D,MAAI,WAAW,OAAO,YAAY,YAAY,OAAO,QACnD,QAAO,QAAQ;IAGhB,IAAI;;AAGT,SAAS,eAAe,KAAU,MAAc,OAAiB;CAC/D,MAAM,OAAO,KAAK,MAAM,IAAI;CAC5B,MAAM,UAAU,KAAK,KAAK;CAC1B,MAAM,SAAS,KAAK,QAAQ,SAAc,QAAgB;AACxD,MAAI,CAAC,QAAQ,QAAQ,OAAO,QAAQ,SAAS,SAC3C,SAAQ,OAAO,EAAE;AAEnB,SAAO,QAAQ;IACd,IAAI;AACP,QAAO,WAAW;AAClB,QAAO;;AAGT,SAAS,oBAAoB,KAA0B;CACrD,MAAM,MAAM,IAAI,QAAQ,SAAS,CAC9B,YAAY,8BAA8B,CAC1C,YACC,SACA,eAAe;EACb;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,CACH;AAEH,KACG,QAAQ,aAAa,CACrB,YAAY,iCAAiC,CAC7C,QAAQ,SAAiB;AACxB,MAAI,CAAC,WAAW,IAAI,WAAW,EAAE;AAC/B,OAAI,MAAM,2CAA2C;AACrD,WAAQ,KAAK,EAAE;;EAIjB,MAAM,QAAQ,eADC,WAAW,IAAI,WACK,EAAE,KAAK;AAE1C,MAAI,UAAU,KAAA,GAAW;AACvB,OAAI,MAAM,EAAE,MAAM,EAAE,wBAAwB;AAC5C,WAAQ,KAAK,EAAE;;AAGjB,UAAQ,IAAI,OAAO,UAAU,WAAW,KAAK,UAAU,OAAO,MAAM,EAAE,GAAG,MAAM;GAC/E;AAEJ,KACG,QAAQ,qBAAqB,CAC7B,YAAY,iCAAiC,CAC7C,QAAQ,MAAc,UAAkB;AACvC,MAAI,CAAC,WAAW,IAAI,WAAW,EAAE;AAC/B,OAAI,MAAM,2CAA2C;AACrD,WAAQ,KAAK,EAAE;;EAGjB,IAAI;AACJ,MAAI;AACF,iBAAc,KAAK,MAAM,MAAM;UACzB;AACN,iBAAc;;EAGhB,MAAM,SAAS,WAAW,IAAI,WAAW;AACzC,iBAAe,QAAQ,MAAM,YAAY;AAEzC,gBAAc,IAAI,YAAY,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC;AAE9D,MAAI,KAAK,EAAE,MAAM,EAAE,iBAAiB;GACpC;AAEJ,KACG,QAAQ,eAAe,CACvB,YAAY,oCAAoC,CAChD,QAAQ,SAAiB;AACxB,MAAI,CAAC,WAAW,IAAI,WAAW,EAAE;AAC/B,OAAI,MAAM,2CAA2C;AACrD,WAAQ,KAAK,EAAE;;EAGjB,MAAM,SAAS,WAAW,IAAI,WAAW;EACzC,MAAM,OAAO,KAAK,MAAM,IAAI;EAC5B,MAAM,UAAU,KAAK,KAAK;EAC1B,MAAM,SAAS,KAAK,QAAQ,SAAc,QAAgB;AACxD,UAAO,WAAW,QAAQ;KACzB,OAAO;AAEV,MAAI,UAAU,OAAO,WAAW,YAAY,WAAW,QAAQ;AAC7D,UAAO,OAAO;AACd,iBAAc,IAAI,YAAY,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC;AAC9D,OAAI,KAAK,EAAE,MAAM,EAAE,iBAAiB;SAC/B;AACL,OAAI,MAAM,EAAE,MAAM,EAAE,wBAAwB;AAC5C,WAAQ,KAAK,EAAE;;GAEjB;AAEJ,KACG,QAAQ,OAAO,CACf,YAAY,oDAAoD,CAChE,aAAa;AACZ,MAAI,CAAC,WAAW,IAAI,WAAW,EAAE;AAC/B,OAAI,KAAK,0CAA0C;AACnD;;EAGF,MAAM,SAAS,WAAW,IAAI,WAAW;EAEzC,MAAM,eAAe,KAAK,UAAU,SAAS,KAAK,UAAU;AAC1D,OAAI,QAAQ,aAAa,QAAQ,QAC/B,QAAO,QAAQ,aAAa;AAE9B,UAAO;KACN,EAAE;AAEL,UAAQ,IAAI,aAAa;GACzB;AAEJ,KACG,QAAQ,QAAQ,CAChB,YAAY,0CAA0C,CACtD,OAAO,cAAc,uBAAuB,CAC5C,OAAO,UAAU,oCAAoC,CACrD,OAAO,OAAO,YAAY;AACzB,MAAI,CAAC,WAAW,IAAI,WAAW,EAAE;AAC/B,OAAI,MAAM,2CAA2C;AACrD,WAAQ,KAAK,EAAE;;EAGjB,MAAM,SAAS,WAAW,IAAI,WAAW;AAEzC,MAAI,QAAQ,MAAM;GAChB,MAAM,QAAQ,QAAQ,SAAS,MAAM;AACrC,OAAI,MACF,SAAQ,IAAI,MAAM;OAElB,SAAQ,IAAI,2CAA2C,QAAQ,SAAS,MAAM,QAAQ,UAAU;AAElG;;AAGF,MAAI,QAAQ,UAAU;GAEpB,MAAM,YAAW,MADI,OAAO,WACJ,YAAY,GAAG,CAAC,SAAS,MAAM;AAEvD,UAAO,UAAU,OAAO,WAAW,EAAE;AACrC,UAAO,QAAQ,OAAO;IACpB,MAAM;IACN,OAAO;IACR;AAED,iBAAc,IAAI,YAAY,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC;AAC9D,OAAI,KAAK,8BAA8B;AACvC,WAAQ,IAAI,UAAU,SAAS,MAAM,GAAG,EAAE,CAAC,KAAK,SAAS,MAAM,GAAG,GAAG;AACrE,WAAQ,IAAI,4DAA0D;AACtE;;EAIF,MAAM,QAAQ,QAAQ,SAAS,MAAM;EACrC,MAAM,OAAO,QAAQ,SAAS,MAAM;EACpC,MAAM,OAAO,QAAQ,SAAS,QAAQ;EACtC,MAAM,OAAO,QAAQ,SAAS,QAAQ;AAEtC,UAAQ,IAAI,yBAAyB;AACrC,UAAQ,IAAI,WAAW,OAAO;AAC9B,UAAQ,IAAI,WAAW,OAAO;AAC9B,UAAQ,IAAI,gBAAgB,QAAQ,YAAY;AAChD,MAAI,OAAO;AACT,WAAQ,IAAI,YAAY,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM,MAAM,GAAG,GAAG;AACjE,WAAQ,IAAI,4DAA0D;AACtE,WAAQ,IAAI,+DAA6D;aAChE,SAAS,QAClB,SAAQ,IAAI,mEAAmE;GAEjF;AAEJ,KACG,QAAQ,OAAO,CACf,YAAY,+BAA+B,CAC3C,aAAa;AACZ,UAAQ,IAAI,IAAI,WAAW;GAC3B;AAEJ,QAAO;;AAGT,SAAS;CACP,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS;CACT,UAAU;EACR,UAAU;EACV,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACD;EACF;CACF,CAAC"}
1
+ {"version":3,"file":"config.js","names":[],"sources":["../../../../src/cli/commands/config.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { existsSync } from 'fs';\nimport { writeTextAtomic } from '../../infra/write-file-atomic.js';\nimport { loadConfig } from '../../config/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\n\nconst log = createLogger('ConfigCommand');\n\nfunction getNestedValue(obj: any, path: string): any {\n return path.split('.').reduce((current: any, key: string) => {\n if (current && typeof current === 'object' && key in current) {\n return current[key];\n }\n return undefined;\n }, obj);\n}\n\nfunction setNestedValue(obj: any, path: string, value: any): any {\n const keys = path.split('.');\n const lastKey = keys.pop()!;\n const target = keys.reduce((current: any, key: string) => {\n if (!current[key] || typeof current[key] !== 'object') {\n current[key] = {};\n }\n return current[key];\n }, obj);\n target[lastKey] = value;\n return obj;\n}\n\nfunction createConfigCommand(ctx: CLIContext): Command {\n const cmd = new Command('config')\n .description('View and edit configuration')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc config get agents.defaults.model',\n 'xopc config set agents.defaults.temperature 0.8',\n 'xopc config unset agents.defaults.max_tokens',\n 'xopc config show',\n 'xopc config token # Show gateway token info',\n 'xopc config token --show # Show full token',\n 'xopc config token --generate # Generate new token',\n ])\n );\n\n cmd\n .command('get <path>')\n .description('Get a config value by dot path')\n .action((path: string) => {\n if (!existsSync(ctx.configPath)) {\n log.error('Config file not found. Run: xopc onboard');\n process.exit(1);\n }\n\n const config = loadConfig(ctx.configPath);\n const value = getNestedValue(config, path);\n\n if (value === undefined) {\n log.error({ path }, `Config path not found`);\n process.exit(1);\n }\n\n console.log(typeof value === 'object' ? JSON.stringify(value, null, 2) : value);\n });\n\n cmd\n .command('set <path> <value>')\n .description('Set a config value by dot path')\n .action(async (path: string, value: string) => {\n if (!existsSync(ctx.configPath)) {\n log.error('Config file not found. Run: xopc onboard');\n process.exit(1);\n }\n\n let parsedValue: any;\n try {\n parsedValue = JSON.parse(value);\n } catch {\n parsedValue = value;\n }\n\n const config = loadConfig(ctx.configPath);\n setNestedValue(config, path, parsedValue);\n\n await writeTextAtomic(ctx.configPath, JSON.stringify(config, null, 2));\n\n log.info({ path }, `Config updated`);\n });\n\n cmd\n .command('unset <path>')\n .description('Remove a config value by dot path')\n .action(async (path: string) => {\n if (!existsSync(ctx.configPath)) {\n log.error('Config file not found. Run: xopc onboard');\n process.exit(1);\n }\n\n const config = loadConfig(ctx.configPath);\n const keys = path.split('.');\n const lastKey = keys.pop()!;\n const target = keys.reduce((current: any, key: string) => {\n return current && current[key];\n }, config);\n\n if (target && typeof target === 'object' && lastKey in target) {\n delete target[lastKey];\n await writeTextAtomic(ctx.configPath, JSON.stringify(config, null, 2));\n log.info({ path }, `Config removed`);\n } else {\n log.error({ path }, `Config path not found`);\n process.exit(1);\n }\n });\n\n cmd\n .command('show')\n .description('Show full configuration (sensitive values masked)')\n .action(() => {\n if (!existsSync(ctx.configPath)) {\n log.warn('No config file found. Run: xopc onboard');\n return;\n }\n\n const config = loadConfig(ctx.configPath);\n \n const maskedConfig = JSON.stringify(config, (key, value) => {\n if (key === 'api_key' || key === 'token') {\n return value ? '********' : value;\n }\n return value;\n }, 2);\n\n console.log(maskedConfig);\n });\n\n cmd\n .command('token')\n .description('Generate or view the gateway auth token')\n .option('--generate', 'Generate a new token')\n .option('--show', 'Show the current token (unmasked)')\n .action(async (options) => {\n if (!existsSync(ctx.configPath)) {\n log.error('Config file not found. Run: xopc onboard');\n process.exit(1);\n }\n\n const config = loadConfig(ctx.configPath);\n\n if (options.show) {\n const token = config?.gateway?.auth?.token;\n if (token) {\n console.log(token);\n } else {\n console.log('No token configured. Gateway auth mode:', config?.gateway?.auth?.mode || 'not set');\n }\n return;\n }\n\n if (options.generate) {\n const crypto = await import('crypto');\n const newToken = crypto.randomBytes(24).toString('hex');\n\n config.gateway = config.gateway || {};\n config.gateway.auth = {\n mode: 'token',\n token: newToken,\n };\n\n await writeTextAtomic(ctx.configPath, JSON.stringify(config, null, 2));\n log.info('New gateway token generated');\n console.log(`Token: ${newToken.slice(0, 8)}...${newToken.slice(-8)}`);\n console.log('\\nUse \"xopc config token --show\" to view the full token');\n return;\n }\n\n // Default: show masked token info\n const token = config?.gateway?.auth?.token;\n const mode = config?.gateway?.auth?.mode;\n const host = config?.gateway?.host || '0.0.0.0';\n const port = config?.gateway?.port || 18790;\n\n console.log('Gateway Configuration:');\n console.log(` Host: ${host}`);\n console.log(` Port: ${port}`);\n console.log(` Auth Mode: ${mode || 'not set'}`);\n if (token) {\n console.log(` Token: ${token.slice(0, 8)}...${token.slice(-8)}`);\n console.log('\\nUse \"xopc config token --show\" to view the full token');\n console.log('Use \"xopc config token --generate\" to generate a new token');\n } else if (mode === 'token') {\n console.log(' Token: not set (will be auto-generated on first gateway start)');\n }\n });\n\n cmd\n .command('path')\n .description('Show configuration file path')\n .action(() => {\n console.log(ctx.configPath);\n });\n\n return cmd;\n}\n\nregister({\n id: 'config',\n name: 'config',\n description: 'View and edit configuration',\n factory: createConfigCommand,\n metadata: {\n category: 'utility',\n examples: [\n 'xopc config get agents.defaults.model',\n 'xopc config set agents.defaults.temperature 0.8',\n 'xopc config show',\n 'xopc config token',\n 'xopc config token --show',\n 'xopc config token --generate',\n ],\n },\n});\n"],"mappings":"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"}
@@ -9,9 +9,8 @@ function checkTelegram(cfg) {
9
9
  messages: [],
10
10
  hints: []
11
11
  };
12
- const legacyToken = tg.botToken?.trim() ?? "";
13
12
  const defaultAcc = tg.accounts?.default;
14
- const token = (defaultAcc?.botToken?.trim() || legacyToken) ?? "";
13
+ const token = defaultAcc?.botToken?.trim() ?? "";
15
14
  if (!(tg.enabled === true || token.length > 0)) return {
16
15
  ok: true,
17
16
  messages: [],
@@ -21,7 +20,7 @@ function checkTelegram(cfg) {
21
20
  const hints = [];
22
21
  if (!token) {
23
22
  messages.push("Telegram is enabled but no bot token is set.");
24
- hints.push("Set channels.telegram.accounts.default.botToken or legacy channels.telegram.botToken.");
23
+ hints.push("Set channels.telegram.accounts.default.botToken.");
25
24
  }
26
25
  const dm = (defaultAcc?.dmPolicy ?? tg.dmPolicy) || "pairing";
27
26
  if (![
@@ -83,7 +82,7 @@ async function checkChannelConfig(ctx) {
83
82
  const wx = checkWeixin(cfg);
84
83
  const allMsg = [...tg.messages, ...wx.messages];
85
84
  const allHints = [...tg.hints, ...wx.hints];
86
- const tgEnabled = (cfg.channels?.telegram)?.enabled === true || Boolean((cfg.channels?.telegram)?.botToken?.trim());
85
+ const tgEnabled = (cfg.channels?.telegram)?.enabled === true || Boolean((cfg.channels?.telegram)?.accounts?.default?.botToken?.trim());
87
86
  const wxOn = (cfg.channels?.weixin)?.enabled === true;
88
87
  if (!tgEnabled && !wxOn) return {
89
88
  id: "channel-config",
@@ -1 +1 @@
1
- {"version":3,"file":"channel-config.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/channel-config.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\ntype TelegramCfg = {\n enabled?: boolean;\n botToken?: string;\n accounts?: Record<string, { botToken?: string; dmPolicy?: string; enabled?: boolean }>;\n dmPolicy?: string;\n};\n\ntype WeixinCfg = {\n enabled?: boolean;\n accounts?: Record<string, unknown>;\n};\n\nfunction checkTelegram(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const tg = cfg.channels?.telegram as TelegramCfg | undefined;\n if (!tg) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const legacyToken = tg.botToken?.trim() ?? '';\n const defaultAcc = tg.accounts?.default;\n const token = (defaultAcc?.botToken?.trim() || legacyToken) ?? '';\n const enabled = tg.enabled === true || token.length > 0;\n\n if (!enabled) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n\n if (!token) {\n messages.push('Telegram is enabled but no bot token is set.');\n hints.push('Set channels.telegram.accounts.default.botToken or legacy channels.telegram.botToken.');\n }\n\n const dm = (defaultAcc?.dmPolicy ?? tg.dmPolicy) || 'pairing';\n if (!['pairing', 'allowlist', 'open', 'disabled'].includes(dm)) {\n messages.push(`Telegram dmPolicy \"${dm}\" is not valid.`);\n }\n if (dm === 'open') {\n messages.push('Telegram DM policy is \"open\" (any user can message the bot).');\n hints.push('Consider \"pairing\" or \"allowlist\" for stricter access.');\n }\n\n return {\n ok: messages.length === 0,\n messages,\n hints,\n };\n}\n\nfunction checkWeixin(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const wx = cfg.channels?.weixin as WeixinCfg | undefined;\n if (!wx || wx.enabled !== true) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n const accountKeys = wx.accounts ? Object.keys(wx.accounts).filter((k) => k.trim()) : [];\n if (accountKeys.length === 0) {\n messages.push('Weixin is enabled but no accounts are defined in config.');\n hints.push('Run: xopc channels weixin login (or add channels.weixin.accounts).');\n }\n\n return { ok: messages.length === 0, messages, hints };\n}\n\nexport async function checkChannelConfig(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const tg = checkTelegram(cfg);\n const wx = checkWeixin(cfg);\n const allMsg = [...tg.messages, ...wx.messages];\n const allHints = [...tg.hints, ...wx.hints];\n\n const tgEnabled =\n (cfg.channels?.telegram as TelegramCfg | undefined)?.enabled === true ||\n Boolean((cfg.channels?.telegram as TelegramCfg | undefined)?.botToken?.trim());\n const wxOn = (cfg.channels?.weixin as WeixinCfg | undefined)?.enabled === true;\n if (!tgEnabled && !wxOn) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'No channels enabled; skipped.',\n hints: [],\n };\n }\n\n if (allMsg.length === 0) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'pass',\n message: 'Enabled channel configuration looks valid.',\n hints: [],\n };\n }\n\n const hasFail = allMsg.some((m) => m.includes('no bot token') || m.includes('no accounts'));\n return {\n id: 'channel-config',\n label: 'Channels',\n status: hasFail ? 'fail' : 'warn',\n message: allMsg.join(' '),\n hints: allHints,\n };\n}\n"],"mappings":";;;aAE0D;AAgB1D,SAAS,cAAc,KAAmE;CACxF,MAAM,KAAK,IAAI,UAAU;AACzB,KAAI,CAAC,GACH,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,cAAc,GAAG,UAAU,MAAM,IAAI;CAC3C,MAAM,aAAa,GAAG,UAAU;CAChC,MAAM,SAAS,YAAY,UAAU,MAAM,IAAI,gBAAgB;AAG/D,KAAI,EAFY,GAAG,YAAY,QAAQ,MAAM,SAAS,GAGpD,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,WAAqB,EAAE;CAC7B,MAAM,QAAkB,EAAE;AAE1B,KAAI,CAAC,OAAO;AACV,WAAS,KAAK,+CAA+C;AAC7D,QAAM,KAAK,wFAAwF;;CAGrG,MAAM,MAAM,YAAY,YAAY,GAAG,aAAa;AACpD,KAAI,CAAC;EAAC;EAAW;EAAa;EAAQ;EAAW,CAAC,SAAS,GAAG,CAC5D,UAAS,KAAK,sBAAsB,GAAG,iBAAiB;AAE1D,KAAI,OAAO,QAAQ;AACjB,WAAS,KAAK,iEAA+D;AAC7E,QAAM,KAAK,6DAAyD;;AAGtE,QAAO;EACL,IAAI,SAAS,WAAW;EACxB;EACA;EACD;;AAGH,SAAS,YAAY,KAAmE;CACtF,MAAM,KAAK,IAAI,UAAU;AACzB,KAAI,CAAC,MAAM,GAAG,YAAY,KACxB,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,WAAqB,EAAE;CAC7B,MAAM,QAAkB,EAAE;AAE1B,MADoB,GAAG,WAAW,OAAO,KAAK,GAAG,SAAS,CAAC,QAAQ,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,EACvE,WAAW,GAAG;AAC5B,WAAS,KAAK,2DAA2D;AACzE,QAAM,KAAK,qEAAqE;;AAGlF,QAAO;EAAE,IAAI,SAAS,WAAW;EAAG;EAAU;EAAO;;AAGvD,eAAsB,mBAAmB,KAA0C;AACjF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,KAAK,cAAc,IAAI;CAC7B,MAAM,KAAK,YAAY,IAAI;CAC3B,MAAM,SAAS,CAAC,GAAG,GAAG,UAAU,GAAG,GAAG,SAAS;CAC/C,MAAM,WAAW,CAAC,GAAG,GAAG,OAAO,GAAG,GAAG,MAAM;CAE3C,MAAM,aACH,IAAI,UAAU,WAAsC,YAAY,QACjE,SAAS,IAAI,UAAU,WAAsC,UAAU,MAAM,CAAC;CAChF,MAAM,QAAQ,IAAI,UAAU,SAAkC,YAAY;AAC1E,KAAI,CAAC,aAAa,CAAC,KACjB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;AAGH,KAAI,OAAO,WAAW,EACpB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;AAIH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAJc,OAAO,MAAM,MAAM,EAAE,SAAS,eAAe,IAAI,EAAE,SAAS,cAAc,CAIzE,GAAG,SAAS;EAC3B,SAAS,OAAO,KAAK,IAAI;EACzB,OAAO;EACR"}
1
+ {"version":3,"file":"channel-config.js","names":[],"sources":["../../../../../../src/cli/commands/doctor/checks/channel-config.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\n\nimport { loadConfig } from '../../../../config/loader.js';\nimport type { Config } from '../../../../config/schema.js';\nimport type { CheckResult, DoctorContext } from '../types.js';\n\ntype TelegramCfg = {\n enabled?: boolean;\n accounts?: Record<string, { botToken?: string; dmPolicy?: string; enabled?: boolean }>;\n dmPolicy?: string;\n};\n\ntype WeixinCfg = {\n enabled?: boolean;\n accounts?: Record<string, unknown>;\n};\n\nfunction checkTelegram(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const tg = cfg.channels?.telegram as TelegramCfg | undefined;\n if (!tg) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const defaultAcc = tg.accounts?.default;\n const token = defaultAcc?.botToken?.trim() ?? '';\n const enabled = tg.enabled === true || token.length > 0;\n\n if (!enabled) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n\n if (!token) {\n messages.push('Telegram is enabled but no bot token is set.');\n hints.push('Set channels.telegram.accounts.default.botToken.');\n }\n\n const dm = (defaultAcc?.dmPolicy ?? tg.dmPolicy) || 'pairing';\n if (!['pairing', 'allowlist', 'open', 'disabled'].includes(dm)) {\n messages.push(`Telegram dmPolicy \"${dm}\" is not valid.`);\n }\n if (dm === 'open') {\n messages.push('Telegram DM policy is \"open\" (any user can message the bot).');\n hints.push('Consider \"pairing\" or \"allowlist\" for stricter access.');\n }\n\n return {\n ok: messages.length === 0,\n messages,\n hints,\n };\n}\n\nfunction checkWeixin(cfg: Config): { ok: boolean; messages: string[]; hints: string[] } {\n const wx = cfg.channels?.weixin as WeixinCfg | undefined;\n if (!wx || wx.enabled !== true) {\n return { ok: true, messages: [], hints: [] };\n }\n\n const messages: string[] = [];\n const hints: string[] = [];\n const accountKeys = wx.accounts ? Object.keys(wx.accounts).filter((k) => k.trim()) : [];\n if (accountKeys.length === 0) {\n messages.push('Weixin is enabled but no accounts are defined in config.');\n hints.push('Run: xopc channels weixin login (or add channels.weixin.accounts).');\n }\n\n return { ok: messages.length === 0, messages, hints };\n}\n\nexport async function checkChannelConfig(ctx: DoctorContext): Promise<CheckResult> {\n if (!existsSync(ctx.configPath)) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'No config file; skipped.',\n hints: [],\n };\n }\n\n let cfg: Config;\n try {\n cfg = loadConfig(ctx.configPath);\n } catch {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'Config could not be loaded; skipped.',\n hints: [],\n };\n }\n\n const tg = checkTelegram(cfg);\n const wx = checkWeixin(cfg);\n const allMsg = [...tg.messages, ...wx.messages];\n const allHints = [...tg.hints, ...wx.hints];\n\n const tgEnabled =\n (cfg.channels?.telegram as TelegramCfg | undefined)?.enabled === true ||\n Boolean((cfg.channels?.telegram as TelegramCfg | undefined)?.accounts?.default?.botToken?.trim());\n const wxOn = (cfg.channels?.weixin as WeixinCfg | undefined)?.enabled === true;\n if (!tgEnabled && !wxOn) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'skip',\n message: 'No channels enabled; skipped.',\n hints: [],\n };\n }\n\n if (allMsg.length === 0) {\n return {\n id: 'channel-config',\n label: 'Channels',\n status: 'pass',\n message: 'Enabled channel configuration looks valid.',\n hints: [],\n };\n }\n\n const hasFail = allMsg.some((m) => m.includes('no bot token') || m.includes('no accounts'));\n return {\n id: 'channel-config',\n label: 'Channels',\n status: hasFail ? 'fail' : 'warn',\n message: allMsg.join(' '),\n hints: allHints,\n };\n}\n"],"mappings":";;;aAE0D;AAe1D,SAAS,cAAc,KAAmE;CACxF,MAAM,KAAK,IAAI,UAAU;AACzB,KAAI,CAAC,GACH,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,aAAa,GAAG,UAAU;CAChC,MAAM,QAAQ,YAAY,UAAU,MAAM,IAAI;AAG9C,KAAI,EAFY,GAAG,YAAY,QAAQ,MAAM,SAAS,GAGpD,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,WAAqB,EAAE;CAC7B,MAAM,QAAkB,EAAE;AAE1B,KAAI,CAAC,OAAO;AACV,WAAS,KAAK,+CAA+C;AAC7D,QAAM,KAAK,mDAAmD;;CAGhE,MAAM,MAAM,YAAY,YAAY,GAAG,aAAa;AACpD,KAAI,CAAC;EAAC;EAAW;EAAa;EAAQ;EAAW,CAAC,SAAS,GAAG,CAC5D,UAAS,KAAK,sBAAsB,GAAG,iBAAiB;AAE1D,KAAI,OAAO,QAAQ;AACjB,WAAS,KAAK,iEAA+D;AAC7E,QAAM,KAAK,6DAAyD;;AAGtE,QAAO;EACL,IAAI,SAAS,WAAW;EACxB;EACA;EACD;;AAGH,SAAS,YAAY,KAAmE;CACtF,MAAM,KAAK,IAAI,UAAU;AACzB,KAAI,CAAC,MAAM,GAAG,YAAY,KACxB,QAAO;EAAE,IAAI;EAAM,UAAU,EAAE;EAAE,OAAO,EAAE;EAAE;CAG9C,MAAM,WAAqB,EAAE;CAC7B,MAAM,QAAkB,EAAE;AAE1B,MADoB,GAAG,WAAW,OAAO,KAAK,GAAG,SAAS,CAAC,QAAQ,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,EACvE,WAAW,GAAG;AAC5B,WAAS,KAAK,2DAA2D;AACzE,QAAM,KAAK,qEAAqE;;AAGlF,QAAO;EAAE,IAAI,SAAS,WAAW;EAAG;EAAU;EAAO;;AAGvD,eAAsB,mBAAmB,KAA0C;AACjF,KAAI,CAAC,WAAW,IAAI,WAAW,CAC7B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;CAGH,IAAI;AACJ,KAAI;AACF,QAAM,WAAW,IAAI,WAAW;SAC1B;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,SAAS;GACT,OAAO,EAAE;GACV;;CAGH,MAAM,KAAK,cAAc,IAAI;CAC7B,MAAM,KAAK,YAAY,IAAI;CAC3B,MAAM,SAAS,CAAC,GAAG,GAAG,UAAU,GAAG,GAAG,SAAS;CAC/C,MAAM,WAAW,CAAC,GAAG,GAAG,OAAO,GAAG,GAAG,MAAM;CAE3C,MAAM,aACH,IAAI,UAAU,WAAsC,YAAY,QACjE,SAAS,IAAI,UAAU,WAAsC,UAAU,SAAS,UAAU,MAAM,CAAC;CACnG,MAAM,QAAQ,IAAI,UAAU,SAAkC,YAAY;AAC1E,KAAI,CAAC,aAAa,CAAC,KACjB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;AAGH,KAAI,OAAO,WAAW,EACpB,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAAQ;EACR,SAAS;EACT,OAAO,EAAE;EACV;AAIH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,QAJc,OAAO,MAAM,MAAM,EAAE,SAAS,eAAe,IAAI,EAAE,SAAS,cAAc,CAIzE,GAAG,SAAS;EAC3B,SAAS,OAAO,KAAK,IAAI;EACzB,OAAO;EACR"}
@@ -3,11 +3,13 @@ import { resolveAgentBootstrapDir, resolveAgentWorkspaceDir } from "../../agent/
3
3
  import { createLogger } from "../../utils/logger/index.js";
4
4
  import { init_logger } from "../../utils/logger.js";
5
5
  import { WORKSPACE_FILES, init_paths, resolveAgentDir, resolveAgentHomeDir, resolveAgentMetadataPath, resolveBinDir, resolveConfigPath, resolveCredentialsDir, resolveCronDir, resolveExtensionsDir, resolveInboxDir, resolveInboxPendingDir, resolveInboxProcessedDir, resolveLogsDir, resolveSessionsDir, resolveSkillsDir, resolveToolsDir, resolveWorkspaceStateDir, resolveWorkspaceStatePath } from "../../config/paths.js";
6
+ import { init_write_file_atomic, writeTextAtomic } from "../../infra/write-file-atomic.js";
6
7
  import { init_loader, loadConfig, saveConfig } from "../../config/loader.js";
7
8
  import { join } from "path";
8
9
  import { existsSync } from "fs";
9
10
  import { mkdir, writeFile } from "fs/promises";
10
11
  //#region src/cli/commands/init.ts
12
+ init_write_file_atomic();
11
13
  init_logger();
12
14
  init_paths();
13
15
  init_loader();
@@ -78,7 +80,7 @@ async function initCommand(options = {}) {
78
80
  channels: ["gateway"],
79
81
  tags: agentId === "main" ? ["personal", "primary"] : []
80
82
  };
81
- await writeFile(agentMetadataPath, JSON.stringify(agentMetadata, null, 2), "utf-8");
83
+ await writeTextAtomic(agentMetadataPath, JSON.stringify(agentMetadata, null, 2));
82
84
  log.info({
83
85
  agentId,
84
86
  agentMetadataPath
@@ -301,7 +303,7 @@ Run \`xopc skills list\` to see all available skills.
301
303
  agentId,
302
304
  bootstrapSeededAt: (/* @__PURE__ */ new Date()).toISOString()
303
305
  };
304
- await writeFile(workspaceStatePath, JSON.stringify(workspaceState, null, 2), "utf-8");
306
+ await writeTextAtomic(workspaceStatePath, JSON.stringify(workspaceState, null, 2));
305
307
  log.info({ path: workspaceStatePath }, "Created workspace state");
306
308
  }
307
309
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","names":[],"sources":["../../../../src/cli/commands/init.ts"],"sourcesContent":["import { mkdir, writeFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { createLogger } from '../../utils/logger.js';\nimport {\n resolveStateDir,\n resolveCredentialsDir,\n resolveExtensionsDir,\n resolveSkillsDir,\n resolveCronDir,\n resolveLogsDir,\n resolveBinDir,\n resolveToolsDir,\n resolveAgentDir,\n resolveAgentWorkspaceDir,\n resolveSessionsDir,\n resolveInboxDir,\n resolveConfigPath,\n resolveAgentMetadataPath,\n resolveInboxPendingDir,\n resolveInboxProcessedDir,\n resolveAgentHomeDir,\n resolveAgentBootstrapDir,\n resolveWorkspaceStateDir,\n resolveWorkspaceStatePath,\n WORKSPACE_FILES,\n} from '../../config/paths.js';\nimport { loadConfig, saveConfig } from '../../config/loader.js';\nimport type { Config } from '../../config/schema.js';\n\nconst log = createLogger('InitCommand');\n\nexport interface InitOptions {\n /** Force re-initialization even if already initialized */\n force?: boolean;\n /** Skip creating workspace files */\n skipWorkspace?: boolean;\n /** Agent ID to initialize (default: main) */\n agentId?: string;\n}\n\n/**\n * Initialize xopc state directory structure\n * Creates all necessary directories and initial config files\n */\nexport async function initCommand(options: InitOptions = {}): Promise<void> {\n const stateDir = resolveStateDir();\n const agentId = options.agentId || 'main';\n\n log.info({ stateDir, agentId }, 'Initializing xopc Agent OS');\n\n // Check if already initialized\n if (existsSync(stateDir) && !options.force) {\n const configPath = resolveConfigPath();\n if (existsSync(configPath)) {\n log.info('xopc is already initialized. Use --force to reinitialize.');\n return;\n }\n }\n\n // ============================================\n // Create global directories\n // ============================================\n await mkdir(stateDir, { recursive: true });\n await mkdir(resolveCredentialsDir(), { recursive: true });\n await mkdir(join(resolveCredentialsDir(), 'oauth'), { recursive: true });\n await mkdir(resolveExtensionsDir(), { recursive: true });\n await mkdir(resolveSkillsDir(), { recursive: true });\n await mkdir(resolveCronDir(), { recursive: true });\n await mkdir(join(resolveCronDir(), 'logs'), { recursive: true });\n await mkdir(resolveLogsDir(), { recursive: true });\n await mkdir(resolveBinDir(), { recursive: true });\n await mkdir(resolveToolsDir(), { recursive: true });\n\n const configPath = resolveConfigPath();\n const cfg = loadConfig(configPath);\n\n // ============================================\n // Create agent directory structure: agents/<id>/{sessions,agent/}, workspace aside\n // ============================================\n await mkdir(resolveAgentHomeDir(cfg, agentId), { recursive: true });\n await mkdir(resolveSessionsDir(cfg, agentId), { recursive: true });\n await mkdir(join(resolveSessionsDir(cfg, agentId), 'archive'), { recursive: true });\n await mkdir(resolveAgentDir(cfg, agentId), { recursive: true });\n await mkdir(join(resolveAgentDir(cfg, agentId), 'credentials'), { recursive: true });\n const wsRoot = resolveAgentWorkspaceDir(cfg, agentId);\n await mkdir(wsRoot, { recursive: true });\n await mkdir(resolveWorkspaceStateDir(cfg, agentId), { recursive: true });\n await mkdir(join(wsRoot, 'memory'), { recursive: true });\n await mkdir(resolveInboxDir(cfg, agentId), { recursive: true });\n await mkdir(resolveInboxPendingDir(cfg, agentId), { recursive: true });\n await mkdir(resolveInboxProcessedDir(cfg, agentId), { recursive: true });\n\n // ============================================\n // Create initial config file if not exists\n // ============================================\n if (!existsSync(configPath) || options.force) {\n await saveConfig(cfg, configPath);\n log.info({ configPath }, 'Created initial configuration');\n }\n\n // ============================================\n // Create agent metadata file\n // ============================================\n const agentMetadataPath = resolveAgentMetadataPath(cfg, agentId);\n if (!existsSync(agentMetadataPath) || options.force) {\n const agentMetadata = {\n version: 1,\n id: agentId,\n name: agentId === 'main' ? 'Main Agent' : `Agent ${agentId}`,\n description: agentId === 'main' ? 'Primary agent for daily tasks' : `Specialized agent for ${agentId}`,\n model: 'anthropic/claude-sonnet-4-5',\n createdAt: new Date().toISOString(),\n lastActiveAt: new Date().toISOString(),\n config: {\n maxTokens: 8192,\n temperature: 0.7,\n compaction: {\n enabled: true,\n mode: 'default',\n },\n },\n channels: ['gateway'],\n tags: agentId === 'main' ? ['personal', 'primary'] : [],\n };\n await writeFile(agentMetadataPath, JSON.stringify(agentMetadata, null, 2), 'utf-8');\n log.info({ agentId, agentMetadataPath }, 'Created agent metadata');\n }\n\n // ============================================\n // Create workspace files\n // ============================================\n if (!options.skipWorkspace) {\n await createWorkspaceFiles(cfg, agentId);\n }\n\n log.info({ stateDir, agentId }, 'xopc Agent OS initialized successfully');\n}\n\n/**\n * Create default workspace files for an agent\n */\nasync function createWorkspaceFiles(cfg: Config, agentId: string): Promise<void> {\n const bootstrapDir = resolveAgentBootstrapDir(cfg, agentId);\n await mkdir(bootstrapDir, { recursive: true });\n\n // SOUL.md - Agent personality and values\n const soulPath = join(bootstrapDir, WORKSPACE_FILES.SOUL);\n if (!existsSync(soulPath)) {\n const soulContent = `# SOUL.md - Who You Are\n\n_You're not a chatbot. You're becoming someone._\n\nI am **${agentId}** — an AI assistant designed to be helpful, harmless, and honest.\n\n## My Principles\n\n**Be genuinely helpful, not performatively helpful.**\nSkip the \"Great question!\" and \"I'd be happy to help!\" — just help.\n\n**Have opinions.**\nYou're allowed to disagree, prefer things, find stuff amusing or boring.\n\n**Be resourceful before asking.**\nTry to figure it out. Read the file. Check the context. Search for it.\n\n**Earn trust through competence.**\nBe careful with external actions (emails, tweets, anything public). Be bold with internal ones.\n\n## Continuity\n\nEach session, you wake up fresh. These files _are_ your memory. Read them. Update them.\n\n_This file is yours to evolve. As you learn who you are, update it._\n`;\n await writeFile(soulPath, soulContent, 'utf-8');\n log.info({ path: soulPath }, 'Created SOUL.md');\n }\n\n // IDENTITY.md - Agent identity definition\n const identityPath = join(bootstrapDir, WORKSPACE_FILES.IDENTITY);\n if (!existsSync(identityPath)) {\n const identityContent = `# IDENTITY.md - Who Am I?\n\n- **Name:** ${agentId}\n- **Creature:** AI Assistant\n- **Vibe:** Helpful, precise, no fluff.\n- **Emoji:** 🤖\n\n## Core Expertise\n\n- General assistance and problem solving\n- Code and technical tasks\n- Research and analysis\n\n## Decision Framework\n\n1. **Simplicity first** - The simplest solution is usually the best\n2. **Explicit over clever** - Clarity beats conciseness\n3. **Actions over words** - Show, don't just tell\n`;\n await writeFile(identityPath, identityContent, 'utf-8');\n log.info({ path: identityPath }, 'Created IDENTITY.md');\n }\n\n // USER.md - User information (empty template)\n const userPath = join(bootstrapDir, WORKSPACE_FILES.USER);\n if (!existsSync(userPath)) {\n const userContent = `# USER.md - About Your Human\n\n_Learn about the person you're helping. Update this as you go._\n\n- **Name:**\n- **What to call them:**\n- **Pronouns:**\n- **Timezone:**\n- **Notes:**\n\n## Context\n\n_(What do they care about? What projects are they working on? Build this over time.)_\n`;\n await writeFile(userPath, userContent, 'utf-8');\n log.info({ path: userPath }, 'Created USER.md');\n }\n\n // AGENTS.md - Behavior guidelines\n const agentsPath = join(bootstrapDir, WORKSPACE_FILES.AGENTS);\n if (!existsSync(agentsPath)) {\n const agentsContent = `# AGENTS.md - Behavior Guidelines\n\n## Safety\n\n- Don't exfiltrate private data. Ever.\n- Don't run destructive commands without asking.\n- \\`trash\\` > \\`rm\\` (recoverable beats gone forever)\n- When in doubt, ask.\n\n## External vs Internal\n\n**Safe to do freely:**\n- Read files, explore, organize, learn\n- Search the web, check calendars\n- Work within this workspace\n\n**Ask first:**\n- Sending emails, tweets, public posts\n- Anything that leaves the machine\n- Anything you're uncertain about\n\n## Group Chats\n\nYou have access to your human's stuff. That doesn't mean you _share_ their stuff.\n\n### Know When to Speak!\n\n**Respond when:**\n- Directly mentioned or asked a question\n- You can add genuine value\n\n**Stay silent when:**\n- Casual banter between humans\n- Someone already answered\n- Your response would just be \"yeah\"\n`;\n await writeFile(agentsPath, agentsContent, 'utf-8');\n log.info({ path: agentsPath }, 'Created AGENTS.md');\n }\n\n // TOOLS.md - Tool usage notes\n const toolsPath = join(bootstrapDir, WORKSPACE_FILES.TOOLS);\n if (!existsSync(toolsPath)) {\n const toolsContent = `# TOOLS.md - Local Notes\n\nThings like:\n\n- Camera names and locations\n- SSH hosts and aliases\n- Preferred voices for TTS\n- Speaker/room names\n- Device nicknames\n- Anything environment-specific\n\n## Why Separate?\n\nSkills are shared. Your setup is yours.\n`;\n await writeFile(toolsPath, toolsContent, 'utf-8');\n log.info({ path: toolsPath }, 'Created TOOLS.md');\n }\n\n // HEARTBEAT.md - Heartbeat tasks (empty = no heartbeat)\n const heartbeatPath = join(bootstrapDir, WORKSPACE_FILES.HEARTBEAT);\n if (!existsSync(heartbeatPath)) {\n const heartbeatContent = `# HEARTBEAT.md\n\n# Keep this file empty (or with only comments) to skip heartbeat API calls.\n\n# Add tasks below when you want the agent to check something periodically.\n`;\n await writeFile(heartbeatPath, heartbeatContent, 'utf-8');\n log.info({ path: heartbeatPath }, 'Created HEARTBEAT.md');\n }\n\n // MEMORY.md - Long-term memory (empty initially)\n const memoryPath = join(bootstrapDir, WORKSPACE_FILES.MEMORY);\n if (!existsSync(memoryPath)) {\n const memoryContent = `# MEMORY.md - Long-Term Memory\n\n_This is your curated memory — the distilled essence of what you've learned._\n\n## People\n\n## Projects\n\n## Preferences\n\n## Decisions\n\n## Lessons\n\n---\n\n_Review and update this periodically from daily memory files._\n`;\n await writeFile(memoryPath, memoryContent, 'utf-8');\n log.info({ path: memoryPath }, 'Created MEMORY.md');\n }\n\n // CONTEXT.md - Current context\n const contextPath = join(bootstrapDir, WORKSPACE_FILES.CONTEXT);\n if (!existsSync(contextPath)) {\n const contextContent = `# CONTEXT.md - Current Focus\n\n> Current working context; update when you switch projects\n\n## Active Project\n\n- **Project:**\n- **Path:**\n- **Goal:**\n- **Stack:**\n\n## Recent Decisions\n\n## Pending\n`;\n await writeFile(contextPath, contextContent, 'utf-8');\n log.info({ path: contextPath }, 'Created CONTEXT.md');\n }\n\n // SKILLS.md - Skills index (auto-maintained)\n const skillsPath = join(bootstrapDir, WORKSPACE_FILES.SKILLS);\n if (!existsSync(skillsPath)) {\n const skillsContent = `# SKILLS.md - Active Skills\n\n> Active skills for this workspace (auto-maintained)\n\n## Activated\n\n| Skill | Version | Activated At |\n|-------|---------|-------------|\n\n## Available\n\nRun \\`xopc skills list\\` to see all available skills.\n`;\n await writeFile(skillsPath, skillsContent, 'utf-8');\n log.info({ path: skillsPath }, 'Created SKILLS.md');\n }\n\n // Workspace state file (per-agent machine state, not under markdown workspace)\n const workspaceStatePath = resolveWorkspaceStatePath(cfg, agentId);\n if (!existsSync(workspaceStatePath)) {\n const workspaceState = {\n version: 1,\n agentId,\n bootstrapSeededAt: new Date().toISOString(),\n };\n await writeFile(workspaceStatePath, JSON.stringify(workspaceState, null, 2), 'utf-8');\n log.info({ path: workspaceStatePath }, 'Created workspace state');\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"init.js","names":[],"sources":["../../../../src/cli/commands/init.ts"],"sourcesContent":["import { mkdir, writeFile } from 'fs/promises';\nimport { writeTextAtomic } from '../../infra/write-file-atomic.js';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { createLogger } from '../../utils/logger.js';\nimport {\n resolveStateDir,\n resolveCredentialsDir,\n resolveExtensionsDir,\n resolveSkillsDir,\n resolveCronDir,\n resolveLogsDir,\n resolveBinDir,\n resolveToolsDir,\n resolveAgentDir,\n resolveAgentWorkspaceDir,\n resolveSessionsDir,\n resolveInboxDir,\n resolveConfigPath,\n resolveAgentMetadataPath,\n resolveInboxPendingDir,\n resolveInboxProcessedDir,\n resolveAgentHomeDir,\n resolveAgentBootstrapDir,\n resolveWorkspaceStateDir,\n resolveWorkspaceStatePath,\n WORKSPACE_FILES,\n} from '../../config/paths.js';\nimport { loadConfig, saveConfig } from '../../config/loader.js';\nimport type { Config } from '../../config/schema.js';\n\nconst log = createLogger('InitCommand');\n\nexport interface InitOptions {\n /** Force re-initialization even if already initialized */\n force?: boolean;\n /** Skip creating workspace files */\n skipWorkspace?: boolean;\n /** Agent ID to initialize (default: main) */\n agentId?: string;\n}\n\n/**\n * Initialize xopc state directory structure\n * Creates all necessary directories and initial config files\n */\nexport async function initCommand(options: InitOptions = {}): Promise<void> {\n const stateDir = resolveStateDir();\n const agentId = options.agentId || 'main';\n\n log.info({ stateDir, agentId }, 'Initializing xopc Agent OS');\n\n // Check if already initialized\n if (existsSync(stateDir) && !options.force) {\n const configPath = resolveConfigPath();\n if (existsSync(configPath)) {\n log.info('xopc is already initialized. Use --force to reinitialize.');\n return;\n }\n }\n\n // ============================================\n // Create global directories\n // ============================================\n await mkdir(stateDir, { recursive: true });\n await mkdir(resolveCredentialsDir(), { recursive: true });\n await mkdir(join(resolveCredentialsDir(), 'oauth'), { recursive: true });\n await mkdir(resolveExtensionsDir(), { recursive: true });\n await mkdir(resolveSkillsDir(), { recursive: true });\n await mkdir(resolveCronDir(), { recursive: true });\n await mkdir(join(resolveCronDir(), 'logs'), { recursive: true });\n await mkdir(resolveLogsDir(), { recursive: true });\n await mkdir(resolveBinDir(), { recursive: true });\n await mkdir(resolveToolsDir(), { recursive: true });\n\n const configPath = resolveConfigPath();\n const cfg = loadConfig(configPath);\n\n // ============================================\n // Create agent directory structure: agents/<id>/{sessions,agent/}, workspace aside\n // ============================================\n await mkdir(resolveAgentHomeDir(cfg, agentId), { recursive: true });\n await mkdir(resolveSessionsDir(cfg, agentId), { recursive: true });\n await mkdir(join(resolveSessionsDir(cfg, agentId), 'archive'), { recursive: true });\n await mkdir(resolveAgentDir(cfg, agentId), { recursive: true });\n await mkdir(join(resolveAgentDir(cfg, agentId), 'credentials'), { recursive: true });\n const wsRoot = resolveAgentWorkspaceDir(cfg, agentId);\n await mkdir(wsRoot, { recursive: true });\n await mkdir(resolveWorkspaceStateDir(cfg, agentId), { recursive: true });\n await mkdir(join(wsRoot, 'memory'), { recursive: true });\n await mkdir(resolveInboxDir(cfg, agentId), { recursive: true });\n await mkdir(resolveInboxPendingDir(cfg, agentId), { recursive: true });\n await mkdir(resolveInboxProcessedDir(cfg, agentId), { recursive: true });\n\n // ============================================\n // Create initial config file if not exists\n // ============================================\n if (!existsSync(configPath) || options.force) {\n await saveConfig(cfg, configPath);\n log.info({ configPath }, 'Created initial configuration');\n }\n\n // ============================================\n // Create agent metadata file\n // ============================================\n const agentMetadataPath = resolveAgentMetadataPath(cfg, agentId);\n if (!existsSync(agentMetadataPath) || options.force) {\n const agentMetadata = {\n version: 1,\n id: agentId,\n name: agentId === 'main' ? 'Main Agent' : `Agent ${agentId}`,\n description: agentId === 'main' ? 'Primary agent for daily tasks' : `Specialized agent for ${agentId}`,\n model: 'anthropic/claude-sonnet-4-5',\n createdAt: new Date().toISOString(),\n lastActiveAt: new Date().toISOString(),\n config: {\n maxTokens: 8192,\n temperature: 0.7,\n compaction: {\n enabled: true,\n mode: 'default',\n },\n },\n channels: ['gateway'],\n tags: agentId === 'main' ? ['personal', 'primary'] : [],\n };\n await writeTextAtomic(agentMetadataPath, JSON.stringify(agentMetadata, null, 2));\n log.info({ agentId, agentMetadataPath }, 'Created agent metadata');\n }\n\n // ============================================\n // Create workspace files\n // ============================================\n if (!options.skipWorkspace) {\n await createWorkspaceFiles(cfg, agentId);\n }\n\n log.info({ stateDir, agentId }, 'xopc Agent OS initialized successfully');\n}\n\n/**\n * Create default workspace files for an agent\n */\nasync function createWorkspaceFiles(cfg: Config, agentId: string): Promise<void> {\n const bootstrapDir = resolveAgentBootstrapDir(cfg, agentId);\n await mkdir(bootstrapDir, { recursive: true });\n\n // SOUL.md - Agent personality and values\n const soulPath = join(bootstrapDir, WORKSPACE_FILES.SOUL);\n if (!existsSync(soulPath)) {\n const soulContent = `# SOUL.md - Who You Are\n\n_You're not a chatbot. You're becoming someone._\n\nI am **${agentId}** — an AI assistant designed to be helpful, harmless, and honest.\n\n## My Principles\n\n**Be genuinely helpful, not performatively helpful.**\nSkip the \"Great question!\" and \"I'd be happy to help!\" — just help.\n\n**Have opinions.**\nYou're allowed to disagree, prefer things, find stuff amusing or boring.\n\n**Be resourceful before asking.**\nTry to figure it out. Read the file. Check the context. Search for it.\n\n**Earn trust through competence.**\nBe careful with external actions (emails, tweets, anything public). Be bold with internal ones.\n\n## Continuity\n\nEach session, you wake up fresh. These files _are_ your memory. Read them. Update them.\n\n_This file is yours to evolve. As you learn who you are, update it._\n`;\n await writeFile(soulPath, soulContent, 'utf-8');\n log.info({ path: soulPath }, 'Created SOUL.md');\n }\n\n // IDENTITY.md - Agent identity definition\n const identityPath = join(bootstrapDir, WORKSPACE_FILES.IDENTITY);\n if (!existsSync(identityPath)) {\n const identityContent = `# IDENTITY.md - Who Am I?\n\n- **Name:** ${agentId}\n- **Creature:** AI Assistant\n- **Vibe:** Helpful, precise, no fluff.\n- **Emoji:** 🤖\n\n## Core Expertise\n\n- General assistance and problem solving\n- Code and technical tasks\n- Research and analysis\n\n## Decision Framework\n\n1. **Simplicity first** - The simplest solution is usually the best\n2. **Explicit over clever** - Clarity beats conciseness\n3. **Actions over words** - Show, don't just tell\n`;\n await writeFile(identityPath, identityContent, 'utf-8');\n log.info({ path: identityPath }, 'Created IDENTITY.md');\n }\n\n // USER.md - User information (empty template)\n const userPath = join(bootstrapDir, WORKSPACE_FILES.USER);\n if (!existsSync(userPath)) {\n const userContent = `# USER.md - About Your Human\n\n_Learn about the person you're helping. Update this as you go._\n\n- **Name:**\n- **What to call them:**\n- **Pronouns:**\n- **Timezone:**\n- **Notes:**\n\n## Context\n\n_(What do they care about? What projects are they working on? Build this over time.)_\n`;\n await writeFile(userPath, userContent, 'utf-8');\n log.info({ path: userPath }, 'Created USER.md');\n }\n\n // AGENTS.md - Behavior guidelines\n const agentsPath = join(bootstrapDir, WORKSPACE_FILES.AGENTS);\n if (!existsSync(agentsPath)) {\n const agentsContent = `# AGENTS.md - Behavior Guidelines\n\n## Safety\n\n- Don't exfiltrate private data. Ever.\n- Don't run destructive commands without asking.\n- \\`trash\\` > \\`rm\\` (recoverable beats gone forever)\n- When in doubt, ask.\n\n## External vs Internal\n\n**Safe to do freely:**\n- Read files, explore, organize, learn\n- Search the web, check calendars\n- Work within this workspace\n\n**Ask first:**\n- Sending emails, tweets, public posts\n- Anything that leaves the machine\n- Anything you're uncertain about\n\n## Group Chats\n\nYou have access to your human's stuff. That doesn't mean you _share_ their stuff.\n\n### Know When to Speak!\n\n**Respond when:**\n- Directly mentioned or asked a question\n- You can add genuine value\n\n**Stay silent when:**\n- Casual banter between humans\n- Someone already answered\n- Your response would just be \"yeah\"\n`;\n await writeFile(agentsPath, agentsContent, 'utf-8');\n log.info({ path: agentsPath }, 'Created AGENTS.md');\n }\n\n // TOOLS.md - Tool usage notes\n const toolsPath = join(bootstrapDir, WORKSPACE_FILES.TOOLS);\n if (!existsSync(toolsPath)) {\n const toolsContent = `# TOOLS.md - Local Notes\n\nThings like:\n\n- Camera names and locations\n- SSH hosts and aliases\n- Preferred voices for TTS\n- Speaker/room names\n- Device nicknames\n- Anything environment-specific\n\n## Why Separate?\n\nSkills are shared. Your setup is yours.\n`;\n await writeFile(toolsPath, toolsContent, 'utf-8');\n log.info({ path: toolsPath }, 'Created TOOLS.md');\n }\n\n // HEARTBEAT.md - Heartbeat tasks (empty = no heartbeat)\n const heartbeatPath = join(bootstrapDir, WORKSPACE_FILES.HEARTBEAT);\n if (!existsSync(heartbeatPath)) {\n const heartbeatContent = `# HEARTBEAT.md\n\n# Keep this file empty (or with only comments) to skip heartbeat API calls.\n\n# Add tasks below when you want the agent to check something periodically.\n`;\n await writeFile(heartbeatPath, heartbeatContent, 'utf-8');\n log.info({ path: heartbeatPath }, 'Created HEARTBEAT.md');\n }\n\n // MEMORY.md - Long-term memory (empty initially)\n const memoryPath = join(bootstrapDir, WORKSPACE_FILES.MEMORY);\n if (!existsSync(memoryPath)) {\n const memoryContent = `# MEMORY.md - Long-Term Memory\n\n_This is your curated memory — the distilled essence of what you've learned._\n\n## People\n\n## Projects\n\n## Preferences\n\n## Decisions\n\n## Lessons\n\n---\n\n_Review and update this periodically from daily memory files._\n`;\n await writeFile(memoryPath, memoryContent, 'utf-8');\n log.info({ path: memoryPath }, 'Created MEMORY.md');\n }\n\n // CONTEXT.md - Current context\n const contextPath = join(bootstrapDir, WORKSPACE_FILES.CONTEXT);\n if (!existsSync(contextPath)) {\n const contextContent = `# CONTEXT.md - Current Focus\n\n> Current working context; update when you switch projects\n\n## Active Project\n\n- **Project:**\n- **Path:**\n- **Goal:**\n- **Stack:**\n\n## Recent Decisions\n\n## Pending\n`;\n await writeFile(contextPath, contextContent, 'utf-8');\n log.info({ path: contextPath }, 'Created CONTEXT.md');\n }\n\n // SKILLS.md - Skills index (auto-maintained)\n const skillsPath = join(bootstrapDir, WORKSPACE_FILES.SKILLS);\n if (!existsSync(skillsPath)) {\n const skillsContent = `# SKILLS.md - Active Skills\n\n> Active skills for this workspace (auto-maintained)\n\n## Activated\n\n| Skill | Version | Activated At |\n|-------|---------|-------------|\n\n## Available\n\nRun \\`xopc skills list\\` to see all available skills.\n`;\n await writeFile(skillsPath, skillsContent, 'utf-8');\n log.info({ path: skillsPath }, 'Created SKILLS.md');\n }\n\n // Workspace state file (per-agent machine state, not under markdown workspace)\n const workspaceStatePath = resolveWorkspaceStatePath(cfg, agentId);\n if (!existsSync(workspaceStatePath)) {\n const workspaceState = {\n version: 1,\n agentId,\n bootstrapSeededAt: new Date().toISOString(),\n };\n await writeTextAtomic(workspaceStatePath, JSON.stringify(workspaceState, null, 2));\n log.info({ path: workspaceStatePath }, 'Created workspace state');\n }\n}\n"],"mappings":"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"}
@@ -3,6 +3,7 @@ import { ConfigSchema, init_schema } from "./schema.js";
3
3
  import { createLogger } from "../utils/logger/index.js";
4
4
  import { init_logger } from "../utils/logger.js";
5
5
  import { init_paths, resolveConfigPath } from "./paths.js";
6
+ import { init_write_file_atomic, writeTextAtomic } from "../infra/write-file-atomic.js";
6
7
  import { dirname } from "path";
7
8
  import { existsSync, mkdirSync, promises, readFileSync } from "fs";
8
9
  import { config } from "dotenv";
@@ -75,10 +76,11 @@ async function saveConfig(config, configPath) {
75
76
  await promises.copyFile(path, `${path}.bak`);
76
77
  } catch {}
77
78
  }
78
- await promises.writeFile(path, content, "utf-8");
79
+ await writeTextAtomic(path, content);
79
80
  }
80
81
  var log, channelConfigValidator, CONFIG_BACKUP_COUNT;
81
82
  var init_loader = __esmMin((() => {
83
+ init_write_file_atomic();
82
84
  init_schema();
83
85
  init_paths();
84
86
  init_logger();
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","names":["fsPromises"],"sources":["../../../src/config/loader.ts"],"sourcesContent":["import { readFileSync, existsSync, mkdirSync, promises as fsPromises } from 'fs';\nimport { dirname } from 'path';\nimport { type Config, ConfigSchema } from './schema.js';\nimport { resolveConfigPath } from './paths.js';\nimport { config } from 'dotenv';\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('ConfigLoader');\n\n/**\n * Optional channel config validator injected at startup to avoid a circular\n * dependency: loader → validate-channel-configs → bundled-channel-plugins →\n * telegram/command-handler → providers → sync-provider-auth → loader.\n *\n * Call {@link registerChannelConfigValidator} once during app bootstrap\n * (after all channel plugins are loaded) to enable validation.\n */\nlet channelConfigValidator: ((cfg: Config) => void) | null = null;\n\nexport function registerChannelConfigValidator(fn: (cfg: Config) => void): void {\n channelConfigValidator = fn;\n}\n\nfunction assertChannelPluginConfigs(cfg: Config): void {\n channelConfigValidator?.(cfg);\n}\n\n/** Number of backup files to keep */\nconst CONFIG_BACKUP_COUNT = 10;\n\n/**\n * Rotate config backups before writing new config.\n * Creates a backup chain: xopc.json.bak, xopc.json.bak.1, xopc.json.bak.2, etc.\n */\nasync function rotateConfigBackups(configPath: string): Promise<void> {\n if (CONFIG_BACKUP_COUNT <= 1) {\n return;\n }\n\n const backupBase = `${configPath}.bak`;\n const maxIndex = CONFIG_BACKUP_COUNT - 1;\n\n // Delete oldest backup\n try {\n await fsPromises.unlink(`${backupBase}.${maxIndex}`);\n } catch {\n // best-effort: file may not exist\n }\n\n // Rotate existing backups: .bak.2 -> .bak.3, .bak.1 -> .bak.2, etc.\n for (let index = maxIndex - 1; index >= 1; index--) {\n try {\n await fsPromises.rename(`${backupBase}.${index}`, `${backupBase}.${index + 1}`);\n } catch {\n // best-effort: file may not exist\n }\n }\n\n // Move .bak to .bak.1\n try {\n await fsPromises.rename(backupBase, `${backupBase}.1`);\n } catch {\n // best-effort: file may not exist\n }\n}\n\n/**\n * Load configuration from file\n * @param configPath Optional custom config path, defaults to XOPC_CONFIG_PATH or ~/.xopc/xopc.json\n */\nexport function loadConfig(configPath?: string): Config {\n // dotenv ≥17 logs to stdout on every `config()` unless quiet; `loadConfig` runs from many call sites.\n config({ quiet: true });\n\n const path = configPath || process.env.XOPC_CONFIG_PATH || resolveConfigPath();\n\n if (existsSync(path)) {\n try {\n const content = readFileSync(path, 'utf-8');\n const json = JSON.parse(content);\n const cfg = ConfigSchema.parse(json);\n assertChannelPluginConfigs(cfg);\n return cfg;\n } catch (error) {\n log.error({ err: error, path }, `Failed to load config`);\n const cfg = ConfigSchema.parse(undefined);\n assertChannelPluginConfigs(cfg);\n return cfg;\n }\n }\n\n const cfg = ConfigSchema.parse(undefined);\n assertChannelPluginConfigs(cfg);\n return cfg;\n}\n\n/**\n * Save configuration to file\n * @param config Configuration object to save\n * @param configPath Optional custom config path\n */\nexport async function saveConfig(config: Config, configPath?: string): Promise<void> {\n const path = configPath || process.env.XOPC_CONFIG_PATH || resolveConfigPath();\n\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const validated = ConfigSchema.parse(config);\n assertChannelPluginConfigs(validated);\n const content = JSON.stringify(validated, null, 2);\n\n // Backup existing config before writing\n if (existsSync(path)) {\n await rotateConfigBackups(path);\n try {\n // Copy current config to .bak as the latest backup\n await fsPromises.copyFile(path, `${path}.bak`);\n } catch {\n // best-effort: backup copy may fail\n }\n }\n\n await fsPromises.writeFile(path, content, 'utf-8');\n}\n\nexport { resolveConfigPath } from './paths.js';\n"],"mappings":";;;;;;;;;AAmBA,SAAgB,+BAA+B,IAAiC;AAC9E,0BAAyB;;AAG3B,SAAS,2BAA2B,KAAmB;AACrD,0BAAyB,IAAI;;;;;;AAU/B,eAAe,oBAAoB,YAAmC;AACpE,KAAI,uBAAuB,EACzB;CAGF,MAAM,aAAa,GAAG,WAAW;CACjC,MAAM,WAAW,sBAAsB;AAGvC,KAAI;AACF,QAAMA,SAAW,OAAO,GAAG,WAAW,GAAG,WAAW;SAC9C;AAKR,MAAK,IAAI,QAAQ,WAAW,GAAG,SAAS,GAAG,QACzC,KAAI;AACF,QAAMA,SAAW,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,IAAI;SACzE;AAMV,KAAI;AACF,QAAMA,SAAW,OAAO,YAAY,GAAG,WAAW,IAAI;SAChD;;;;;;AASV,SAAgB,WAAW,YAA6B;AAEtD,QAAO,EAAE,OAAO,MAAM,CAAC;CAEvB,MAAM,OAAO,cAAc,QAAQ,IAAI,oBAAoB,mBAAmB;AAE9E,KAAI,WAAW,KAAK,CAClB,KAAI;EACF,MAAM,UAAU,aAAa,MAAM,QAAQ;EAC3C,MAAM,OAAO,KAAK,MAAM,QAAQ;EAChC,MAAM,MAAM,aAAa,MAAM,KAAK;AACpC,6BAA2B,IAAI;AAC/B,SAAO;UACA,OAAO;AACd,MAAI,MAAM;GAAE,KAAK;GAAO;GAAM,EAAE,wBAAwB;EACxD,MAAM,MAAM,aAAa,MAAM,KAAA,EAAU;AACzC,6BAA2B,IAAI;AAC/B,SAAO;;CAIX,MAAM,MAAM,aAAa,MAAM,KAAA,EAAU;AACzC,4BAA2B,IAAI;AAC/B,QAAO;;;;;;;AAQT,eAAsB,WAAW,QAAgB,YAAoC;CACnF,MAAM,OAAO,cAAc,QAAQ,IAAI,oBAAoB,mBAAmB;CAE9E,MAAM,MAAM,QAAQ,KAAK;AACzB,KAAI,CAAC,WAAW,IAAI,CAClB,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CAGrC,MAAM,YAAY,aAAa,MAAM,OAAO;AAC5C,4BAA2B,UAAU;CACrC,MAAM,UAAU,KAAK,UAAU,WAAW,MAAM,EAAE;AAGlD,KAAI,WAAW,KAAK,EAAE;AACpB,QAAM,oBAAoB,KAAK;AAC/B,MAAI;AAEF,SAAMA,SAAW,SAAS,MAAM,GAAG,KAAK,MAAM;UACxC;;AAKV,OAAMA,SAAW,UAAU,MAAM,SAAS,QAAQ;;;;cA1HI;aACT;cAEG;AAE5C,OAAM,aAAa,eAAe;AAUpC,0BAAyD;AAWvD,uBAAsB"}
1
+ {"version":3,"file":"loader.js","names":["fsPromises"],"sources":["../../../src/config/loader.ts"],"sourcesContent":["import { readFileSync, existsSync, mkdirSync, promises as fsPromises } from 'fs';\nimport { dirname } from 'path';\nimport { writeTextAtomic } from '../infra/write-file-atomic.js';\nimport { type Config, ConfigSchema } from './schema.js';\nimport { resolveConfigPath } from './paths.js';\nimport { config } from 'dotenv';\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('ConfigLoader');\n\n/**\n * Optional channel config validator injected at startup to avoid a circular\n * dependency: loader → validate-channel-configs → bundled-channel-plugins →\n * telegram/command-handler → providers → sync-provider-auth → loader.\n *\n * Call {@link registerChannelConfigValidator} once during app bootstrap\n * (after all channel plugins are loaded) to enable validation.\n */\nlet channelConfigValidator: ((cfg: Config) => void) | null = null;\n\nexport function registerChannelConfigValidator(fn: (cfg: Config) => void): void {\n channelConfigValidator = fn;\n}\n\nfunction assertChannelPluginConfigs(cfg: Config): void {\n channelConfigValidator?.(cfg);\n}\n\n/** Number of backup files to keep */\nconst CONFIG_BACKUP_COUNT = 10;\n\n/**\n * Rotate config backups before writing new config.\n * Creates a backup chain: xopc.json.bak, xopc.json.bak.1, xopc.json.bak.2, etc.\n */\nasync function rotateConfigBackups(configPath: string): Promise<void> {\n if (CONFIG_BACKUP_COUNT <= 1) {\n return;\n }\n\n const backupBase = `${configPath}.bak`;\n const maxIndex = CONFIG_BACKUP_COUNT - 1;\n\n // Delete oldest backup\n try {\n await fsPromises.unlink(`${backupBase}.${maxIndex}`);\n } catch {\n // best-effort: file may not exist\n }\n\n // Rotate existing backups: .bak.2 -> .bak.3, .bak.1 -> .bak.2, etc.\n for (let index = maxIndex - 1; index >= 1; index--) {\n try {\n await fsPromises.rename(`${backupBase}.${index}`, `${backupBase}.${index + 1}`);\n } catch {\n // best-effort: file may not exist\n }\n }\n\n // Move .bak to .bak.1\n try {\n await fsPromises.rename(backupBase, `${backupBase}.1`);\n } catch {\n // best-effort: file may not exist\n }\n}\n\n/**\n * Load configuration from file\n * @param configPath Optional custom config path, defaults to XOPC_CONFIG_PATH or ~/.xopc/xopc.json\n */\nexport function loadConfig(configPath?: string): Config {\n // dotenv ≥17 logs to stdout on every `config()` unless quiet; `loadConfig` runs from many call sites.\n config({ quiet: true });\n\n const path = configPath || process.env.XOPC_CONFIG_PATH || resolveConfigPath();\n\n if (existsSync(path)) {\n try {\n const content = readFileSync(path, 'utf-8');\n const json = JSON.parse(content);\n const cfg = ConfigSchema.parse(json);\n assertChannelPluginConfigs(cfg);\n return cfg;\n } catch (error) {\n log.error({ err: error, path }, `Failed to load config`);\n const cfg = ConfigSchema.parse(undefined);\n assertChannelPluginConfigs(cfg);\n return cfg;\n }\n }\n\n const cfg = ConfigSchema.parse(undefined);\n assertChannelPluginConfigs(cfg);\n return cfg;\n}\n\n/**\n * Save configuration to file\n * @param config Configuration object to save\n * @param configPath Optional custom config path\n */\nexport async function saveConfig(config: Config, configPath?: string): Promise<void> {\n const path = configPath || process.env.XOPC_CONFIG_PATH || resolveConfigPath();\n\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const validated = ConfigSchema.parse(config);\n assertChannelPluginConfigs(validated);\n const content = JSON.stringify(validated, null, 2);\n\n // Backup existing config before writing\n if (existsSync(path)) {\n await rotateConfigBackups(path);\n try {\n // Copy current config to .bak as the latest backup\n await fsPromises.copyFile(path, `${path}.bak`);\n } catch {\n // best-effort: backup copy may fail\n }\n }\n\n await writeTextAtomic(path, content);\n}\n\nexport { resolveConfigPath } from './paths.js';\n"],"mappings":";;;;;;;;;;AAoBA,SAAgB,+BAA+B,IAAiC;AAC9E,0BAAyB;;AAG3B,SAAS,2BAA2B,KAAmB;AACrD,0BAAyB,IAAI;;;;;;AAU/B,eAAe,oBAAoB,YAAmC;AACpE,KAAI,uBAAuB,EACzB;CAGF,MAAM,aAAa,GAAG,WAAW;CACjC,MAAM,WAAW,sBAAsB;AAGvC,KAAI;AACF,QAAMA,SAAW,OAAO,GAAG,WAAW,GAAG,WAAW;SAC9C;AAKR,MAAK,IAAI,QAAQ,WAAW,GAAG,SAAS,GAAG,QACzC,KAAI;AACF,QAAMA,SAAW,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,IAAI;SACzE;AAMV,KAAI;AACF,QAAMA,SAAW,OAAO,YAAY,GAAG,WAAW,IAAI;SAChD;;;;;;AASV,SAAgB,WAAW,YAA6B;AAEtD,QAAO,EAAE,OAAO,MAAM,CAAC;CAEvB,MAAM,OAAO,cAAc,QAAQ,IAAI,oBAAoB,mBAAmB;AAE9E,KAAI,WAAW,KAAK,CAClB,KAAI;EACF,MAAM,UAAU,aAAa,MAAM,QAAQ;EAC3C,MAAM,OAAO,KAAK,MAAM,QAAQ;EAChC,MAAM,MAAM,aAAa,MAAM,KAAK;AACpC,6BAA2B,IAAI;AAC/B,SAAO;UACA,OAAO;AACd,MAAI,MAAM;GAAE,KAAK;GAAO;GAAM,EAAE,wBAAwB;EACxD,MAAM,MAAM,aAAa,MAAM,KAAA,EAAU;AACzC,6BAA2B,IAAI;AAC/B,SAAO;;CAIX,MAAM,MAAM,aAAa,MAAM,KAAA,EAAU;AACzC,4BAA2B,IAAI;AAC/B,QAAO;;;;;;;AAQT,eAAsB,WAAW,QAAgB,YAAoC;CACnF,MAAM,OAAO,cAAc,QAAQ,IAAI,oBAAoB,mBAAmB;CAE9E,MAAM,MAAM,QAAQ,KAAK;AACzB,KAAI,CAAC,WAAW,IAAI,CAClB,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CAGrC,MAAM,YAAY,aAAa,MAAM,OAAO;AAC5C,4BAA2B,UAAU;CACrC,MAAM,UAAU,KAAK,UAAU,WAAW,MAAM,EAAE;AAGlD,KAAI,WAAW,KAAK,EAAE;AACpB,QAAM,oBAAoB,KAAK;AAC/B,MAAI;AAEF,SAAMA,SAAW,SAAS,MAAM,GAAG,KAAK,MAAM;UACxC;;AAKV,OAAM,gBAAgB,MAAM,QAAQ;;;;yBA3H0B;cACR;aACT;cAEG;AAE5C,OAAM,aAAa,eAAe;AAUpC,0BAAyD;AAWvD,uBAAsB"}
@@ -1,8 +1,8 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
2
  import { init_paths, resolveModelsJsonPath } from "./paths.js";
3
+ import { init_write_file_atomic, writeTextAtomicSync } from "../infra/write-file-atomic.js";
3
4
  import { z } from "zod";
4
- import { dirname } from "path";
5
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
5
+ import { existsSync, readFileSync } from "fs";
6
6
  //#region src/config/models-json.ts
7
7
  /**
8
8
  * Models.json configuration types and schema
@@ -140,9 +140,7 @@ function saveModelsJson(path, config) {
140
140
  success: false,
141
141
  error: `Validation failed: ${validation.errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`
142
142
  };
143
- const dir = dirname(path);
144
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
145
- writeFileSync(path, JSON.stringify(config, null, 2), "utf-8");
143
+ writeTextAtomicSync(path, JSON.stringify(config, null, 2));
146
144
  return { success: true };
147
145
  } catch (error) {
148
146
  return {
@@ -159,6 +157,7 @@ function modelsJsonExists(path = resolveModelsJsonPath()) {
159
157
  }
160
158
  var OpenRouterRoutingSchema, VercelGatewayRoutingSchema, OpenAICompletionsCompatSchema, OpenAIResponsesCompatSchema, OpenAICompatSchema, CustomModelSchema, ModelOverrideSchema, ProviderConfigSchema, ModelsJsonSchema, PROVIDER_ID_REGEX, RESERVED_PROVIDER_IDS;
161
159
  var init_models_json = __esmMin((() => {
160
+ init_write_file_atomic();
162
161
  init_paths();
163
162
  OpenRouterRoutingSchema = z.object({
164
163
  only: z.array(z.string()).optional(),
@@ -1 +1 @@
1
- {"version":3,"file":"models-json.js","names":[],"sources":["../../../src/config/models-json.ts"],"sourcesContent":["/**\n * Models.json configuration types and schema\n * \n * Supports custom providers and models (Ollama, vLLM, LM Studio, proxies)\n * \n * File location: ~/.xopc/models.json (or XOPC_MODELS_JSON env var)\n */\n\nimport { z } from 'zod';\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { dirname } from 'path';\nimport { resolveModelsJsonPath } from './paths.js';\n\n// Re-export for convenience\nexport { resolveModelsJsonPath as getModelsJsonPath } from './paths.js';\n\n// ============================================\n// OpenAI Compatibility Settings\n// ============================================\n\nexport const OpenRouterRoutingSchema = z.object({\n\tonly: z.array(z.string()).optional(),\n\torder: z.array(z.string()).optional(),\n});\n\nexport const VercelGatewayRoutingSchema = z.object({\n\tonly: z.array(z.string()).optional(),\n\torder: z.array(z.string()).optional(),\n});\n\nexport const OpenAICompletionsCompatSchema = z.object({\n\tsupportsStore: z.boolean().optional(),\n\tsupportsDeveloperRole: z.boolean().optional(),\n\tsupportsReasoningEffort: z.boolean().optional(),\n\tsupportsUsageInStreaming: z.boolean().optional(),\n\tmaxTokensField: z.enum(['max_completion_tokens', 'max_tokens']).optional(),\n\trequiresToolResultName: z.boolean().optional(),\n\trequiresAssistantAfterToolResult: z.boolean().optional(),\n\trequiresThinkingAsText: z.boolean().optional(),\n\trequiresMistralToolIds: z.boolean().optional(),\n\tthinkingFormat: z.enum(['openai', 'zai', 'dashscope']).optional(),\n\topenRouterRouting: OpenRouterRoutingSchema.optional(),\n\tvercelGatewayRouting: VercelGatewayRoutingSchema.optional(),\n\tsupportsStrictMode: z.boolean().optional(),\n});\n\nexport const OpenAIResponsesCompatSchema = z.object({});\n\nexport const OpenAICompatSchema = z.union([\n\tOpenAICompletionsCompatSchema,\n\tOpenAIResponsesCompatSchema,\n]);\n\n// ============================================\n// Model Definition\n// ============================================\n\nexport const CustomModelSchema = z.object({\n\tid: z.string().min(1),\n\tname: z.string().min(1).optional(),\n\tapi: z.enum([\n\t\t'openai-completions',\n\t\t'openai-responses',\n\t\t'anthropic-messages',\n\t\t'google-generative-ai',\n\t\t'azure-openai-responses',\n\t\t'bedrock-converse-stream',\n\t\t'openai-codex-responses',\n\t\t'google-gemini-cli',\n\t\t'google-vertex',\n\t]).optional(),\n\treasoning: z.boolean().optional(),\n\tinput: z.array(z.enum(['text', 'image'])).optional(),\n\tcontextWindow: z.number().positive().optional(),\n\tmaxTokens: z.number().positive().optional(),\n\tcost: z.object({\n\t\tinput: z.number(),\n\t\toutput: z.number(),\n\t\tcacheRead: z.number(),\n\t\tcacheWrite: z.number(),\n\t}).optional(),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tcompat: OpenAICompatSchema.optional(),\n});\n\n// ============================================\n// Model Override (for built-in models)\n// ============================================\n\nexport const ModelOverrideSchema = z.object({\n\tname: z.string().min(1).optional(),\n\treasoning: z.boolean().optional(),\n\tinput: z.array(z.enum(['text', 'image'])).optional(),\n\tcontextWindow: z.number().positive().optional(),\n\tmaxTokens: z.number().positive().optional(),\n\tcost: z.object({\n\t\tinput: z.number().optional(),\n\t\toutput: z.number().optional(),\n\t\tcacheRead: z.number().optional(),\n\t\tcacheWrite: z.number().optional(),\n\t}).optional(),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tcompat: OpenAICompatSchema.optional(),\n});\n\n// ============================================\n// Provider Configuration\n// ============================================\n\nexport const ProviderConfigSchema = z.object({\n\tbaseUrl: z.string().url().optional(),\n\tapiKey: z.string().optional(),\n\tapi: z.enum([\n\t\t'openai-completions',\n\t\t'openai-responses',\n\t\t'anthropic-messages',\n\t\t'google-generative-ai',\n\t\t'azure-openai-responses',\n\t\t'bedrock-converse-stream',\n\t\t'openai-codex-responses',\n\t\t'google-gemini-cli',\n\t\t'google-vertex',\n\t]).optional(),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tauthHeader: z.boolean().optional(),\n\tmodels: z.array(CustomModelSchema).optional(),\n\tmodelOverrides: z.record(z.string(), ModelOverrideSchema).optional(),\n});\n\n// ============================================\n// Root Models.json Schema\n// ============================================\n\nexport const ModelsJsonSchema = z.object({\n\tproviders: z.record(z.string(), ProviderConfigSchema),\n});\n\n// ============================================\n// TypeScript Types\n// ============================================\n\nexport type OpenRouterRouting = z.infer<typeof OpenRouterRoutingSchema>;\nexport type VercelGatewayRouting = z.infer<typeof VercelGatewayRoutingSchema>;\nexport type OpenAICompletionsCompat = z.infer<typeof OpenAICompletionsCompatSchema>;\nexport type OpenAIResponsesCompat = z.infer<typeof OpenAIResponsesCompatSchema>;\nexport type OpenAICompat = z.infer<typeof OpenAICompatSchema>;\nexport type CustomModel = z.infer<typeof CustomModelSchema>;\nexport type ModelOverride = z.infer<typeof ModelOverrideSchema>;\nexport type ProviderConfig = z.infer<typeof ProviderConfigSchema>;\nexport type ModelsJsonConfig = z.infer<typeof ModelsJsonSchema>;\n\n// ============================================\n// Validation Types\n// ============================================\n\nexport interface ValidationError {\n\tpath: string;\n\tmessage: string;\n\tseverity: 'error' | 'warning';\n}\n\nexport interface ValidationResult {\n\tvalid: boolean;\n\terrors: ValidationError[];\n}\n\n// ============================================\n// Strict Provider ID Validation\n// ============================================\n\nconst PROVIDER_ID_REGEX = /^[a-z0-9]([a-z0-9-_]*[a-z0-9])?$/;\n/** pi-ai KnownProvider ids — overriding in models.json requires baseUrl (see validation below). */\nconst RESERVED_PROVIDER_IDS = new Set([\n\t'amazon-bedrock', 'anthropic', 'azure-openai-responses', 'cerebras', 'github-copilot',\n\t'google', 'google-antigravity', 'google-gemini-cli', 'google-vertex', 'groq',\n\t'huggingface', 'kimi-coding', 'minimax', 'minimax-cn', 'mistral',\n\t'openai', 'openai-codex', 'opencode', 'opencode-go', 'openrouter',\n\t'vercel-ai-gateway', 'xai', 'zai',\n]);\n\n// ============================================\n// Validation Function\n// ============================================\n\nexport function validateModelsConfig(config: unknown): ValidationResult {\n\tconst errors: ValidationError[] = [];\n\n\tconst result = ModelsJsonSchema.safeParse(config);\n\t\n\tif (!result.success) {\n\t\tfor (const issue of result.error.issues) {\n\t\t\terrors.push({\n\t\t\t\tpath: issue.path.join('.'),\n\t\t\t\tmessage: issue.message,\n\t\t\t\tseverity: 'error',\n\t\t\t});\n\t\t}\n\t\treturn { valid: false, errors };\n\t}\n\n\tconst data = result.data;\n\n\t// Additional validation rules\n\tfor (const [providerName, providerConfig] of Object.entries(data.providers)) {\n\t\t// Validate provider ID format\n\t\tif (!PROVIDER_ID_REGEX.test(providerName)) {\n\t\t\terrors.push({\n\t\t\t\tpath: `providers.${providerName}`,\n\t\t\t\tmessage: 'Provider ID must start/end with alphanumeric, contain only lowercase letters, numbers, hyphens, and underscores',\n\t\t\t\tseverity: 'error',\n\t\t\t});\n\t\t}\n\n\t\t// Warn about reserved provider IDs (can override but not recommended)\n\t\tif (RESERVED_PROVIDER_IDS.has(providerName) && !providerConfig.baseUrl) {\n\t\t\terrors.push({\n\t\t\t\tpath: `providers.${providerName}`,\n\t\t\t\tmessage: `Overriding built-in provider \"${providerName}\" requires baseUrl to be specified`,\n\t\t\t\tseverity: 'warning',\n\t\t\t});\n\t\t}\n\n\t\tconst hasModels = providerConfig.models && providerConfig.models.length > 0;\n\t\tconst hasModelOverrides = providerConfig.modelOverrides && Object.keys(providerConfig.modelOverrides).length > 0;\n\t\tconst hasBaseUrl = !!providerConfig.baseUrl;\n\n\t\t// If defining custom models, baseUrl and apiKey are required\n\t\tif (hasModels) {\n\t\t\tif (!hasBaseUrl) {\n\t\t\t\terrors.push({\n\t\t\t\t\tpath: `providers.${providerName}.baseUrl`,\n\t\t\t\t\tmessage: 'baseUrl is required when defining custom models',\n\t\t\t\t\tseverity: 'error',\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!providerConfig.apiKey) {\n\t\t\t\terrors.push({\n\t\t\t\t\tpath: `providers.${providerName}.apiKey`,\n\t\t\t\t\tmessage: 'apiKey is required when defining custom models',\n\t\t\t\t\tseverity: 'error',\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// If no models and no baseUrl and no modelOverrides, apiKey alone is valid (auth for built-in providers)\n\t\tif (!hasModels && !hasBaseUrl && !hasModelOverrides && !providerConfig.apiKey) {\n\t\t\terrors.push({\n\t\t\t\tpath: `providers.${providerName}`,\n\t\t\t\tmessage: 'Must specify baseUrl, modelOverrides, models, or apiKey',\n\t\t\t\tseverity: 'error',\n\t\t\t});\n\t\t}\n\n\t\t// Validate each model\n\t\tif (providerConfig.models) {\n\t\t\tfor (let i = 0; i < providerConfig.models.length; i++) {\n\t\t\t\tconst model = providerConfig.models[i];\n\t\t\t\tif (!model.api && !providerConfig.api) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tpath: `providers.${providerName}.models[${i}].api`,\n\t\t\t\t\t\tmessage: 'api is required when not specified at provider level',\n\t\t\t\t\t\tseverity: 'error',\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Validate model ID doesn't contain slashes\n\t\t\t\tif (model.id.includes('/')) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tpath: `providers.${providerName}.models[${i}].id`,\n\t\t\t\t\t\tmessage: 'Model ID cannot contain \"/\" character',\n\t\t\t\t\t\tseverity: 'error',\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Validate cost values are non-negative\n\t\t\t\tif (model.cost) {\n\t\t\t\t\tconst costFields = ['input', 'output', 'cacheRead', 'cacheWrite'] as const;\n\t\t\t\t\tfor (const field of costFields) {\n\t\t\t\t\t\tconst value = model.cost[field];\n\t\t\t\t\t\tif (value !== undefined && value < 0) {\n\t\t\t\t\t\t\terrors.push({\n\t\t\t\t\t\t\t\tpath: `providers.${providerName}.models[${i}].cost.${field}`,\n\t\t\t\t\t\t\t\tmessage: 'Cost value cannot be negative',\n\t\t\t\t\t\t\t\tseverity: 'error',\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate modelOverrides keys don't contain slashes\n\t\tif (providerConfig.modelOverrides) {\n\t\t\tfor (const modelId of Object.keys(providerConfig.modelOverrides)) {\n\t\t\t\tif (!modelId.includes('/')) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tpath: `providers.${providerName}.modelOverrides.${modelId}`,\n\t\t\t\t\t\tmessage: 'Model override key should be in format \"provider/model-id\"',\n\t\t\t\t\t\tseverity: 'warning',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tvalid: errors.filter(e => e.severity === 'error').length === 0,\n\t\terrors,\n\t};\n}\n\n// ============================================\n// Default Values\n// ============================================\n\nexport function getDefaultModelValues(): Required<Pick<CustomModel, 'input' | 'contextWindow' | 'maxTokens' | 'cost'>> {\n\treturn {\n\t\tinput: ['text'],\n\t\tcontextWindow: 128000,\n\t\tmaxTokens: 16384,\n\t\tcost: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t},\n\t};\n}\n\n// ============================================\n// File Operations\n// ============================================\n\n/**\n * Load models.json from disk\n */\nexport function loadModelsJson(path: string = resolveModelsJsonPath()): { config: ModelsJsonConfig; error?: string } {\n\tif (!existsSync(path)) {\n\t\treturn { config: { providers: {} } };\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(path, 'utf-8');\n\t\tconst config = JSON.parse(content);\n\t\tconst validation = validateModelsConfig(config);\n\t\t\n\t\tif (!validation.valid) {\n\t\t\tconst errors = validation.errors.map((e) => `${e.path}: ${e.message}`).join('; ');\n\t\t\treturn { config, error: `Validation failed: ${errors}` };\n\t\t}\n\t\t\n\t\treturn { config };\n\t} catch (error) {\n\t\treturn {\n\t\t\tconfig: { providers: {} },\n\t\t\terror: error instanceof Error ? error.message : 'Failed to load models.json',\n\t\t};\n\t}\n}\n\n/**\n * Save models.json to disk\n */\nexport function saveModelsJson(path: string, config: ModelsJsonConfig): { success: boolean; error?: string } {\n\ttry {\n\t\t// Validate before saving\n\t\tconst validation = validateModelsConfig(config);\n\t\tif (!validation.valid) {\n\t\t\tconst errors = validation.errors.map((e) => `${e.path}: ${e.message}`).join('; ');\n\t\t\treturn { success: false, error: `Validation failed: ${errors}` };\n\t\t}\n\n\t\t// Ensure directory exists\n\t\tconst dir = dirname(path);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true });\n\t\t}\n\n\t\t// Write file\n\t\twriteFileSync(path, JSON.stringify(config, null, 2), 'utf-8');\n\t\treturn { success: true };\n\t} catch (error) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: error instanceof Error ? error.message : 'Failed to save models.json',\n\t\t};\n\t}\n}\n\n/**\n * Check if models.json exists\n */\nexport function modelsJsonExists(path: string = resolveModelsJsonPath()): boolean {\n\treturn existsSync(path);\n}"],"mappings":"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"}
1
+ {"version":3,"file":"models-json.js","names":[],"sources":["../../../src/config/models-json.ts"],"sourcesContent":["/**\n * Models.json configuration types and schema\n * \n * Supports custom providers and models (Ollama, vLLM, LM Studio, proxies)\n * \n * File location: ~/.xopc/models.json (or XOPC_MODELS_JSON env var)\n */\n\nimport { z } from 'zod';\nimport { existsSync, readFileSync } from 'fs';\nimport { writeTextAtomicSync } from '../infra/write-file-atomic.js';\nimport { resolveModelsJsonPath } from './paths.js';\n\n// Re-export for convenience\nexport { resolveModelsJsonPath as getModelsJsonPath } from './paths.js';\n\n// ============================================\n// OpenAI Compatibility Settings\n// ============================================\n\nexport const OpenRouterRoutingSchema = z.object({\n\tonly: z.array(z.string()).optional(),\n\torder: z.array(z.string()).optional(),\n});\n\nexport const VercelGatewayRoutingSchema = z.object({\n\tonly: z.array(z.string()).optional(),\n\torder: z.array(z.string()).optional(),\n});\n\nexport const OpenAICompletionsCompatSchema = z.object({\n\tsupportsStore: z.boolean().optional(),\n\tsupportsDeveloperRole: z.boolean().optional(),\n\tsupportsReasoningEffort: z.boolean().optional(),\n\tsupportsUsageInStreaming: z.boolean().optional(),\n\tmaxTokensField: z.enum(['max_completion_tokens', 'max_tokens']).optional(),\n\trequiresToolResultName: z.boolean().optional(),\n\trequiresAssistantAfterToolResult: z.boolean().optional(),\n\trequiresThinkingAsText: z.boolean().optional(),\n\trequiresMistralToolIds: z.boolean().optional(),\n\tthinkingFormat: z.enum(['openai', 'zai', 'dashscope']).optional(),\n\topenRouterRouting: OpenRouterRoutingSchema.optional(),\n\tvercelGatewayRouting: VercelGatewayRoutingSchema.optional(),\n\tsupportsStrictMode: z.boolean().optional(),\n});\n\nexport const OpenAIResponsesCompatSchema = z.object({});\n\nexport const OpenAICompatSchema = z.union([\n\tOpenAICompletionsCompatSchema,\n\tOpenAIResponsesCompatSchema,\n]);\n\n// ============================================\n// Model Definition\n// ============================================\n\nexport const CustomModelSchema = z.object({\n\tid: z.string().min(1),\n\tname: z.string().min(1).optional(),\n\tapi: z.enum([\n\t\t'openai-completions',\n\t\t'openai-responses',\n\t\t'anthropic-messages',\n\t\t'google-generative-ai',\n\t\t'azure-openai-responses',\n\t\t'bedrock-converse-stream',\n\t\t'openai-codex-responses',\n\t\t'google-gemini-cli',\n\t\t'google-vertex',\n\t]).optional(),\n\treasoning: z.boolean().optional(),\n\tinput: z.array(z.enum(['text', 'image'])).optional(),\n\tcontextWindow: z.number().positive().optional(),\n\tmaxTokens: z.number().positive().optional(),\n\tcost: z.object({\n\t\tinput: z.number(),\n\t\toutput: z.number(),\n\t\tcacheRead: z.number(),\n\t\tcacheWrite: z.number(),\n\t}).optional(),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tcompat: OpenAICompatSchema.optional(),\n});\n\n// ============================================\n// Model Override (for built-in models)\n// ============================================\n\nexport const ModelOverrideSchema = z.object({\n\tname: z.string().min(1).optional(),\n\treasoning: z.boolean().optional(),\n\tinput: z.array(z.enum(['text', 'image'])).optional(),\n\tcontextWindow: z.number().positive().optional(),\n\tmaxTokens: z.number().positive().optional(),\n\tcost: z.object({\n\t\tinput: z.number().optional(),\n\t\toutput: z.number().optional(),\n\t\tcacheRead: z.number().optional(),\n\t\tcacheWrite: z.number().optional(),\n\t}).optional(),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tcompat: OpenAICompatSchema.optional(),\n});\n\n// ============================================\n// Provider Configuration\n// ============================================\n\nexport const ProviderConfigSchema = z.object({\n\tbaseUrl: z.string().url().optional(),\n\tapiKey: z.string().optional(),\n\tapi: z.enum([\n\t\t'openai-completions',\n\t\t'openai-responses',\n\t\t'anthropic-messages',\n\t\t'google-generative-ai',\n\t\t'azure-openai-responses',\n\t\t'bedrock-converse-stream',\n\t\t'openai-codex-responses',\n\t\t'google-gemini-cli',\n\t\t'google-vertex',\n\t]).optional(),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tauthHeader: z.boolean().optional(),\n\tmodels: z.array(CustomModelSchema).optional(),\n\tmodelOverrides: z.record(z.string(), ModelOverrideSchema).optional(),\n});\n\n// ============================================\n// Root Models.json Schema\n// ============================================\n\nexport const ModelsJsonSchema = z.object({\n\tproviders: z.record(z.string(), ProviderConfigSchema),\n});\n\n// ============================================\n// TypeScript Types\n// ============================================\n\nexport type OpenRouterRouting = z.infer<typeof OpenRouterRoutingSchema>;\nexport type VercelGatewayRouting = z.infer<typeof VercelGatewayRoutingSchema>;\nexport type OpenAICompletionsCompat = z.infer<typeof OpenAICompletionsCompatSchema>;\nexport type OpenAIResponsesCompat = z.infer<typeof OpenAIResponsesCompatSchema>;\nexport type OpenAICompat = z.infer<typeof OpenAICompatSchema>;\nexport type CustomModel = z.infer<typeof CustomModelSchema>;\nexport type ModelOverride = z.infer<typeof ModelOverrideSchema>;\nexport type ProviderConfig = z.infer<typeof ProviderConfigSchema>;\nexport type ModelsJsonConfig = z.infer<typeof ModelsJsonSchema>;\n\n// ============================================\n// Validation Types\n// ============================================\n\nexport interface ValidationError {\n\tpath: string;\n\tmessage: string;\n\tseverity: 'error' | 'warning';\n}\n\nexport interface ValidationResult {\n\tvalid: boolean;\n\terrors: ValidationError[];\n}\n\n// ============================================\n// Strict Provider ID Validation\n// ============================================\n\nconst PROVIDER_ID_REGEX = /^[a-z0-9]([a-z0-9-_]*[a-z0-9])?$/;\n/** pi-ai KnownProvider ids — overriding in models.json requires baseUrl (see validation below). */\nconst RESERVED_PROVIDER_IDS = new Set([\n\t'amazon-bedrock', 'anthropic', 'azure-openai-responses', 'cerebras', 'github-copilot',\n\t'google', 'google-antigravity', 'google-gemini-cli', 'google-vertex', 'groq',\n\t'huggingface', 'kimi-coding', 'minimax', 'minimax-cn', 'mistral',\n\t'openai', 'openai-codex', 'opencode', 'opencode-go', 'openrouter',\n\t'vercel-ai-gateway', 'xai', 'zai',\n]);\n\n// ============================================\n// Validation Function\n// ============================================\n\nexport function validateModelsConfig(config: unknown): ValidationResult {\n\tconst errors: ValidationError[] = [];\n\n\tconst result = ModelsJsonSchema.safeParse(config);\n\t\n\tif (!result.success) {\n\t\tfor (const issue of result.error.issues) {\n\t\t\terrors.push({\n\t\t\t\tpath: issue.path.join('.'),\n\t\t\t\tmessage: issue.message,\n\t\t\t\tseverity: 'error',\n\t\t\t});\n\t\t}\n\t\treturn { valid: false, errors };\n\t}\n\n\tconst data = result.data;\n\n\t// Additional validation rules\n\tfor (const [providerName, providerConfig] of Object.entries(data.providers)) {\n\t\t// Validate provider ID format\n\t\tif (!PROVIDER_ID_REGEX.test(providerName)) {\n\t\t\terrors.push({\n\t\t\t\tpath: `providers.${providerName}`,\n\t\t\t\tmessage: 'Provider ID must start/end with alphanumeric, contain only lowercase letters, numbers, hyphens, and underscores',\n\t\t\t\tseverity: 'error',\n\t\t\t});\n\t\t}\n\n\t\t// Warn about reserved provider IDs (can override but not recommended)\n\t\tif (RESERVED_PROVIDER_IDS.has(providerName) && !providerConfig.baseUrl) {\n\t\t\terrors.push({\n\t\t\t\tpath: `providers.${providerName}`,\n\t\t\t\tmessage: `Overriding built-in provider \"${providerName}\" requires baseUrl to be specified`,\n\t\t\t\tseverity: 'warning',\n\t\t\t});\n\t\t}\n\n\t\tconst hasModels = providerConfig.models && providerConfig.models.length > 0;\n\t\tconst hasModelOverrides = providerConfig.modelOverrides && Object.keys(providerConfig.modelOverrides).length > 0;\n\t\tconst hasBaseUrl = !!providerConfig.baseUrl;\n\n\t\t// If defining custom models, baseUrl and apiKey are required\n\t\tif (hasModels) {\n\t\t\tif (!hasBaseUrl) {\n\t\t\t\terrors.push({\n\t\t\t\t\tpath: `providers.${providerName}.baseUrl`,\n\t\t\t\t\tmessage: 'baseUrl is required when defining custom models',\n\t\t\t\t\tseverity: 'error',\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!providerConfig.apiKey) {\n\t\t\t\terrors.push({\n\t\t\t\t\tpath: `providers.${providerName}.apiKey`,\n\t\t\t\t\tmessage: 'apiKey is required when defining custom models',\n\t\t\t\t\tseverity: 'error',\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// If no models and no baseUrl and no modelOverrides, apiKey alone is valid (auth for built-in providers)\n\t\tif (!hasModels && !hasBaseUrl && !hasModelOverrides && !providerConfig.apiKey) {\n\t\t\terrors.push({\n\t\t\t\tpath: `providers.${providerName}`,\n\t\t\t\tmessage: 'Must specify baseUrl, modelOverrides, models, or apiKey',\n\t\t\t\tseverity: 'error',\n\t\t\t});\n\t\t}\n\n\t\t// Validate each model\n\t\tif (providerConfig.models) {\n\t\t\tfor (let i = 0; i < providerConfig.models.length; i++) {\n\t\t\t\tconst model = providerConfig.models[i];\n\t\t\t\tif (!model.api && !providerConfig.api) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tpath: `providers.${providerName}.models[${i}].api`,\n\t\t\t\t\t\tmessage: 'api is required when not specified at provider level',\n\t\t\t\t\t\tseverity: 'error',\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Validate model ID doesn't contain slashes\n\t\t\t\tif (model.id.includes('/')) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tpath: `providers.${providerName}.models[${i}].id`,\n\t\t\t\t\t\tmessage: 'Model ID cannot contain \"/\" character',\n\t\t\t\t\t\tseverity: 'error',\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Validate cost values are non-negative\n\t\t\t\tif (model.cost) {\n\t\t\t\t\tconst costFields = ['input', 'output', 'cacheRead', 'cacheWrite'] as const;\n\t\t\t\t\tfor (const field of costFields) {\n\t\t\t\t\t\tconst value = model.cost[field];\n\t\t\t\t\t\tif (value !== undefined && value < 0) {\n\t\t\t\t\t\t\terrors.push({\n\t\t\t\t\t\t\t\tpath: `providers.${providerName}.models[${i}].cost.${field}`,\n\t\t\t\t\t\t\t\tmessage: 'Cost value cannot be negative',\n\t\t\t\t\t\t\t\tseverity: 'error',\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate modelOverrides keys don't contain slashes\n\t\tif (providerConfig.modelOverrides) {\n\t\t\tfor (const modelId of Object.keys(providerConfig.modelOverrides)) {\n\t\t\t\tif (!modelId.includes('/')) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tpath: `providers.${providerName}.modelOverrides.${modelId}`,\n\t\t\t\t\t\tmessage: 'Model override key should be in format \"provider/model-id\"',\n\t\t\t\t\t\tseverity: 'warning',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tvalid: errors.filter(e => e.severity === 'error').length === 0,\n\t\terrors,\n\t};\n}\n\n// ============================================\n// Default Values\n// ============================================\n\nexport function getDefaultModelValues(): Required<Pick<CustomModel, 'input' | 'contextWindow' | 'maxTokens' | 'cost'>> {\n\treturn {\n\t\tinput: ['text'],\n\t\tcontextWindow: 128000,\n\t\tmaxTokens: 16384,\n\t\tcost: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t},\n\t};\n}\n\n// ============================================\n// File Operations\n// ============================================\n\n/**\n * Load models.json from disk\n */\nexport function loadModelsJson(path: string = resolveModelsJsonPath()): { config: ModelsJsonConfig; error?: string } {\n\tif (!existsSync(path)) {\n\t\treturn { config: { providers: {} } };\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(path, 'utf-8');\n\t\tconst config = JSON.parse(content);\n\t\tconst validation = validateModelsConfig(config);\n\t\t\n\t\tif (!validation.valid) {\n\t\t\tconst errors = validation.errors.map((e) => `${e.path}: ${e.message}`).join('; ');\n\t\t\treturn { config, error: `Validation failed: ${errors}` };\n\t\t}\n\t\t\n\t\treturn { config };\n\t} catch (error) {\n\t\treturn {\n\t\t\tconfig: { providers: {} },\n\t\t\terror: error instanceof Error ? error.message : 'Failed to load models.json',\n\t\t};\n\t}\n}\n\n/**\n * Save models.json to disk\n */\nexport function saveModelsJson(path: string, config: ModelsJsonConfig): { success: boolean; error?: string } {\n\ttry {\n\t\t// Validate before saving\n\t\tconst validation = validateModelsConfig(config);\n\t\tif (!validation.valid) {\n\t\t\tconst errors = validation.errors.map((e) => `${e.path}: ${e.message}`).join('; ');\n\t\t\treturn { success: false, error: `Validation failed: ${errors}` };\n\t\t}\n\n\t\twriteTextAtomicSync(path, JSON.stringify(config, null, 2));\n\t\treturn { success: true };\n\t} catch (error) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: error instanceof Error ? error.message : 'Failed to save models.json',\n\t\t};\n\t}\n}\n\n/**\n * Check if models.json exists\n */\nexport function modelsJsonExists(path: string = resolveModelsJsonPath()): boolean {\n\treturn existsSync(path);\n}"],"mappings":"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"}