@poolzin/pool-bot 2026.2.17 → 2026.2.18

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 (469) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/agents/agent-scope.js +4 -0
  3. package/dist/agents/announce-idempotency.js +14 -0
  4. package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +23 -0
  5. package/dist/agents/bash-tools.exec-runtime.js +438 -0
  6. package/dist/agents/bash-tools.shared.js +6 -0
  7. package/dist/agents/cli-runner/reliability.js +61 -0
  8. package/dist/agents/cli-watchdog-defaults.js +11 -0
  9. package/dist/agents/command-poll-backoff.js +63 -0
  10. package/dist/agents/current-time.js +16 -0
  11. package/dist/agents/model-alias-lines.js +18 -0
  12. package/dist/agents/model-auth-label.js +61 -0
  13. package/dist/agents/models-config.e2e-harness.js +115 -0
  14. package/dist/agents/ollama-stream.js +11 -3
  15. package/dist/agents/openclaw-tools.js +135 -0
  16. package/dist/agents/pi-auth-json.js +118 -0
  17. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +147 -0
  18. package/dist/agents/pi-embedded-subscribe.e2e-harness.js +90 -0
  19. package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +63 -0
  20. package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +30 -0
  21. package/dist/agents/pi-extensions/session-manager-runtime-registry.js +23 -0
  22. package/dist/agents/pi-tools.js +2 -0
  23. package/dist/agents/queued-file-writer.js +22 -0
  24. package/dist/agents/sandbox/docker.js +133 -40
  25. package/dist/agents/sandbox/fs-bridge.js +146 -0
  26. package/dist/agents/sandbox/fs-paths.js +205 -0
  27. package/dist/agents/sandbox/hash.js +4 -0
  28. package/dist/agents/sandbox-paths.js +3 -0
  29. package/dist/agents/session-dirs.js +20 -0
  30. package/dist/agents/skills/filter.js +24 -0
  31. package/dist/agents/skills/tools-dir.js +9 -0
  32. package/dist/agents/skills-install-download.js +290 -0
  33. package/dist/agents/skills-install-output.js +30 -0
  34. package/dist/agents/skills-install.download-test-utils.js +36 -0
  35. package/dist/agents/skills.e2e-test-helpers.js +13 -0
  36. package/dist/agents/subagent-announce-queue.js +59 -15
  37. package/dist/agents/subagent-depth.js +137 -0
  38. package/dist/agents/subagent-registry.js +448 -96
  39. package/dist/agents/subagent-spawn.js +262 -0
  40. package/dist/agents/test-helpers/fast-tool-stubs.js +18 -0
  41. package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +74 -0
  42. package/dist/agents/tool-display-common.js +782 -0
  43. package/dist/agents/tools/image-tool.js +1 -1
  44. package/dist/agents/tools/sessions-access.js +178 -0
  45. package/dist/agents/tools/sessions-resolution.js +206 -0
  46. package/dist/agents/tools/subagents-tool.js +616 -0
  47. package/dist/agents/workspace-dir.js +18 -0
  48. package/dist/agents/workspace-dirs.js +14 -0
  49. package/dist/agents/workspace.js +70 -0
  50. package/dist/auto-reply/heartbeat-reply-payload.js +18 -0
  51. package/dist/auto-reply/reply/commands-export-session.js +163 -0
  52. package/dist/auto-reply/reply/commands-mesh.js +245 -0
  53. package/dist/auto-reply/reply/commands-setunset.js +28 -0
  54. package/dist/auto-reply/reply/commands-slash-parse.js +31 -0
  55. package/dist/auto-reply/reply/commands-system-prompt.js +117 -0
  56. package/dist/auto-reply/reply/directive-handling.levels.js +17 -0
  57. package/dist/auto-reply/reply/directive-handling.params.js +1 -0
  58. package/dist/auto-reply/reply/directive-parsing.js +36 -0
  59. package/dist/auto-reply/reply/dispatcher-registry.js +43 -0
  60. package/dist/auto-reply/reply/elevated-unavailable.js +20 -0
  61. package/dist/auto-reply/reply/reply-delivery.js +92 -0
  62. package/dist/auto-reply/reply/session-reset-prompt.js +1 -0
  63. package/dist/auto-reply/reply/session-run-accounting.js +33 -0
  64. package/dist/auto-reply/reply.directive.directive-behavior.e2e-harness.js +115 -0
  65. package/dist/auto-reply/reply.directive.directive-behavior.e2e-mocks.js +12 -0
  66. package/dist/browser/bridge-auth-registry.js +26 -0
  67. package/dist/browser/client-actions-url.js +10 -0
  68. package/dist/browser/control-auth.js +73 -0
  69. package/dist/browser/csrf.js +64 -0
  70. package/dist/browser/http-auth.js +52 -0
  71. package/dist/browser/paths.js +37 -0
  72. package/dist/browser/proxy-files.js +32 -0
  73. package/dist/browser/pw-ai-state.js +7 -0
  74. package/dist/browser/resolved-config-refresh.js +42 -0
  75. package/dist/browser/routes/path-output.js +1 -0
  76. package/dist/browser/server-context.chrome-test-harness.js +20 -0
  77. package/dist/browser/server-middleware.js +31 -0
  78. package/dist/browser/test-port.js +16 -0
  79. package/dist/build-info.json +3 -3
  80. package/dist/canvas-host/file-resolver.js +43 -0
  81. package/dist/channels/account-summary.js +19 -0
  82. package/dist/channels/draft-stream-loop.js +77 -0
  83. package/dist/channels/plugins/account-helpers.js +26 -0
  84. package/dist/channels/telegram/allow-from.js +10 -0
  85. package/dist/cli/browser-cli-resize.js +22 -0
  86. package/dist/cli/browser-cli-shared.js +8 -0
  87. package/dist/cli/clawbot-cli.js +5 -0
  88. package/dist/cli/completion-cli.js +566 -0
  89. package/dist/cli/config-cli.js +63 -5
  90. package/dist/cli/daemon-cli/lifecycle-core.js +256 -0
  91. package/dist/cli/daemon-cli/register-service-commands.js +60 -0
  92. package/dist/cli/daemon-cli-compat.js +80 -0
  93. package/dist/cli/nodes-cli/pairing-render.js +26 -0
  94. package/dist/cli/program/action-reparse.js +17 -0
  95. package/dist/cli/program/command-registry.js +17 -0
  96. package/dist/cli/program/program-context.js +8 -0
  97. package/dist/cli/program/register.subclis.js +7 -0
  98. package/dist/cli/program/routes.js +233 -0
  99. package/dist/cli/qr-cli.js +132 -0
  100. package/dist/cli/requirements-test-fixtures.js +17 -0
  101. package/dist/cli/respawn-policy.js +4 -0
  102. package/dist/cli/shared/parse-port.js +18 -0
  103. package/dist/cli/skills-cli.format.js +241 -0
  104. package/dist/cli/update-cli/progress.js +121 -0
  105. package/dist/cli/update-cli/restart-helper.js +108 -0
  106. package/dist/cli/update-cli/shared.js +196 -0
  107. package/dist/cli/update-cli/status.js +97 -0
  108. package/dist/cli/update-cli/suppress-deprecations.js +17 -0
  109. package/dist/cli/update-cli/update-command.js +506 -0
  110. package/dist/cli/update-cli/wizard.js +130 -0
  111. package/dist/cli/update-cli.js +3 -9
  112. package/dist/cli/windows-argv.js +69 -0
  113. package/dist/commands/auth-choice-legacy.js +20 -0
  114. package/dist/commands/auth-choice.apply-helpers.js +8 -0
  115. package/dist/commands/channel-test-helpers.js +19 -0
  116. package/dist/commands/cleanup-plan.js +10 -0
  117. package/dist/commands/cleanup-utils.js +7 -0
  118. package/dist/commands/config-validation.js +15 -0
  119. package/dist/commands/doctor-completion.js +112 -0
  120. package/dist/commands/doctor-memory-search.js +119 -0
  121. package/dist/commands/doctor-session-locks.js +73 -0
  122. package/dist/commands/doctor.e2e-harness.js +364 -0
  123. package/dist/commands/gateway-presence.js +19 -0
  124. package/dist/commands/model-default.js +35 -0
  125. package/dist/commands/models/fallbacks-shared.js +102 -0
  126. package/dist/commands/models/shared.js +24 -0
  127. package/dist/commands/onboard-auth.config-gateways.js +64 -0
  128. package/dist/commands/onboard-auth.config-litellm.js +45 -0
  129. package/dist/commands/onboard-auth.config-shared.js +116 -0
  130. package/dist/commands/onboard-config.js +16 -0
  131. package/dist/commands/onboard-non-interactive.test-helpers.js +31 -0
  132. package/dist/commands/onboard-provider-auth-flags.js +136 -0
  133. package/dist/commands/openai-codex-oauth.js +40 -0
  134. package/dist/commands/test-runtime-config-helpers.js +21 -0
  135. package/dist/commands/test-wizard-helpers.js +68 -0
  136. package/dist/commands/vllm-setup.js +66 -0
  137. package/dist/compat/legacy-names.js +2 -0
  138. package/dist/config/backup-rotation.js +19 -0
  139. package/dist/config/env-preserve.js +122 -0
  140. package/dist/config/includes-scan.js +78 -0
  141. package/dist/config/plugins-allowlist.js +13 -0
  142. package/dist/config/schema.help.js +256 -0
  143. package/dist/config/schema.hints.js +189 -0
  144. package/dist/config/schema.irc.js +20 -0
  145. package/dist/config/schema.labels.js +317 -0
  146. package/dist/config/sessions/delivery-info.js +40 -0
  147. package/dist/config/types.irc.js +1 -0
  148. package/dist/config/zod-schema.agent-model.js +10 -0
  149. package/dist/config/zod-schema.allowdeny.js +35 -0
  150. package/dist/config/zod-schema.sensitive.js +4 -0
  151. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  152. package/dist/cron/isolated-agent/skills-snapshot.js +26 -0
  153. package/dist/cron/isolated-agent/subagent-followup.js +127 -0
  154. package/dist/cron/isolated-agent.mocks.js +12 -0
  155. package/dist/cron/isolated-agent.test-setup.js +22 -0
  156. package/dist/cron/legacy-delivery.js +43 -0
  157. package/dist/cron/webhook-url.js +22 -0
  158. package/dist/daemon/arg-split.js +40 -0
  159. package/dist/daemon/exec-file.js +23 -0
  160. package/dist/daemon/output.js +6 -0
  161. package/dist/daemon/runtime-format.js +31 -0
  162. package/dist/daemon/schtasks-exec.js +4 -0
  163. package/dist/daemon/service-audit.js +22 -0
  164. package/dist/discord/client.js +41 -0
  165. package/dist/discord/components-registry.js +57 -0
  166. package/dist/discord/components.js +816 -0
  167. package/dist/discord/guilds.js +12 -0
  168. package/dist/discord/monitor/gateway-plugin.js +48 -0
  169. package/dist/discord/monitor/presence.js +30 -0
  170. package/dist/discord/send.components.js +115 -0
  171. package/dist/discord/send.shared.js +4 -0
  172. package/dist/discord/ui.js +26 -0
  173. package/dist/discord/voice-message.js +254 -0
  174. package/dist/gateway/agent-event-assistant-text.js +5 -0
  175. package/dist/gateway/agent-prompt.js +33 -0
  176. package/dist/gateway/auth-rate-limit.js +136 -0
  177. package/dist/gateway/channel-health-monitor.js +114 -0
  178. package/dist/gateway/control-ui-contract.js +1 -0
  179. package/dist/gateway/control-ui-csp.js +15 -0
  180. package/dist/gateway/gateway-config-prompts.shared.js +25 -0
  181. package/dist/gateway/http-auth-helpers.js +18 -0
  182. package/dist/gateway/http-common.js +18 -0
  183. package/dist/gateway/http-endpoint-helpers.js +27 -0
  184. package/dist/gateway/node-invoke-sanitize.js +11 -0
  185. package/dist/gateway/node-invoke-system-run-approval.js +205 -0
  186. package/dist/gateway/probe-auth.js +21 -0
  187. package/dist/gateway/protocol/index.js +7 -2
  188. package/dist/gateway/protocol/schema/mesh.js +54 -0
  189. package/dist/gateway/protocol/schema/protocol-schemas.js +7 -0
  190. package/dist/gateway/protocol/schema.js +1 -0
  191. package/dist/gateway/server/ws-connection/auth-messages.js +54 -0
  192. package/dist/gateway/server-channels.js +11 -0
  193. package/dist/gateway/server-methods/attachment-normalize.js +16 -0
  194. package/dist/gateway/server-methods/base-hash.js +8 -0
  195. package/dist/gateway/server-methods/mesh.js +700 -0
  196. package/dist/gateway/server-methods/nodes.handlers.invoke-result.js +55 -0
  197. package/dist/gateway/server-methods/restart-request.js +13 -0
  198. package/dist/gateway/server-methods/validation.js +8 -0
  199. package/dist/gateway/server.agent.gateway-server-agent.mocks.js +35 -0
  200. package/dist/gateway/server.e2e-registry-helpers.js +1 -0
  201. package/dist/gateway/server.e2e-ws-harness.js +20 -0
  202. package/dist/gateway/test-helpers.js +2 -0
  203. package/dist/gateway/test-helpers.server.js +3 -1
  204. package/dist/gateway/test-http-response.js +12 -0
  205. package/dist/gateway/test-openai-responses-model.js +20 -0
  206. package/dist/gateway/test-temp-config.js +30 -0
  207. package/dist/gateway/test-with-server.js +32 -0
  208. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +46 -0
  209. package/dist/imessage/monitor/abort-handler.js +23 -0
  210. package/dist/imessage/monitor/inbound-processing.js +346 -0
  211. package/dist/imessage/monitor/parse-notification.js +64 -0
  212. package/dist/imessage/target-parsing-helpers.js +92 -0
  213. package/dist/infra/archive.js +244 -20
  214. package/dist/infra/detect-package-manager.js +26 -0
  215. package/dist/infra/exec-approvals-allowlist.js +257 -0
  216. package/dist/infra/exec-approvals-analysis.js +770 -0
  217. package/dist/infra/exec-approvals.js +13 -0
  218. package/dist/infra/file-lock.js +1 -0
  219. package/dist/infra/gemini-auth.js +39 -0
  220. package/dist/infra/heartbeat-active-hours.js +85 -0
  221. package/dist/infra/heartbeat-events-filter.js +50 -0
  222. package/dist/infra/heartbeat-runner.test-utils.js +39 -0
  223. package/dist/infra/http-body.js +265 -0
  224. package/dist/infra/install-package-dir.js +50 -0
  225. package/dist/infra/install-safe-path.js +49 -0
  226. package/dist/infra/json-files.js +49 -0
  227. package/dist/infra/jsonl-socket.js +52 -0
  228. package/dist/infra/map-size.js +14 -0
  229. package/dist/infra/net/hostname.js +7 -0
  230. package/dist/infra/npm-registry-spec.js +39 -0
  231. package/dist/infra/openclaw-root.js +109 -0
  232. package/dist/infra/outbound/delivery-queue.js +214 -0
  233. package/dist/infra/outbound/identity.js +23 -0
  234. package/dist/infra/outbound/message-action-params.js +307 -0
  235. package/dist/infra/outbound/tool-payload.js +21 -0
  236. package/dist/infra/package-json.js +23 -0
  237. package/dist/infra/pairing-files.js +19 -0
  238. package/dist/infra/pairing-token.js +9 -0
  239. package/dist/infra/path-prepend.js +51 -0
  240. package/dist/infra/process-respawn.js +49 -0
  241. package/dist/infra/runtime-status.js +16 -0
  242. package/dist/infra/session-cost-usage.types.js +1 -0
  243. package/dist/infra/session-maintenance-warning.js +89 -0
  244. package/dist/infra/system-run-command.js +78 -0
  245. package/dist/infra/tmp-openclaw-dir.js +81 -0
  246. package/dist/infra/tmp-poolbot-dir.js +2 -0
  247. package/dist/infra/update-channels.js +19 -0
  248. package/dist/line/actions.js +45 -0
  249. package/dist/line/channel-access-token.js +9 -0
  250. package/dist/line/flex-templates/basic-cards.js +332 -0
  251. package/dist/line/flex-templates/common.js +18 -0
  252. package/dist/line/flex-templates/media-control-cards.js +453 -0
  253. package/dist/line/flex-templates/message.js +10 -0
  254. package/dist/line/flex-templates/schedule-cards.js +399 -0
  255. package/dist/line/flex-templates/types.js +1 -0
  256. package/dist/line/webhook-node.js +100 -0
  257. package/dist/line/webhook-utils.js +11 -0
  258. package/dist/logging/timestamps.js +14 -0
  259. package/dist/markdown/whatsapp.js +62 -0
  260. package/dist/media/base64.js +34 -0
  261. package/dist/media/local-roots.js +32 -0
  262. package/dist/media/outbound-attachment.js +10 -0
  263. package/dist/media/read-response-with-limit.js +41 -0
  264. package/dist/media/sniff-mime-from-base64.js +19 -0
  265. package/dist/media-understanding/audio-preflight.js +67 -0
  266. package/dist/media-understanding/fs.js +13 -0
  267. package/dist/media-understanding/output-extract.js +26 -0
  268. package/dist/media-understanding/providers/audio.test-helpers.js +34 -0
  269. package/dist/media-understanding/providers/google/inline-data.js +64 -0
  270. package/dist/media-understanding/providers/shared.js +7 -0
  271. package/dist/media-understanding/runner.entries.js +459 -0
  272. package/dist/memory/batch-error-utils.js +11 -0
  273. package/dist/memory/batch-http.js +27 -0
  274. package/dist/memory/batch-output.js +29 -0
  275. package/dist/memory/batch-runner.js +22 -0
  276. package/dist/memory/batch-upload.js +23 -0
  277. package/dist/memory/batch-utils.js +26 -0
  278. package/dist/memory/embeddings-debug.js +11 -0
  279. package/dist/memory/embeddings-remote-client.js +22 -0
  280. package/dist/memory/embeddings-remote-fetch.js +14 -0
  281. package/dist/memory/manager-embedding-ops.js +616 -0
  282. package/dist/memory/manager-sync-ops.js +953 -0
  283. package/dist/memory/qmd-manager.js +1061 -0
  284. package/dist/memory/qmd-query-parser.js +107 -0
  285. package/dist/memory/qmd-scope.js +93 -0
  286. package/dist/memory/search-manager.js +0 -1
  287. package/dist/memory/sync-index.js +21 -0
  288. package/dist/memory/sync-progress.js +22 -0
  289. package/dist/memory/sync-stale.js +30 -0
  290. package/dist/memory/test-embeddings-mock.js +16 -0
  291. package/dist/memory/test-manager-helpers.js +14 -0
  292. package/dist/memory/test-runtime-mocks.js +11 -0
  293. package/dist/node-host/invoke-browser.js +177 -0
  294. package/dist/node-host/invoke.js +685 -0
  295. package/dist/pairing/setup-code.js +285 -0
  296. package/dist/plugin-sdk/account-id.js +1 -0
  297. package/dist/plugin-sdk/agent-media-payload.js +13 -0
  298. package/dist/plugin-sdk/allow-from.js +47 -0
  299. package/dist/plugin-sdk/command-auth.js +23 -0
  300. package/dist/plugin-sdk/config-paths.js +9 -0
  301. package/dist/plugin-sdk/file-lock.js +116 -0
  302. package/dist/plugin-sdk/json-store.js +31 -0
  303. package/dist/plugin-sdk/onboarding.js +28 -0
  304. package/dist/plugin-sdk/provider-auth-result.js +29 -0
  305. package/dist/plugin-sdk/slack-message-actions.js +133 -0
  306. package/dist/plugin-sdk/status-helpers.js +35 -0
  307. package/dist/plugin-sdk/text-chunking.js +31 -0
  308. package/dist/plugin-sdk/tool-send.js +12 -0
  309. package/dist/plugin-sdk/webhook-path.js +27 -0
  310. package/dist/plugin-sdk/webhook-targets.js +34 -0
  311. package/dist/plugins/hooks.test-helpers.js +21 -0
  312. package/dist/plugins/uninstall.js +171 -0
  313. package/dist/process/supervisor/adapters/child.js +143 -0
  314. package/dist/process/supervisor/adapters/env.js +13 -0
  315. package/dist/process/supervisor/adapters/pty.js +148 -0
  316. package/dist/process/supervisor/index.js +10 -0
  317. package/dist/process/supervisor/registry.js +117 -0
  318. package/dist/process/supervisor/supervisor.js +244 -0
  319. package/dist/process/supervisor/types.js +1 -0
  320. package/dist/providers/google-shared.test-helpers.js +75 -0
  321. package/dist/security/audit-channel.js +419 -0
  322. package/dist/security/audit-tool-policy.js +1 -0
  323. package/dist/security/scan-paths.js +12 -0
  324. package/dist/sessions/input-provenance.js +55 -0
  325. package/dist/sessions/session-key-utils.js +7 -0
  326. package/dist/shared/chat-content.js +31 -0
  327. package/dist/shared/chat-envelope.js +45 -0
  328. package/dist/shared/config-eval.js +117 -0
  329. package/dist/shared/device-auth.js +16 -0
  330. package/dist/shared/entry-metadata.js +9 -0
  331. package/dist/shared/entry-status.js +25 -0
  332. package/dist/shared/frontmatter.js +98 -0
  333. package/dist/shared/model-param-b.js +19 -0
  334. package/dist/shared/net/ipv4.js +17 -0
  335. package/dist/shared/node-match.js +53 -0
  336. package/dist/shared/requirements.js +128 -0
  337. package/dist/shared/subagents-format.js +84 -0
  338. package/dist/shared/usage-aggregates.js +28 -0
  339. package/dist/signal/monitor/mentions.js +45 -0
  340. package/dist/signal/rpc-context.js +19 -0
  341. package/dist/slack/blocks-fallback.js +76 -0
  342. package/dist/slack/blocks-input.js +40 -0
  343. package/dist/slack/draft-stream.js +106 -0
  344. package/dist/slack/message-actions.js +51 -0
  345. package/dist/slack/modal-metadata.js +32 -0
  346. package/dist/slack/monitor/events/interactions.js +462 -0
  347. package/dist/slack/monitor/room-context.js +17 -0
  348. package/dist/slack/stream-mode.js +41 -0
  349. package/dist/telegram/bot-native-command-menu.js +64 -0
  350. package/dist/telegram/bot.media.e2e-harness.js +81 -0
  351. package/dist/telegram/button-types.js +1 -0
  352. package/dist/telegram/group-access.js +65 -0
  353. package/dist/telegram/outbound-params.js +21 -0
  354. package/dist/telegram/poll-vote-cache.js +21 -0
  355. package/dist/terminal/health-style.js +36 -0
  356. package/dist/test-utils/chunk-test-helpers.js +21 -0
  357. package/dist/test-utils/env.js +72 -0
  358. package/dist/test-utils/exec-assertions.js +12 -0
  359. package/dist/test-utils/imessage-test-plugin.js +54 -0
  360. package/dist/test-utils/mock-http-response.js +17 -0
  361. package/dist/test-utils/vitest-mock-fn.js +1 -0
  362. package/dist/tts/tts-core.js +550 -0
  363. package/dist/utils/chunk-items.js +10 -0
  364. package/dist/utils/reaction-level.js +52 -0
  365. package/dist/utils/safe-json.js +22 -0
  366. package/dist/utils/with-timeout.js +14 -0
  367. package/dist/web/media.js +17 -5
  368. package/dist/whatsapp/resolve-outbound-target.js +42 -0
  369. package/dist/wizard/onboarding.completion.js +74 -0
  370. package/extensions/bluebubbles/src/account-resolve.ts +29 -0
  371. package/extensions/bluebubbles/src/monitor-normalize.ts +796 -0
  372. package/extensions/bluebubbles/src/monitor-processing.ts +1007 -0
  373. package/extensions/bluebubbles/src/monitor-reply-cache.ts +185 -0
  374. package/extensions/bluebubbles/src/monitor-shared.ts +51 -0
  375. package/extensions/bluebubbles/src/multipart.ts +32 -0
  376. package/extensions/bluebubbles/src/send-helpers.ts +53 -0
  377. package/extensions/bluebubbles/src/test-harness.ts +50 -0
  378. package/extensions/bluebubbles/src/test-mocks.ts +11 -0
  379. package/extensions/device-pair/index.ts +554 -0
  380. package/extensions/discord/src/channel.js +366 -0
  381. package/extensions/discord/src/runtime.js +10 -0
  382. package/extensions/feishu/index.ts +63 -0
  383. package/extensions/feishu/src/accounts.ts +114 -0
  384. package/extensions/feishu/src/bitable.ts +739 -0
  385. package/extensions/feishu/src/bot.ts +965 -0
  386. package/extensions/feishu/src/channel.ts +351 -0
  387. package/extensions/feishu/src/client.ts +118 -0
  388. package/extensions/feishu/src/config-schema.ts +206 -0
  389. package/extensions/feishu/src/dedup.ts +33 -0
  390. package/extensions/feishu/src/directory.ts +177 -0
  391. package/extensions/feishu/src/doc-schema.ts +47 -0
  392. package/extensions/feishu/src/docx.ts +536 -0
  393. package/extensions/feishu/src/drive-schema.ts +46 -0
  394. package/extensions/feishu/src/drive.ts +227 -0
  395. package/extensions/feishu/src/dynamic-agent.ts +131 -0
  396. package/extensions/feishu/src/media.ts +449 -0
  397. package/extensions/feishu/src/mention.ts +126 -0
  398. package/extensions/feishu/src/monitor.ts +330 -0
  399. package/extensions/feishu/src/onboarding.ts +359 -0
  400. package/extensions/feishu/src/outbound.ts +55 -0
  401. package/extensions/feishu/src/perm-schema.ts +52 -0
  402. package/extensions/feishu/src/perm.ts +173 -0
  403. package/extensions/feishu/src/policy.ts +84 -0
  404. package/extensions/feishu/src/probe.ts +44 -0
  405. package/extensions/feishu/src/reactions.ts +160 -0
  406. package/extensions/feishu/src/reply-dispatcher.ts +239 -0
  407. package/extensions/feishu/src/runtime.ts +14 -0
  408. package/extensions/feishu/src/send-result.ts +29 -0
  409. package/extensions/feishu/src/send.ts +335 -0
  410. package/extensions/feishu/src/streaming-card.ts +223 -0
  411. package/extensions/feishu/src/targets.ts +78 -0
  412. package/extensions/feishu/src/tools-config.ts +21 -0
  413. package/extensions/feishu/src/types.ts +81 -0
  414. package/extensions/feishu/src/typing.ts +80 -0
  415. package/extensions/feishu/src/wiki-schema.ts +55 -0
  416. package/extensions/feishu/src/wiki.ts +232 -0
  417. package/extensions/imessage/src/channel.js +253 -0
  418. package/extensions/imessage/src/runtime.js +10 -0
  419. package/extensions/irc/index.ts +17 -0
  420. package/extensions/irc/src/accounts.ts +268 -0
  421. package/extensions/irc/src/channel.ts +367 -0
  422. package/extensions/irc/src/client.ts +439 -0
  423. package/extensions/irc/src/config-schema.ts +97 -0
  424. package/extensions/irc/src/connect-options.ts +30 -0
  425. package/extensions/irc/src/control-chars.ts +22 -0
  426. package/extensions/irc/src/inbound.ts +334 -0
  427. package/extensions/irc/src/monitor.ts +147 -0
  428. package/extensions/irc/src/normalize.ts +117 -0
  429. package/extensions/irc/src/onboarding.ts +479 -0
  430. package/extensions/irc/src/policy.ts +157 -0
  431. package/extensions/irc/src/probe.ts +53 -0
  432. package/extensions/irc/src/protocol.ts +169 -0
  433. package/extensions/irc/src/runtime.ts +14 -0
  434. package/extensions/irc/src/send.ts +88 -0
  435. package/extensions/irc/src/types.ts +93 -0
  436. package/extensions/matrix/src/matrix/client-bootstrap.ts +39 -0
  437. package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
  438. package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
  439. package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
  440. package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
  441. package/extensions/minimax-portal-auth/index.ts +161 -0
  442. package/extensions/minimax-portal-auth/oauth.ts +247 -0
  443. package/extensions/msteams/src/file-lock.ts +1 -0
  444. package/extensions/msteams/src/graph.ts +92 -0
  445. package/extensions/msteams/src/mentions.ts +114 -0
  446. package/extensions/msteams/src/test-runtime.ts +16 -0
  447. package/extensions/openai-codex-auth/index.ts +177 -0
  448. package/extensions/phone-control/index.ts +421 -0
  449. package/extensions/shared/resolve-target-test-helpers.ts +66 -0
  450. package/extensions/signal/src/channel.js +273 -0
  451. package/extensions/signal/src/runtime.js +10 -0
  452. package/extensions/slack/src/channel.js +489 -0
  453. package/extensions/slack/src/runtime.js +10 -0
  454. package/extensions/talk-voice/index.ts +150 -0
  455. package/extensions/telegram/src/channel.js +424 -0
  456. package/extensions/telegram/src/runtime.js +10 -0
  457. package/extensions/thread-ownership/index.ts +133 -0
  458. package/extensions/tlon/src/account-fields.ts +25 -0
  459. package/extensions/tlon/src/urbit/base-url.ts +57 -0
  460. package/extensions/tlon/src/urbit/channel-client.ts +157 -0
  461. package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
  462. package/extensions/tlon/src/urbit/context.ts +47 -0
  463. package/extensions/tlon/src/urbit/errors.ts +51 -0
  464. package/extensions/tlon/src/urbit/fetch.ts +39 -0
  465. package/extensions/twitch/src/test-fixtures.ts +30 -0
  466. package/extensions/voice-call/src/allowlist.ts +19 -0
  467. package/extensions/whatsapp/src/channel.js +429 -0
  468. package/extensions/whatsapp/src/runtime.js +10 -0
  469. package/package.json +1 -1
@@ -0,0 +1,285 @@
1
+ import os from "node:os";
2
+ const DEFAULT_GATEWAY_PORT = 18789;
3
+ function normalizeUrl(raw, schemeFallback) {
4
+ const trimmed = raw.trim();
5
+ if (!trimmed) {
6
+ return null;
7
+ }
8
+ try {
9
+ const parsed = new URL(trimmed);
10
+ const scheme = parsed.protocol.replace(":", "");
11
+ if (!scheme) {
12
+ return null;
13
+ }
14
+ const resolvedScheme = scheme === "http" ? "ws" : scheme === "https" ? "wss" : scheme;
15
+ if (resolvedScheme !== "ws" && resolvedScheme !== "wss") {
16
+ return null;
17
+ }
18
+ const host = parsed.hostname;
19
+ if (!host) {
20
+ return null;
21
+ }
22
+ const port = parsed.port ? `:${parsed.port}` : "";
23
+ return `${resolvedScheme}://${host}${port}`;
24
+ }
25
+ catch {
26
+ // Fall through to host:port parsing.
27
+ }
28
+ const withoutPath = trimmed.split("/")[0] ?? "";
29
+ if (!withoutPath) {
30
+ return null;
31
+ }
32
+ return `${schemeFallback}://${withoutPath}`;
33
+ }
34
+ function resolveGatewayPort(cfg, env) {
35
+ const envRaw = env.POOLBOT_GATEWAY_PORT?.trim() || env.CLAWDBOT_GATEWAY_PORT?.trim();
36
+ if (envRaw) {
37
+ const parsed = Number.parseInt(envRaw, 10);
38
+ if (Number.isFinite(parsed) && parsed > 0) {
39
+ return parsed;
40
+ }
41
+ }
42
+ const configPort = cfg.gateway?.port;
43
+ if (typeof configPort === "number" && Number.isFinite(configPort) && configPort > 0) {
44
+ return configPort;
45
+ }
46
+ return DEFAULT_GATEWAY_PORT;
47
+ }
48
+ function resolveScheme(cfg, opts) {
49
+ if (opts?.forceSecure) {
50
+ return "wss";
51
+ }
52
+ return cfg.gateway?.tls?.enabled === true ? "wss" : "ws";
53
+ }
54
+ function parseIPv4Octets(address) {
55
+ const parts = address.split(".");
56
+ if (parts.length !== 4) {
57
+ return null;
58
+ }
59
+ const octets = parts.map((part) => Number.parseInt(part, 10));
60
+ if (octets.some((value) => !Number.isFinite(value) || value < 0 || value > 255)) {
61
+ return null;
62
+ }
63
+ return [octets[0], octets[1], octets[2], octets[3]];
64
+ }
65
+ function isPrivateIPv4(address) {
66
+ const octets = parseIPv4Octets(address);
67
+ if (!octets) {
68
+ return false;
69
+ }
70
+ const [a, b] = octets;
71
+ if (a === 10) {
72
+ return true;
73
+ }
74
+ if (a === 172 && b >= 16 && b <= 31) {
75
+ return true;
76
+ }
77
+ if (a === 192 && b === 168) {
78
+ return true;
79
+ }
80
+ return false;
81
+ }
82
+ function isTailnetIPv4(address) {
83
+ const octets = parseIPv4Octets(address);
84
+ if (!octets) {
85
+ return false;
86
+ }
87
+ const [a, b] = octets;
88
+ return a === 100 && b >= 64 && b <= 127;
89
+ }
90
+ function pickIPv4Matching(networkInterfaces, matches) {
91
+ const nets = networkInterfaces();
92
+ for (const entries of Object.values(nets)) {
93
+ if (!entries) {
94
+ continue;
95
+ }
96
+ for (const entry of entries) {
97
+ const family = entry?.family;
98
+ const isIpv4 = family === "IPv4";
99
+ if (!entry || entry.internal || !isIpv4) {
100
+ continue;
101
+ }
102
+ const address = entry.address?.trim() ?? "";
103
+ if (!address) {
104
+ continue;
105
+ }
106
+ if (matches(address)) {
107
+ return address;
108
+ }
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+ function pickLanIPv4(networkInterfaces) {
114
+ return pickIPv4Matching(networkInterfaces, isPrivateIPv4);
115
+ }
116
+ function pickTailnetIPv4(networkInterfaces) {
117
+ return pickIPv4Matching(networkInterfaces, isTailnetIPv4);
118
+ }
119
+ function parsePossiblyNoisyJsonObject(raw) {
120
+ const start = raw.indexOf("{");
121
+ const end = raw.lastIndexOf("}");
122
+ if (start === -1 || end <= start) {
123
+ return {};
124
+ }
125
+ try {
126
+ return JSON.parse(raw.slice(start, end + 1));
127
+ }
128
+ catch {
129
+ return {};
130
+ }
131
+ }
132
+ async function resolveTailnetHost(runCommandWithTimeout) {
133
+ if (!runCommandWithTimeout) {
134
+ return null;
135
+ }
136
+ const candidates = ["tailscale", "/Applications/Tailscale.app/Contents/MacOS/Tailscale"];
137
+ for (const candidate of candidates) {
138
+ try {
139
+ const result = await runCommandWithTimeout([candidate, "status", "--json"], {
140
+ timeoutMs: 5000,
141
+ });
142
+ if (result.code !== 0) {
143
+ continue;
144
+ }
145
+ const raw = result.stdout.trim();
146
+ if (!raw) {
147
+ continue;
148
+ }
149
+ const parsed = parsePossiblyNoisyJsonObject(raw);
150
+ const self = typeof parsed.Self === "object" && parsed.Self !== null
151
+ ? parsed.Self
152
+ : undefined;
153
+ const dns = typeof self?.DNSName === "string" ? self.DNSName : undefined;
154
+ if (dns && dns.length > 0) {
155
+ return dns.replace(/\.$/, "");
156
+ }
157
+ const ips = Array.isArray(self?.TailscaleIPs) ? self.TailscaleIPs : [];
158
+ if (ips.length > 0) {
159
+ return ips[0] ?? null;
160
+ }
161
+ }
162
+ catch {
163
+ continue;
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ function resolveAuth(cfg, env) {
169
+ const mode = cfg.gateway?.auth?.mode;
170
+ const token = env.POOLBOT_GATEWAY_TOKEN?.trim() ||
171
+ env.CLAWDBOT_GATEWAY_TOKEN?.trim() ||
172
+ cfg.gateway?.auth?.token?.trim();
173
+ const password = env.POOLBOT_GATEWAY_PASSWORD?.trim() ||
174
+ env.CLAWDBOT_GATEWAY_PASSWORD?.trim() ||
175
+ cfg.gateway?.auth?.password?.trim();
176
+ if (mode === "password") {
177
+ if (!password) {
178
+ return { error: "Gateway auth is set to password, but no password is configured." };
179
+ }
180
+ return { password, label: "password" };
181
+ }
182
+ if (mode === "token") {
183
+ if (!token) {
184
+ return { error: "Gateway auth is set to token, but no token is configured." };
185
+ }
186
+ return { token, label: "token" };
187
+ }
188
+ if (token) {
189
+ return { token, label: "token" };
190
+ }
191
+ if (password) {
192
+ return { password, label: "password" };
193
+ }
194
+ return { error: "Gateway auth is not configured (no token or password)." };
195
+ }
196
+ async function resolveGatewayUrl(cfg, opts) {
197
+ const scheme = resolveScheme(cfg, { forceSecure: opts.forceSecure });
198
+ const port = resolveGatewayPort(cfg, opts.env);
199
+ if (typeof opts.publicUrl === "string" && opts.publicUrl.trim()) {
200
+ const url = normalizeUrl(opts.publicUrl, scheme);
201
+ if (url) {
202
+ return { url, source: "plugins.entries.device-pair.config.publicUrl" };
203
+ }
204
+ return { error: "Configured publicUrl is invalid." };
205
+ }
206
+ const remoteUrlRaw = cfg.gateway?.remote?.url;
207
+ const remoteUrl = typeof remoteUrlRaw === "string" && remoteUrlRaw.trim()
208
+ ? normalizeUrl(remoteUrlRaw, scheme)
209
+ : null;
210
+ if (opts.preferRemoteUrl && remoteUrl) {
211
+ return { url: remoteUrl, source: "gateway.remote.url" };
212
+ }
213
+ const tailscaleMode = cfg.gateway?.tailscale?.mode ?? "off";
214
+ if (tailscaleMode === "serve" || tailscaleMode === "funnel") {
215
+ const host = await resolveTailnetHost(opts.runCommandWithTimeout);
216
+ if (!host) {
217
+ return { error: "Tailscale Serve is enabled, but MagicDNS could not be resolved." };
218
+ }
219
+ return { url: `wss://${host}`, source: `gateway.tailscale.mode=${tailscaleMode}` };
220
+ }
221
+ if (remoteUrl) {
222
+ return { url: remoteUrl, source: "gateway.remote.url" };
223
+ }
224
+ const bind = cfg.gateway?.bind ?? "loopback";
225
+ if (bind === "custom") {
226
+ const host = cfg.gateway?.customBindHost?.trim();
227
+ if (host) {
228
+ return { url: `${scheme}://${host}:${port}`, source: "gateway.bind=custom" };
229
+ }
230
+ return { error: "gateway.bind=custom requires gateway.customBindHost." };
231
+ }
232
+ if (bind === "tailnet") {
233
+ const host = pickTailnetIPv4(opts.networkInterfaces);
234
+ if (host) {
235
+ return { url: `${scheme}://${host}:${port}`, source: "gateway.bind=tailnet" };
236
+ }
237
+ return { error: "gateway.bind=tailnet set, but no tailnet IP was found." };
238
+ }
239
+ if (bind === "lan") {
240
+ const host = pickLanIPv4(opts.networkInterfaces);
241
+ if (host) {
242
+ return { url: `${scheme}://${host}:${port}`, source: "gateway.bind=lan" };
243
+ }
244
+ return { error: "gateway.bind=lan set, but no private LAN IP was found." };
245
+ }
246
+ return {
247
+ error: "Gateway is only bound to loopback. Set gateway.bind=lan, enable tailscale serve, or configure plugins.entries.device-pair.config.publicUrl.",
248
+ };
249
+ }
250
+ export function encodePairingSetupCode(payload) {
251
+ const json = JSON.stringify(payload);
252
+ const base64 = Buffer.from(json, "utf8").toString("base64");
253
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
254
+ }
255
+ export async function resolvePairingSetupFromConfig(cfg, options = {}) {
256
+ const env = options.env ?? process.env;
257
+ const auth = resolveAuth(cfg, env);
258
+ if (auth.error) {
259
+ return { ok: false, error: auth.error };
260
+ }
261
+ const urlResult = await resolveGatewayUrl(cfg, {
262
+ env,
263
+ publicUrl: options.publicUrl,
264
+ preferRemoteUrl: options.preferRemoteUrl,
265
+ forceSecure: options.forceSecure,
266
+ runCommandWithTimeout: options.runCommandWithTimeout,
267
+ networkInterfaces: options.networkInterfaces ?? os.networkInterfaces,
268
+ });
269
+ if (!urlResult.url) {
270
+ return { ok: false, error: urlResult.error ?? "Gateway URL unavailable." };
271
+ }
272
+ if (!auth.label) {
273
+ return { ok: false, error: "Gateway auth is not configured (no token or password)." };
274
+ }
275
+ return {
276
+ ok: true,
277
+ payload: {
278
+ url: urlResult.url,
279
+ token: auth.token,
280
+ password: auth.password,
281
+ },
282
+ authLabel: auth.label,
283
+ urlSource: urlResult.source ?? "unknown",
284
+ };
285
+ }
@@ -0,0 +1 @@
1
+ export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
@@ -0,0 +1,13 @@
1
+ export function buildAgentMediaPayload(mediaList) {
2
+ const first = mediaList[0];
3
+ const mediaPaths = mediaList.map((media) => media.path);
4
+ const mediaTypes = mediaList.map((media) => media.contentType).filter(Boolean);
5
+ return {
6
+ MediaPath: first?.path,
7
+ MediaType: first?.contentType ?? undefined,
8
+ MediaUrl: first?.path,
9
+ MediaPaths: mediaPaths.length > 0 ? mediaPaths : undefined,
10
+ MediaUrls: mediaPaths.length > 0 ? mediaPaths : undefined,
11
+ MediaTypes: mediaTypes.length > 0 ? mediaTypes : undefined,
12
+ };
13
+ }
@@ -0,0 +1,47 @@
1
+ export function formatAllowFromLowercase(params) {
2
+ return params.allowFrom
3
+ .map((entry) => String(entry).trim())
4
+ .filter(Boolean)
5
+ .map((entry) => (params.stripPrefixRe ? entry.replace(params.stripPrefixRe, "") : entry))
6
+ .map((entry) => entry.toLowerCase());
7
+ }
8
+ export function isAllowedParsedChatSender(params) {
9
+ const allowFrom = params.allowFrom.map((entry) => String(entry).trim());
10
+ if (allowFrom.length === 0) {
11
+ return true;
12
+ }
13
+ if (allowFrom.includes("*")) {
14
+ return true;
15
+ }
16
+ const senderNormalized = params.normalizeSender(params.sender);
17
+ const chatId = params.chatId ?? undefined;
18
+ const chatGuid = params.chatGuid?.trim();
19
+ const chatIdentifier = params.chatIdentifier?.trim();
20
+ for (const entry of allowFrom) {
21
+ if (!entry) {
22
+ continue;
23
+ }
24
+ const parsed = params.parseAllowTarget(entry);
25
+ if (parsed.kind === "chat_id" && chatId !== undefined) {
26
+ if (parsed.chatId === chatId) {
27
+ return true;
28
+ }
29
+ }
30
+ else if (parsed.kind === "chat_guid" && chatGuid) {
31
+ if (parsed.chatGuid === chatGuid) {
32
+ return true;
33
+ }
34
+ }
35
+ else if (parsed.kind === "chat_identifier" && chatIdentifier) {
36
+ if (parsed.chatIdentifier === chatIdentifier) {
37
+ return true;
38
+ }
39
+ }
40
+ else if (parsed.kind === "handle" && senderNormalized) {
41
+ if (parsed.handle === senderNormalized) {
42
+ return true;
43
+ }
44
+ }
45
+ }
46
+ return false;
47
+ }
@@ -0,0 +1,23 @@
1
+ export async function resolveSenderCommandAuthorization(params) {
2
+ const shouldComputeAuth = params.shouldComputeCommandAuthorized(params.rawBody, params.cfg);
3
+ const storeAllowFrom = !params.isGroup && (params.dmPolicy !== "open" || shouldComputeAuth)
4
+ ? await params.readAllowFromStore().catch(() => [])
5
+ : [];
6
+ const effectiveAllowFrom = [...params.configuredAllowFrom, ...storeAllowFrom];
7
+ const useAccessGroups = params.cfg.commands?.useAccessGroups !== false;
8
+ const senderAllowedForCommands = params.isSenderAllowed(params.senderId, effectiveAllowFrom);
9
+ const commandAuthorized = shouldComputeAuth
10
+ ? params.resolveCommandAuthorizedFromAuthorizers({
11
+ useAccessGroups,
12
+ authorizers: [
13
+ { configured: effectiveAllowFrom.length > 0, allowed: senderAllowedForCommands },
14
+ ],
15
+ })
16
+ : undefined;
17
+ return {
18
+ shouldComputeAuth,
19
+ effectiveAllowFrom,
20
+ senderAllowedForCommands,
21
+ commandAuthorized,
22
+ };
23
+ }
@@ -0,0 +1,9 @@
1
+ export function resolveChannelAccountConfigBasePath(params) {
2
+ const channels = params.cfg.channels;
3
+ const channelSection = channels?.[params.channelKey];
4
+ const accounts = channelSection?.accounts;
5
+ const useAccountPath = Boolean(accounts?.[params.accountId]);
6
+ return useAccountPath
7
+ ? `channels.${params.channelKey}.accounts.${params.accountId}.`
8
+ : `channels.${params.channelKey}.`;
9
+ }
@@ -0,0 +1,116 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { isPidAlive } from "../shared/pid-alive.js";
4
+ import { resolveProcessScopedMap } from "../shared/process-scoped-map.js";
5
+ const HELD_LOCKS_KEY = Symbol.for("poolbot.fileLockHeldLocks");
6
+ const HELD_LOCKS = resolveProcessScopedMap(HELD_LOCKS_KEY);
7
+ function computeDelayMs(retries, attempt) {
8
+ const base = Math.min(retries.maxTimeout, Math.max(retries.minTimeout, retries.minTimeout * retries.factor ** attempt));
9
+ const jitter = retries.randomize ? 1 + Math.random() : 1;
10
+ return Math.min(retries.maxTimeout, Math.round(base * jitter));
11
+ }
12
+ async function readLockPayload(lockPath) {
13
+ try {
14
+ const raw = await fs.readFile(lockPath, "utf8");
15
+ const parsed = JSON.parse(raw);
16
+ if (typeof parsed.pid !== "number" || typeof parsed.createdAt !== "string") {
17
+ return null;
18
+ }
19
+ return { pid: parsed.pid, createdAt: parsed.createdAt };
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ async function resolveNormalizedFilePath(filePath) {
26
+ const resolved = path.resolve(filePath);
27
+ const dir = path.dirname(resolved);
28
+ await fs.mkdir(dir, { recursive: true });
29
+ try {
30
+ const realDir = await fs.realpath(dir);
31
+ return path.join(realDir, path.basename(resolved));
32
+ }
33
+ catch {
34
+ return resolved;
35
+ }
36
+ }
37
+ async function isStaleLock(lockPath, staleMs) {
38
+ const payload = await readLockPayload(lockPath);
39
+ if (payload?.pid && !isPidAlive(payload.pid)) {
40
+ return true;
41
+ }
42
+ if (payload?.createdAt) {
43
+ const createdAt = Date.parse(payload.createdAt);
44
+ if (!Number.isFinite(createdAt) || Date.now() - createdAt > staleMs) {
45
+ return true;
46
+ }
47
+ }
48
+ try {
49
+ const stat = await fs.stat(lockPath);
50
+ return Date.now() - stat.mtimeMs > staleMs;
51
+ }
52
+ catch {
53
+ return true;
54
+ }
55
+ }
56
+ async function releaseHeldLock(normalizedFile) {
57
+ const current = HELD_LOCKS.get(normalizedFile);
58
+ if (!current) {
59
+ return;
60
+ }
61
+ current.count -= 1;
62
+ if (current.count > 0) {
63
+ return;
64
+ }
65
+ HELD_LOCKS.delete(normalizedFile);
66
+ await current.handle.close().catch(() => undefined);
67
+ await fs.rm(current.lockPath, { force: true }).catch(() => undefined);
68
+ }
69
+ export async function acquireFileLock(filePath, options) {
70
+ const normalizedFile = await resolveNormalizedFilePath(filePath);
71
+ const lockPath = `${normalizedFile}.lock`;
72
+ const held = HELD_LOCKS.get(normalizedFile);
73
+ if (held) {
74
+ held.count += 1;
75
+ return {
76
+ lockPath,
77
+ release: () => releaseHeldLock(normalizedFile),
78
+ };
79
+ }
80
+ const attempts = Math.max(1, options.retries.retries + 1);
81
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
82
+ try {
83
+ const handle = await fs.open(lockPath, "wx");
84
+ await handle.writeFile(JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }, null, 2), "utf8");
85
+ HELD_LOCKS.set(normalizedFile, { count: 1, handle, lockPath });
86
+ return {
87
+ lockPath,
88
+ release: () => releaseHeldLock(normalizedFile),
89
+ };
90
+ }
91
+ catch (err) {
92
+ const code = err.code;
93
+ if (code !== "EEXIST") {
94
+ throw err;
95
+ }
96
+ if (await isStaleLock(lockPath, options.stale)) {
97
+ await fs.rm(lockPath, { force: true }).catch(() => undefined);
98
+ continue;
99
+ }
100
+ if (attempt >= attempts - 1) {
101
+ break;
102
+ }
103
+ await new Promise((resolve) => setTimeout(resolve, computeDelayMs(options.retries, attempt)));
104
+ }
105
+ }
106
+ throw new Error(`file lock timeout for ${normalizedFile}`);
107
+ }
108
+ export async function withFileLock(filePath, options, fn) {
109
+ const lock = await acquireFileLock(filePath, options);
110
+ try {
111
+ return await fn();
112
+ }
113
+ finally {
114
+ await lock.release();
115
+ }
116
+ }
@@ -0,0 +1,31 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { safeParseJson } from "../utils.js";
5
+ export async function readJsonFileWithFallback(filePath, fallback) {
6
+ try {
7
+ const raw = await fs.promises.readFile(filePath, "utf-8");
8
+ const parsed = safeParseJson(raw);
9
+ if (parsed == null) {
10
+ return { value: fallback, exists: true };
11
+ }
12
+ return { value: parsed, exists: true };
13
+ }
14
+ catch (err) {
15
+ const code = err.code;
16
+ if (code === "ENOENT") {
17
+ return { value: fallback, exists: false };
18
+ }
19
+ return { value: fallback, exists: false };
20
+ }
21
+ }
22
+ export async function writeJsonFileAtomically(filePath, value) {
23
+ const dir = path.dirname(filePath);
24
+ await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
25
+ const tmp = path.join(dir, `${path.basename(filePath)}.${crypto.randomUUID()}.tmp`);
26
+ await fs.promises.writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, {
27
+ encoding: "utf-8",
28
+ });
29
+ await fs.promises.chmod(tmp, 0o600);
30
+ await fs.promises.rename(tmp, filePath);
31
+ }
@@ -0,0 +1,28 @@
1
+ import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
2
+ export async function promptAccountId(params) {
3
+ const existingIds = params.listAccountIds(params.cfg);
4
+ const initial = params.currentId?.trim() || params.defaultAccountId || DEFAULT_ACCOUNT_ID;
5
+ const choice = await params.prompter.select({
6
+ message: `${params.label} account`,
7
+ options: [
8
+ ...existingIds.map((id) => ({
9
+ value: id,
10
+ label: id === DEFAULT_ACCOUNT_ID ? "default (primary)" : id,
11
+ })),
12
+ { value: "__new__", label: "Add a new account" },
13
+ ],
14
+ initialValue: initial,
15
+ });
16
+ if (choice !== "__new__") {
17
+ return normalizeAccountId(choice);
18
+ }
19
+ const entered = await params.prompter.text({
20
+ message: `New ${params.label} account id`,
21
+ validate: (value) => (value?.trim() ? undefined : "Required"),
22
+ });
23
+ const normalized = normalizeAccountId(String(entered));
24
+ if (String(entered).trim() !== normalized) {
25
+ await params.prompter.note(`Normalized account id to "${normalized}".`, `${params.label} account`);
26
+ }
27
+ return normalized;
28
+ }
@@ -0,0 +1,29 @@
1
+ export function buildOauthProviderAuthResult(params) {
2
+ const email = params.email ?? undefined;
3
+ const profilePrefix = params.profilePrefix ?? params.providerId;
4
+ const profileId = `${profilePrefix}:${email ?? "default"}`;
5
+ const credential = {
6
+ type: "oauth",
7
+ provider: params.providerId,
8
+ access: params.access,
9
+ ...(params.refresh ? { refresh: params.refresh } : {}),
10
+ ...(Number.isFinite(params.expires) ? { expires: params.expires } : {}),
11
+ ...(email ? { email } : {}),
12
+ ...params.credentialExtra,
13
+ };
14
+ return {
15
+ profiles: [{ profileId, credential }],
16
+ configPatch: params.configPatch ??
17
+ {
18
+ agents: {
19
+ defaults: {
20
+ models: {
21
+ [params.defaultModel]: {},
22
+ },
23
+ },
24
+ },
25
+ },
26
+ defaultModel: params.defaultModel,
27
+ notes: params.notes,
28
+ };
29
+ }