@xopcai/xopc 0.0.89 → 0.0.91

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 (267) hide show
  1. package/README.md +36 -12
  2. package/README.zh-CN.md +36 -12
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +41 -0
  6. package/dist/gateway/static/root/assets/agents-bVWUlrlD.js +222 -0
  7. package/dist/gateway/static/root/assets/apps-page-CIC8bmvZ.js +1 -0
  8. package/dist/gateway/static/root/assets/{attachment-preview-renderer-CpyoFbs4.js → attachment-preview-renderer-DBAxQXb-.js} +2 -2
  9. package/dist/gateway/static/root/assets/{attachment-process-heavy-CqVriadb.js → attachment-process-heavy-Csq3TrrP.js} +4 -4
  10. package/dist/gateway/static/root/assets/channels-settings-C8G8RAAP.js +1 -0
  11. package/dist/gateway/static/root/assets/{channels-status-swr-DaHGkRF1.js → channels-status-swr-CYWL5DLD.js} +1 -1
  12. package/dist/gateway/static/root/assets/circle-check-C23XjkUj.js +1 -0
  13. package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +1 -0
  14. package/dist/gateway/static/root/assets/cron-api-TVqLlGAC.js +1 -0
  15. package/dist/gateway/static/root/assets/cron-dreaming-jobs-Ip703-qM.js +2 -0
  16. package/dist/gateway/static/root/assets/cron-page-BtcFYlvv.js +1 -0
  17. package/dist/gateway/static/root/assets/dist-CUV1uY5f.js +1 -0
  18. package/dist/gateway/static/root/assets/{extension-debug-page-CtuKJ9tE.js → extension-debug-page-mTLHRDp1.js} +1 -1
  19. package/dist/gateway/static/root/assets/{extension-page-ykzjOkR5.js → extension-page-iI8BI7WK.js} +1 -1
  20. package/dist/gateway/static/root/assets/{extension-settings-page-Ce2qrdpO.js → extension-settings-page-ByXcdubM.js} +1 -1
  21. package/dist/gateway/static/root/assets/{fetch-C9FFJjuH.js → fetch-BWtQq_Ys.js} +1 -1
  22. package/dist/gateway/static/root/assets/{field-primitives-BFcrNeTU.js → field-primitives-BsZ-4VT5.js} +1 -1
  23. package/dist/gateway/static/root/assets/{heartbeat-config-api-CEg4Vr9R.js → heartbeat-config-api-WjTsRLCU.js} +1 -1
  24. package/dist/gateway/static/root/assets/{index-CZfy9oxs.js → index-CKkR-v9U.js} +101 -97
  25. package/dist/gateway/static/root/assets/index-VlELBY99.css +1 -0
  26. package/dist/gateway/static/root/assets/logs-page-ClnIpxfd.js +1 -0
  27. package/dist/gateway/static/root/assets/note-detail-page-B91pLkEI.css +1 -0
  28. package/dist/gateway/static/root/assets/note-detail-page-DJ2Mb4x7.js +179 -0
  29. package/dist/gateway/static/root/assets/note-time-JLBPSLzK.js +1 -0
  30. package/dist/gateway/static/root/assets/notes-page-BE-75qz9.js +1 -0
  31. package/dist/gateway/static/root/assets/{pdf-BnEvgIXZ.js → pdf-epILhEOn.js} +1 -1
  32. package/dist/gateway/static/root/assets/preload-helper-zJ_50EbN.js +1 -0
  33. package/dist/gateway/static/root/assets/sessions-page-bJJkWtTl.js +1 -0
  34. package/dist/gateway/static/root/assets/{settings-form-section-BqdzA28u.js → settings-form-section-DSYCknxM.js} +1 -1
  35. package/dist/gateway/static/root/assets/settings-page-WcMXLq2U.js +3 -0
  36. package/dist/gateway/static/root/assets/share-preview-page-awRqs4hV.js +2 -0
  37. package/dist/gateway/static/root/assets/skills-page-Lu-i1JG7.js +2 -0
  38. package/dist/gateway/static/root/assets/{theme-store-CNqbmTNV.js → theme-store-BC-42BoZ.js} +1 -1
  39. package/dist/gateway/static/root/assets/toast-z0toXu32.js +1 -0
  40. package/dist/gateway/static/root/assets/url-CY1RQKTU.js +3 -0
  41. package/dist/gateway/static/root/assets/{utils-BWm2tG2w.js → utils-DX3TQuap.js} +1 -1
  42. package/dist/gateway/static/root/assets/vendor-codemirror-DYoKfS8f.js +45 -0
  43. package/dist/gateway/static/root/assets/voice-api-key-field-B5uKlDqA.js +1 -0
  44. package/dist/gateway/static/root/assets/workflow-page.utils-ClC37yEp.js +1 -0
  45. package/dist/gateway/static/root/assets/workflows-page-C7VhIXtR.js +27 -0
  46. package/dist/gateway/static/root/index.html +11 -7
  47. package/dist/package.js +1 -1
  48. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js +20 -18
  49. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js.map +1 -1
  50. package/dist/src/agent/tools/cronjob-tool.d.ts +6 -0
  51. package/dist/src/agent/tools/cronjob-tool.js +74 -9
  52. package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
  53. package/dist/src/agent/tools/edit.d.ts +5 -1
  54. package/dist/src/agent/tools/edit.js +7 -5
  55. package/dist/src/agent/tools/edit.js.map +1 -1
  56. package/dist/src/agent/tools/factory.js +2 -2
  57. package/dist/src/agent/tools/factory.js.map +1 -1
  58. package/dist/src/agent/tools/write.d.ts +5 -1
  59. package/dist/src/agent/tools/write.js +7 -5
  60. package/dist/src/agent/tools/write.js.map +1 -1
  61. package/dist/src/agent/workflow/agent-progress.js +2 -0
  62. package/dist/src/agent/workflow/agent-progress.js.map +1 -1
  63. package/dist/src/agent/workflow/builtins/client-proposal.d.ts +12 -0
  64. package/dist/src/agent/workflow/builtins/client-proposal.js +155 -0
  65. package/dist/src/agent/workflow/builtins/client-proposal.js.map +1 -0
  66. package/dist/src/agent/workflow/builtins/competitor-scan.d.ts +12 -0
  67. package/dist/src/agent/workflow/builtins/competitor-scan.js +150 -0
  68. package/dist/src/agent/workflow/builtins/competitor-scan.js.map +1 -0
  69. package/dist/src/agent/workflow/builtins/content-draft.d.ts +13 -0
  70. package/dist/src/agent/workflow/builtins/content-draft.js +146 -0
  71. package/dist/src/agent/workflow/builtins/content-draft.js.map +1 -0
  72. package/dist/src/agent/workflow/builtins/content-repurpose.d.ts +11 -0
  73. package/dist/src/agent/workflow/builtins/content-repurpose.js +137 -0
  74. package/dist/src/agent/workflow/builtins/content-repurpose.js.map +1 -0
  75. package/dist/src/agent/workflow/builtins/decision-compare.d.ts +13 -0
  76. package/dist/src/agent/workflow/builtins/decision-compare.js +173 -0
  77. package/dist/src/agent/workflow/builtins/decision-compare.js.map +1 -0
  78. package/dist/src/agent/workflow/builtins/inbox-triage.d.ts +11 -0
  79. package/dist/src/agent/workflow/builtins/inbox-triage.js +148 -0
  80. package/dist/src/agent/workflow/builtins/inbox-triage.js.map +1 -0
  81. package/dist/src/agent/workflow/builtins/index.d.ts +10 -1
  82. package/dist/src/agent/workflow/builtins/index.js +46 -1
  83. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  84. package/dist/src/agent/workflow/builtins/meeting-prep.d.ts +12 -0
  85. package/dist/src/agent/workflow/builtins/meeting-prep.js +144 -0
  86. package/dist/src/agent/workflow/builtins/meeting-prep.js.map +1 -0
  87. package/dist/src/agent/workflow/builtins/offer-design.d.ts +12 -0
  88. package/dist/src/agent/workflow/builtins/offer-design.js +161 -0
  89. package/dist/src/agent/workflow/builtins/offer-design.js.map +1 -0
  90. package/dist/src/agent/workflow/builtins/weekly-review.d.ts +12 -0
  91. package/dist/src/agent/workflow/builtins/weekly-review.js +131 -0
  92. package/dist/src/agent/workflow/builtins/weekly-review.js.map +1 -0
  93. package/dist/src/agent/workflow/step-labels.js +2 -2
  94. package/dist/src/agent/workflow/step-labels.js.map +1 -1
  95. package/dist/src/agent/workflow/subagent-runner.js +3 -1
  96. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  97. package/dist/src/agent/workflow/types.d.ts +4 -0
  98. package/dist/src/chat-commands/agent-edit.d.ts +4 -0
  99. package/dist/src/chat-commands/agent-edit.js +136 -0
  100. package/dist/src/chat-commands/agent-edit.js.map +1 -0
  101. package/dist/src/chat-commands/index.d.ts +1 -0
  102. package/dist/src/chat-commands/index.js +3 -1
  103. package/dist/src/chat-commands/index.js.map +1 -1
  104. package/dist/src/cli/bin.js +2 -0
  105. package/dist/src/cli/bin.js.map +1 -1
  106. package/dist/src/cli/commands/cron.js +42 -3
  107. package/dist/src/cli/commands/cron.js.map +1 -1
  108. package/dist/src/cli/commands/doctor/checks/session-integrity.js +79 -56
  109. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
  110. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  111. package/dist/src/cli/commands/update.js +86 -79
  112. package/dist/src/cli/commands/update.js.map +1 -1
  113. package/dist/src/commands/agents.config.d.ts +3 -2
  114. package/dist/src/commands/agents.config.js +5 -2
  115. package/dist/src/commands/agents.config.js.map +1 -1
  116. package/dist/src/config/agent-typed-models.d.ts +2 -7
  117. package/dist/src/config/agent-typed-models.js +3 -14
  118. package/dist/src/config/agent-typed-models.js.map +1 -1
  119. package/dist/src/config/localized-text.d.ts +6 -0
  120. package/dist/src/config/localized-text.js +42 -0
  121. package/dist/src/config/localized-text.js.map +1 -0
  122. package/dist/src/config/models-json.d.ts +6 -6
  123. package/dist/src/config/schema.d.ts +6 -21
  124. package/dist/src/config/schema.js +4 -4
  125. package/dist/src/config/schema.js.map +1 -1
  126. package/dist/src/cron/executor.d.ts +2 -0
  127. package/dist/src/cron/executor.js +111 -1
  128. package/dist/src/cron/executor.js.map +1 -1
  129. package/dist/src/cron/types.d.ts +8 -1
  130. package/dist/src/cron/validation.d.ts +4 -0
  131. package/dist/src/cron/validation.js +4 -3
  132. package/dist/src/cron/validation.js.map +1 -1
  133. package/dist/src/cron/workflow-run-completion.d.ts +23 -0
  134. package/dist/src/cron/workflow-run-completion.js +72 -0
  135. package/dist/src/cron/workflow-run-completion.js.map +1 -0
  136. package/dist/src/extensions/update.d.ts +51 -0
  137. package/dist/src/extensions/update.js +260 -0
  138. package/dist/src/extensions/update.js.map +1 -0
  139. package/dist/src/gateway/agents-admin.d.ts +15 -8
  140. package/dist/src/gateway/agents-admin.js +77 -28
  141. package/dist/src/gateway/agents-admin.js.map +1 -1
  142. package/dist/src/gateway/heartbeat/service.js +1 -1
  143. package/dist/src/gateway/hono/lib/config-payload.d.ts +6 -0
  144. package/dist/src/gateway/hono/lib/config-payload.js +3 -1
  145. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  146. package/dist/src/gateway/hono/middleware/auth.d.ts +2 -0
  147. package/dist/src/gateway/hono/middleware/auth.js +11 -7
  148. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  149. package/dist/src/gateway/hono/routes/agents.js +55 -12
  150. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  151. package/dist/src/gateway/hono/routes/config-patch/agents.js +1 -1
  152. package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +2 -2
  153. package/dist/src/gateway/hono/routes/config-patch/gateway.js +12 -0
  154. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  155. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  156. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  157. package/dist/src/gateway/hono/routes/notes.d.ts +3 -0
  158. package/dist/src/gateway/hono/routes/notes.js +274 -0
  159. package/dist/src/gateway/hono/routes/notes.js.map +1 -0
  160. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  161. package/dist/src/gateway/hono/routes/update.js +55 -107
  162. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  163. package/dist/src/gateway/hono/routes/workflows.js +3 -1
  164. package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
  165. package/dist/src/gateway/server.js +2 -0
  166. package/dist/src/gateway/server.js.map +1 -1
  167. package/dist/src/gateway/service.d.ts +3 -0
  168. package/dist/src/gateway/service.js +12 -1
  169. package/dist/src/gateway/service.js.map +1 -1
  170. package/dist/src/gateway/workspace-ripgrep.d.ts +6 -0
  171. package/dist/src/gateway/workspace-ripgrep.js +62 -11
  172. package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
  173. package/dist/src/heartbeat/index.js +1 -1
  174. package/dist/src/infra/brew.d.ts +4 -0
  175. package/dist/src/infra/brew.js +20 -0
  176. package/dist/src/infra/brew.js.map +1 -0
  177. package/dist/src/infra/package-json.d.ts +2 -0
  178. package/dist/src/infra/package-json.js +23 -0
  179. package/dist/src/infra/package-json.js.map +1 -0
  180. package/dist/src/infra/package-update-steps.d.ts +35 -0
  181. package/dist/src/infra/package-update-steps.js +304 -0
  182. package/dist/src/infra/package-update-steps.js.map +1 -0
  183. package/dist/src/infra/path-env.d.ts +11 -0
  184. package/dist/src/infra/path-env.js +90 -0
  185. package/dist/src/infra/path-env.js.map +1 -0
  186. package/dist/src/infra/path-prepend.d.ts +7 -0
  187. package/dist/src/infra/path-prepend.js +44 -0
  188. package/dist/src/infra/path-prepend.js.map +1 -0
  189. package/dist/src/infra/stable-node-path.d.ts +2 -0
  190. package/dist/src/infra/stable-node-path.js +28 -0
  191. package/dist/src/infra/stable-node-path.js.map +1 -0
  192. package/dist/src/infra/update-global.d.ts +30 -23
  193. package/dist/src/infra/update-global.js +113 -64
  194. package/dist/src/infra/update-global.js.map +1 -1
  195. package/dist/src/infra/update-log.d.ts +1 -0
  196. package/dist/src/infra/update-log.js +12 -0
  197. package/dist/src/infra/update-log.js.map +1 -0
  198. package/dist/src/infra/update-restart.d.ts +20 -0
  199. package/dist/src/infra/update-restart.js +165 -0
  200. package/dist/src/infra/update-restart.js.map +1 -0
  201. package/dist/src/infra/update-runner.d.ts +89 -1
  202. package/dist/src/infra/update-runner.js +604 -173
  203. package/dist/src/infra/update-runner.js.map +1 -1
  204. package/dist/src/infra/update-startup.d.ts +3 -0
  205. package/dist/src/infra/update-startup.js +8 -4
  206. package/dist/src/infra/update-startup.js.map +1 -1
  207. package/dist/src/notes/attachment-ref.d.ts +9 -0
  208. package/dist/src/notes/attachment-ref.js +27 -0
  209. package/dist/src/notes/attachment-ref.js.map +1 -0
  210. package/dist/src/notes/index.d.ts +4 -0
  211. package/dist/src/notes/index.js +4 -0
  212. package/dist/src/notes/note-attachment-sync.d.ts +7 -0
  213. package/dist/src/notes/note-attachment-sync.js +46 -0
  214. package/dist/src/notes/note-attachment-sync.js.map +1 -0
  215. package/dist/src/notes/note-index-meta.d.ts +14 -0
  216. package/dist/src/notes/note-index-meta.js +87 -0
  217. package/dist/src/notes/note-index-meta.js.map +1 -0
  218. package/dist/src/notes/paths.d.ts +5 -0
  219. package/dist/src/notes/paths.js +23 -0
  220. package/dist/src/notes/paths.js.map +1 -0
  221. package/dist/src/notes/service.d.ts +42 -0
  222. package/dist/src/notes/service.js +331 -0
  223. package/dist/src/notes/service.js.map +1 -0
  224. package/dist/src/notes/store.d.ts +33 -0
  225. package/dist/src/notes/store.js +317 -0
  226. package/dist/src/notes/store.js.map +1 -0
  227. package/dist/src/notes/types.d.ts +162 -0
  228. package/dist/src/notes/types.js +1 -0
  229. package/dist/src/routing/resolve-route.d.ts +3 -1
  230. package/dist/src/routing/resolve-route.js.map +1 -1
  231. package/dist/src/session/store.d.ts +5 -3
  232. package/dist/src/session/store.js +66 -20
  233. package/dist/src/session/store.js.map +1 -1
  234. package/dist/src/utils/logger/stats.d.ts +1 -1
  235. package/dist/src/workflows/domain/event.d.ts +3 -0
  236. package/dist/src/workflows/domain/run.d.ts +3 -0
  237. package/dist/src/workflows/domain/run.js.map +1 -1
  238. package/dist/src/workflows/engine/projector.js +17 -0
  239. package/dist/src/workflows/engine/projector.js.map +1 -1
  240. package/dist/src/workflows/engine/workflow-engine.js +127 -0
  241. package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
  242. package/dist/src/workflows/index.js +1 -1
  243. package/dist/src/workflows/service/run-view-to-snapshot.js +3 -1
  244. package/dist/src/workflows/service/run-view-to-snapshot.js.map +1 -1
  245. package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
  246. package/dist/src/workflows/service/workflow-run-service.js +4 -1
  247. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  248. package/dist/src/workflows/service/workflow-session-bridge.js +1 -1
  249. package/package.json +1 -1
  250. package/dist/gateway/static/root/assets/agents-B6PJB07W.js +0 -222
  251. package/dist/gateway/static/root/assets/apps-page-BOr0B1wv.js +0 -1
  252. package/dist/gateway/static/root/assets/channels-settings-BelUKggl.js +0 -1
  253. package/dist/gateway/static/root/assets/cron-api-CjOg-BIj.js +0 -1
  254. package/dist/gateway/static/root/assets/cron-dreaming-jobs-DueM3rBz.js +0 -2
  255. package/dist/gateway/static/root/assets/cron-page-DhoZmZXb.js +0 -1
  256. package/dist/gateway/static/root/assets/dist-6LecgDx5.js +0 -1
  257. package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +0 -45
  258. package/dist/gateway/static/root/assets/index-CiN1cQiQ.css +0 -1
  259. package/dist/gateway/static/root/assets/logs-page-BwWLfqvd.js +0 -1
  260. package/dist/gateway/static/root/assets/sessions-page-DV5WN8uk.js +0 -1
  261. package/dist/gateway/static/root/assets/settings-page-CfOBRbPX.js +0 -3
  262. package/dist/gateway/static/root/assets/share-preview-page-Di5Bzh4g.js +0 -2
  263. package/dist/gateway/static/root/assets/skills-page-D0H5Kaxg.js +0 -2
  264. package/dist/gateway/static/root/assets/url-aYn-Rj1C.js +0 -7
  265. package/dist/gateway/static/root/assets/vendor-codemirror-D0yxdRpg.js +0 -58
  266. package/dist/gateway/static/root/assets/voice-api-key-field-X2UfnHeq.js +0 -1
  267. package/dist/gateway/static/root/assets/workflows-page-BOPpO3NG.js +0 -27
@@ -0,0 +1,260 @@
1
+ import { createLogger } from "../utils/logger/index.js";
2
+ import { init_logger } from "../utils/logger.js";
3
+ import { init_paths, resolveBundledExtensionsDir, resolveExtensionsDir } from "../config/paths.js";
4
+ import { init_loader, loadConfig } from "../config/loader.js";
5
+ import { getExtensionLockfileManager } from "./lockfile.js";
6
+ import { installExtensionFromStoreZip, installFromNpm } from "./install.js";
7
+ import { findExtension } from "./marketplace.js";
8
+ import { downloadExtensionStoreZipBuffer, resolveExtensionZipDownloadUrl, resolveExtensionsStoreBaseUrl } from "../agent/skills/marketplace/adapters/store/store-api-client.js";
9
+ import { join } from "node:path";
10
+ import { existsSync, readFileSync, readdirSync, rmSync } from "node:fs";
11
+ import semver from "semver";
12
+ //#region src/extensions/update.ts
13
+ /**
14
+ * Post-update extension sync — refresh lockfile-managed npm / store extensions.
15
+ */
16
+ init_paths();
17
+ init_loader();
18
+ init_logger();
19
+ const log = createLogger("ExtensionUpdate");
20
+ const MANIFEST = "xopc.extension.json";
21
+ function readInstalledExtensionVersion(targetDir, extensionId) {
22
+ const manifestPath = join(targetDir, extensionId, MANIFEST);
23
+ if (!existsSync(manifestPath)) return;
24
+ try {
25
+ const raw = JSON.parse(readFileSync(manifestPath, "utf-8"));
26
+ return (typeof raw.version === "string" ? semver.valid(raw.version) : null) ?? void 0;
27
+ } catch {
28
+ return;
29
+ }
30
+ }
31
+ async function resolveNpmPackageForId(extensionId) {
32
+ return (await findExtension(extensionId))?.npmPackage;
33
+ }
34
+ async function upsertNpmExtensionLock(lock, targetDir, result, spec) {
35
+ if (!result.extensionId) return;
36
+ const reg = await findExtension(result.extensionId);
37
+ const resolved = reg?.npmPackage ?? spec;
38
+ let ver = reg?.version ?? "0.0.0";
39
+ try {
40
+ const raw = readFileSync(join(targetDir, result.extensionId, MANIFEST), "utf-8");
41
+ const m = JSON.parse(raw);
42
+ const mv = typeof m.version === "string" ? semver.valid(m.version) : null;
43
+ if (mv) ver = mv;
44
+ } catch {}
45
+ await lock.upsert(result.extensionId, {
46
+ name: result.extensionId,
47
+ version: ver,
48
+ resolved,
49
+ source: "npm"
50
+ });
51
+ }
52
+ async function installExtensionFromStoreWithLock(params) {
53
+ try {
54
+ const { downloadUrl, version } = await resolveExtensionZipDownloadUrl(params.storeBase, params.packageName, params.version);
55
+ const result = await installExtensionFromStoreZip(await downloadExtensionStoreZipBuffer(params.storeBase, downloadUrl), params.targetDir);
56
+ if (!result.ok || !result.extensionId) return {
57
+ ok: false,
58
+ error: result.error ?? "install failed"
59
+ };
60
+ await params.lock.upsert(result.extensionId, {
61
+ name: result.extensionId,
62
+ version,
63
+ resolved: params.packageName,
64
+ source: "store"
65
+ });
66
+ return {
67
+ ok: true,
68
+ extensionId: result.extensionId,
69
+ version
70
+ };
71
+ } catch (err) {
72
+ return {
73
+ ok: false,
74
+ error: err instanceof Error ? err.message : String(err)
75
+ };
76
+ }
77
+ }
78
+ function listBundledExtensionIds() {
79
+ const ids = /* @__PURE__ */ new Set();
80
+ const bundledDir = resolveBundledExtensionsDir();
81
+ if (!bundledDir || !existsSync(bundledDir)) return ids;
82
+ for (const entry of readdirSync(bundledDir, { withFileTypes: true })) if (entry.isDirectory()) ids.add(entry.name);
83
+ return ids;
84
+ }
85
+ async function syncExtensionsForUpdateChannel(params) {
86
+ const logger = params.logger ?? {};
87
+ const summary = {
88
+ skippedBundled: [],
89
+ warnings: []
90
+ };
91
+ const skipIds = /* @__PURE__ */ new Set();
92
+ if (params.channel !== "dev") return {
93
+ skipIds,
94
+ summary
95
+ };
96
+ const data = await getExtensionLockfileManager().load();
97
+ const bundledIds = listBundledExtensionIds();
98
+ for (const extensionId of Object.keys(data.extensions)) {
99
+ if (!bundledIds.has(extensionId)) continue;
100
+ skipIds.add(extensionId);
101
+ summary.skippedBundled.push(extensionId);
102
+ logger.info?.(`Skipping "${extensionId}" on dev channel (bundled copy preferred).`);
103
+ }
104
+ return {
105
+ skipIds,
106
+ summary
107
+ };
108
+ }
109
+ async function updateSingleExtension(params) {
110
+ const { extensionId, entry, targetDir, storeBase, lock, timeoutMs, logger } = params;
111
+ const currentVersion = readInstalledExtensionVersion(targetDir, extensionId);
112
+ if (entry.source === "store") {
113
+ const pkgName = entry.resolved?.trim() || extensionId;
114
+ if (existsSync(join(targetDir, extensionId))) rmSync(join(targetDir, extensionId), {
115
+ recursive: true,
116
+ force: true
117
+ });
118
+ const result = await installExtensionFromStoreWithLock({
119
+ storeBase,
120
+ packageName: pkgName,
121
+ targetDir,
122
+ lock
123
+ });
124
+ if (result.ok === false) {
125
+ logger.error?.(`Failed to update ${extensionId}: ${result.error}`);
126
+ return {
127
+ extensionId,
128
+ status: "error",
129
+ message: result.error,
130
+ currentVersion
131
+ };
132
+ }
133
+ const nextVersion = result.version;
134
+ const status = currentVersion && nextVersion && currentVersion === nextVersion ? "unchanged" : "updated";
135
+ return {
136
+ extensionId,
137
+ status,
138
+ currentVersion,
139
+ nextVersion,
140
+ message: status === "unchanged" ? `${extensionId} is up to date (${currentVersion}).` : `Updated ${extensionId}: ${currentVersion ?? "unknown"} -> ${nextVersion}.`
141
+ };
142
+ }
143
+ if (entry.source !== "npm") return {
144
+ extensionId,
145
+ status: "skipped",
146
+ message: `Skipping "${extensionId}" (source: ${entry.source}).`
147
+ };
148
+ const spec = entry.resolved?.trim() || await resolveNpmPackageForId(extensionId);
149
+ if (!spec) return {
150
+ extensionId,
151
+ status: "skipped",
152
+ message: `Skipping "${extensionId}" (could not resolve npm package).`
153
+ };
154
+ if (existsSync(join(targetDir, extensionId))) rmSync(join(targetDir, extensionId), {
155
+ recursive: true,
156
+ force: true
157
+ });
158
+ const installResult = await installFromNpm(spec, targetDir, timeoutMs);
159
+ if (!installResult.ok) {
160
+ const message = installResult.error ?? "npm install failed";
161
+ logger.error?.(`Failed to update ${extensionId}: ${message}`);
162
+ return {
163
+ extensionId,
164
+ status: "error",
165
+ message,
166
+ currentVersion
167
+ };
168
+ }
169
+ await upsertNpmExtensionLock(lock, targetDir, installResult, spec);
170
+ const nextVersion = (installResult.extensionId ? readInstalledExtensionVersion(targetDir, installResult.extensionId) : void 0) ?? entry.version;
171
+ const resolvedId = installResult.extensionId ?? extensionId;
172
+ const status = currentVersion && nextVersion && currentVersion === nextVersion ? "unchanged" : "updated";
173
+ return {
174
+ extensionId: resolvedId,
175
+ status,
176
+ currentVersion,
177
+ nextVersion,
178
+ message: status === "unchanged" ? `${resolvedId} is up to date (${currentVersion}).` : `Updated ${resolvedId}: ${currentVersion ?? "unknown"} -> ${nextVersion}.`
179
+ };
180
+ }
181
+ async function updateNpmInstalledExtensions(params) {
182
+ const logger = params.logger ?? {};
183
+ const config = params.config ?? loadConfig();
184
+ const targetDir = resolveExtensionsDir();
185
+ const storeBase = resolveExtensionsStoreBaseUrl(config);
186
+ const lock = getExtensionLockfileManager();
187
+ const data = await lock.load();
188
+ const ids = params.extensionIds?.length ? params.extensionIds : Object.keys(data.extensions);
189
+ if (ids.length === 0) return {
190
+ outcomes: [],
191
+ status: "skipped"
192
+ };
193
+ const outcomes = [];
194
+ let hasError = false;
195
+ for (const extensionId of ids) {
196
+ if (params.skipIds?.has(extensionId)) {
197
+ outcomes.push({
198
+ extensionId,
199
+ status: "skipped",
200
+ message: `Skipping "${extensionId}" (channel sync).`
201
+ });
202
+ continue;
203
+ }
204
+ const entry = data.extensions[extensionId];
205
+ if (!entry) {
206
+ outcomes.push({
207
+ extensionId,
208
+ status: "skipped",
209
+ message: `No lockfile entry for "${extensionId}".`
210
+ });
211
+ continue;
212
+ }
213
+ const outcome = await updateSingleExtension({
214
+ extensionId,
215
+ entry,
216
+ targetDir,
217
+ storeBase,
218
+ lock,
219
+ timeoutMs: params.timeoutMs,
220
+ logger
221
+ });
222
+ outcomes.push(outcome);
223
+ if (outcome.status === "error") hasError = true;
224
+ else if (outcome.status === "updated") log.info({
225
+ extensionId: outcome.extensionId,
226
+ nextVersion: outcome.nextVersion
227
+ }, outcome.message);
228
+ }
229
+ return {
230
+ outcomes,
231
+ status: hasError ? "error" : "ok"
232
+ };
233
+ }
234
+ async function runPostUpdateExtensionSync(params) {
235
+ const logger = params.logger ?? {
236
+ info: (message) => log.info(message),
237
+ warn: (message) => log.warn(message),
238
+ error: (message) => log.error(message)
239
+ };
240
+ const channelSyncResult = await syncExtensionsForUpdateChannel({
241
+ channel: params.channel,
242
+ config: params.config,
243
+ logger
244
+ });
245
+ const updateResult = await updateNpmInstalledExtensions({
246
+ skipIds: channelSyncResult.skipIds,
247
+ config: params.config,
248
+ timeoutMs: params.timeoutMs,
249
+ logger
250
+ });
251
+ return {
252
+ status: updateResult.status,
253
+ outcomes: updateResult.outcomes,
254
+ channelSync: channelSyncResult.summary
255
+ };
256
+ }
257
+ //#endregion
258
+ export { runPostUpdateExtensionSync, syncExtensionsForUpdateChannel, updateNpmInstalledExtensions };
259
+
260
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","names":["marketplace.findExtension"],"sources":["../../../src/extensions/update.ts"],"sourcesContent":["/**\n * Post-update extension sync — refresh lockfile-managed npm / store extensions.\n */\n\nimport { existsSync, readFileSync, readdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport semver from 'semver';\n\nimport {\n downloadExtensionStoreZipBuffer,\n resolveExtensionZipDownloadUrl,\n resolveExtensionsStoreBaseUrl,\n} from '../agent/skills/marketplace/adapters/store/store-api-client.js';\nimport type { Config } from '../config/schema.js';\nimport { resolveBundledExtensionsDir, resolveExtensionsDir } from '../config/paths.js';\nimport { loadConfig } from '../config/loader.js';\nimport type { UpdateChannel } from '../infra/update-channels.js';\nimport { createLogger } from '../utils/logger.js';\nimport {\n installExtensionFromStoreZip,\n installFromNpm,\n type InstallResult,\n} from './install.js';\nimport {\n getExtensionLockfileManager,\n type ExtensionLockEntry,\n} from './lockfile.js';\nimport * as marketplace from './marketplace.js';\n\nconst log = createLogger('ExtensionUpdate');\n\nconst MANIFEST = 'xopc.extension.json';\n\nexport type ExtensionUpdateLogger = {\n info?: (message: string) => void;\n warn?: (message: string) => void;\n error?: (message: string) => void;\n};\n\nexport type ExtensionUpdateStatus = 'updated' | 'unchanged' | 'skipped' | 'error';\n\nexport type ExtensionUpdateOutcome = {\n extensionId: string;\n status: ExtensionUpdateStatus;\n message: string;\n currentVersion?: string;\n nextVersion?: string;\n};\n\nexport type ExtensionChannelSyncSummary = {\n skippedBundled: string[];\n warnings: string[];\n};\n\nexport type ExtensionPostUpdateResult = {\n status: 'ok' | 'error' | 'skipped';\n outcomes: ExtensionUpdateOutcome[];\n channelSync?: ExtensionChannelSyncSummary;\n};\n\nfunction readInstalledExtensionVersion(targetDir: string, extensionId: string): string | undefined {\n const manifestPath = join(targetDir, extensionId, MANIFEST);\n if (!existsSync(manifestPath)) {\n return undefined;\n }\n try {\n const raw = JSON.parse(readFileSync(manifestPath, 'utf-8')) as { version?: string };\n const version = typeof raw.version === 'string' ? semver.valid(raw.version) : null;\n return version ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nasync function resolveNpmPackageForId(extensionId: string): Promise<string | undefined> {\n const found = await marketplace.findExtension(extensionId);\n return found?.npmPackage;\n}\n\nasync function upsertNpmExtensionLock(\n lock: ReturnType<typeof getExtensionLockfileManager>,\n targetDir: string,\n result: InstallResult,\n spec: string,\n): Promise<void> {\n if (!result.extensionId) return;\n const reg = await marketplace.findExtension(result.extensionId);\n const resolved = reg?.npmPackage ?? spec;\n let ver = reg?.version ?? '0.0.0';\n try {\n const raw = readFileSync(join(targetDir, result.extensionId, MANIFEST), 'utf-8');\n const m = JSON.parse(raw) as { version?: string };\n const mv = typeof m.version === 'string' ? semver.valid(m.version) : null;\n if (mv) ver = mv;\n } catch {\n // keep registry / fallback version\n }\n await lock.upsert(result.extensionId, {\n name: result.extensionId,\n version: ver,\n resolved,\n source: 'npm',\n });\n}\n\nasync function installExtensionFromStoreWithLock(params: {\n storeBase: string;\n packageName: string;\n version?: string;\n targetDir: string;\n lock: ReturnType<typeof getExtensionLockfileManager>;\n}): Promise<{ ok: true; extensionId: string; version: string } | { ok: false; error: string }> {\n try {\n const { downloadUrl, version } = await resolveExtensionZipDownloadUrl(\n params.storeBase,\n params.packageName,\n params.version,\n );\n const buf = await downloadExtensionStoreZipBuffer(params.storeBase, downloadUrl);\n const result = await installExtensionFromStoreZip(buf, params.targetDir);\n if (!result.ok || !result.extensionId) {\n return { ok: false, error: result.error ?? 'install failed' };\n }\n await params.lock.upsert(result.extensionId, {\n name: result.extensionId,\n version,\n resolved: params.packageName,\n source: 'store',\n });\n return { ok: true, extensionId: result.extensionId, version };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n}\n\nfunction listBundledExtensionIds(): Set<string> {\n const ids = new Set<string>();\n const bundledDir = resolveBundledExtensionsDir();\n if (!bundledDir || !existsSync(bundledDir)) {\n return ids;\n }\n for (const entry of readdirSync(bundledDir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n ids.add(entry.name);\n }\n }\n return ids;\n}\n\nexport async function syncExtensionsForUpdateChannel(params: {\n channel: UpdateChannel;\n config?: Config;\n logger?: ExtensionUpdateLogger;\n}): Promise<{ skipIds: Set<string>; summary: ExtensionChannelSyncSummary }> {\n const logger = params.logger ?? {};\n const summary: ExtensionChannelSyncSummary = {\n skippedBundled: [],\n warnings: [],\n };\n const skipIds = new Set<string>();\n\n if (params.channel !== 'dev') {\n return { skipIds, summary };\n }\n\n const lock = getExtensionLockfileManager();\n const data = await lock.load();\n const bundledIds = listBundledExtensionIds();\n\n for (const extensionId of Object.keys(data.extensions)) {\n if (!bundledIds.has(extensionId)) {\n continue;\n }\n skipIds.add(extensionId);\n summary.skippedBundled.push(extensionId);\n logger.info?.(`Skipping \"${extensionId}\" on dev channel (bundled copy preferred).`);\n }\n\n return { skipIds, summary };\n}\n\nasync function updateSingleExtension(params: {\n extensionId: string;\n entry: ExtensionLockEntry;\n targetDir: string;\n storeBase: string;\n lock: ReturnType<typeof getExtensionLockfileManager>;\n timeoutMs?: number;\n logger?: ExtensionUpdateLogger;\n}): Promise<ExtensionUpdateOutcome> {\n const { extensionId, entry, targetDir, storeBase, lock, timeoutMs, logger } = params;\n const currentVersion = readInstalledExtensionVersion(targetDir, extensionId);\n\n if (entry.source === 'store') {\n const pkgName = entry.resolved?.trim() || extensionId;\n if (existsSync(join(targetDir, extensionId))) {\n rmSync(join(targetDir, extensionId), { recursive: true, force: true });\n }\n const result = await installExtensionFromStoreWithLock({\n storeBase,\n packageName: pkgName,\n targetDir,\n lock,\n });\n if (result.ok === false) {\n logger.error?.(`Failed to update ${extensionId}: ${result.error}`);\n return {\n extensionId,\n status: 'error',\n message: result.error,\n currentVersion,\n };\n }\n const nextVersion = result.version;\n const status =\n currentVersion && nextVersion && currentVersion === nextVersion ? 'unchanged' : 'updated';\n return {\n extensionId,\n status,\n currentVersion,\n nextVersion,\n message:\n status === 'unchanged'\n ? `${extensionId} is up to date (${currentVersion}).`\n : `Updated ${extensionId}: ${currentVersion ?? 'unknown'} -> ${nextVersion}.`,\n };\n }\n\n if (entry.source !== 'npm') {\n return {\n extensionId,\n status: 'skipped',\n message: `Skipping \"${extensionId}\" (source: ${entry.source}).`,\n };\n }\n\n const spec = entry.resolved?.trim() || (await resolveNpmPackageForId(extensionId));\n if (!spec) {\n return {\n extensionId,\n status: 'skipped',\n message: `Skipping \"${extensionId}\" (could not resolve npm package).`,\n };\n }\n\n if (existsSync(join(targetDir, extensionId))) {\n rmSync(join(targetDir, extensionId), { recursive: true, force: true });\n }\n\n const installResult = await installFromNpm(spec, targetDir, timeoutMs);\n if (!installResult.ok) {\n const message = installResult.error ?? 'npm install failed';\n logger.error?.(`Failed to update ${extensionId}: ${message}`);\n return {\n extensionId,\n status: 'error',\n message,\n currentVersion,\n };\n }\n\n await upsertNpmExtensionLock(lock, targetDir, installResult, spec);\n const nextVersion =\n (installResult.extensionId\n ? readInstalledExtensionVersion(targetDir, installResult.extensionId)\n : undefined) ?? entry.version;\n const resolvedId = installResult.extensionId ?? extensionId;\n const status =\n currentVersion && nextVersion && currentVersion === nextVersion ? 'unchanged' : 'updated';\n return {\n extensionId: resolvedId,\n status,\n currentVersion,\n nextVersion,\n message:\n status === 'unchanged'\n ? `${resolvedId} is up to date (${currentVersion}).`\n : `Updated ${resolvedId}: ${currentVersion ?? 'unknown'} -> ${nextVersion}.`,\n };\n}\n\nexport async function updateNpmInstalledExtensions(params: {\n extensionIds?: string[];\n skipIds?: Set<string>;\n config?: Config;\n timeoutMs?: number;\n logger?: ExtensionUpdateLogger;\n}): Promise<{ outcomes: ExtensionUpdateOutcome[]; status: 'ok' | 'error' | 'skipped' }> {\n const logger = params.logger ?? {};\n const config = params.config ?? loadConfig();\n const targetDir = resolveExtensionsDir();\n const storeBase = resolveExtensionsStoreBaseUrl(config);\n const lock = getExtensionLockfileManager();\n const data = await lock.load();\n\n const ids = params.extensionIds?.length\n ? params.extensionIds\n : Object.keys(data.extensions);\n\n if (ids.length === 0) {\n return { outcomes: [], status: 'skipped' };\n }\n\n const outcomes: ExtensionUpdateOutcome[] = [];\n let hasError = false;\n\n for (const extensionId of ids) {\n if (params.skipIds?.has(extensionId)) {\n outcomes.push({\n extensionId,\n status: 'skipped',\n message: `Skipping \"${extensionId}\" (channel sync).`,\n });\n continue;\n }\n\n const entry = data.extensions[extensionId];\n if (!entry) {\n outcomes.push({\n extensionId,\n status: 'skipped',\n message: `No lockfile entry for \"${extensionId}\".`,\n });\n continue;\n }\n\n const outcome = await updateSingleExtension({\n extensionId,\n entry,\n targetDir,\n storeBase,\n lock,\n timeoutMs: params.timeoutMs,\n logger,\n });\n outcomes.push(outcome);\n if (outcome.status === 'error') {\n hasError = true;\n } else if (outcome.status === 'updated') {\n log.info(\n { extensionId: outcome.extensionId, nextVersion: outcome.nextVersion },\n outcome.message,\n );\n }\n }\n\n return { outcomes, status: hasError ? 'error' : 'ok' };\n}\n\nexport async function runPostUpdateExtensionSync(params: {\n channel: UpdateChannel;\n config?: Config;\n timeoutMs?: number;\n logger?: ExtensionUpdateLogger;\n}): Promise<ExtensionPostUpdateResult> {\n const logger = params.logger ?? {\n info: (message) => log.info(message),\n warn: (message) => log.warn(message),\n error: (message) => log.error(message),\n };\n\n const channelSyncResult = await syncExtensionsForUpdateChannel({\n channel: params.channel,\n config: params.config,\n logger,\n });\n\n const updateResult = await updateNpmInstalledExtensions({\n skipIds: channelSyncResult.skipIds,\n config: params.config,\n timeoutMs: params.timeoutMs,\n logger,\n });\n\n return {\n status: updateResult.status,\n outcomes: updateResult.outcomes,\n channelSync: channelSyncResult.summary,\n };\n}\n"],"mappings":"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"}
@@ -2,16 +2,20 @@
2
2
  * Gateway REST helpers for multi-agent management.
3
3
  */
4
4
  import type { Config } from '../config/schema.js';
5
+ import type { LocalizedText } from '../config/localized-text.js';
5
6
  import type { AgentTypedModel } from '../config/schema.js';
6
7
  export type GatewayAgentTypedModelsInfo = {
7
8
  defaults: AgentTypedModel[];
8
- entry?: AgentTypedModel[];
9
9
  effective: AgentTypedModel[];
10
10
  };
11
11
  export type GatewayAgentRow = {
12
12
  id: string;
13
13
  name?: string;
14
14
  description?: string;
15
+ localized?: {
16
+ name?: LocalizedText;
17
+ description?: LocalizedText;
18
+ };
15
19
  /** Value from `IDENTITY.md` **Avatar:** line when present (may be URL, `xopc:…`, etc.). */
16
20
  avatar?: string;
17
21
  workspace: string;
@@ -41,20 +45,24 @@ export type GatewayAgentsListResponse = {
41
45
  };
42
46
  /** Extract `**Avatar:**` value from profile IDENTITY.md (same line shape as the gateway console parser). */
43
47
  export declare function extractAvatarFromIdentityMarkdown(content: string): string | undefined;
44
- export declare function listGatewayAgents(cfg: Config): Promise<GatewayAgentsListResponse>;
48
+ export declare function listGatewayAgents(cfg: Config, options?: {
49
+ locale?: string;
50
+ }): Promise<GatewayAgentsListResponse>;
45
51
  export type CreateAgentBody = {
46
52
  /** Display name stored on the agent entry. */
47
- name: string;
53
+ name: LocalizedText;
48
54
  /** Optional id seed; normalized agent id defaults from `name` when omitted. */
49
55
  id?: string;
50
56
  workspace: string;
51
57
  model?: string;
52
58
  agentDir?: string;
53
- description?: string;
59
+ description?: LocalizedText;
54
60
  /** Initial `agents.list[].tools.disable` for the new entry. */
55
61
  toolsDisable?: string[];
56
62
  /** Profile markdown files to write after seeding (e.g. `IDENTITY.md`, `SOUL.md`). */
57
63
  profileFiles?: Record<string, string>;
64
+ /** Clone from an existing agent id — copies config entry fields and profile directory. */
65
+ cloneFrom?: string;
58
66
  };
59
67
  export type AgentAdminHttpStatus = 400 | 404 | 409;
60
68
  export type AgentAdminResult<T> = {
@@ -80,10 +88,11 @@ export declare function prepareCreateAgentsBatch(cfg: Config, bodies: CreateAgen
80
88
  }>;
81
89
  export declare function finalizeCreateAgentDirs(cfg: Config, agentId: string, opts?: {
82
90
  profileFiles?: Record<string, string>;
91
+ cloneFrom?: string;
83
92
  }): Promise<AgentAdminResult<void>>;
84
93
  export type UpdateAgentBody = {
85
- name?: string;
86
- description?: string | null;
94
+ name?: LocalizedText;
95
+ description?: LocalizedText | null;
87
96
  workspace?: string;
88
97
  model?: string | null;
89
98
  agentDir?: string | null;
@@ -92,8 +101,6 @@ export type UpdateAgentBody = {
92
101
  skills?: string[] | null;
93
102
  /** Replace `agents.list[].tools.disable`; `null` clears entry-level disables. */
94
103
  toolsDisable?: string[] | null;
95
- /** Replace `agents.list[].models`; `null` removes entry overrides (inherit defaults). */
96
- models?: AgentTypedModel[] | null;
97
104
  };
98
105
  export declare function prepareUpdateAgent(cfg: Config, agentIdRaw: string, body: UpdateAgentBody): AgentAdminResult<{
99
106
  nextConfig: Config;
@@ -1,20 +1,20 @@
1
+ import { init_localized_text, normalizeLocalizedText, resolveLocalizedText } from "../config/localized-text.js";
1
2
  import { DEFAULT_AGENT_ID, init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentDir, resolveAgentProfileDir, resolveAgentWorkspaceDir, resolveDefaultAgentId, resolveUserPath, validateAgentIdForNewAgent } from "../agent/agent-scope.js";
2
3
  import { resolveEffectiveAgentProfile } from "../config/agent-profile.js";
3
4
  import { WORKSPACE_FILES, init_paths } from "../config/paths.js";
4
5
  import { AGENT_PROFILE_MARKDOWN_SYSTEM_FILES } from "../agent/context/workspace.js";
5
6
  import { isPathUnderWorkspace, resolveWorkspaceSafePath } from "./workspace-editor-path.js";
6
7
  import { applyAgentConfig, findAgentEntryIndex, pruneAgentConfig, removeAgentDirsFromDisk } from "../commands/agents.config.js";
7
- import { resolveEffectiveTypedModels } from "../config/agent-typed-models.js";
8
8
  import { GATEWAY_BUILTIN_TOOL_IDS } from "./agent-builtin-tools.js";
9
9
  import { seedAgentProfileMarkdownFiles } from "../agent/context/workspace-seed.js";
10
- import { normalizePatchTypedModels } from "./hono/lib/agent-model.js";
11
10
  import { join, resolve } from "node:path";
12
- import { mkdir, readFile, realpath, stat, unlink, writeFile } from "node:fs/promises";
11
+ import { cp, mkdir, readFile, readdir, realpath, stat, unlink, writeFile } from "node:fs/promises";
13
12
  //#region src/gateway/agents-admin.ts
14
13
  /**
15
14
  * Gateway REST helpers for multi-agent management.
16
15
  */
17
16
  init_agent_scope();
17
+ init_localized_text();
18
18
  init_paths();
19
19
  const EDITABLE_PROFILE_MARKDOWN_NAMES = new Set([...AGENT_PROFILE_MARKDOWN_SYSTEM_FILES, WORKSPACE_FILES.BOOTSTRAP]);
20
20
  function collectAgentIdsForList(cfg) {
@@ -36,7 +36,7 @@ function extractAvatarFromIdentityMarkdown(content) {
36
36
  }
37
37
  }
38
38
  }
39
- async function listGatewayAgents(cfg) {
39
+ async function listGatewayAgents(cfg, options = {}) {
40
40
  const defaultId = resolveDefaultAgentId(cfg);
41
41
  const agents = [];
42
42
  const defaultsSkills = cfg.agents?.defaults?.skills;
@@ -51,23 +51,29 @@ async function listGatewayAgents(cfg) {
51
51
  } : void 0;
52
52
  const entrySkills = entry?.skills;
53
53
  const entryDisable = entry?.tools?.disable ?? [];
54
- const entryTypedModels = entry?.models;
55
- const effectiveTypedModels = [...resolveEffectiveTypedModels(cfg, id).values()];
54
+ const effectiveTypedModels = [...defaultsTypedModels];
56
55
  let avatar;
57
56
  try {
58
57
  avatar = extractAvatarFromIdentityMarkdown(await readFile(join(resolveAgentProfileDir(cfg, id), WORKSPACE_FILES.IDENTITY), "utf-8"));
59
58
  } catch {}
59
+ const localizedName = normalizeLocalizedText(entry?.name);
60
+ const localizedDescription = normalizeLocalizedText(entry?.description);
61
+ const displayName = resolveLocalizedText(localizedName, options.locale);
62
+ const displayDescription = resolveLocalizedText(localizedDescription, options.locale);
60
63
  agents.push({
61
64
  id,
62
- ...entry?.name?.trim() ? { name: entry.name.trim() } : {},
63
- ...entry?.description?.trim() ? { description: entry.description.trim() } : {},
65
+ ...displayName ? { name: displayName } : {},
66
+ ...displayDescription ? { description: displayDescription } : {},
67
+ ...localizedName || localizedDescription ? { localized: {
68
+ ...localizedName ? { name: localizedName } : {},
69
+ ...localizedDescription ? { description: localizedDescription } : {}
70
+ } } : {},
64
71
  ...avatar ? { avatar } : {},
65
72
  workspace: profile.resolvedWorkspacePath,
66
73
  profileDir: resolveAgentProfileDir(cfg, id),
67
74
  ...model ? { model } : {},
68
75
  typedModels: {
69
76
  defaults: [...defaultsTypedModels],
70
- ...entryTypedModels !== void 0 ? { entry: [...entryTypedModels] } : {},
71
77
  effective: effectiveTypedModels
72
78
  },
73
79
  isDefault: id === defaultId,
@@ -99,8 +105,9 @@ function requireNonMain(id) {
99
105
  return null;
100
106
  }
101
107
  function prepareCreateAgent(cfg, body) {
102
- const name = body.name?.trim() ?? "";
103
- if (!name) return {
108
+ const normalizedName = normalizeLocalizedText(body.name);
109
+ const nameSeed = resolveLocalizedText(normalizedName, "en") ?? "";
110
+ if (!nameSeed) return {
104
111
  ok: false,
105
112
  error: "name is required",
106
113
  status: 400
@@ -111,7 +118,7 @@ function prepareCreateAgent(cfg, body) {
111
118
  error: "workspace is required",
112
119
  status: 400
113
120
  };
114
- const idRes = validateAgentIdForNewAgent(body.id, name);
121
+ const idRes = validateAgentIdForNewAgent(body.id, nameSeed);
115
122
  if (idRes.ok === false) return {
116
123
  ok: false,
117
124
  error: idRes.error,
@@ -139,25 +146,53 @@ function prepareCreateAgent(cfg, body) {
139
146
  };
140
147
  }
141
148
  }
149
+ let cloneSourceEntry;
150
+ if (body.cloneFrom) {
151
+ const srcId = normalizeAgentId(body.cloneFrom);
152
+ cloneSourceEntry = listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === srcId);
153
+ if (!cloneSourceEntry && srcId !== resolveDefaultAgentId(cfg)) return {
154
+ ok: false,
155
+ error: `source agent "${srcId}" not found`,
156
+ status: 404
157
+ };
158
+ }
142
159
  const wsAbs = resolveUserPath(workspace);
160
+ const effectiveModel = body.model?.trim() ? body.model.trim() : cloneSourceEntry?.model?.primary;
143
161
  let next = applyAgentConfig(cfg, {
144
162
  agentId,
145
- name,
163
+ name: normalizedName,
146
164
  workspace: wsAbs,
147
- ...body.model?.trim() ? { model: body.model.trim() } : {},
165
+ ...effectiveModel ? { model: effectiveModel } : {},
148
166
  ...body.agentDir?.trim() ? { agentDir: body.agentDir.trim() } : {},
149
- ...body.description?.trim() ? { description: body.description.trim() } : {}
167
+ ...normalizeLocalizedText(body.description) ? { description: normalizeLocalizedText(body.description) } : {}
150
168
  });
151
- if (body.toolsDisable !== void 0) {
169
+ const toolsDisable = body.toolsDisable ?? cloneSourceEntry?.tools?.disable;
170
+ if (toolsDisable !== void 0) {
152
171
  const list = [...listAgentEntries(next)];
153
172
  const idx = findAgentEntryIndex(list, agentId);
154
173
  if (idx >= 0) {
155
174
  const entry = { ...list[idx] };
156
- const disable = body.toolsDisable.map((s) => String(s).trim()).filter(Boolean);
175
+ const disable = toolsDisable.map((s) => String(s).trim()).filter(Boolean);
157
176
  entry.tools = {
158
177
  ...entry.tools,
159
178
  disable
160
179
  };
180
+ if (cloneSourceEntry?.skills !== void 0 && body.cloneFrom) entry.skills = [...cloneSourceEntry.skills];
181
+ list[idx] = entry;
182
+ next = {
183
+ ...next,
184
+ agents: {
185
+ ...next.agents,
186
+ list
187
+ }
188
+ };
189
+ }
190
+ } else if (cloneSourceEntry && body.cloneFrom) {
191
+ const list = [...listAgentEntries(next)];
192
+ const idx = findAgentEntryIndex(list, agentId);
193
+ if (idx >= 0) {
194
+ const entry = { ...list[idx] };
195
+ if (cloneSourceEntry.skills !== void 0) entry.skills = [...cloneSourceEntry.skills];
161
196
  list[idx] = entry;
162
197
  next = {
163
198
  ...next,
@@ -210,7 +245,24 @@ async function finalizeCreateAgentDirs(cfg, agentId, opts) {
210
245
  await mkdir(profilePath, { recursive: true });
211
246
  await mkdir(adPath, { recursive: true });
212
247
  const id = normalizeAgentId(agentId);
213
- seedAgentProfileMarkdownFiles(profilePath, wsPath, { displayName: listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === id)?.name?.trim() || id });
248
+ const displayName = resolveLocalizedText(normalizeLocalizedText(listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === id)?.name), "en") || id;
249
+ if (opts?.cloneFrom) {
250
+ const srcProfilePath = resolveAgentProfileDir(cfg, normalizeAgentId(opts.cloneFrom));
251
+ try {
252
+ if ((await stat(srcProfilePath)).isDirectory()) {
253
+ const srcFiles = await readdir(srcProfilePath);
254
+ for (const fileName of srcFiles) {
255
+ const srcFile = join(srcProfilePath, fileName);
256
+ const dstFile = join(profilePath, fileName);
257
+ try {
258
+ if ((await stat(srcFile)).isFile()) await cp(srcFile, dstFile);
259
+ } catch {}
260
+ }
261
+ }
262
+ } catch {
263
+ seedAgentProfileMarkdownFiles(profilePath, wsPath, { displayName });
264
+ }
265
+ } else seedAgentProfileMarkdownFiles(profilePath, wsPath, { displayName });
214
266
  const profileFiles = opts?.profileFiles;
215
267
  if (profileFiles && Object.keys(profileFiles).length > 0) for (const [name, content] of Object.entries(profileFiles)) {
216
268
  const written = await writeAgentProfileFile(cfg, agentId, name, content);
@@ -239,11 +291,14 @@ function prepareUpdateAgent(cfg, agentIdRaw, body) {
239
291
  };
240
292
  const entry = { ...list[idx] };
241
293
  if (body.name !== void 0) {
242
- const n = body.name.trim();
243
- if (n) entry.name = n;
294
+ const nextName = normalizeLocalizedText(body.name);
295
+ if (nextName) entry.name = nextName;
296
+ }
297
+ if (body.description !== void 0) {
298
+ const nextDescription = body.description === null ? void 0 : normalizeLocalizedText(body.description);
299
+ if (nextDescription) entry.description = nextDescription;
300
+ else delete entry.description;
244
301
  }
245
- if (body.description !== void 0) if (body.description === null || String(body.description).trim() === "") delete entry.description;
246
- else entry.description = String(body.description).trim();
247
302
  if (body.workspace !== void 0) {
248
303
  const w = body.workspace.trim();
249
304
  if (w) entry.workspace = resolveUserPath(w);
@@ -278,12 +333,6 @@ function prepareUpdateAgent(cfg, agentIdRaw, body) {
278
333
  disable: next
279
334
  };
280
335
  }
281
- if (body.models !== void 0) if (body.models === null) delete entry.models;
282
- else {
283
- const normalized = normalizePatchTypedModels(body.models);
284
- if (normalized === null || normalized === void 0) delete entry.models;
285
- else entry.models = normalized;
286
- }
287
336
  list[idx] = entry;
288
337
  let next = {
289
338
  ...cfg,