negotium 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/README.md +26 -2
  2. package/dist/canonical-mcp-bridge.js +14 -0
  3. package/dist/canonical-mcp-bridge.js.map +9 -0
  4. package/dist/chunk-10ct8814.js +429 -0
  5. package/dist/chunk-10ct8814.js.map +11 -0
  6. package/dist/chunk-18fzftjf.js +214 -0
  7. package/dist/chunk-18fzftjf.js.map +10 -0
  8. package/dist/chunk-1k7t7bnx.js +97 -0
  9. package/dist/chunk-1k7t7bnx.js.map +10 -0
  10. package/dist/chunk-1msje2zf.js +23 -0
  11. package/dist/chunk-1msje2zf.js.map +10 -0
  12. package/dist/chunk-2dgkah1z.js +706 -0
  13. package/dist/chunk-2dgkah1z.js.map +14 -0
  14. package/dist/chunk-3t4dtm26.js +61 -0
  15. package/dist/chunk-3t4dtm26.js.map +10 -0
  16. package/dist/chunk-3vn2vnj1.js +51 -0
  17. package/dist/chunk-3vn2vnj1.js.map +10 -0
  18. package/dist/chunk-6hknphtc.js +2054 -0
  19. package/dist/chunk-6hknphtc.js.map +29 -0
  20. package/dist/chunk-6tfzpcvk.js +2149 -0
  21. package/dist/chunk-6tfzpcvk.js.map +15 -0
  22. package/dist/chunk-8g0c4as0.js +23 -0
  23. package/dist/chunk-8g0c4as0.js.map +10 -0
  24. package/dist/chunk-9spx3h9n.js +279 -0
  25. package/dist/chunk-9spx3h9n.js.map +10 -0
  26. package/dist/chunk-c500xe9z.js +33 -0
  27. package/dist/chunk-c500xe9z.js.map +10 -0
  28. package/dist/chunk-e9b2xpff.js +147 -0
  29. package/dist/chunk-e9b2xpff.js.map +10 -0
  30. package/dist/chunk-edb07sjp.js +567 -0
  31. package/dist/chunk-edb07sjp.js.map +14 -0
  32. package/dist/chunk-eexqsee7.js +368 -0
  33. package/dist/chunk-eexqsee7.js.map +11 -0
  34. package/dist/chunk-efyxk97v.js +500 -0
  35. package/dist/chunk-efyxk97v.js.map +13 -0
  36. package/dist/chunk-epqxwkvh.js +3534 -0
  37. package/dist/chunk-epqxwkvh.js.map +20 -0
  38. package/dist/chunk-f1pzh3xh.js +115 -0
  39. package/dist/chunk-f1pzh3xh.js.map +10 -0
  40. package/dist/chunk-f5xgprg1.js +59 -0
  41. package/dist/chunk-f5xgprg1.js.map +10 -0
  42. package/dist/chunk-f97qybwk.js +584 -0
  43. package/dist/chunk-f97qybwk.js.map +11 -0
  44. package/dist/chunk-fvc7ncxd.js +252 -0
  45. package/dist/chunk-fvc7ncxd.js.map +10 -0
  46. package/dist/chunk-g7pvfj5e.js +574 -0
  47. package/dist/chunk-g7pvfj5e.js.map +14 -0
  48. package/dist/chunk-h8vmb9gf.js +315 -0
  49. package/dist/chunk-h8vmb9gf.js.map +10 -0
  50. package/dist/chunk-hgw7n8cm.js +43 -0
  51. package/dist/chunk-hgw7n8cm.js.map +10 -0
  52. package/dist/chunk-hsq7j9jk.js +126 -0
  53. package/dist/chunk-hsq7j9jk.js.map +10 -0
  54. package/dist/chunk-hwy3n31w.js +21 -0
  55. package/dist/chunk-hwy3n31w.js.map +9 -0
  56. package/dist/chunk-j0kjscb7.js +53 -0
  57. package/dist/chunk-j0kjscb7.js.map +10 -0
  58. package/dist/chunk-jzz91jwh.js +70 -0
  59. package/dist/chunk-jzz91jwh.js.map +10 -0
  60. package/dist/chunk-k9z47mab.js +247 -0
  61. package/dist/chunk-k9z47mab.js.map +12 -0
  62. package/dist/chunk-kbm35t7w.js +1331 -0
  63. package/dist/chunk-kbm35t7w.js.map +16 -0
  64. package/dist/chunk-m4veth0c.js +1649 -0
  65. package/dist/chunk-m4veth0c.js.map +17 -0
  66. package/dist/chunk-mhty3nna.js +150 -0
  67. package/dist/chunk-mhty3nna.js.map +11 -0
  68. package/dist/chunk-mmgh670x.js +248 -0
  69. package/dist/chunk-mmgh670x.js.map +11 -0
  70. package/dist/chunk-n0kk5xbq.js +2358 -0
  71. package/dist/chunk-n0kk5xbq.js.map +26 -0
  72. package/dist/chunk-nmv41qx9.js +2847 -0
  73. package/dist/chunk-nmv41qx9.js.map +23 -0
  74. package/dist/chunk-nqdp52aj.js +113 -0
  75. package/dist/chunk-nqdp52aj.js.map +12 -0
  76. package/dist/chunk-ns2m25dz.js +139 -0
  77. package/dist/chunk-ns2m25dz.js.map +10 -0
  78. package/dist/chunk-pkdmypdd.js +147 -0
  79. package/dist/chunk-pkdmypdd.js.map +11 -0
  80. package/dist/chunk-rmyfmrrr.js +1634 -0
  81. package/dist/chunk-rmyfmrrr.js.map +15 -0
  82. package/dist/chunk-sa874j5c.js +9 -0
  83. package/dist/chunk-sa874j5c.js.map +10 -0
  84. package/dist/chunk-sgsz2fpq.js +289 -0
  85. package/dist/chunk-sgsz2fpq.js.map +10 -0
  86. package/dist/chunk-sn51wnf1.js +3020 -0
  87. package/dist/chunk-sn51wnf1.js.map +32 -0
  88. package/dist/chunk-sqhr1a7e.js +587 -0
  89. package/dist/chunk-sqhr1a7e.js.map +11 -0
  90. package/dist/chunk-tmj5r2zv.js +2933 -0
  91. package/dist/chunk-tmj5r2zv.js.map +20 -0
  92. package/dist/chunk-vefj3880.js +56 -0
  93. package/dist/chunk-vefj3880.js.map +10 -0
  94. package/dist/chunk-vp2db42d.js +121 -0
  95. package/dist/chunk-vp2db42d.js.map +10 -0
  96. package/dist/chunk-wry79q55.js +108 -0
  97. package/dist/chunk-wry79q55.js.map +10 -0
  98. package/dist/chunk-x1arr7ze.js +63 -0
  99. package/dist/chunk-x1arr7ze.js.map +10 -0
  100. package/dist/chunk-z2xy5n2q.js +103 -0
  101. package/dist/chunk-z2xy5n2q.js.map +10 -0
  102. package/dist/hosted-agent.js +53 -0
  103. package/dist/hosted-agent.js.map +10 -0
  104. package/dist/main.js +132 -20
  105. package/dist/main.js.map +4 -4
  106. package/dist/runtime/scripts/faster-whisper-wrapper.py +87 -0
  107. package/dist/runtime/scripts/mcp-patchright-http.mjs +349 -0
  108. package/dist/runtime/src/agents/api-topic-agent-switch.ts +178 -0
  109. package/dist/runtime/src/agents/archiver.ts +396 -0
  110. package/dist/runtime/src/agents/auth-check.ts +64 -0
  111. package/dist/runtime/src/agents/claude-provider.ts +737 -0
  112. package/dist/runtime/src/agents/claude-registry.ts +115 -0
  113. package/dist/runtime/src/agents/codex-native-multi-agent.ts +177 -0
  114. package/dist/runtime/src/agents/codex-provider.ts +785 -0
  115. package/dist/runtime/src/agents/codex-registry.ts +115 -0
  116. package/dist/runtime/src/agents/codex-tree-kill.ts +365 -0
  117. package/dist/runtime/src/agents/contracts.ts +69 -0
  118. package/dist/runtime/src/agents/deep-map.ts +13 -0
  119. package/dist/runtime/src/agents/execution-host.ts +91 -0
  120. package/dist/runtime/src/agents/fixtures/claude-attachments.jsonl +2 -0
  121. package/dist/runtime/src/agents/fixtures/codex-shell.jsonl +5 -0
  122. package/dist/runtime/src/agents/fork.ts +65 -0
  123. package/dist/runtime/src/agents/hosted-agent.ts +49 -0
  124. package/dist/runtime/src/agents/idle-archiver.ts +128 -0
  125. package/dist/runtime/src/agents/index.ts +205 -0
  126. package/dist/runtime/src/agents/maestro-provider.ts +206 -0
  127. package/dist/runtime/src/agents/maestro-registry.ts +31 -0
  128. package/dist/runtime/src/agents/mcp-tools/ask-user.ts +280 -0
  129. package/dist/runtime/src/agents/mcp-tools/common.ts +21 -0
  130. package/dist/runtime/src/agents/mcp-tools/self-config.ts +220 -0
  131. package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +525 -0
  132. package/dist/runtime/src/agents/mcp-tools/visuals.ts +88 -0
  133. package/dist/runtime/src/agents/model-catalog.ts +151 -0
  134. package/dist/runtime/src/agents/registry.ts +24 -0
  135. package/dist/runtime/src/agents/rollout/claude.ts +331 -0
  136. package/dist/runtime/src/agents/rollout/codex.ts +569 -0
  137. package/dist/runtime/src/agents/rollout/shared.ts +205 -0
  138. package/dist/runtime/src/agents/self-config-core.ts +415 -0
  139. package/dist/runtime/src/agents/task-events.ts +44 -0
  140. package/dist/runtime/src/agents/tool-format.ts +352 -0
  141. package/dist/runtime/src/agents/topic-agent-switch.ts +193 -0
  142. package/dist/runtime/src/agents/topic-cleanup.ts +231 -0
  143. package/dist/runtime/src/agents/vault-tool-policy.ts +33 -0
  144. package/dist/runtime/src/application/execute-external-user-turn.ts +23 -0
  145. package/dist/runtime/src/application/submit-user-message.ts +50 -0
  146. package/dist/runtime/src/application/switch-topic-effort.ts +48 -0
  147. package/dist/runtime/src/application/switch-topic-model.ts +79 -0
  148. package/dist/runtime/src/application/topic-service.ts +139 -0
  149. package/dist/runtime/src/application/vault-command.ts +170 -0
  150. package/dist/runtime/src/bus.ts +319 -0
  151. package/dist/runtime/src/index.ts +333 -0
  152. package/dist/runtime/src/mcp/agent-health-server.ts +309 -0
  153. package/dist/runtime/src/mcp/background-bash-server.ts +346 -0
  154. package/dist/runtime/src/mcp/canonical-bridge-config.ts +84 -0
  155. package/dist/runtime/src/mcp/canonical-proxy-server.ts +171 -0
  156. package/dist/runtime/src/mcp/dm-ipc.ts +60 -0
  157. package/dist/runtime/src/mcp/mcp-helpers.ts +40 -0
  158. package/dist/runtime/src/mcp/paddleocr-server.ts +105 -0
  159. package/dist/runtime/src/mcp/peer-bridge.ts +114 -0
  160. package/dist/runtime/src/mcp/runtime-spec.ts +137 -0
  161. package/dist/runtime/src/mcp/session-comm/bridge-ipc-config.ts +27 -0
  162. package/dist/runtime/src/mcp/session-comm/peer-forward.ts +165 -0
  163. package/dist/runtime/src/mcp/session-comm/runtime.ts +115 -0
  164. package/dist/runtime/src/mcp/session-comm/server.ts +649 -0
  165. package/dist/runtime/src/mcp/session-comm/topic-config.ts +131 -0
  166. package/dist/runtime/src/mcp/session-comm/topics.ts +229 -0
  167. package/dist/runtime/src/mcp/stdio-protect.ts +84 -0
  168. package/dist/runtime/src/mcp/system-health-server.ts +130 -0
  169. package/dist/runtime/src/mcp/task-server.ts +173 -0
  170. package/dist/runtime/src/mcp/token-stats-server.ts +98 -0
  171. package/dist/runtime/src/mcp/vault-http.ts +219 -0
  172. package/dist/runtime/src/mcp/vault-run.ts +153 -0
  173. package/dist/runtime/src/mcp/vault-server.ts +104 -0
  174. package/dist/runtime/src/mcp/wiki-server.ts +755 -0
  175. package/dist/runtime/src/media/file-events.ts +33 -0
  176. package/dist/runtime/src/media/text-extractor.ts +312 -0
  177. package/dist/runtime/src/media/video.ts +92 -0
  178. package/dist/runtime/src/outbox/file-ops.ts +210 -0
  179. package/dist/runtime/src/outbox/index.ts +8 -0
  180. package/dist/runtime/src/outbox/utils.ts +51 -0
  181. package/dist/runtime/src/platform/background-bash/manager.ts +279 -0
  182. package/dist/runtime/src/platform/config.ts +371 -0
  183. package/dist/runtime/src/platform/constants.ts +18 -0
  184. package/dist/runtime/src/platform/debug-users.ts +14 -0
  185. package/dist/runtime/src/platform/delay.ts +1 -0
  186. package/dist/runtime/src/platform/error.ts +9 -0
  187. package/dist/runtime/src/platform/file-utils.ts +16 -0
  188. package/dist/runtime/src/platform/jsonl.ts +202 -0
  189. package/dist/runtime/src/platform/lifecycle.ts +174 -0
  190. package/dist/runtime/src/platform/logger.ts +31 -0
  191. package/dist/runtime/src/platform/maestro-bootstrap-env.ts +3 -0
  192. package/dist/runtime/src/platform/mcp-config.ts +878 -0
  193. package/dist/runtime/src/platform/modules.ts +145 -0
  194. package/dist/runtime/src/platform/node-plugins.ts +46 -0
  195. package/dist/runtime/src/platform/playwright/manager.ts +1233 -0
  196. package/dist/runtime/src/prompts/agents/wiki-archiver.md +261 -0
  197. package/dist/runtime/src/prompts/builders.ts +368 -0
  198. package/dist/runtime/src/prompts/sessions/channel-system.md +48 -0
  199. package/dist/runtime/src/prompts/sessions/manager-system.md +37 -0
  200. package/dist/runtime/src/prompts/sessions/topic-system.md +47 -0
  201. package/dist/runtime/src/prompts/sessions/visual-design.md +86 -0
  202. package/dist/runtime/src/query/active-rooms.ts +534 -0
  203. package/dist/runtime/src/query/control.ts +313 -0
  204. package/dist/runtime/src/query/index.ts +24 -0
  205. package/dist/runtime/src/query/session-inbox-cleanup.ts +44 -0
  206. package/dist/runtime/src/query/session-inbox-path.ts +42 -0
  207. package/dist/runtime/src/query/state.ts +94 -0
  208. package/dist/runtime/src/query/types.ts +83 -0
  209. package/dist/runtime/src/runtime/ask-callbacks.ts +153 -0
  210. package/dist/runtime/src/runtime/attachments.ts +151 -0
  211. package/dist/runtime/src/runtime/background-sessions.ts +83 -0
  212. package/dist/runtime/src/runtime/channel-context.ts +107 -0
  213. package/dist/runtime/src/runtime/errors.ts +69 -0
  214. package/dist/runtime/src/runtime/event-heartbeat.ts +77 -0
  215. package/dist/runtime/src/runtime/file-hooks.ts +81 -0
  216. package/dist/runtime/src/runtime/footer.ts +23 -0
  217. package/dist/runtime/src/runtime/inbox.ts +946 -0
  218. package/dist/runtime/src/runtime/tasks.ts +56 -0
  219. package/dist/runtime/src/runtime/topic-config.ts +29 -0
  220. package/dist/runtime/src/runtime/turn-runner.ts +2298 -0
  221. package/dist/runtime/src/runtime/usage-alert.ts +84 -0
  222. package/dist/runtime/src/runtime/visual-html.ts +109 -0
  223. package/dist/runtime/src/runtime/visual-store.ts +430 -0
  224. package/dist/runtime/src/runtime/visuals.ts +171 -0
  225. package/dist/runtime/src/security/sanitize.ts +27 -0
  226. package/dist/runtime/src/security/sensitive-path.ts +34 -0
  227. package/dist/runtime/src/storage/activity-log.ts +152 -0
  228. package/dist/runtime/src/storage/api-messages.ts +617 -0
  229. package/dist/runtime/src/storage/api-topic-brief.ts +127 -0
  230. package/dist/runtime/src/storage/api-topic-config.ts +192 -0
  231. package/dist/runtime/src/storage/api-topics.ts +844 -0
  232. package/dist/runtime/src/storage/app-settings.ts +44 -0
  233. package/dist/runtime/src/storage/browser-profiles.ts +133 -0
  234. package/dist/runtime/src/storage/conversations.ts +268 -0
  235. package/dist/runtime/src/storage/forum/agent-settings.ts +20 -0
  236. package/dist/runtime/src/storage/forum/index.ts +4 -0
  237. package/dist/runtime/src/storage/forum/repository.ts +283 -0
  238. package/dist/runtime/src/storage/forum/schema.ts +300 -0
  239. package/dist/runtime/src/storage/forum-db.ts +23 -0
  240. package/dist/runtime/src/storage/runtime-events.ts +130 -0
  241. package/dist/runtime/src/storage/runtime-leases.ts +189 -0
  242. package/dist/runtime/src/storage/runtime-process-leases.ts +216 -0
  243. package/dist/runtime/src/storage/runtime-topic-state.ts +148 -0
  244. package/dist/runtime/src/storage/runtime-turn-requests.ts +301 -0
  245. package/dist/runtime/src/storage/self-schedules.ts +298 -0
  246. package/dist/runtime/src/storage/session-asks.ts +347 -0
  247. package/dist/runtime/src/storage/sqlite.ts +129 -0
  248. package/dist/runtime/src/storage/tasks.ts +179 -0
  249. package/dist/runtime/src/storage/token-stats.ts +144 -0
  250. package/dist/runtime/src/storage/topic-archive-state.ts +59 -0
  251. package/dist/runtime/src/storage/topic-archive.ts +74 -0
  252. package/dist/runtime/src/storage/topic-settings.ts +67 -0
  253. package/dist/runtime/src/storage/topic-transcript.ts +98 -0
  254. package/dist/runtime/src/storage/vault-crypto.ts +65 -0
  255. package/dist/runtime/src/storage/vault.ts +225 -0
  256. package/dist/runtime/src/storage/wiki-summary-names.ts +33 -0
  257. package/dist/runtime/src/storage/wiki.ts +85 -0
  258. package/dist/runtime/src/topics/create.ts +123 -0
  259. package/dist/runtime/src/topics/derive.ts +398 -0
  260. package/dist/runtime/src/topics/lifecycle.ts +311 -0
  261. package/dist/runtime/src/topics/links.ts +7 -0
  262. package/dist/runtime/src/topics/personal-general.ts +65 -0
  263. package/dist/runtime/src/topics/session.ts +381 -0
  264. package/dist/runtime/src/types/api.ts +497 -0
  265. package/dist/runtime/src/types.ts +257 -0
  266. package/dist/runtime/src/version.ts +1 -0
  267. package/dist/runtime/tsconfig.json +13 -0
  268. package/package.json +28 -3
  269. package/dist/main.d.ts +0 -3
  270. package/dist/main.d.ts.map +0 -1
@@ -0,0 +1,279 @@
1
+ // @bun
2
+ import {
3
+ CRON_JOBS_DIR,
4
+ createCronJob,
5
+ cronScriptExists,
6
+ deleteCronJob,
7
+ getCronJob,
8
+ getCronJobByOwnerAndName,
9
+ listCronJobs,
10
+ listCronRuns,
11
+ queueCronPromptSummary,
12
+ requestCronCancel,
13
+ requestCronRun,
14
+ resetCronTopicContext,
15
+ setCronJobEnabled,
16
+ updateCronJobWithContextReset
17
+ } from "./chunk-m4veth0c.js";
18
+ import"./chunk-sn51wnf1.js";
19
+ import"./chunk-x1arr7ze.js";
20
+ import"./chunk-g7pvfj5e.js";
21
+ import"./chunk-z2xy5n2q.js";
22
+ import"./chunk-sgsz2fpq.js";
23
+ import"./chunk-1k7t7bnx.js";
24
+ import"./chunk-nmv41qx9.js";
25
+ import"./chunk-h8vmb9gf.js";
26
+ import"./chunk-6hknphtc.js";
27
+ import"./chunk-kbm35t7w.js";
28
+ import {
29
+ getTopic,
30
+ getTopicByNameForUser
31
+ } from "./chunk-f97qybwk.js";
32
+ import"./chunk-hgw7n8cm.js";
33
+ import {
34
+ getRegistry
35
+ } from "./chunk-nqdp52aj.js";
36
+ import"./chunk-mhty3nna.js";
37
+ import"./chunk-vp2db42d.js";
38
+ import"./chunk-hsq7j9jk.js";
39
+ import"./chunk-n0kk5xbq.js";
40
+ import"./chunk-2dgkah1z.js";
41
+ import"./chunk-pkdmypdd.js";
42
+ import"./chunk-j0kjscb7.js";
43
+ import"./chunk-efyxk97v.js";
44
+ import"./chunk-1msje2zf.js";
45
+ import"./chunk-f5xgprg1.js";
46
+ import {
47
+ EFFORT_VALUES,
48
+ isAgentKind
49
+ } from "./chunk-k9z47mab.js";
50
+ import"./chunk-hwy3n31w.js";
51
+
52
+ // ../cli/src/commands/cron.ts
53
+ var USER_ID = "local";
54
+ function flag(args, name) {
55
+ return args.find((value) => value.startsWith(`--${name}=`))?.slice(name.length + 3);
56
+ }
57
+ function hasFlag(args, name) {
58
+ return args.some((value) => value.startsWith(`--${name}=`));
59
+ }
60
+ function positional(args) {
61
+ return args.filter((value) => !value.startsWith("--"));
62
+ }
63
+ function resolveJob(ref) {
64
+ return getCronJob(ref) ?? getCronJobByOwnerAndName(USER_ID, ref);
65
+ }
66
+ function printJob(job) {
67
+ const last = listCronRuns(job.id, 1)[0];
68
+ console.log(`${job.name} ${job.enabled ? "enabled" : "paused"} ${job.schedule}` + `${job.timezone ? ` (${job.timezone})` : ""} next=${job.nextRunAt}` + `${last ? ` last=${last.status}` : ""} ${job.id}`);
69
+ }
70
+ function usage() {
71
+ console.log([
72
+ "negotium cron commands:",
73
+ " list",
74
+ " create <topic> <name> '<schedule>' <prompt...> [--timezone=IANA] [--agent=...] [--model=...] [--effort=...]",
75
+ " create <topic> <name> '<schedule>' --script=job.py [--timezone=IANA] [--agent=...]",
76
+ " edit <name|id> [prompt...] [--name=...] [--topic=...] [--schedule=...] [--script=job.py]",
77
+ " inspect|logs <name|id>",
78
+ " run|pause|resume|restart|kill|reset|delete <name|id>",
79
+ "",
80
+ "The node must stay alive (`negotium serve`, chat, or an adapter) for schedules to run.",
81
+ `Python prompt scripts live in ${CRON_JOBS_DIR}.`
82
+ ].join(`
83
+ `));
84
+ }
85
+ async function cronCommand(args) {
86
+ const [sub, ...rest] = args;
87
+ try {
88
+ switch (sub) {
89
+ case undefined:
90
+ case "help":
91
+ case "--help":
92
+ usage();
93
+ return;
94
+ case "list": {
95
+ const jobs = listCronJobs(USER_ID);
96
+ if (jobs.length === 0) {
97
+ console.log("no cron jobs");
98
+ return;
99
+ }
100
+ jobs.forEach(printJob);
101
+ return;
102
+ }
103
+ case "create": {
104
+ const values = positional(rest);
105
+ const [topicName, name, schedule, ...promptParts] = values;
106
+ const script = flag(rest, "script")?.trim() || undefined;
107
+ if (!topicName || !name || !schedule || promptParts.length === 0 && !script) {
108
+ usage();
109
+ process.exitCode = 1;
110
+ return;
111
+ }
112
+ if (script && promptParts.length > 0) {
113
+ throw new Error("provide a prompt or --script, not both");
114
+ }
115
+ if (script && !cronScriptExists(script))
116
+ throw new Error(`cron script not found: ${script}`);
117
+ const topic = getTopicByNameForUser(topicName, USER_ID);
118
+ if (!topic?.agent)
119
+ throw new Error(`topic not found or has no agent: ${topicName}`);
120
+ const agentRaw = flag(rest, "agent");
121
+ if (agentRaw && !isAgentKind(agentRaw))
122
+ throw new Error(`invalid agent: ${agentRaw}`);
123
+ const agent = isAgentKind(agentRaw) ? agentRaw : undefined;
124
+ const effectiveAgent = agent ?? topic.agent;
125
+ const registry = getRegistry(effectiveAgent);
126
+ const model = flag(rest, "model");
127
+ if (model && !registry.validateModel(model)) {
128
+ throw new Error(`model '${model}' is invalid for ${effectiveAgent}`);
129
+ }
130
+ const effortRaw = flag(rest, "effort");
131
+ if (effortRaw && !EFFORT_VALUES.includes(effortRaw)) {
132
+ throw new Error(`invalid effort: ${effortRaw}`);
133
+ }
134
+ const effort = effortRaw;
135
+ if (effort && !registry.validateEffort(effort)) {
136
+ throw new Error(`effort '${effort}' is invalid for ${effectiveAgent}`);
137
+ }
138
+ const job = createCronJob({
139
+ name,
140
+ ownerUserId: USER_ID,
141
+ topicId: topic.id,
142
+ prompt: promptParts.length > 0 ? promptParts.join(" ") : undefined,
143
+ script,
144
+ schedule,
145
+ timezone: flag(rest, "timezone"),
146
+ agent,
147
+ model,
148
+ effort
149
+ });
150
+ if (job.prompt)
151
+ queueCronPromptSummary(job.id, job.prompt);
152
+ printJob(job);
153
+ return;
154
+ }
155
+ case "edit": {
156
+ const values = positional(rest);
157
+ const [ref, ...promptParts] = values;
158
+ if (!ref)
159
+ throw new Error("provide a cron name or id");
160
+ const job = resolveJob(ref);
161
+ if (!job || job.ownerUserId !== USER_ID)
162
+ throw new Error(`cron job not found: ${ref}`);
163
+ const topicName = flag(rest, "topic");
164
+ const topic = topicName ? getTopicByNameForUser(topicName, USER_ID) : null;
165
+ if (topicName && !topic?.agent)
166
+ throw new Error(`topic not found or has no agent: ${topicName}`);
167
+ const scriptTouched = hasFlag(rest, "script");
168
+ const script = flag(rest, "script")?.trim() || undefined;
169
+ if (script && !cronScriptExists(script))
170
+ throw new Error(`cron script not found: ${script}`);
171
+ if (script && promptParts.length > 0)
172
+ throw new Error("provide a prompt or --script, not both");
173
+ const sourceTouched = scriptTouched || promptParts.length > 0;
174
+ const nextPrompt = promptParts.length > 0 ? promptParts.join(" ") : sourceTouched ? undefined : job.prompt;
175
+ const nextScript = scriptTouched ? script : promptParts.length > 0 ? undefined : job.script;
176
+ if (sourceTouched && Boolean(nextPrompt) === Boolean(nextScript)) {
177
+ throw new Error("cron job requires exactly one of prompt or script");
178
+ }
179
+ const agentRaw = flag(rest, "agent");
180
+ const agent = agentRaw === "default" ? undefined : agentRaw;
181
+ if (agent && !isAgentKind(agent))
182
+ throw new Error(`invalid agent: ${agent}`);
183
+ const effectiveAgent = (isAgentKind(agent) ? agent : hasFlag(rest, "agent") ? undefined : job.agent) ?? topic?.agent ?? getTopic(job.topicId)?.agent;
184
+ const modelRaw = flag(rest, "model");
185
+ const model = modelRaw === "default" ? undefined : modelRaw?.trim() || undefined;
186
+ const effortRaw = flag(rest, "effort");
187
+ const effort = effortRaw === "default" ? undefined : effortRaw;
188
+ if (effort && !EFFORT_VALUES.includes(effort)) {
189
+ throw new Error(`invalid effort: ${effort}`);
190
+ }
191
+ if (effectiveAgent) {
192
+ const registry = getRegistry(effectiveAgent);
193
+ if (model && !registry.validateModel(model)) {
194
+ throw new Error(`model '${model}' is invalid for ${effectiveAgent}`);
195
+ }
196
+ if (effort && !registry.validateEffort(effort)) {
197
+ throw new Error(`effort '${effort}' is invalid for ${effectiveAgent}`);
198
+ }
199
+ }
200
+ const updated = await updateCronJobWithContextReset(job.id, {
201
+ name: flag(rest, "name"),
202
+ topicId: topic?.id,
203
+ prompt: sourceTouched ? nextPrompt ?? null : undefined,
204
+ script: sourceTouched ? nextScript ?? null : undefined,
205
+ summary: sourceTouched ? null : undefined,
206
+ schedule: flag(rest, "schedule"),
207
+ timezone: hasFlag(rest, "timezone") ? flag(rest, "timezone") || null : undefined,
208
+ agent: hasFlag(rest, "agent") ? isAgentKind(agent) ? agent : null : undefined,
209
+ model: hasFlag(rest, "model") ? model ?? null : undefined,
210
+ effort: hasFlag(rest, "effort") ? effort ?? null : undefined
211
+ });
212
+ if (!updated)
213
+ throw new Error(`cron job not found: ${ref}`);
214
+ if (sourceTouched && updated.prompt)
215
+ queueCronPromptSummary(updated.id, updated.prompt);
216
+ printJob(updated);
217
+ return;
218
+ }
219
+ case "inspect":
220
+ case "logs": {
221
+ const ref = positional(rest)[0];
222
+ if (!ref)
223
+ throw new Error("provide a cron name or id");
224
+ const job = resolveJob(ref);
225
+ if (!job || job.ownerUserId !== USER_ID)
226
+ throw new Error(`cron job not found: ${ref}`);
227
+ console.log(JSON.stringify({ ...job, runs: listCronRuns(job.id, 20) }, null, 2));
228
+ return;
229
+ }
230
+ case "run":
231
+ case "pause":
232
+ case "resume":
233
+ case "restart":
234
+ case "kill":
235
+ case "reset":
236
+ case "delete": {
237
+ const ref = positional(rest)[0];
238
+ if (!ref)
239
+ throw new Error("provide a cron name or id");
240
+ const job = resolveJob(ref);
241
+ if (!job || job.ownerUserId !== USER_ID)
242
+ throw new Error(`cron job not found: ${ref}`);
243
+ if (sub === "run") {
244
+ if (!job.enabled)
245
+ throw new Error("job is disabled; resume it first");
246
+ console.log(`queued ${requestCronRun(job.id)}`);
247
+ }
248
+ if (sub === "pause")
249
+ printJob(setCronJobEnabled(job.id, false));
250
+ if (sub === "resume")
251
+ printJob(setCronJobEnabled(job.id, true));
252
+ if (sub === "restart")
253
+ printJob(setCronJobEnabled(job.id, true));
254
+ if (sub === "kill")
255
+ console.log(`cancellation queued ${requestCronCancel(job.id)}`);
256
+ if (sub === "reset") {
257
+ await resetCronTopicContext(job.topicId);
258
+ console.log(`reset shared Cron context for topic ${job.topicId}`);
259
+ }
260
+ if (sub === "delete") {
261
+ deleteCronJob(job.id);
262
+ console.log(`deleted ${job.name}`);
263
+ }
264
+ return;
265
+ }
266
+ default:
267
+ usage();
268
+ process.exitCode = 1;
269
+ }
270
+ } catch (error) {
271
+ console.error(error instanceof Error ? error.message : String(error));
272
+ process.exitCode = 1;
273
+ }
274
+ }
275
+ export {
276
+ cronCommand
277
+ };
278
+
279
+ //# debugId=7C47FA5396346C1764756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../cli/src/commands/cron.ts"],
4
+ "sourcesContent": [
5
+ "/** `negotium cron ...` — persistent scheduled agent turns. */\n\nimport {\n EFFORT_VALUES,\n type EffortLevel,\n getRegistry,\n getTopic,\n getTopicByNameForUser,\n isAgentKind,\n} from \"@negotium/core\";\nimport {\n CRON_JOBS_DIR,\n createCronJob,\n cronScriptExists,\n deleteCronJob,\n getCronJob,\n getCronJobByOwnerAndName,\n listCronJobs,\n listCronRuns,\n queueCronPromptSummary,\n requestCronCancel,\n requestCronRun,\n resetCronTopicContext,\n setCronJobEnabled,\n updateCronJobWithContextReset,\n} from \"@negotium/module-cron\";\n\nconst USER_ID = \"local\";\n\nfunction flag(args: string[], name: string): string | undefined {\n return args.find((value) => value.startsWith(`--${name}=`))?.slice(name.length + 3);\n}\n\nfunction hasFlag(args: string[], name: string): boolean {\n return args.some((value) => value.startsWith(`--${name}=`));\n}\n\nfunction positional(args: string[]): string[] {\n return args.filter((value) => !value.startsWith(\"--\"));\n}\n\nfunction resolveJob(ref: string) {\n return getCronJob(ref) ?? getCronJobByOwnerAndName(USER_ID, ref);\n}\n\nfunction printJob(job: NonNullable<ReturnType<typeof getCronJob>>): void {\n const last = listCronRuns(job.id, 1)[0];\n console.log(\n `${job.name} ${job.enabled ? \"enabled\" : \"paused\"} ${job.schedule}` +\n `${job.timezone ? ` (${job.timezone})` : \"\"} next=${job.nextRunAt}` +\n `${last ? ` last=${last.status}` : \"\"} ${job.id}`,\n );\n}\n\nfunction usage(): void {\n console.log(\n [\n \"negotium cron commands:\",\n \" list\",\n \" create <topic> <name> '<schedule>' <prompt...> [--timezone=IANA] [--agent=...] [--model=...] [--effort=...]\",\n \" create <topic> <name> '<schedule>' --script=job.py [--timezone=IANA] [--agent=...]\",\n \" edit <name|id> [prompt...] [--name=...] [--topic=...] [--schedule=...] [--script=job.py]\",\n \" inspect|logs <name|id>\",\n \" run|pause|resume|restart|kill|reset|delete <name|id>\",\n \"\",\n \"The node must stay alive (`negotium serve`, chat, or an adapter) for schedules to run.\",\n `Python prompt scripts live in ${CRON_JOBS_DIR}.`,\n ].join(\"\\n\"),\n );\n}\n\nexport async function cronCommand(args: string[]): Promise<void> {\n const [sub, ...rest] = args;\n try {\n switch (sub) {\n case undefined:\n case \"help\":\n case \"--help\":\n usage();\n return;\n case \"list\": {\n const jobs = listCronJobs(USER_ID);\n if (jobs.length === 0) {\n console.log(\"no cron jobs\");\n return;\n }\n jobs.forEach(printJob);\n return;\n }\n case \"create\": {\n const values = positional(rest);\n const [topicName, name, schedule, ...promptParts] = values;\n const script = flag(rest, \"script\")?.trim() || undefined;\n if (!topicName || !name || !schedule || (promptParts.length === 0 && !script)) {\n usage();\n process.exitCode = 1;\n return;\n }\n if (script && promptParts.length > 0) {\n throw new Error(\"provide a prompt or --script, not both\");\n }\n if (script && !cronScriptExists(script))\n throw new Error(`cron script not found: ${script}`);\n const topic = getTopicByNameForUser(topicName, USER_ID);\n if (!topic?.agent) throw new Error(`topic not found or has no agent: ${topicName}`);\n const agentRaw = flag(rest, \"agent\");\n if (agentRaw && !isAgentKind(agentRaw)) throw new Error(`invalid agent: ${agentRaw}`);\n const agent = isAgentKind(agentRaw) ? agentRaw : undefined;\n const effectiveAgent = agent ?? topic.agent;\n const registry = getRegistry(effectiveAgent);\n const model = flag(rest, \"model\");\n if (model && !registry.validateModel(model)) {\n throw new Error(`model '${model}' is invalid for ${effectiveAgent}`);\n }\n const effortRaw = flag(rest, \"effort\");\n if (effortRaw && !(EFFORT_VALUES as readonly string[]).includes(effortRaw)) {\n throw new Error(`invalid effort: ${effortRaw}`);\n }\n const effort = effortRaw as EffortLevel | undefined;\n if (effort && !registry.validateEffort(effort)) {\n throw new Error(`effort '${effort}' is invalid for ${effectiveAgent}`);\n }\n const job = createCronJob({\n name,\n ownerUserId: USER_ID,\n topicId: topic.id,\n prompt: promptParts.length > 0 ? promptParts.join(\" \") : undefined,\n script,\n schedule,\n timezone: flag(rest, \"timezone\"),\n agent,\n model,\n effort,\n });\n if (job.prompt) queueCronPromptSummary(job.id, job.prompt);\n printJob(job);\n return;\n }\n case \"edit\": {\n const values = positional(rest);\n const [ref, ...promptParts] = values;\n if (!ref) throw new Error(\"provide a cron name or id\");\n const job = resolveJob(ref);\n if (!job || job.ownerUserId !== USER_ID) throw new Error(`cron job not found: ${ref}`);\n const topicName = flag(rest, \"topic\");\n const topic = topicName ? getTopicByNameForUser(topicName, USER_ID) : null;\n if (topicName && !topic?.agent)\n throw new Error(`topic not found or has no agent: ${topicName}`);\n const scriptTouched = hasFlag(rest, \"script\");\n const script = flag(rest, \"script\")?.trim() || undefined;\n if (script && !cronScriptExists(script))\n throw new Error(`cron script not found: ${script}`);\n if (script && promptParts.length > 0)\n throw new Error(\"provide a prompt or --script, not both\");\n const sourceTouched = scriptTouched || promptParts.length > 0;\n const nextPrompt =\n promptParts.length > 0 ? promptParts.join(\" \") : sourceTouched ? undefined : job.prompt;\n const nextScript = scriptTouched ? script : promptParts.length > 0 ? undefined : job.script;\n if (sourceTouched && Boolean(nextPrompt) === Boolean(nextScript)) {\n throw new Error(\"cron job requires exactly one of prompt or script\");\n }\n\n const agentRaw = flag(rest, \"agent\");\n const agent = agentRaw === \"default\" ? undefined : agentRaw;\n if (agent && !isAgentKind(agent)) throw new Error(`invalid agent: ${agent}`);\n const effectiveAgent =\n (isAgentKind(agent) ? agent : hasFlag(rest, \"agent\") ? undefined : job.agent) ??\n topic?.agent ??\n getTopic(job.topicId)?.agent;\n const modelRaw = flag(rest, \"model\");\n const model = modelRaw === \"default\" ? undefined : modelRaw?.trim() || undefined;\n const effortRaw = flag(rest, \"effort\");\n const effort = effortRaw === \"default\" ? undefined : effortRaw;\n if (effort && !(EFFORT_VALUES as readonly string[]).includes(effort)) {\n throw new Error(`invalid effort: ${effort}`);\n }\n if (effectiveAgent) {\n const registry = getRegistry(effectiveAgent);\n if (model && !registry.validateModel(model)) {\n throw new Error(`model '${model}' is invalid for ${effectiveAgent}`);\n }\n if (effort && !registry.validateEffort(effort as EffortLevel)) {\n throw new Error(`effort '${effort}' is invalid for ${effectiveAgent}`);\n }\n }\n\n const updated = await updateCronJobWithContextReset(job.id, {\n name: flag(rest, \"name\"),\n topicId: topic?.id,\n prompt: sourceTouched ? (nextPrompt ?? null) : undefined,\n script: sourceTouched ? (nextScript ?? null) : undefined,\n summary: sourceTouched ? null : undefined,\n schedule: flag(rest, \"schedule\"),\n timezone: hasFlag(rest, \"timezone\") ? flag(rest, \"timezone\") || null : undefined,\n agent: hasFlag(rest, \"agent\") ? (isAgentKind(agent) ? agent : null) : undefined,\n model: hasFlag(rest, \"model\") ? (model ?? null) : undefined,\n effort: hasFlag(rest, \"effort\")\n ? ((effort as EffortLevel | undefined) ?? null)\n : undefined,\n });\n if (!updated) throw new Error(`cron job not found: ${ref}`);\n if (sourceTouched && updated.prompt) queueCronPromptSummary(updated.id, updated.prompt);\n printJob(updated);\n return;\n }\n case \"inspect\":\n case \"logs\": {\n const ref = positional(rest)[0];\n if (!ref) throw new Error(\"provide a cron name or id\");\n const job = resolveJob(ref);\n if (!job || job.ownerUserId !== USER_ID) throw new Error(`cron job not found: ${ref}`);\n console.log(JSON.stringify({ ...job, runs: listCronRuns(job.id, 20) }, null, 2));\n return;\n }\n case \"run\":\n case \"pause\":\n case \"resume\":\n case \"restart\":\n case \"kill\":\n case \"reset\":\n case \"delete\": {\n const ref = positional(rest)[0];\n if (!ref) throw new Error(\"provide a cron name or id\");\n const job = resolveJob(ref);\n if (!job || job.ownerUserId !== USER_ID) throw new Error(`cron job not found: ${ref}`);\n if (sub === \"run\") {\n if (!job.enabled) throw new Error(\"job is disabled; resume it first\");\n console.log(`queued ${requestCronRun(job.id)}`);\n }\n if (sub === \"pause\") printJob(setCronJobEnabled(job.id, false)!);\n if (sub === \"resume\") printJob(setCronJobEnabled(job.id, true)!);\n if (sub === \"restart\") printJob(setCronJobEnabled(job.id, true)!);\n if (sub === \"kill\") console.log(`cancellation queued ${requestCronCancel(job.id)}`);\n if (sub === \"reset\") {\n await resetCronTopicContext(job.topicId);\n console.log(`reset shared Cron context for topic ${job.topicId}`);\n }\n if (sub === \"delete\") {\n deleteCronJob(job.id);\n console.log(`deleted ${job.name}`);\n }\n return;\n }\n default:\n usage();\n process.exitCode = 1;\n }\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "7C47FA5396346C1764756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,33 @@
1
+ // ../../adapters/otium/src/protocol.ts
2
+ var PEER_PROTOCOL_VERSION = 1;
3
+ var MAX_PEER_MESSAGE_LENGTH = 1e4;
4
+ var MAX_PEER_INPUT_FILE_BYTES = 2 * 1024 * 1024 * 1024;
5
+ var MAX_PEER_INPUT_REQUEST_BYTES = MAX_PEER_INPUT_FILE_BYTES + 8 * 1024 * 1024;
6
+ function str(body, field) {
7
+ const value = body[field];
8
+ return typeof value === "string" && value.trim() ? value : null;
9
+ }
10
+ function parseExecutionSpec(value) {
11
+ if (!value || typeof value !== "object" || Array.isArray(value))
12
+ return null;
13
+ const raw = value;
14
+ const agent = str(raw, "agent");
15
+ const model = str(raw, "model");
16
+ const effort = str(raw, "effort");
17
+ const rawMcp = raw.mcp;
18
+ if (!agent || !model || !effort || !Array.isArray(rawMcp) || !rawMcp.every((entry) => typeof entry === "string" && entry.trim().length > 0) || typeof raw.canSpawnSubagents !== "boolean") {
19
+ return null;
20
+ }
21
+ return {
22
+ agent,
23
+ model,
24
+ effort,
25
+ ...str(raw, "description") ? { description: str(raw, "description") } : {},
26
+ mcp: [...new Set(rawMcp.map((entry) => entry.trim()))],
27
+ canSpawnSubagents: raw.canSpawnSubagents
28
+ };
29
+ }
30
+
31
+ export { PEER_PROTOCOL_VERSION, MAX_PEER_MESSAGE_LENGTH, MAX_PEER_INPUT_FILE_BYTES, MAX_PEER_INPUT_REQUEST_BYTES, parseExecutionSpec };
32
+
33
+ //# debugId=536F85318EA03AF964756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../adapters/otium/src/protocol.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * otium peer protocol — payload shapes exchanged between workspace runtime\n * nodes. Field-for-field copy of otium's\n * `apps/runtime-api/src/peer/protocol.ts` (PEER_PROTOCOL_VERSION 1): the hub\n * trusts these shapes without schema validation, so this file must track the\n * otium side exactly.\n */\n\nexport const PEER_PROTOCOL_VERSION = 1;\n\nexport interface PeerFeatureCapabilities {\n remoteAsk: boolean;\n inputFiles: boolean;\n outputFiles: boolean;\n visualBridge: boolean;\n}\n\nexport const MAX_PEER_MESSAGE_LENGTH = 10_000;\nexport const MAX_PEER_INPUT_FILE_BYTES = 2 * 1024 * 1024 * 1024;\nexport const MAX_PEER_INPUT_REQUEST_BYTES = MAX_PEER_INPUT_FILE_BYTES + 8 * 1024 * 1024;\n\nexport interface PeerSessionEntry {\n topicId: string;\n name: string;\n agent: string | null;\n hasSession: boolean;\n description?: string;\n}\n\n/** Fully-resolved room configuration pinned by the hub for one worker turn.\n * The worker must not re-resolve these values from its hidden mirror topic. */\nexport interface PlacedTopicExecutionSpec {\n agent: string;\n model: string;\n effort: string;\n description?: string;\n /** Optional MCP whitelist after hub-side topic override resolution. */\n mcp: string[];\n /** Preserves the host room's real top-level delegation capability. */\n canSpawnSubagents: boolean;\n}\n\n/** Hub → worker: run one turn of a hub-hosted agent room. */\nexport interface PeerTurnRequest {\n v: number;\n requestId: string;\n userId: string;\n /** The hub's canonical topic id — the worker keys its hidden topic on it. */\n hostTopicId: string;\n /** Room title, mirrored onto the hidden topic so tell/ask by title works. */\n topicTitle: string;\n execution?: PlacedTopicExecutionSpec;\n /** Legacy additive fields accepted during rolling upgrades. */\n agent?: string;\n model?: string;\n effort?: string;\n /** File ids already copied into the worker hidden topic's upload store. */\n attachments?: string[];\n message: string;\n}\n\n/** Hub → worker: create/update the hidden mirror before the first turn. */\nexport interface PeerProvisionRequest {\n v: number;\n userId: string;\n hostTopicId: string;\n topicTitle: string;\n execution: PlacedTopicExecutionSpec;\n}\n\n/** Worker → hub: one coarse turn event, ordered by seq per requestId. */\nexport interface PeerEventRequest {\n v: number;\n requestId: string;\n seq: number;\n event: Record<string, unknown>;\n}\n\nfunction str(body: Record<string, unknown>, field: string): string | null {\n const value = body[field];\n return typeof value === \"string\" && value.trim() ? value : null;\n}\n\n/** Parse and normalize an inbound execution spec (mirrors otium routes.ts). */\nexport function parseExecutionSpec(value: unknown): PlacedTopicExecutionSpec | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n const raw = value as Record<string, unknown>;\n const agent = str(raw, \"agent\");\n const model = str(raw, \"model\");\n const effort = str(raw, \"effort\");\n const rawMcp = raw.mcp;\n if (\n !agent ||\n !model ||\n !effort ||\n !Array.isArray(rawMcp) ||\n !rawMcp.every((entry) => typeof entry === \"string\" && entry.trim().length > 0) ||\n typeof raw.canSpawnSubagents !== \"boolean\"\n ) {\n return null;\n }\n return {\n agent,\n model,\n effort,\n ...(str(raw, \"description\") ? { description: str(raw, \"description\") as string } : {}),\n mcp: [...new Set(rawMcp.map((entry) => entry.trim()))],\n canSpawnSubagents: raw.canSpawnSubagents,\n };\n}\n"
6
+ ],
7
+ "mappings": ";AAQO,IAAM,wBAAwB;AAS9B,IAAM,0BAA0B;AAChC,IAAM,4BAA4B,IAAI,OAAO,OAAO;AACpD,IAAM,+BAA+B,4BAA4B,IAAI,OAAO;AA2DnF,SAAS,GAAG,CAAC,MAA+B,OAA8B;AAAA,EACxE,MAAM,QAAQ,KAAK;AAAA,EACnB,OAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAAA;AAItD,SAAS,kBAAkB,CAAC,OAAiD;AAAA,EAClF,KAAK,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK;AAAA,IAAG,OAAO;AAAA,EACxE,MAAM,MAAM;AAAA,EACZ,MAAM,QAAQ,IAAI,KAAK,OAAO;AAAA,EAC9B,MAAM,QAAQ,IAAI,KAAK,OAAO;AAAA,EAC9B,MAAM,SAAS,IAAI,KAAK,QAAQ;AAAA,EAChC,MAAM,SAAS,IAAI;AAAA,EACnB,KACG,UACA,UACA,WACA,MAAM,QAAQ,MAAM,MACpB,OAAO,MAAM,CAAC,UAAU,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,CAAC,KAC7E,OAAO,IAAI,sBAAsB,WACjC;AAAA,IACA,OAAO;AAAA,EACT;AAAA,EACA,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,OACI,IAAI,KAAK,aAAa,IAAI,EAAE,aAAa,IAAI,KAAK,aAAa,EAAY,IAAI,CAAC;AAAA,IACpF,KAAK,CAAC,GAAG,IAAI,IAAI,OAAO,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,CAAC,CAAC;AAAA,IACrD,mBAAmB,IAAI;AAAA,EACzB;AAAA;",
8
+ "debugId": "536F85318EA03AF964756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,147 @@
1
+ // @bun
2
+ import"./chunk-sn51wnf1.js";
3
+ import"./chunk-x1arr7ze.js";
4
+ import"./chunk-g7pvfj5e.js";
5
+ import"./chunk-z2xy5n2q.js";
6
+ import"./chunk-sgsz2fpq.js";
7
+ import"./chunk-1k7t7bnx.js";
8
+ import"./chunk-nmv41qx9.js";
9
+ import"./chunk-h8vmb9gf.js";
10
+ import"./chunk-6hknphtc.js";
11
+ import"./chunk-kbm35t7w.js";
12
+ import"./chunk-f97qybwk.js";
13
+ import"./chunk-hgw7n8cm.js";
14
+ import"./chunk-nqdp52aj.js";
15
+ import"./chunk-mhty3nna.js";
16
+ import"./chunk-vp2db42d.js";
17
+ import"./chunk-hsq7j9jk.js";
18
+ import"./chunk-n0kk5xbq.js";
19
+ import"./chunk-2dgkah1z.js";
20
+ import"./chunk-pkdmypdd.js";
21
+ import"./chunk-j0kjscb7.js";
22
+ import"./chunk-efyxk97v.js";
23
+ import"./chunk-1msje2zf.js";
24
+ import"./chunk-f5xgprg1.js";
25
+ import {
26
+ logger
27
+ } from "./chunk-k9z47mab.js";
28
+ import"./chunk-hwy3n31w.js";
29
+
30
+ // ../../adapters/otium/src/central.ts
31
+ var NODES_CACHE_MS = 30000;
32
+ var VERIFY_CACHE_MS = 30000;
33
+ var joinConfig = null;
34
+ var nodesCache = null;
35
+ var verifyCache = new Map;
36
+ var tokenCache = new Map;
37
+ function configureOtiumCentral(join) {
38
+ joinConfig = join;
39
+ resetPeerCentralCaches();
40
+ }
41
+ function otiumCentralConfig() {
42
+ return joinConfig;
43
+ }
44
+ function centralFetch(path, init) {
45
+ if (!joinConfig)
46
+ throw new Error("otium: join credentials missing");
47
+ return fetch(`${joinConfig.central}${path}`, {
48
+ ...init,
49
+ headers: {
50
+ authorization: `Bearer ${joinConfig.secret}`,
51
+ "content-type": "application/json",
52
+ ...init.headers ?? {}
53
+ },
54
+ signal: AbortSignal.timeout(5000)
55
+ });
56
+ }
57
+ async function listPeerNodes(opts = {}) {
58
+ if (!opts.fresh && nodesCache && Date.now() - nodesCache.at < NODES_CACHE_MS) {
59
+ return nodesCache.nodes;
60
+ }
61
+ const response = await centralFetch("/peer/nodes", { method: "GET" });
62
+ const body = await response.json();
63
+ if (!response.ok || !body.ok || !Array.isArray(body.nodes)) {
64
+ throw new Error(`otium: node discovery failed: ${body.error ?? response.status}`);
65
+ }
66
+ nodesCache = { nodes: body.nodes, workspaceId: body.workspaceId ?? "", at: Date.now() };
67
+ return body.nodes;
68
+ }
69
+ async function selfPeerNode() {
70
+ const nodes = await listPeerNodes();
71
+ return nodes.find((node) => node.self) ?? null;
72
+ }
73
+ async function resolvePeerNodeByCellId(cellId) {
74
+ const find = (nodes) => nodes.find((node) => node.cellId === cellId) ?? null;
75
+ const cached = find(await listPeerNodes());
76
+ if (cached)
77
+ return cached;
78
+ return find(await listPeerNodes({ fresh: true }));
79
+ }
80
+ async function mintPeerToken(toCellId) {
81
+ const cached = tokenCache.get(toCellId);
82
+ if (cached && cached.expiresAtMs - Date.now() > 30000)
83
+ return cached.token;
84
+ const response = await centralFetch("/peer/token", {
85
+ method: "POST",
86
+ body: JSON.stringify({ toCellId })
87
+ });
88
+ const body = await response.json();
89
+ if (!response.ok || !body.ok || !body.token) {
90
+ throw new Error(`otium: peer token mint failed: ${body.error ?? response.status}`);
91
+ }
92
+ tokenCache.set(toCellId, {
93
+ token: body.token,
94
+ expiresAtMs: Date.parse(body.expiresAt ?? "") || Date.now() + 60000
95
+ });
96
+ return body.token;
97
+ }
98
+ async function verifyPeerToken(token) {
99
+ const cached = verifyCache.get(token);
100
+ if (cached && Date.now() - cached.at < VERIFY_CACHE_MS)
101
+ return cached.verified;
102
+ let response;
103
+ try {
104
+ response = await centralFetch("/peer/verify", {
105
+ method: "POST",
106
+ body: JSON.stringify({ token })
107
+ });
108
+ } catch (err) {
109
+ logger.warn({ err }, "otium: central verify unreachable");
110
+ return null;
111
+ }
112
+ const body = await response.json().catch(() => null);
113
+ if (!response.ok || !body?.ok)
114
+ return null;
115
+ const verified = {
116
+ workspaceId: body.workspaceId,
117
+ fromCellId: body.fromCellId,
118
+ fromNodeName: body.fromNodeName,
119
+ fromIsPrimary: body.fromIsPrimary,
120
+ expiresAt: body.expiresAt
121
+ };
122
+ verifyCache.set(token, { verified, at: Date.now() });
123
+ for (const [key, entry] of verifyCache) {
124
+ if (Date.now() - entry.at > VERIFY_CACHE_MS)
125
+ verifyCache.delete(key);
126
+ }
127
+ return verified;
128
+ }
129
+ function resetPeerCentralCaches() {
130
+ nodesCache = null;
131
+ verifyCache.clear();
132
+ tokenCache.clear();
133
+ }
134
+ export {
135
+ verifyPeerToken,
136
+ selfPeerNode,
137
+ resolvePeerNodeByCellId,
138
+ resetPeerCentralCaches,
139
+ otiumCentralConfig,
140
+ mintPeerToken,
141
+ listPeerNodes,
142
+ configureOtiumCentral
143
+ };
144
+
145
+ export { configureOtiumCentral, otiumCentralConfig, listPeerNodes, selfPeerNode, resolvePeerNodeByCellId, mintPeerToken, verifyPeerToken, resetPeerCentralCaches };
146
+
147
+ //# debugId=2A6DD94C7F57CC9C64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../adapters/otium/src/central.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Central peer API client — token mint/verify and node discovery for this\n * cell. Port of otium's `apps/runtime-api/src/peer/central.ts` (identical\n * cache policy) with `hostedRuntimeConfig()` replaced by the adapter's join\n * credentials. The cell secret never leaves this process; peers only ever see\n * the short-lived `ptk_…` tokens central mints here.\n */\n\nimport { logger } from \"@negotium/core\";\nimport type { OtiumJoin } from \"@/join\";\n\nexport interface PeerNode {\n cellId: string;\n nodeName: string | null;\n isPrimary: boolean;\n baseUrl: string;\n self: boolean;\n}\n\nexport interface VerifiedPeer {\n workspaceId: string;\n fromCellId: string;\n fromNodeName: string | null;\n fromIsPrimary: boolean;\n expiresAt: string;\n}\n\nconst NODES_CACHE_MS = 30_000;\n/** Positive verify cache — deliberately short so assignment revocation bites\n * within seconds, not the token TTL. */\nconst VERIFY_CACHE_MS = 30_000;\n\nlet joinConfig: OtiumJoin | null = null;\nlet nodesCache: { nodes: PeerNode[]; workspaceId: string; at: number } | null = null;\nconst verifyCache = new Map<string, { verified: VerifiedPeer; at: number }>();\nconst tokenCache = new Map<string, { token: string; expiresAtMs: number }>();\n\n/** Install (or clear) the join credentials every central call authenticates with. */\nexport function configureOtiumCentral(join: OtiumJoin | null): void {\n joinConfig = join;\n resetPeerCentralCaches();\n}\n\nexport function otiumCentralConfig(): OtiumJoin | null {\n return joinConfig;\n}\n\nfunction centralFetch(path: string, init: RequestInit): Promise<Response> {\n if (!joinConfig) throw new Error(\"otium: join credentials missing\");\n return fetch(`${joinConfig.central}${path}`, {\n ...init,\n headers: {\n authorization: `Bearer ${joinConfig.secret}`,\n \"content-type\": \"application/json\",\n ...(init.headers ?? {}),\n },\n signal: AbortSignal.timeout(5000),\n });\n}\n\n/** Active nodes of this cell's workspace (30s cache; pass fresh=true after a\n * resolution miss so a just-attached node is visible). */\nexport async function listPeerNodes(opts: { fresh?: boolean } = {}): Promise<PeerNode[]> {\n if (!opts.fresh && nodesCache && Date.now() - nodesCache.at < NODES_CACHE_MS) {\n return nodesCache.nodes;\n }\n const response = await centralFetch(\"/peer/nodes\", { method: \"GET\" });\n const body = (await response.json()) as {\n ok: boolean;\n error?: string;\n workspaceId?: string;\n nodes?: PeerNode[];\n };\n if (!response.ok || !body.ok || !Array.isArray(body.nodes)) {\n throw new Error(`otium: node discovery failed: ${body.error ?? response.status}`);\n }\n nodesCache = { nodes: body.nodes, workspaceId: body.workspaceId ?? \"\", at: Date.now() };\n return body.nodes;\n}\n\nexport async function selfPeerNode(): Promise<PeerNode | null> {\n const nodes = await listPeerNodes();\n return nodes.find((node) => node.self) ?? null;\n}\n\nexport async function resolvePeerNodeByCellId(cellId: string): Promise<PeerNode | null> {\n const find = (nodes: PeerNode[]) => nodes.find((node) => node.cellId === cellId) ?? null;\n const cached = find(await listPeerNodes());\n if (cached) return cached;\n return find(await listPeerNodes({ fresh: true }));\n}\n\n/** Mint (or reuse a still-fresh) peer token for one target cell. Tokens are\n * reusable within their TTL, so cache until shortly before expiry. */\nexport async function mintPeerToken(toCellId: string): Promise<string> {\n const cached = tokenCache.get(toCellId);\n if (cached && cached.expiresAtMs - Date.now() > 30_000) return cached.token;\n const response = await centralFetch(\"/peer/token\", {\n method: \"POST\",\n body: JSON.stringify({ toCellId }),\n });\n const body = (await response.json()) as {\n ok: boolean;\n error?: string;\n token?: string;\n expiresAt?: string;\n };\n if (!response.ok || !body.ok || !body.token) {\n throw new Error(`otium: peer token mint failed: ${body.error ?? response.status}`);\n }\n tokenCache.set(toCellId, {\n token: body.token,\n expiresAtMs: Date.parse(body.expiresAt ?? \"\") || Date.now() + 60_000,\n });\n return body.token;\n}\n\n/** Verify an inbound peer token against central (fail-closed on outages). */\nexport async function verifyPeerToken(token: string): Promise<VerifiedPeer | null> {\n const cached = verifyCache.get(token);\n if (cached && Date.now() - cached.at < VERIFY_CACHE_MS) return cached.verified;\n let response: Response;\n try {\n response = await centralFetch(\"/peer/verify\", {\n method: \"POST\",\n body: JSON.stringify({ token }),\n });\n } catch (err) {\n logger.warn({ err }, \"otium: central verify unreachable\");\n return null;\n }\n const body = (await response.json().catch(() => null)) as (VerifiedPeer & { ok: boolean }) | null;\n if (!response.ok || !body?.ok) return null;\n const verified: VerifiedPeer = {\n workspaceId: body.workspaceId,\n fromCellId: body.fromCellId,\n fromNodeName: body.fromNodeName,\n fromIsPrimary: body.fromIsPrimary,\n expiresAt: body.expiresAt,\n };\n verifyCache.set(token, { verified, at: Date.now() });\n // Opportunistic sweep — the cache only ever holds a handful of live tokens.\n for (const [key, entry] of verifyCache) {\n if (Date.now() - entry.at > VERIFY_CACHE_MS) verifyCache.delete(key);\n }\n return verified;\n}\n\n/** Test hook — caches are module singletons. */\nexport function resetPeerCentralCaches(): void {\n nodesCache = null;\n verifyCache.clear();\n tokenCache.clear();\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "2A6DD94C7F57CC9C64756E2164756E21",
9
+ "names": []
10
+ }