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
package/dist/main.js CHANGED
@@ -1,24 +1,136 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- var __create = Object.create;
4
- var __getProtoOf = Object.getPrototypeOf;
5
- var __defProp = Object.defineProperty;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __toESM = (mod, isNodeMode, target) => {
9
- target = mod != null ? __create(__getProtoOf(mod)) : {};
10
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
- for (let key of __getOwnPropNames(mod))
12
- if (!__hasOwnProp.call(to, key))
13
- __defProp(to, key, {
14
- get: () => mod[key],
15
- enumerable: true
16
- });
17
- return to;
18
- };
19
- var __require = import.meta.require;
3
+ import {
4
+ loadOtiumCli,
5
+ loadTelegramCli,
6
+ loadTerminalCli
7
+ } from "./chunk-8g0c4as0.js";
8
+ import {
9
+ __require,
10
+ __toESM
11
+ } from "./chunk-hwy3n31w.js";
20
12
 
21
- // src/main.ts
22
- await import("@negotium/cli");
13
+ // ../cli/src/main.ts
14
+ var [, , command, ...args] = process.argv;
15
+ function numericOption(values, name, fallback) {
16
+ const prefix = `--${name}=`;
17
+ const parsed = Number.parseInt(values.find((value) => value.startsWith(prefix))?.slice(prefix.length) ?? "", 10);
18
+ return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
19
+ }
20
+ switch (command) {
21
+ case "init": {
22
+ const { initCommand } = await import("./chunk-vefj3880.js");
23
+ initCommand();
24
+ break;
25
+ }
26
+ case "chat": {
27
+ const { chatCommand } = await import("./chunk-mmgh670x.js");
28
+ await chatCommand(args);
29
+ break;
30
+ }
31
+ case "serve": {
32
+ const { startDefaultNode } = await import("./chunk-rmyfmrrr.js");
33
+ const node = await startDefaultNode({
34
+ port: numericOption(args, "port", 7777),
35
+ advertise: true,
36
+ singleton: true
37
+ });
38
+ console.log(`negotium node listening on 127.0.0.1:${node.port} (ctrl-c to stop)`);
39
+ await node.completed;
40
+ break;
41
+ }
42
+ case "__node-daemon": {
43
+ const { runNodeDaemon } = await import("./chunk-rmyfmrrr.js");
44
+ await runNodeDaemon({ port: numericOption(args, "port", 0) });
45
+ break;
46
+ }
47
+ case "status": {
48
+ const { inspectNodeDaemon } = await import("./chunk-rmyfmrrr.js");
49
+ const status = await inspectNodeDaemon();
50
+ if (status.running && status.info) {
51
+ console.log(`negotium node running (pid ${status.info.pid}, 127.0.0.1:${status.info.port}, since ${status.info.startedAt})`);
52
+ } else if (status.info) {
53
+ console.log(`negotium node not responding (last pid ${status.info.pid}${status.error ? `: ${status.error}` : ""})`);
54
+ process.exitCode = 1;
55
+ } else {
56
+ console.log("negotium node is stopped");
57
+ }
58
+ break;
59
+ }
60
+ case "stop": {
61
+ const { stopNodeDaemon } = await import("./chunk-rmyfmrrr.js");
62
+ const stopped = await stopNodeDaemon();
63
+ console.log(stopped ? "negotium node shutdown requested" : "negotium node is not running");
64
+ break;
65
+ }
66
+ case "topics": {
67
+ const { topicsCommand } = await import("./chunk-3vn2vnj1.js");
68
+ topicsCommand();
69
+ break;
70
+ }
71
+ case "mcp": {
72
+ const { mcpCommand } = await import("./chunk-jzz91jwh.js");
73
+ mcpCommand(args);
74
+ break;
75
+ }
76
+ case "vault": {
77
+ const { vaultCommand } = await import("./chunk-f1pzh3xh.js");
78
+ vaultCommand(args);
79
+ break;
80
+ }
81
+ case "cron": {
82
+ const { cronCommand } = await import("./chunk-9spx3h9n.js");
83
+ await cronCommand(args);
84
+ break;
85
+ }
86
+ case "terminal": {
87
+ const { runTerminalCli } = await loadTerminalCli();
88
+ await runTerminalCli(args);
89
+ break;
90
+ }
91
+ case "telegram": {
92
+ const { runTelegramCli } = await loadTelegramCli();
93
+ await runTelegramCli();
94
+ break;
95
+ }
96
+ case "otium": {
97
+ const { runOtiumCli } = await loadOtiumCli();
98
+ await runOtiumCli(args);
99
+ break;
100
+ }
101
+ case "start":
102
+ case "adapters": {
103
+ const { adaptersCommand } = await import("./chunk-3t4dtm26.js");
104
+ await adaptersCommand(args);
105
+ break;
106
+ }
107
+ default: {
108
+ console.log([
109
+ "negotium \u2014 turn this computer into an agent node",
110
+ "",
111
+ "usage: negotium <init|chat|serve|status|stop|topics|mcp|vault|cron|terminal|telegram|otium|start> [args]",
112
+ "",
113
+ " init bootstrap ~/.negotium and check agent auth",
114
+ " chat [topic] interactive chat (creates the topic if missing)",
115
+ " options: --agent=claude|codex|maestro",
116
+ " serve foreground long-lived node (default port 7777)",
117
+ " status show the local long-lived node status",
118
+ " stop stop the local long-lived node",
119
+ " topics list topics on this node",
120
+ " mcp list|add|remove|enable|disable manage node MCP manifest",
121
+ " vault list|set|get|del node secret store (encrypted at rest)",
122
+ " cron list|create|inspect|logs|run|pause|resume|restart|kill|reset|delete",
123
+ " terminal TUI client; auto-starts and connects to the local node",
124
+ " options: --embedded, --connect=http://host:port, --port=N",
125
+ " telegram Telegram adapter (configured from environment)",
126
+ " otium join|serve join an Otium workspace or serve its worker routes",
127
+ " start <terminal|telegram|otium> run one channel process",
128
+ " Terminal clients may run more than once and share one node"
129
+ ].join(`
130
+ `));
131
+ if (command && command !== "help" && command !== "--help")
132
+ process.exitCode = 1;
133
+ }
134
+ }
23
135
 
24
- //# debugId=3DC0E0AF6AED4BA564756E2164756E21
136
+ //# debugId=29B5A521D233AB8564756E2164756E21
package/dist/main.js.map CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/main.ts"],
3
+ "sources": ["../../cli/src/main.ts"],
4
4
  "sourcesContent": [
5
- "#!/usr/bin/env bun\n\n// Keep the unscoped package functional rather than using it as a name-only\n// placeholder. The canonical implementation lives in @negotium/cli.\nawait import(\"@negotium/cli\");\n\nexport {};\n"
5
+ "#!/usr/bin/env bun\n\nimport { loadOtiumCli, loadTelegramCli, loadTerminalCli } from \"@/adapter-loader\";\n\nconst [, , command, ...args] = process.argv;\n\nfunction numericOption(values: string[], name: string, fallback: number): number {\n const prefix = `--${name}=`;\n const parsed = Number.parseInt(\n values.find((value) => value.startsWith(prefix))?.slice(prefix.length) ?? \"\",\n 10,\n );\n return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;\n}\n\nswitch (command) {\n case \"init\": {\n const { initCommand } = await import(\"@/commands/init\");\n initCommand();\n break;\n }\n case \"chat\": {\n const { chatCommand } = await import(\"@/commands/chat\");\n await chatCommand(args);\n break;\n }\n case \"serve\": {\n const { startDefaultNode } = await import(\"@negotium/node\");\n const node = await startDefaultNode({\n port: numericOption(args, \"port\", 7777),\n advertise: true,\n singleton: true,\n });\n console.log(`negotium node listening on 127.0.0.1:${node.port} (ctrl-c to stop)`);\n await node.completed;\n break;\n }\n case \"__node-daemon\": {\n const { runNodeDaemon } = await import(\"@negotium/node\");\n await runNodeDaemon({ port: numericOption(args, \"port\", 0) });\n break;\n }\n case \"status\": {\n const { inspectNodeDaemon } = await import(\"@negotium/node\");\n const status = await inspectNodeDaemon();\n if (status.running && status.info) {\n console.log(\n `negotium node running (pid ${status.info.pid}, 127.0.0.1:${status.info.port}, since ${status.info.startedAt})`,\n );\n } else if (status.info) {\n console.log(\n `negotium node not responding (last pid ${status.info.pid}${status.error ? `: ${status.error}` : \"\"})`,\n );\n process.exitCode = 1;\n } else {\n console.log(\"negotium node is stopped\");\n }\n break;\n }\n case \"stop\": {\n const { stopNodeDaemon } = await import(\"@negotium/node\");\n const stopped = await stopNodeDaemon();\n console.log(stopped ? \"negotium node shutdown requested\" : \"negotium node is not running\");\n break;\n }\n case \"topics\": {\n const { topicsCommand } = await import(\"@/commands/topics\");\n topicsCommand();\n break;\n }\n case \"mcp\": {\n const { mcpCommand } = await import(\"@/commands/mcp\");\n mcpCommand(args);\n break;\n }\n case \"vault\": {\n const { vaultCommand } = await import(\"@/commands/vault\");\n vaultCommand(args);\n break;\n }\n case \"cron\": {\n const { cronCommand } = await import(\"@/commands/cron\");\n await cronCommand(args);\n break;\n }\n case \"terminal\": {\n const { runTerminalCli } = await loadTerminalCli();\n await runTerminalCli(args);\n break;\n }\n case \"telegram\": {\n const { runTelegramCli } = await loadTelegramCli();\n await runTelegramCli();\n break;\n }\n case \"otium\": {\n const { runOtiumCli } = await loadOtiumCli();\n await runOtiumCli(args);\n break;\n }\n case \"start\":\n case \"adapters\": {\n const { adaptersCommand } = await import(\"@/commands/adapters\");\n await adaptersCommand(args);\n break;\n }\n default: {\n console.log(\n [\n \"negotium — turn this computer into an agent node\",\n \"\",\n \"usage: negotium <init|chat|serve|status|stop|topics|mcp|vault|cron|terminal|telegram|otium|start> [args]\",\n \"\",\n \" init bootstrap ~/.negotium and check agent auth\",\n \" chat [topic] interactive chat (creates the topic if missing)\",\n \" options: --agent=claude|codex|maestro\",\n \" serve foreground long-lived node (default port 7777)\",\n \" status show the local long-lived node status\",\n \" stop stop the local long-lived node\",\n \" topics list topics on this node\",\n \" mcp list|add|remove|enable|disable manage node MCP manifest\",\n \" vault list|set|get|del node secret store (encrypted at rest)\",\n \" cron list|create|inspect|logs|run|pause|resume|restart|kill|reset|delete\",\n \" terminal TUI client; auto-starts and connects to the local node\",\n \" options: --embedded, --connect=http://host:port, --port=N\",\n \" telegram Telegram adapter (configured from environment)\",\n \" otium join|serve join an Otium workspace or serve its worker routes\",\n \" start <terminal|telegram|otium> run one channel process\",\n \" Terminal clients may run more than once and share one node\",\n ].join(\"\\n\"),\n );\n if (command && command !== \"help\" && command !== \"--help\") process.exitCode = 1;\n }\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;AAIA,MAAa;",
8
- "debugId": "3DC0E0AF6AED4BA564756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "29B5A521D233AB8564756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env python3
2
+ """faster-whisper wrapper used by Otium media extraction.
3
+
4
+ Usage:
5
+ faster-whisper-wrapper.py INPUT --model MODEL --language LANG --output-dir DIR --output-format txt
6
+
7
+ Arguments are the subset used by the TypeScript media extractor:
8
+ --model faster-whisper model size (e.g. medium, large-v3, turbo)
9
+ --language ISO language code (e.g. ko)
10
+ --output-dir Directory to write output files to
11
+ --output-format Output format (txt supported)
12
+
13
+ The script transcribes INPUT and writes {basename}.txt to --output-dir.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import os
20
+ import sys
21
+
22
+
23
+ def parse_args() -> argparse.Namespace:
24
+ parser = argparse.ArgumentParser()
25
+ parser.add_argument("input", help="Audio file path")
26
+ parser.add_argument("--model", default="medium", help="Whisper model size")
27
+ parser.add_argument("--language", default=None, help="Language code (e.g. ko)")
28
+ parser.add_argument("--output-dir", default=".", help="Output directory")
29
+ parser.add_argument("--output-format", default="txt", help="Output format")
30
+ return parser.parse_args()
31
+
32
+
33
+ def main() -> None:
34
+ args = parse_args()
35
+
36
+ if args.output_format != "txt":
37
+ print(f"Unsupported output format: {args.output_format}", file=sys.stderr)
38
+ sys.exit(1)
39
+
40
+ try:
41
+ from faster_whisper import WhisperModel
42
+ except ImportError:
43
+ print(
44
+ "faster-whisper not installed. Run: uv pip install faster-whisper",
45
+ file=sys.stderr,
46
+ )
47
+ sys.exit(1)
48
+
49
+ compute_type = "int8" # CPU-friendly
50
+ model_size = args.model
51
+ # Normalize common model aliases to faster-whisper size identifiers.
52
+ if "turbo" in model_size:
53
+ model_size = "turbo"
54
+ elif "large-v3" in model_size:
55
+ model_size = "large-v3"
56
+ elif "large-v2" in model_size:
57
+ model_size = "large-v2"
58
+ elif "medium" in model_size:
59
+ model_size = "medium"
60
+ elif "small" in model_size:
61
+ model_size = "small"
62
+ elif "tiny" in model_size:
63
+ model_size = "tiny"
64
+
65
+ os.makedirs(args.output_dir, exist_ok=True)
66
+
67
+ model = WhisperModel(model_size, device="cpu", compute_type=compute_type)
68
+
69
+ segments, info = model.transcribe(
70
+ args.input,
71
+ language=args.language,
72
+ beam_size=5,
73
+ vad_filter=True,
74
+ )
75
+
76
+ base = os.path.splitext(os.path.basename(args.input))[0]
77
+ out_path = os.path.join(args.output_dir, f"{base}.txt")
78
+
79
+ with open(out_path, "w", encoding="utf-8") as f:
80
+ for segment in segments:
81
+ f.write(segment.text.strip() + "\n")
82
+
83
+ print(f"Transcription written to {out_path}", file=sys.stderr)
84
+
85
+
86
+ if __name__ == "__main__":
87
+ main()
@@ -0,0 +1,349 @@
1
+ #!/usr/bin/env node
2
+ import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, resolve } from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
7
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
9
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
10
+ import {
11
+ CallToolRequestSchema,
12
+ isInitializeRequest,
13
+ ListToolsRequestSchema,
14
+ } from "@modelcontextprotocol/sdk/types.js";
15
+
16
+ function parseCli(argv = process.argv.slice(2)) {
17
+ const options = { host: "127.0.0.1" };
18
+ for (let index = 0; index < argv.length; index += 1) {
19
+ const arg = argv[index];
20
+ const [flag, inlineValue] = arg.includes("=") ? arg.split(/=(.*)/s, 2) : [arg, undefined];
21
+ const nextValue = () => inlineValue ?? argv[++index];
22
+ switch (flag) {
23
+ case "--host":
24
+ options.host = nextValue();
25
+ break;
26
+ case "--port": {
27
+ const value = Number(nextValue());
28
+ if (!Number.isInteger(value) || value <= 0) throw new Error(`Invalid --port: ${value}`);
29
+ options.port = value;
30
+ break;
31
+ }
32
+ case "--user-data-dir":
33
+ options.userDataDir = nextValue();
34
+ break;
35
+ case "--headless":
36
+ options.headless = true;
37
+ break;
38
+ case "--headed":
39
+ options.headless = false;
40
+ break;
41
+ default:
42
+ throw new Error(`Unknown argument: ${arg}`);
43
+ }
44
+ }
45
+ if (!options.port) throw new Error("--port is required");
46
+ return options;
47
+ }
48
+
49
+ const require = createRequire(import.meta.url);
50
+ const packageRoot = dirname(require.resolve("mcp-patchright/package.json"));
51
+ const [{ BrowserManager }, { handleTool }, { tools }] = await Promise.all([
52
+ import(pathToFileURL(resolve(packageRoot, "dist/browser/manager.js")).href),
53
+ import(pathToFileURL(resolve(packageRoot, "dist/tools/handlers.js")).href),
54
+ import(pathToFileURL(resolve(packageRoot, "dist/tools/registry.js")).href),
55
+ ]);
56
+
57
+ function createMcpServer(defaultStartOptions, sharedManager, owner) {
58
+ const server = new Server(
59
+ { name: "mcp-patchright", version: "0.1.0" },
60
+ { capabilities: { tools: {} } },
61
+ );
62
+ const manager = sharedManager ?? new BrowserManager(defaultStartOptions);
63
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
64
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
65
+ try {
66
+ return await manager.runAsOwner(owner, () =>
67
+ handleTool(manager, request.params.name, request.params.arguments ?? {}),
68
+ );
69
+ } catch (error) {
70
+ return {
71
+ content: [
72
+ {
73
+ type: "text",
74
+ text: error instanceof Error ? `${error.name}: ${error.message}` : String(error),
75
+ },
76
+ ],
77
+ isError: true,
78
+ };
79
+ }
80
+ });
81
+ return server;
82
+ }
83
+
84
+ function requestOwner(req) {
85
+ const owner = req.header("x-browser-owner");
86
+ if (!owner || owner.length > 256) return undefined;
87
+ return owner;
88
+ }
89
+
90
+ function sseRequestOwner(req) {
91
+ const owner = requestOwner(req) ?? (typeof req.query.owner === "string" ? req.query.owner : "");
92
+ if (!owner || owner.length > 256) return undefined;
93
+ return owner;
94
+ }
95
+
96
+ const expectedCapability = process.env.NEGOTIUM_BROWSER_CAPABILITY;
97
+ if (!expectedCapability) throw new Error("NEGOTIUM_BROWSER_CAPABILITY is required");
98
+
99
+ function safeCapabilityEqual(provided, expectedValue) {
100
+ if (!provided) return false;
101
+ const actual = Buffer.from(provided);
102
+ const expected = Buffer.from(expectedValue);
103
+ return actual.length === expected.length && timingSafeEqual(actual, expected);
104
+ }
105
+
106
+ function hasCapability(req) {
107
+ const provided = req.header("x-browser-capability");
108
+ return safeCapabilityEqual(provided, expectedCapability);
109
+ }
110
+
111
+ function hasOwnerCapability(req, owner) {
112
+ const expected = createHmac("sha256", expectedCapability).update(owner).digest("hex");
113
+ return safeCapabilityEqual(req.header("x-browser-capability"), expected);
114
+ }
115
+
116
+ function requireCapability(req, res) {
117
+ if (hasCapability(req)) return true;
118
+ res.status(401).json({ ok: false, error: "invalid browser capability" });
119
+ return false;
120
+ }
121
+
122
+ function requireOwnerCapability(req, res, owner) {
123
+ const queryCapability =
124
+ req.path === "/sse" && typeof req.query.capability === "string"
125
+ ? req.query.capability
126
+ : undefined;
127
+ const expected = createHmac("sha256", expectedCapability).update(owner).digest("hex");
128
+ if (hasOwnerCapability(req, owner) || safeCapabilityEqual(queryCapability, expected)) return true;
129
+ res.status(401).json({ ok: false, error: "invalid browser owner capability" });
130
+ return false;
131
+ }
132
+
133
+ // Egress proxy is passed via env (not argv) so credentials stay out of `ps`.
134
+ // The parent backend sets NEGOTIUM_BROWSER_PROXY_* from resolveBrowserProxy().
135
+ function proxyFromEnv() {
136
+ const server = process.env.NEGOTIUM_BROWSER_PROXY_SERVER;
137
+ if (!server) return undefined;
138
+ const proxy = { server };
139
+ if (process.env.NEGOTIUM_BROWSER_PROXY_USERNAME)
140
+ proxy.username = process.env.NEGOTIUM_BROWSER_PROXY_USERNAME;
141
+ if (process.env.NEGOTIUM_BROWSER_PROXY_PASSWORD)
142
+ proxy.password = process.env.NEGOTIUM_BROWSER_PROXY_PASSWORD;
143
+ if (process.env.NEGOTIUM_BROWSER_PROXY_BYPASS)
144
+ proxy.bypass = process.env.NEGOTIUM_BROWSER_PROXY_BYPASS;
145
+ return proxy;
146
+ }
147
+
148
+ const options = parseCli();
149
+ const proxy = proxyFromEnv();
150
+ const defaultStartOptions = {
151
+ ...(options.userDataDir ? { userDataDir: options.userDataDir } : {}),
152
+ ...(options.headless !== undefined ? { headless: options.headless } : {}),
153
+ ...(proxy ? { proxy } : {}),
154
+ };
155
+ const sharedManager = new BrowserManager(defaultStartOptions);
156
+ const app = createMcpExpressApp({ host: options.host });
157
+
158
+ const transports = {};
159
+ const servers = {};
160
+ const sessionOwners = {};
161
+ const sseTransports = {};
162
+ const sseServers = {};
163
+ const sseMessageTokens = {};
164
+ let httpServer;
165
+
166
+ app.get("/health", (_req, res) => {
167
+ res.json({
168
+ ok: true,
169
+ name: "mcp-patchright",
170
+ });
171
+ });
172
+
173
+ // Claude Code and Maestro use the legacy two-endpoint SSE transport. The
174
+ // initial connection is authenticated with the owner capability. A fresh,
175
+ // per-session token embedded in the advertised message endpoint prevents
176
+ // another loopback process from injecting JSON-RPC messages by session ID.
177
+ app.get("/sse", async (req, res) => {
178
+ const owner = sseRequestOwner(req);
179
+ if (!owner) {
180
+ res.status(400).json({ ok: false, error: "browser owner is required in SSE mode" });
181
+ return;
182
+ }
183
+ if (!requireOwnerCapability(req, res, owner)) return;
184
+
185
+ const messageToken = randomBytes(32).toString("hex");
186
+ const transport = new SSEServerTransport(
187
+ `/message?token=${encodeURIComponent(messageToken)}`,
188
+ res,
189
+ );
190
+ const server = createMcpServer(defaultStartOptions, sharedManager, owner);
191
+ sseTransports[transport.sessionId] = transport;
192
+ sseServers[transport.sessionId] = server;
193
+ sseMessageTokens[transport.sessionId] = messageToken;
194
+ transport.onclose = () => {
195
+ const sessionId = transport.sessionId;
196
+ delete sseTransports[sessionId];
197
+ delete sseMessageTokens[sessionId];
198
+ const managedServer = sseServers[sessionId];
199
+ delete sseServers[sessionId];
200
+ void managedServer?.close().catch(() => undefined);
201
+ };
202
+ await server.connect(transport);
203
+ });
204
+
205
+ app.post("/message", async (req, res) => {
206
+ const sessionId = typeof req.query.sessionId === "string" ? req.query.sessionId : "";
207
+ const messageToken = typeof req.query.token === "string" ? req.query.token : "";
208
+ const transport = sseTransports[sessionId];
209
+ const expectedMessageToken = sseMessageTokens[sessionId];
210
+ if (!transport) {
211
+ res.status(404).send("SSE session not found");
212
+ return;
213
+ }
214
+ if (!expectedMessageToken || !safeCapabilityEqual(messageToken, expectedMessageToken)) {
215
+ res.status(401).send("invalid SSE message token");
216
+ return;
217
+ }
218
+ await transport.handlePostMessage(req, res, req.body);
219
+ });
220
+
221
+ app.all("/mcp", async (req, res) => {
222
+ try {
223
+ const owner = requestOwner(req);
224
+ if (!owner) {
225
+ res.status(400).json({
226
+ jsonrpc: "2.0",
227
+ error: { code: -32600, message: "browser owner is required in HTTP mode" },
228
+ id: null,
229
+ });
230
+ return;
231
+ }
232
+ if (!requireOwnerCapability(req, res, owner)) return;
233
+ const sessionId = req.headers["mcp-session-id"];
234
+ const normalizedSessionId = Array.isArray(sessionId)
235
+ ? sessionId[0]
236
+ : typeof sessionId === "string"
237
+ ? sessionId
238
+ : undefined;
239
+ let transport;
240
+ if (normalizedSessionId && transports[normalizedSessionId]) {
241
+ const existingTransport = transports[normalizedSessionId];
242
+ if (!(existingTransport instanceof StreamableHTTPServerTransport)) {
243
+ res.status(400).json({
244
+ jsonrpc: "2.0",
245
+ error: { code: -32000, message: "Session uses a different transport protocol" },
246
+ id: null,
247
+ });
248
+ return;
249
+ }
250
+ if (sessionOwners[normalizedSessionId] !== owner) {
251
+ res.status(403).json({
252
+ jsonrpc: "2.0",
253
+ error: { code: -32001, message: "Browser owner does not match this session" },
254
+ id: null,
255
+ });
256
+ return;
257
+ }
258
+ transport = existingTransport;
259
+ } else if (!normalizedSessionId && req.method === "POST" && isInitializeRequest(req.body)) {
260
+ let server;
261
+ transport = new StreamableHTTPServerTransport({
262
+ sessionIdGenerator: () => randomUUID(),
263
+ onsessioninitialized: (newSessionId) => {
264
+ if (!transport || !server) return;
265
+ transports[newSessionId] = transport;
266
+ servers[newSessionId] = server;
267
+ sessionOwners[newSessionId] = owner;
268
+ },
269
+ });
270
+ server = createMcpServer(defaultStartOptions, sharedManager, owner);
271
+ transport.onclose = () => {
272
+ const sid = transport?.sessionId;
273
+ if (sid) {
274
+ delete transports[sid];
275
+ delete sessionOwners[sid];
276
+ const managedServer = servers[sid];
277
+ delete servers[sid];
278
+ void managedServer?.close().catch(() => undefined);
279
+ }
280
+ };
281
+ await server.connect(transport);
282
+ } else {
283
+ res.status(400).json({
284
+ jsonrpc: "2.0",
285
+ error: { code: -32000, message: "Bad Request: No valid session ID provided" },
286
+ id: null,
287
+ });
288
+ return;
289
+ }
290
+ await transport.handleRequest(req, res, req.body);
291
+ } catch (error) {
292
+ console.error("Error handling /mcp request:", error);
293
+ if (!res.headersSent) {
294
+ res.status(500).json({
295
+ jsonrpc: "2.0",
296
+ error: { code: -32603, message: "Internal server error" },
297
+ id: null,
298
+ });
299
+ }
300
+ }
301
+ });
302
+
303
+ app.delete("/owners", async (req, res) => {
304
+ if (!requireCapability(req, res)) return;
305
+ const owner = requestOwner(req);
306
+ if (!owner) {
307
+ res.status(400).json({ ok: false, error: "owner is required" });
308
+ return;
309
+ }
310
+ const closed = await sharedManager.closeOwnerPages(owner);
311
+ res.json({ ok: true, owner, closed });
312
+ });
313
+
314
+ async function shutdown() {
315
+ for (const [sessionId, transport] of Object.entries(sseTransports)) {
316
+ await transport.close().catch(() => undefined);
317
+ delete sseTransports[sessionId];
318
+ delete sseMessageTokens[sessionId];
319
+ }
320
+ for (const [sessionId, server] of Object.entries(sseServers)) {
321
+ await server.close().catch(() => undefined);
322
+ delete sseServers[sessionId];
323
+ }
324
+ for (const [sessionId, transport] of Object.entries(transports)) {
325
+ await transport.close().catch(() => undefined);
326
+ delete transports[sessionId];
327
+ delete sessionOwners[sessionId];
328
+ }
329
+ for (const [sessionId, server] of Object.entries(servers)) {
330
+ await server.close().catch(() => undefined);
331
+ delete servers[sessionId];
332
+ }
333
+ await sharedManager.close().catch(() => undefined);
334
+ await new Promise((resolveClose) => httpServer?.close(() => resolveClose()));
335
+ process.exit(0);
336
+ }
337
+
338
+ httpServer = app.listen(options.port, options.host, () => {
339
+ console.error(`mcp-patchright listening on http://${options.host}:${options.port}`);
340
+ console.error(` SSE: http://${options.host}:${options.port}/sse`);
341
+ console.error(` Streamable: http://${options.host}:${options.port}/mcp`);
342
+ });
343
+ httpServer.on("error", (error) => {
344
+ console.error("mcp-patchright HTTP server error:", error);
345
+ process.exit(1);
346
+ });
347
+
348
+ process.on("SIGINT", () => void shutdown());
349
+ process.on("SIGTERM", () => void shutdown());