@poolzin/pool-bot 2026.1.39 → 2026.2.1
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.
- package/assets/chrome-extension/README.md +3 -3
- package/assets/chrome-extension/background.js +5 -5
- package/assets/chrome-extension/manifest.json +3 -3
- package/assets/chrome-extension/options.html +4 -4
- package/assets/chrome-extension/options.js +1 -1
- package/dist/acp/client.js +3 -3
- package/dist/acp/types.js +1 -1
- package/dist/agents/agent-paths.js +3 -3
- package/dist/agents/auth-profiles/paths.js +3 -3
- package/dist/agents/bash-tools.exec.js +76 -25
- package/dist/agents/cli-runner/helpers.js +10 -12
- package/dist/agents/cli-runner.js +2 -2
- package/dist/agents/cloudflare-ai-gateway.js +31 -0
- package/dist/agents/compaction.js +16 -2
- package/dist/agents/context-window-guard.js +13 -10
- package/dist/agents/context.js +4 -4
- package/dist/agents/docs-path.js +1 -1
- package/dist/agents/identity.js +47 -7
- package/dist/agents/memory-search.js +25 -8
- package/dist/agents/minimax-vlm.js +1 -1
- package/dist/agents/model-auth.js +12 -1
- package/dist/agents/model-catalog.js +4 -4
- package/dist/agents/model-selection.js +31 -4
- package/dist/agents/models-config.js +3 -3
- package/dist/agents/models-config.providers.js +147 -39
- package/dist/agents/pi-embedded-block-chunker.js +117 -42
- package/dist/agents/pi-embedded-helpers/errors.js +183 -78
- package/dist/agents/pi-embedded-helpers/openai.js +1 -1
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-runner/compact.js +9 -8
- package/dist/agents/pi-embedded-runner/model.js +63 -4
- package/dist/agents/pi-embedded-runner/run/attempt.js +27 -17
- package/dist/agents/pi-embedded-runner/run.js +203 -50
- package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
- package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
- package/dist/agents/pi-embedded-runner/utils.js +1 -1
- package/dist/agents/pi-embedded-subscribe.js +118 -29
- package/dist/agents/pi-model-discovery.js +10 -0
- package/dist/agents/pi-tool-definition-adapter.js +50 -9
- package/dist/agents/pi-tools.before-tool-call.js +67 -0
- package/dist/agents/pi-tools.js +20 -10
- package/dist/agents/pi-tools.read.js +2 -2
- package/dist/agents/poolbot-tools.js +15 -10
- package/dist/agents/sandbox-paths.js +31 -0
- package/dist/agents/session-file-repair.js +83 -0
- package/dist/agents/session-tool-result-guard.js +94 -15
- package/dist/agents/session-transcript-repair.js +68 -0
- package/dist/agents/shell-utils.js +51 -0
- package/dist/agents/skills/bundled-context.js +23 -0
- package/dist/agents/skills/bundled-dir.js +41 -7
- package/dist/agents/skills/frontmatter.js +1 -1
- package/dist/agents/skills/workspace.js +2 -2
- package/dist/agents/skills-install.js +60 -23
- package/dist/agents/subagent-announce.js +79 -34
- package/dist/agents/system-prompt.js +28 -4
- package/dist/agents/together-models.js +127 -0
- package/dist/agents/tool-images.js +1 -1
- package/dist/agents/tool-policy.conformance.js +14 -0
- package/dist/agents/tool-policy.js +25 -1
- package/dist/agents/tools/browser-tool.js +3 -3
- package/dist/agents/tools/cron-tool.js +166 -19
- package/dist/agents/tools/discord-actions-presence.js +78 -0
- package/dist/agents/tools/image-tool.js +2 -2
- package/dist/agents/tools/memory-tool.js +93 -5
- package/dist/agents/tools/message-tool.js +56 -2
- package/dist/agents/tools/sessions-history-tool.js +69 -1
- package/dist/agents/tools/web-search.js +211 -42
- package/dist/agents/usage.js +23 -1
- package/dist/agents/workspace-run.js +67 -0
- package/dist/agents/workspace-templates.js +44 -0
- package/dist/auto-reply/command-auth.js +121 -6
- package/dist/auto-reply/commands-registry.data.js +1 -1
- package/dist/auto-reply/envelope.js +50 -72
- package/dist/auto-reply/reply/commands-compact.js +1 -0
- package/dist/auto-reply/reply/commands-context-report.js +3 -2
- package/dist/auto-reply/reply/commands-context.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +107 -60
- package/dist/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/reply/commands-session.js +2 -2
- package/dist/auto-reply/reply/get-reply-run.js +16 -5
- package/dist/auto-reply/reply/groups.js +1 -1
- package/dist/auto-reply/reply/inbound-context.js +9 -1
- package/dist/auto-reply/reply/inbound-meta.js +130 -0
- package/dist/auto-reply/reply/model-selection.js +3 -3
- package/dist/auto-reply/reply/untrusted-context.js +15 -0
- package/dist/auto-reply/status.js +1 -1
- package/dist/auto-reply/thinking.js +88 -43
- package/dist/browser/bridge-server.js +13 -0
- package/dist/browser/cdp.helpers.js +38 -24
- package/dist/browser/client-fetch.js +51 -8
- package/dist/browser/config.js +2 -11
- package/dist/browser/extension-relay.js +104 -43
- package/dist/browser/pw-ai.js +1 -1
- package/dist/browser/pw-session.js +143 -8
- package/dist/browser/pw-tools-core.interactions.js +125 -27
- package/dist/browser/pw-tools-core.responses.js +1 -1
- package/dist/browser/pw-tools-core.state.js +1 -1
- package/dist/browser/routes/agent.act.js +86 -41
- package/dist/browser/routes/dispatcher.js +4 -4
- package/dist/browser/screenshot.js +1 -1
- package/dist/browser/server-context.js +2 -2
- package/dist/browser/server.js +13 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui.js +3 -3
- package/dist/channels/plugins/catalog.js +2 -2
- package/dist/channels/plugins/onboarding/imessage.js +1 -1
- package/dist/channels/plugins/onboarding/signal.js +1 -1
- package/dist/channels/plugins/onboarding/slack.js +4 -4
- package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
- package/dist/channels/plugins/pairing-message.js +1 -1
- package/dist/channels/reply-prefix.js +8 -1
- package/dist/cli/browser-cli-extension.js +2 -2
- package/dist/cli/cron-cli/register.cron-add.js +61 -40
- package/dist/cli/cron-cli/register.cron-edit.js +60 -34
- package/dist/cli/cron-cli/shared.js +56 -41
- package/dist/cli/dns-cli.js +26 -14
- package/dist/cli/docs-cli.js +1 -1
- package/dist/cli/gateway-cli/dev.js +1 -1
- package/dist/cli/gateway-cli/register.js +37 -19
- package/dist/cli/memory-cli.js +30 -20
- package/dist/cli/nodes-cli/register.canvas.js +1 -1
- package/dist/cli/parse-bytes.js +37 -0
- package/dist/cli/plugins-cli.js +1 -1
- package/dist/cli/run-main.js +2 -2
- package/dist/cli/security-cli.js +1 -1
- package/dist/cli/tagline.js +1 -1
- package/dist/cli/update-cli.js +173 -52
- package/dist/cli/webhooks-cli.js +5 -5
- package/dist/commands/agent.js +1 -0
- package/dist/commands/agents.commands.add.js +1 -1
- package/dist/commands/auth-choice.apply.api-providers.js +305 -17
- package/dist/commands/auth-choice.apply.js +4 -1
- package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
- package/dist/commands/auth-choice.apply.xai.js +63 -0
- package/dist/commands/auth-choice.preferred-provider.js +7 -1
- package/dist/commands/configure.wizard.js +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/docs.js +1 -1
- package/dist/commands/doctor-config-flow.js +61 -5
- package/dist/commands/doctor-gateway-services.js +3 -3
- package/dist/commands/doctor-state-migrations.js +1 -1
- package/dist/commands/doctor-update.js +3 -3
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/health.js +1 -1
- package/dist/commands/model-allowlist.js +29 -0
- package/dist/commands/model-picker.js +2 -1
- package/dist/commands/models/list.probe.js +2 -2
- package/dist/commands/models/list.registry.js +4 -4
- package/dist/commands/models/list.status-command.js +44 -24
- package/dist/commands/models/shared.js +15 -0
- package/dist/commands/onboard-auth.config-core.js +366 -28
- package/dist/commands/onboard-auth.credentials.js +71 -9
- package/dist/commands/onboard-auth.js +3 -3
- package/dist/commands/onboard-auth.models.js +26 -24
- package/dist/commands/onboard-custom.js +384 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +146 -9
- package/dist/commands/onboard-skills.js +63 -38
- package/dist/commands/openai-model-default.js +41 -0
- package/dist/commands/status-all/report-lines.js +1 -1
- package/dist/commands/status.command.js +1 -1
- package/dist/commands/uninstall.js +3 -3
- package/dist/compat/legacy-names.js +1 -1
- package/dist/config/defaults.js +3 -2
- package/dist/config/io.js +3 -3
- package/dist/config/paths.js +136 -35
- package/dist/config/plugin-auto-enable.js +21 -5
- package/dist/config/redact-snapshot.js +153 -0
- package/dist/config/schema.field-metadata.js +590 -0
- package/dist/config/schema.js +3 -3
- package/dist/config/sessions/store.js +291 -23
- package/dist/config/types.memory.js +1 -0
- package/dist/config/version.js +4 -4
- package/dist/config/zod-schema.agent-defaults.js +3 -0
- package/dist/config/zod-schema.agent-runtime.js +13 -2
- package/dist/config/zod-schema.providers-core.js +142 -0
- package/dist/config/zod-schema.session.js +3 -0
- package/dist/cron/delivery.js +57 -0
- package/dist/cron/isolated-agent/delivery-target.js +18 -3
- package/dist/cron/isolated-agent/helpers.js +22 -5
- package/dist/cron/isolated-agent/run.js +171 -63
- package/dist/cron/isolated-agent/session.js +2 -0
- package/dist/cron/normalize.js +356 -28
- package/dist/cron/parse.js +10 -5
- package/dist/cron/run-log.js +35 -10
- package/dist/cron/schedule.js +41 -6
- package/dist/cron/service/jobs.js +208 -35
- package/dist/cron/service/ops.js +72 -16
- package/dist/cron/service/state.js +2 -0
- package/dist/cron/service/store.js +386 -14
- package/dist/cron/service/timer.js +390 -147
- package/dist/cron/session-reaper.js +86 -0
- package/dist/cron/store.js +23 -8
- package/dist/cron/validate-timestamp.js +43 -0
- package/dist/daemon/constants.js +7 -7
- package/dist/daemon/inspect.js +6 -6
- package/dist/daemon/systemd-unit.js +1 -1
- package/dist/discord/monitor/agent-components.js +438 -0
- package/dist/discord/monitor/allow-list.js +28 -5
- package/dist/discord/monitor/gateway-registry.js +29 -0
- package/dist/discord/monitor/native-command.js +44 -23
- package/dist/discord/monitor/sender-identity.js +45 -0
- package/dist/discord/pluralkit.js +27 -0
- package/dist/discord/send.outbound.js +92 -5
- package/dist/discord/send.shared.js +60 -23
- package/dist/discord/targets.js +84 -1
- package/dist/entry.js +15 -9
- package/dist/extensionAPI.js +8 -0
- package/dist/gateway/control-ui.js +8 -1
- package/dist/gateway/hooks-mapping.js +3 -0
- package/dist/gateway/hooks.js +65 -0
- package/dist/gateway/live-image-probe.js +1 -66
- package/dist/gateway/net.js +96 -31
- package/dist/gateway/node-command-policy.js +50 -15
- package/dist/gateway/openai-http.js +2 -2
- package/dist/gateway/openresponses-http.js +4 -4
- package/dist/gateway/origin-check.js +56 -0
- package/dist/gateway/protocol/client-info.js +9 -0
- package/dist/gateway/protocol/index.js +9 -2
- package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
- package/dist/gateway/protocol/schema/cron.js +22 -10
- package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
- package/dist/gateway/protocol/schema/sessions.js +12 -0
- package/dist/gateway/server/hooks.js +1 -1
- package/dist/gateway/server-broadcast.js +26 -9
- package/dist/gateway/server-chat.js +112 -23
- package/dist/gateway/server-discovery-runtime.js +10 -2
- package/dist/gateway/server-discovery.js +2 -2
- package/dist/gateway/server-http.js +110 -12
- package/dist/gateway/server-methods/agent-timestamp.js +60 -0
- package/dist/gateway/server-methods/agents.js +321 -2
- package/dist/gateway/server-methods/usage.js +559 -16
- package/dist/gateway/server-runtime-state.js +22 -8
- package/dist/gateway/server-startup-memory.js +16 -0
- package/dist/gateway/server.impl.js +7 -3
- package/dist/gateway/session-utils.fs.js +23 -25
- package/dist/gateway/session-utils.js +20 -10
- package/dist/gateway/sessions-patch.js +7 -22
- package/dist/gateway/test-helpers.server.js +35 -2
- package/dist/hooks/frontmatter.js +1 -1
- package/dist/hooks/hooks-status.js +1 -1
- package/dist/hooks/install.js +2 -2
- package/dist/hooks/loader.js +1 -1
- package/dist/hooks/workspace.js +3 -3
- package/dist/imessage/constants.js +2 -0
- package/dist/imessage/monitor/deliver.js +4 -1
- package/dist/imessage/monitor/monitor-provider.js +51 -1
- package/dist/index.js +2 -2
- package/dist/infra/bonjour-discovery.js +131 -70
- package/dist/infra/bonjour.js +3 -3
- package/dist/infra/control-ui-assets.js +134 -12
- package/dist/infra/errors.js +12 -0
- package/dist/infra/exec-approvals.js +266 -57
- package/dist/infra/format-time/format-datetime.js +79 -0
- package/dist/infra/format-time/format-duration.js +81 -0
- package/dist/infra/format-time/format-relative.js +80 -0
- package/dist/infra/heartbeat-runner.js +140 -49
- package/dist/infra/home-dir.js +54 -0
- package/dist/infra/net/fetch-guard.js +122 -0
- package/dist/infra/net/ssrf.js +65 -29
- package/dist/infra/outbound/abort.js +14 -0
- package/dist/infra/outbound/message-action-runner.js +77 -13
- package/dist/infra/outbound/outbound-session.js +143 -37
- package/dist/infra/path-env.js +3 -3
- package/dist/infra/poolbot-root.js +43 -1
- package/dist/infra/provider-usage.fetch.minimax.js +1 -1
- package/dist/infra/restart.js +1 -1
- package/dist/infra/session-cost-usage.js +631 -41
- package/dist/infra/state-migrations.js +317 -47
- package/dist/infra/tailscale.js +1 -1
- package/dist/infra/update-global.js +35 -0
- package/dist/infra/update-runner.js +149 -43
- package/dist/infra/warning-filter.js +65 -0
- package/dist/infra/widearea-dns.js +30 -9
- package/dist/logging/redact-identifier.js +12 -0
- package/dist/macos/relay.js +2 -2
- package/dist/media/fetch.js +81 -58
- package/dist/media/input-files.js +1 -1
- package/dist/media/mime.js +4 -0
- package/dist/media/png-encode.js +74 -0
- package/dist/media-understanding/apply.js +403 -3
- package/dist/media-understanding/attachments.js +38 -27
- package/dist/media-understanding/defaults.js +16 -0
- package/dist/media-understanding/providers/deepgram/audio.js +22 -14
- package/dist/media-understanding/providers/google/audio.js +24 -17
- package/dist/media-understanding/providers/google/video.js +24 -17
- package/dist/media-understanding/providers/image.js +4 -4
- package/dist/media-understanding/providers/index.js +4 -1
- package/dist/media-understanding/providers/openai/audio.js +22 -14
- package/dist/media-understanding/providers/shared.js +16 -11
- package/dist/media-understanding/providers/zai/index.js +6 -0
- package/dist/media-understanding/runner.js +158 -90
- package/dist/memory/backend-config.js +207 -0
- package/dist/memory/batch-voyage.js +277 -0
- package/dist/memory/embeddings-voyage.js +75 -0
- package/dist/memory/embeddings.js +29 -17
- package/dist/memory/internal.js +101 -18
- package/dist/memory/manager.js +155 -48
- package/dist/memory/search-manager.js +173 -0
- package/dist/memory/session-files.js +9 -3
- package/dist/memory/types.js +1 -0
- package/dist/node-host/runner.js +36 -26
- package/dist/node-host/with-timeout.js +27 -0
- package/dist/pairing/pairing-messages.js +1 -1
- package/dist/plugins/commands.js +5 -1
- package/dist/plugins/config-state.js +86 -7
- package/dist/plugins/discovery.js +1 -1
- package/dist/plugins/install.js +2 -2
- package/dist/plugins/source-display.js +51 -0
- package/dist/plugins/update.js +1 -1
- package/dist/process/exec.js +20 -2
- package/dist/routing/resolve-route.js +12 -0
- package/dist/routing/session-key.js +15 -0
- package/dist/runtime.js +2 -0
- package/dist/security/audit-extra.async.js +601 -0
- package/dist/security/audit-extra.js +2 -830
- package/dist/security/audit-extra.sync.js +505 -0
- package/dist/security/audit.js +2 -2
- package/dist/security/channel-metadata.js +34 -0
- package/dist/security/external-content.js +88 -6
- package/dist/security/skill-scanner.js +330 -0
- package/dist/sessions/session-key-utils.js +7 -0
- package/dist/shared/text/reasoning-tags.js +52 -7
- package/dist/signal/monitor/event-handler.js +80 -1
- package/dist/slack/monitor/media.js +85 -15
- package/dist/tailscale/detect.js +145 -0
- package/dist/telegram/bot/helpers.js +109 -28
- package/dist/telegram/bot-handlers.js +144 -3
- package/dist/telegram/bot-message-context.js +38 -11
- package/dist/telegram/bot-message-dispatch.js +48 -15
- package/dist/telegram/bot-native-commands.js +86 -29
- package/dist/telegram/bot.js +30 -29
- package/dist/telegram/model-buttons.js +163 -0
- package/dist/telegram/monitor.js +110 -85
- package/dist/telegram/send.js +129 -47
- package/dist/terminal/restore.js +45 -0
- package/dist/test-helpers/state-dir-env.js +16 -0
- package/dist/test-helpers/workspace.js +11 -0
- package/dist/test-utils/channel-plugins.js +82 -0
- package/dist/test-utils/ports.js +73 -0
- package/dist/tts/tts.js +12 -6
- package/dist/tui/tui-session-actions.js +166 -54
- package/dist/utils/fetch-timeout.js +20 -0
- package/dist/utils/normalize-secret-input.js +19 -0
- package/dist/utils/shell-argv.js +61 -0
- package/dist/utils/transcript-tools.js +58 -0
- package/dist/utils.js +55 -14
- package/dist/version.js +42 -5
- package/dist/web/qr-image.js +1 -61
- package/dist/wizard/onboarding.finalize.js +7 -7
- package/dist/wizard/onboarding.js +3 -3
- package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
- package/docs/_config.yml +2 -2
- package/docs/_layouts/default.html +9 -9
- package/docs/concepts/typebox.md +1 -1
- package/docs/docs.json +1 -1
- package/docs/northflank.mdx +7 -7
- package/docs/railway.mdx +3 -3
- package/docs/render.mdx +5 -5
- package/docs/start/lore.md +2 -2
- package/extensions/bluebubbles/index.ts +2 -2
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/bluebubbles/src/accounts.ts +8 -8
- package/extensions/bluebubbles/src/actions.test.ts +22 -22
- package/extensions/bluebubbles/src/actions.ts +5 -5
- package/extensions/bluebubbles/src/attachments.ts +2 -2
- package/extensions/bluebubbles/src/channel.ts +16 -16
- package/extensions/bluebubbles/src/chat.ts +2 -2
- package/extensions/bluebubbles/src/media-send.ts +2 -2
- package/extensions/bluebubbles/src/monitor.test.ts +46 -46
- package/extensions/bluebubbles/src/monitor.ts +5 -5
- package/extensions/bluebubbles/src/onboarding.ts +7 -7
- package/extensions/bluebubbles/src/reactions.ts +2 -2
- package/extensions/bluebubbles/src/send.ts +2 -2
- package/extensions/copilot-proxy/README.md +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/index.ts +2 -2
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/diagnostics-otel/src/service.ts +3 -3
- package/extensions/discord/index.ts +2 -2
- package/extensions/discord/package.json +1 -1
- package/extensions/google-antigravity-auth/README.md +1 -1
- package/extensions/google-antigravity-auth/index.ts +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/README.md +1 -1
- package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/index.ts +3 -3
- package/extensions/googlechat/package.json +1 -1
- package/extensions/googlechat/src/accounts.ts +8 -8
- package/extensions/googlechat/src/actions.ts +6 -6
- package/extensions/googlechat/src/channel.ts +21 -21
- package/extensions/googlechat/src/monitor.ts +8 -8
- package/extensions/googlechat/src/onboarding.ts +10 -10
- package/extensions/imessage/index.ts +2 -2
- package/extensions/imessage/package.json +1 -1
- package/extensions/line/index.ts +2 -2
- package/extensions/line/package.json +1 -1
- package/extensions/line/src/card-command.ts +2 -2
- package/extensions/line/src/channel.logout.test.ts +4 -4
- package/extensions/line/src/channel.sendPayload.test.ts +8 -8
- package/extensions/line/src/channel.ts +3 -3
- package/extensions/llm-task/README.md +3 -3
- package/extensions/llm-task/index.ts +2 -2
- package/extensions/llm-task/package.json +1 -1
- package/extensions/llm-task/src/llm-task-tool.ts +4 -4
- package/extensions/lobster/README.md +6 -6
- package/extensions/lobster/index.ts +2 -2
- package/extensions/lobster/src/lobster-tool.test.ts +4 -4
- package/extensions/lobster/src/lobster-tool.ts +2 -2
- package/extensions/matrix/index.ts +2 -2
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/client/config.ts +1 -1
- package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
- package/extensions/matrix/src/onboarding.ts +1 -1
- package/extensions/mattermost/index.ts +2 -2
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
- package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
- package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
- package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
- package/extensions/mattermost/src/onboarding.ts +2 -2
- package/extensions/memory-core/index.ts +2 -2
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/index.ts +3 -3
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/msteams/index.ts +2 -2
- package/extensions/msteams/package.json +1 -1
- package/extensions/msteams/src/channel.directory.test.ts +2 -2
- package/extensions/msteams/src/channel.ts +2 -2
- package/extensions/msteams/src/graph-upload.ts +4 -4
- package/extensions/msteams/src/monitor-handler.ts +2 -2
- package/extensions/msteams/src/monitor.ts +2 -2
- package/extensions/msteams/src/onboarding.ts +9 -9
- package/extensions/msteams/src/reply-dispatcher.ts +2 -2
- package/extensions/msteams/src/send-context.ts +2 -2
- package/extensions/msteams/src/send.ts +4 -4
- package/extensions/nextcloud-talk/index.ts +2 -2
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nextcloud-talk/src/channel.ts +7 -7
- package/extensions/nextcloud-talk/src/inbound.ts +7 -7
- package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
- package/extensions/nostr/README.md +2 -2
- package/extensions/nostr/index.ts +5 -5
- package/extensions/nostr/package.json +1 -1
- package/extensions/nostr/src/types.ts +4 -4
- package/extensions/open-prose/index.ts +2 -2
- package/extensions/qwen-portal-auth/README.md +1 -1
- package/extensions/signal/index.ts +2 -2
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/index.ts +2 -2
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/index.ts +2 -2
- package/extensions/telegram/package.json +1 -1
- package/extensions/telegram/src/channel.ts +2 -2
- package/extensions/tlon/README.md +2 -2
- package/extensions/tlon/index.ts +2 -2
- package/extensions/tlon/package.json +1 -1
- package/extensions/tlon/src/channel.ts +13 -13
- package/extensions/tlon/src/monitor/index.ts +3 -3
- package/extensions/tlon/src/onboarding.ts +3 -3
- package/extensions/tlon/src/types.ts +3 -3
- package/extensions/twitch/README.md +1 -1
- package/extensions/twitch/index.ts +2 -2
- package/extensions/twitch/package.json +1 -1
- package/extensions/twitch/src/config.ts +3 -3
- package/extensions/twitch/src/monitor.ts +3 -3
- package/extensions/twitch/src/onboarding.ts +9 -9
- package/extensions/twitch/src/outbound.test.ts +2 -2
- package/extensions/twitch/src/plugin.test.ts +2 -2
- package/extensions/twitch/src/plugin.ts +8 -8
- package/extensions/twitch/src/send.test.ts +2 -2
- package/extensions/twitch/src/send.ts +4 -4
- package/extensions/twitch/src/token.test.ts +8 -8
- package/extensions/twitch/src/token.ts +3 -3
- package/extensions/twitch/src/twitch-client.ts +3 -3
- package/extensions/twitch/src/types.ts +3 -3
- package/extensions/twitch/src/utils/markdown.ts +1 -1
- package/extensions/voice-call/README.md +3 -3
- package/extensions/voice-call/package.json +1 -1
- package/extensions/voice-call/src/core-bridge.ts +2 -2
- package/extensions/voice-call/src/response-generator.ts +1 -1
- package/extensions/whatsapp/index.ts +2 -2
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/README.md +1 -1
- package/extensions/zalo/index.ts +2 -2
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalo/src/accounts.ts +8 -8
- package/extensions/zalo/src/actions.ts +4 -4
- package/extensions/zalo/src/channel.directory.test.ts +2 -2
- package/extensions/zalo/src/channel.ts +18 -18
- package/extensions/zalo/src/monitor.ts +9 -9
- package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
- package/extensions/zalo/src/onboarding.ts +24 -24
- package/extensions/zalo/src/send.ts +2 -2
- package/extensions/zalouser/README.md +2 -2
- package/extensions/zalouser/index.ts +2 -2
- package/extensions/zalouser/package.json +1 -1
- package/extensions/zalouser/src/accounts.ts +9 -9
- package/extensions/zalouser/src/channel.ts +24 -24
- package/extensions/zalouser/src/monitor.ts +4 -4
- package/extensions/zalouser/src/onboarding.ts +28 -28
- package/package.json +13 -251
- package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
- package/skills/tmux/scripts/find-sessions.sh +1 -1
- package/CHANGELOG.md +0 -102
- package/README-header.png +0 -0
- package/git-hooks/pre-commit +0 -4
- package/scripts/format-staged.js +0 -148
- package/scripts/postinstall.js +0 -300
- package/scripts/setup-git-hooks.js +0 -96
package/dist/discord/targets.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { buildMessagingTarget, ensureTargetId, requireTargetKind, } from "../channels/targets.js";
|
|
2
|
+
import { listDiscordDirectoryPeersLive } from "./directory-live.js";
|
|
2
3
|
export function parseDiscordTarget(raw, options = {}) {
|
|
3
4
|
const trimmed = raw.trim();
|
|
4
|
-
if (!trimmed)
|
|
5
|
+
if (!trimmed) {
|
|
5
6
|
return undefined;
|
|
7
|
+
}
|
|
6
8
|
const mentionMatch = trimmed.match(/^<@!?(\d+)>$/);
|
|
7
9
|
if (mentionMatch) {
|
|
8
10
|
return buildMessagingTarget("user", mentionMatch[1], trimmed);
|
|
@@ -41,3 +43,84 @@ export function resolveDiscordChannelId(raw) {
|
|
|
41
43
|
const target = parseDiscordTarget(raw, { defaultKind: "channel" });
|
|
42
44
|
return requireTargetKind({ platform: "Discord", target, kind: "channel" });
|
|
43
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve a Discord username to user ID using the directory lookup.
|
|
48
|
+
* This enables sending DMs by username instead of requiring explicit user IDs.
|
|
49
|
+
*
|
|
50
|
+
* @param raw - The username or raw target string (e.g., "john.doe")
|
|
51
|
+
* @param options - Directory configuration params (cfg, accountId, limit)
|
|
52
|
+
* @param parseOptions - Messaging target parsing options (defaults, ambiguity message)
|
|
53
|
+
* @returns Parsed MessagingTarget with user ID, or undefined if not found
|
|
54
|
+
*/
|
|
55
|
+
export async function resolveDiscordTarget(raw, options, parseOptions = {}) {
|
|
56
|
+
const trimmed = raw.trim();
|
|
57
|
+
if (!trimmed) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const likelyUsername = isLikelyUsername(trimmed);
|
|
61
|
+
const shouldLookup = isExplicitUserLookup(trimmed, parseOptions) || likelyUsername;
|
|
62
|
+
// Parse directly if it's already a known format. Use a safe parse so ambiguous
|
|
63
|
+
// numeric targets don't throw when we still want to attempt username lookup.
|
|
64
|
+
const directParse = safeParseDiscordTarget(trimmed, parseOptions);
|
|
65
|
+
if (directParse && directParse.kind !== "channel" && !likelyUsername) {
|
|
66
|
+
return directParse;
|
|
67
|
+
}
|
|
68
|
+
if (!shouldLookup) {
|
|
69
|
+
return directParse ?? parseDiscordTarget(trimmed, parseOptions);
|
|
70
|
+
}
|
|
71
|
+
// Try to resolve as a username via directory lookup
|
|
72
|
+
try {
|
|
73
|
+
const directoryEntries = await listDiscordDirectoryPeersLive({
|
|
74
|
+
...options,
|
|
75
|
+
query: trimmed,
|
|
76
|
+
limit: 1,
|
|
77
|
+
});
|
|
78
|
+
const match = directoryEntries[0];
|
|
79
|
+
if (match && match.kind === "user") {
|
|
80
|
+
// Extract user ID from the directory entry (format: "user:<id>")
|
|
81
|
+
const userId = match.id.replace(/^user:/, "");
|
|
82
|
+
return buildMessagingTarget("user", userId, trimmed);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Directory lookup failed - fall through to parse as-is
|
|
87
|
+
// This preserves existing behavior for channel names
|
|
88
|
+
}
|
|
89
|
+
// Fallback to original parsing (for channels, etc.)
|
|
90
|
+
return parseDiscordTarget(trimmed, parseOptions);
|
|
91
|
+
}
|
|
92
|
+
function safeParseDiscordTarget(input, options) {
|
|
93
|
+
try {
|
|
94
|
+
return parseDiscordTarget(input, options);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function isExplicitUserLookup(input, options) {
|
|
101
|
+
if (/^<@!?(\d+)>$/.test(input)) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
if (/^(user:|discord:)/.test(input)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
if (input.startsWith("@")) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
if (/^\d+$/.test(input)) {
|
|
111
|
+
return options.defaultKind === "user";
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check if a string looks like a Discord username (not a mention, prefix, or ID).
|
|
117
|
+
* Usernames typically don't start with special characters except underscore.
|
|
118
|
+
*/
|
|
119
|
+
function isLikelyUsername(input) {
|
|
120
|
+
// Skip if it's already a known format
|
|
121
|
+
if (/^(user:|channel:|discord:|@|<@!?)|[\d]+$/.test(input)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
// Likely a username if it doesn't match known patterns
|
|
125
|
+
return true;
|
|
126
|
+
}
|
package/dist/entry.js
CHANGED
|
@@ -3,32 +3,38 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { applyCliProfileEnv, parseCliProfileArgs } from "./cli/profile.js";
|
|
6
|
-
import { isTruthyEnvValue } from "./infra/env.js";
|
|
6
|
+
import { isTruthyEnvValue, normalizeEnv } from "./infra/env.js";
|
|
7
7
|
import { installProcessWarningFilter } from "./infra/warnings.js";
|
|
8
8
|
import { attachChildProcessBridge } from "./process/child-process-bridge.js";
|
|
9
9
|
process.title = "poolbot";
|
|
10
10
|
installProcessWarningFilter();
|
|
11
|
+
normalizeEnv();
|
|
11
12
|
if (process.argv.includes("--no-color")) {
|
|
12
13
|
process.env.NO_COLOR = "1";
|
|
13
14
|
process.env.FORCE_COLOR = "0";
|
|
14
15
|
}
|
|
15
16
|
const EXPERIMENTAL_WARNING_FLAG = "--disable-warning=ExperimentalWarning";
|
|
16
|
-
function hasExperimentalWarningSuppressed(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
function hasExperimentalWarningSuppressed() {
|
|
18
|
+
const nodeOptions = process.env.NODE_OPTIONS ?? "";
|
|
19
|
+
if (nodeOptions.includes(EXPERIMENTAL_WARNING_FLAG) || nodeOptions.includes("--no-warnings"))
|
|
20
|
+
return true;
|
|
21
|
+
for (const arg of process.execArgv) {
|
|
22
|
+
if (arg === EXPERIMENTAL_WARNING_FLAG || arg === "--no-warnings")
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
20
26
|
}
|
|
21
27
|
function ensureExperimentalWarningSuppressed() {
|
|
22
28
|
if (isTruthyEnvValue(process.env.CLAWDBOT_NO_RESPAWN))
|
|
23
29
|
return false;
|
|
24
30
|
if (isTruthyEnvValue(process.env.CLAWDBOT_NODE_OPTIONS_READY))
|
|
25
31
|
return false;
|
|
26
|
-
|
|
27
|
-
if (hasExperimentalWarningSuppressed(nodeOptions))
|
|
32
|
+
if (hasExperimentalWarningSuppressed())
|
|
28
33
|
return false;
|
|
34
|
+
// Respawn guard (and keep recursion bounded if something goes wrong).
|
|
29
35
|
process.env.CLAWDBOT_NODE_OPTIONS_READY = "1";
|
|
30
|
-
|
|
31
|
-
const child = spawn(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
|
|
36
|
+
// Pass flag as a Node CLI option, not via NODE_OPTIONS (--disable-warning is disallowed in NODE_OPTIONS).
|
|
37
|
+
const child = spawn(process.execPath, [EXPERIMENTAL_WARNING_FLAG, ...process.execArgv, ...process.argv.slice(1)], {
|
|
32
38
|
stdio: "inherit",
|
|
33
39
|
env: process.env,
|
|
34
40
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { resolveAgentDir, resolveAgentWorkspaceDir } from "./agents/agent-scope.js";
|
|
2
|
+
export { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./agents/defaults.js";
|
|
3
|
+
export { resolveAgentIdentity } from "./agents/identity.js";
|
|
4
|
+
export { resolveThinkingDefault } from "./agents/model-selection.js";
|
|
5
|
+
export { runEmbeddedPiAgent } from "./agents/pi-embedded.js";
|
|
6
|
+
export { resolveAgentTimeoutMs } from "./agents/timeout.js";
|
|
7
|
+
export { ensureAgentWorkspace } from "./agents/workspace.js";
|
|
8
|
+
export { resolveStorePath, loadSessionStore, saveSessionStore, resolveSessionFilePath, } from "./config/sessions.js";
|
|
@@ -204,7 +204,14 @@ export function handleControlUiHttpRequest(req, res, opts) {
|
|
|
204
204
|
if (!pathname.startsWith(`${basePath}/`))
|
|
205
205
|
return false;
|
|
206
206
|
}
|
|
207
|
-
const root =
|
|
207
|
+
const root = (() => {
|
|
208
|
+
if (opts?.root) {
|
|
209
|
+
if (opts.root.kind === "resolved")
|
|
210
|
+
return opts.root.path;
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return resolveControlUiRoot();
|
|
214
|
+
})();
|
|
208
215
|
if (!root) {
|
|
209
216
|
res.statusCode = 503;
|
|
210
217
|
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
@@ -87,6 +87,7 @@ function normalizeHookMapping(mapping, index, transformsDir) {
|
|
|
87
87
|
action,
|
|
88
88
|
wakeMode,
|
|
89
89
|
name: mapping.name,
|
|
90
|
+
agentId: mapping.agentId?.trim() || undefined,
|
|
90
91
|
sessionKey: mapping.sessionKey,
|
|
91
92
|
messageTemplate: mapping.messageTemplate,
|
|
92
93
|
textTemplate: mapping.textTemplate,
|
|
@@ -131,6 +132,7 @@ function buildActionFromMapping(mapping, ctx) {
|
|
|
131
132
|
kind: "agent",
|
|
132
133
|
message,
|
|
133
134
|
name: renderOptional(mapping.name, ctx),
|
|
135
|
+
agentId: mapping.agentId,
|
|
134
136
|
wakeMode: mapping.wakeMode ?? "now",
|
|
135
137
|
sessionKey: renderOptional(mapping.sessionKey, ctx),
|
|
136
138
|
deliver: mapping.deliver,
|
|
@@ -162,6 +164,7 @@ function mergeAction(base, override, defaultAction) {
|
|
|
162
164
|
message,
|
|
163
165
|
wakeMode,
|
|
164
166
|
name: override.name ?? baseAgent?.name,
|
|
167
|
+
agentId: override.agentId ?? baseAgent?.agentId,
|
|
165
168
|
sessionKey: override.sessionKey ?? baseAgent?.sessionKey,
|
|
166
169
|
deliver: typeof override.deliver === "boolean" ? override.deliver : baseAgent?.deliver,
|
|
167
170
|
allowUnsafeExternalContent: typeof override.allowUnsafeExternalContent === "boolean"
|
package/dist/gateway/hooks.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { listAgentIds, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
|
2
3
|
import { listChannelPlugins } from "../channels/plugins/index.js";
|
|
4
|
+
import { normalizeAgentId } from "../routing/session-key.js";
|
|
3
5
|
import { normalizeMessageChannel } from "../utils/message-channel.js";
|
|
4
6
|
import { resolveHookMappings } from "./hooks-mapping.js";
|
|
5
7
|
const DEFAULT_HOOKS_PATH = "/hooks";
|
|
@@ -21,13 +23,48 @@ export function resolveHooksConfig(cfg) {
|
|
|
21
23
|
? cfg.hooks.maxBodyBytes
|
|
22
24
|
: DEFAULT_HOOKS_MAX_BODY_BYTES;
|
|
23
25
|
const mappings = resolveHookMappings(cfg.hooks);
|
|
26
|
+
const defaultAgentId = resolveDefaultAgentId(cfg);
|
|
27
|
+
const knownAgentIds = resolveKnownAgentIds(cfg, defaultAgentId);
|
|
28
|
+
const allowedAgentIds = resolveAllowedAgentIds(cfg.hooks?.allowedAgentIds);
|
|
24
29
|
return {
|
|
25
30
|
basePath: trimmed,
|
|
26
31
|
token,
|
|
27
32
|
maxBodyBytes,
|
|
28
33
|
mappings,
|
|
34
|
+
agentPolicy: {
|
|
35
|
+
defaultAgentId,
|
|
36
|
+
knownAgentIds,
|
|
37
|
+
allowedAgentIds,
|
|
38
|
+
},
|
|
29
39
|
};
|
|
30
40
|
}
|
|
41
|
+
function resolveKnownAgentIds(cfg, defaultAgentId) {
|
|
42
|
+
const known = new Set(listAgentIds(cfg));
|
|
43
|
+
known.add(defaultAgentId);
|
|
44
|
+
return known;
|
|
45
|
+
}
|
|
46
|
+
function resolveAllowedAgentIds(raw) {
|
|
47
|
+
if (!Array.isArray(raw)) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const allowed = new Set();
|
|
51
|
+
let hasWildcard = false;
|
|
52
|
+
for (const entry of raw) {
|
|
53
|
+
const trimmed = entry.trim();
|
|
54
|
+
if (!trimmed) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (trimmed === "*") {
|
|
58
|
+
hasWildcard = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
allowed.add(normalizeAgentId(trimmed));
|
|
62
|
+
}
|
|
63
|
+
if (hasWildcard) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
return allowed;
|
|
67
|
+
}
|
|
31
68
|
export function extractHookToken(req, url) {
|
|
32
69
|
const auth = typeof req.headers.authorization === "string" ? req.headers.authorization.trim() : "";
|
|
33
70
|
if (auth.toLowerCase().startsWith("bearer ")) {
|
|
@@ -120,12 +157,39 @@ export function resolveHookChannel(raw) {
|
|
|
120
157
|
export function resolveHookDeliver(raw) {
|
|
121
158
|
return raw !== false;
|
|
122
159
|
}
|
|
160
|
+
export function resolveHookTargetAgentId(hooksConfig, agentId) {
|
|
161
|
+
const raw = agentId?.trim();
|
|
162
|
+
if (!raw) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
const normalized = normalizeAgentId(raw);
|
|
166
|
+
if (hooksConfig.agentPolicy.knownAgentIds.has(normalized)) {
|
|
167
|
+
return normalized;
|
|
168
|
+
}
|
|
169
|
+
return hooksConfig.agentPolicy.defaultAgentId;
|
|
170
|
+
}
|
|
171
|
+
export function isHookAgentAllowed(hooksConfig, agentId) {
|
|
172
|
+
// Keep backwards compatibility for callers that omit agentId.
|
|
173
|
+
const raw = agentId?.trim();
|
|
174
|
+
if (!raw) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
const allowed = hooksConfig.agentPolicy.allowedAgentIds;
|
|
178
|
+
if (allowed === undefined) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
const resolved = resolveHookTargetAgentId(hooksConfig, raw);
|
|
182
|
+
return resolved ? allowed.has(resolved) : false;
|
|
183
|
+
}
|
|
184
|
+
export const getHookAgentPolicyError = () => "agentId is not allowed by hooks.allowedAgentIds";
|
|
123
185
|
export function normalizeAgentPayload(payload, opts) {
|
|
124
186
|
const message = typeof payload.message === "string" ? payload.message.trim() : "";
|
|
125
187
|
if (!message)
|
|
126
188
|
return { ok: false, error: "message required" };
|
|
127
189
|
const nameRaw = payload.name;
|
|
128
190
|
const name = typeof nameRaw === "string" && nameRaw.trim() ? nameRaw.trim() : "Hook";
|
|
191
|
+
const agentIdRaw = payload.agentId;
|
|
192
|
+
const agentId = typeof agentIdRaw === "string" && agentIdRaw.trim() ? agentIdRaw.trim() : undefined;
|
|
129
193
|
const wakeMode = payload.wakeMode === "next-heartbeat" ? "next-heartbeat" : "now";
|
|
130
194
|
const sessionKeyRaw = payload.sessionKey;
|
|
131
195
|
const idFactory = opts?.idFactory ?? randomUUID;
|
|
@@ -154,6 +218,7 @@ export function normalizeAgentPayload(payload, opts) {
|
|
|
154
218
|
value: {
|
|
155
219
|
message,
|
|
156
220
|
name,
|
|
221
|
+
agentId,
|
|
157
222
|
wakeMode,
|
|
158
223
|
sessionKey,
|
|
159
224
|
deliver,
|
|
@@ -1,69 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const CRC_TABLE = (() => {
|
|
3
|
-
const table = new Uint32Array(256);
|
|
4
|
-
for (let i = 0; i < 256; i += 1) {
|
|
5
|
-
let c = i;
|
|
6
|
-
for (let k = 0; k < 8; k += 1) {
|
|
7
|
-
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
8
|
-
}
|
|
9
|
-
table[i] = c >>> 0;
|
|
10
|
-
}
|
|
11
|
-
return table;
|
|
12
|
-
})();
|
|
13
|
-
function crc32(buf) {
|
|
14
|
-
let crc = 0xffffffff;
|
|
15
|
-
for (let i = 0; i < buf.length; i += 1) {
|
|
16
|
-
crc = CRC_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
|
|
17
|
-
}
|
|
18
|
-
return (crc ^ 0xffffffff) >>> 0;
|
|
19
|
-
}
|
|
20
|
-
function pngChunk(type, data) {
|
|
21
|
-
const typeBuf = Buffer.from(type, "ascii");
|
|
22
|
-
const len = Buffer.alloc(4);
|
|
23
|
-
len.writeUInt32BE(data.length, 0);
|
|
24
|
-
const crc = crc32(Buffer.concat([typeBuf, data]));
|
|
25
|
-
const crcBuf = Buffer.alloc(4);
|
|
26
|
-
crcBuf.writeUInt32BE(crc, 0);
|
|
27
|
-
return Buffer.concat([len, typeBuf, data, crcBuf]);
|
|
28
|
-
}
|
|
29
|
-
function encodePngRgba(buffer, width, height) {
|
|
30
|
-
const stride = width * 4;
|
|
31
|
-
const raw = Buffer.alloc((stride + 1) * height);
|
|
32
|
-
for (let row = 0; row < height; row += 1) {
|
|
33
|
-
const rawOffset = row * (stride + 1);
|
|
34
|
-
raw[rawOffset] = 0; // filter: none
|
|
35
|
-
buffer.copy(raw, rawOffset + 1, row * stride, row * stride + stride);
|
|
36
|
-
}
|
|
37
|
-
const compressed = deflateSync(raw);
|
|
38
|
-
const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
39
|
-
const ihdr = Buffer.alloc(13);
|
|
40
|
-
ihdr.writeUInt32BE(width, 0);
|
|
41
|
-
ihdr.writeUInt32BE(height, 4);
|
|
42
|
-
ihdr[8] = 8; // bit depth
|
|
43
|
-
ihdr[9] = 6; // color type RGBA
|
|
44
|
-
ihdr[10] = 0; // compression
|
|
45
|
-
ihdr[11] = 0; // filter
|
|
46
|
-
ihdr[12] = 0; // interlace
|
|
47
|
-
return Buffer.concat([
|
|
48
|
-
signature,
|
|
49
|
-
pngChunk("IHDR", ihdr),
|
|
50
|
-
pngChunk("IDAT", compressed),
|
|
51
|
-
pngChunk("IEND", Buffer.alloc(0)),
|
|
52
|
-
]);
|
|
53
|
-
}
|
|
54
|
-
function fillPixel(buf, x, y, width, r, g, b, a = 255) {
|
|
55
|
-
if (x < 0 || y < 0)
|
|
56
|
-
return;
|
|
57
|
-
if (x >= width)
|
|
58
|
-
return;
|
|
59
|
-
const idx = (y * width + x) * 4;
|
|
60
|
-
if (idx < 0 || idx + 3 >= buf.length)
|
|
61
|
-
return;
|
|
62
|
-
buf[idx] = r;
|
|
63
|
-
buf[idx + 1] = g;
|
|
64
|
-
buf[idx + 2] = b;
|
|
65
|
-
buf[idx + 3] = a;
|
|
66
|
-
}
|
|
1
|
+
import { encodePngRgba, fillPixel } from "../media/png-encode.js";
|
|
67
2
|
const GLYPH_ROWS_5X7 = {
|
|
68
3
|
"0": [0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110],
|
|
69
4
|
"1": [0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110],
|
package/dist/gateway/net.js
CHANGED
|
@@ -1,83 +1,125 @@
|
|
|
1
1
|
import net from "node:net";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import { pickPrimaryTailnetIPv4, pickPrimaryTailnetIPv6 } from "../infra/tailnet.js";
|
|
4
|
+
/**
|
|
5
|
+
* Pick the primary non-internal IPv4 address (LAN IP).
|
|
6
|
+
* Prefers common interface names (en0, eth0) then falls back to any external IPv4.
|
|
7
|
+
*/
|
|
8
|
+
export function pickPrimaryLanIPv4() {
|
|
9
|
+
const nets = os.networkInterfaces();
|
|
10
|
+
const preferredNames = ["en0", "eth0"];
|
|
11
|
+
for (const name of preferredNames) {
|
|
12
|
+
const list = nets[name];
|
|
13
|
+
const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
|
|
14
|
+
if (entry?.address) {
|
|
15
|
+
return entry.address;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (const list of Object.values(nets)) {
|
|
19
|
+
const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
|
|
20
|
+
if (entry?.address) {
|
|
21
|
+
return entry.address;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
3
26
|
export function isLoopbackAddress(ip) {
|
|
4
|
-
if (!ip)
|
|
27
|
+
if (!ip) {
|
|
5
28
|
return false;
|
|
6
|
-
|
|
29
|
+
}
|
|
30
|
+
if (ip === "127.0.0.1") {
|
|
7
31
|
return true;
|
|
8
|
-
|
|
32
|
+
}
|
|
33
|
+
if (ip.startsWith("127.")) {
|
|
9
34
|
return true;
|
|
10
|
-
|
|
35
|
+
}
|
|
36
|
+
if (ip === "::1") {
|
|
11
37
|
return true;
|
|
12
|
-
|
|
38
|
+
}
|
|
39
|
+
if (ip.startsWith("::ffff:127.")) {
|
|
13
40
|
return true;
|
|
41
|
+
}
|
|
14
42
|
return false;
|
|
15
43
|
}
|
|
16
44
|
function normalizeIPv4MappedAddress(ip) {
|
|
17
|
-
if (ip.startsWith("::ffff:"))
|
|
45
|
+
if (ip.startsWith("::ffff:")) {
|
|
18
46
|
return ip.slice("::ffff:".length);
|
|
47
|
+
}
|
|
19
48
|
return ip;
|
|
20
49
|
}
|
|
21
50
|
function normalizeIp(ip) {
|
|
22
51
|
const trimmed = ip?.trim();
|
|
23
|
-
if (!trimmed)
|
|
52
|
+
if (!trimmed) {
|
|
24
53
|
return undefined;
|
|
54
|
+
}
|
|
25
55
|
return normalizeIPv4MappedAddress(trimmed.toLowerCase());
|
|
26
56
|
}
|
|
27
57
|
function stripOptionalPort(ip) {
|
|
28
58
|
if (ip.startsWith("[")) {
|
|
29
59
|
const end = ip.indexOf("]");
|
|
30
|
-
if (end !== -1)
|
|
60
|
+
if (end !== -1) {
|
|
31
61
|
return ip.slice(1, end);
|
|
62
|
+
}
|
|
32
63
|
}
|
|
33
|
-
if (net.isIP(ip))
|
|
64
|
+
if (net.isIP(ip)) {
|
|
34
65
|
return ip;
|
|
66
|
+
}
|
|
35
67
|
const lastColon = ip.lastIndexOf(":");
|
|
36
68
|
if (lastColon > -1 && ip.includes(".") && ip.indexOf(":") === lastColon) {
|
|
37
69
|
const candidate = ip.slice(0, lastColon);
|
|
38
|
-
if (net.isIP(candidate) === 4)
|
|
70
|
+
if (net.isIP(candidate) === 4) {
|
|
39
71
|
return candidate;
|
|
72
|
+
}
|
|
40
73
|
}
|
|
41
74
|
return ip;
|
|
42
75
|
}
|
|
43
76
|
export function parseForwardedForClientIp(forwardedFor) {
|
|
44
77
|
const raw = forwardedFor?.split(",")[0]?.trim();
|
|
45
|
-
if (!raw)
|
|
78
|
+
if (!raw) {
|
|
46
79
|
return undefined;
|
|
80
|
+
}
|
|
47
81
|
return normalizeIp(stripOptionalPort(raw));
|
|
48
82
|
}
|
|
49
83
|
function parseRealIp(realIp) {
|
|
50
84
|
const raw = realIp?.trim();
|
|
51
|
-
if (!raw)
|
|
85
|
+
if (!raw) {
|
|
52
86
|
return undefined;
|
|
87
|
+
}
|
|
53
88
|
return normalizeIp(stripOptionalPort(raw));
|
|
54
89
|
}
|
|
55
90
|
export function isTrustedProxyAddress(ip, trustedProxies) {
|
|
56
91
|
const normalized = normalizeIp(ip);
|
|
57
|
-
if (!normalized || !trustedProxies || trustedProxies.length === 0)
|
|
92
|
+
if (!normalized || !trustedProxies || trustedProxies.length === 0) {
|
|
58
93
|
return false;
|
|
94
|
+
}
|
|
59
95
|
return trustedProxies.some((proxy) => normalizeIp(proxy) === normalized);
|
|
60
96
|
}
|
|
61
97
|
export function resolveGatewayClientIp(params) {
|
|
62
98
|
const remote = normalizeIp(params.remoteAddr);
|
|
63
|
-
if (!remote)
|
|
99
|
+
if (!remote) {
|
|
64
100
|
return undefined;
|
|
65
|
-
|
|
101
|
+
}
|
|
102
|
+
if (!isTrustedProxyAddress(remote, params.trustedProxies)) {
|
|
66
103
|
return remote;
|
|
104
|
+
}
|
|
67
105
|
return parseForwardedForClientIp(params.forwardedFor) ?? parseRealIp(params.realIp) ?? remote;
|
|
68
106
|
}
|
|
69
107
|
export function isLocalGatewayAddress(ip) {
|
|
70
|
-
if (isLoopbackAddress(ip))
|
|
108
|
+
if (isLoopbackAddress(ip)) {
|
|
71
109
|
return true;
|
|
72
|
-
|
|
110
|
+
}
|
|
111
|
+
if (!ip) {
|
|
73
112
|
return false;
|
|
113
|
+
}
|
|
74
114
|
const normalized = normalizeIPv4MappedAddress(ip.trim().toLowerCase());
|
|
75
115
|
const tailnetIPv4 = pickPrimaryTailnetIPv4();
|
|
76
|
-
if (tailnetIPv4 && normalized === tailnetIPv4.toLowerCase())
|
|
116
|
+
if (tailnetIPv4 && normalized === tailnetIPv4.toLowerCase()) {
|
|
77
117
|
return true;
|
|
118
|
+
}
|
|
78
119
|
const tailnetIPv6 = pickPrimaryTailnetIPv6();
|
|
79
|
-
if (tailnetIPv6 && ip.trim().toLowerCase() === tailnetIPv6.toLowerCase())
|
|
120
|
+
if (tailnetIPv6 && ip.trim().toLowerCase() === tailnetIPv6.toLowerCase()) {
|
|
80
121
|
return true;
|
|
122
|
+
}
|
|
81
123
|
return false;
|
|
82
124
|
}
|
|
83
125
|
/**
|
|
@@ -96,16 +138,19 @@ export async function resolveGatewayBindHost(bind, customHost) {
|
|
|
96
138
|
const mode = bind ?? "loopback";
|
|
97
139
|
if (mode === "loopback") {
|
|
98
140
|
// 127.0.0.1 rarely fails, but handle gracefully
|
|
99
|
-
if (await canBindToHost("127.0.0.1"))
|
|
141
|
+
if (await canBindToHost("127.0.0.1")) {
|
|
100
142
|
return "127.0.0.1";
|
|
143
|
+
}
|
|
101
144
|
return "0.0.0.0"; // extreme fallback
|
|
102
145
|
}
|
|
103
146
|
if (mode === "tailnet") {
|
|
104
147
|
const tailnetIP = pickPrimaryTailnetIPv4();
|
|
105
|
-
if (tailnetIP && (await canBindToHost(tailnetIP)))
|
|
148
|
+
if (tailnetIP && (await canBindToHost(tailnetIP))) {
|
|
106
149
|
return tailnetIP;
|
|
107
|
-
|
|
150
|
+
}
|
|
151
|
+
if (await canBindToHost("127.0.0.1")) {
|
|
108
152
|
return "127.0.0.1";
|
|
153
|
+
}
|
|
109
154
|
return "0.0.0.0";
|
|
110
155
|
}
|
|
111
156
|
if (mode === "lan") {
|
|
@@ -113,16 +158,19 @@ export async function resolveGatewayBindHost(bind, customHost) {
|
|
|
113
158
|
}
|
|
114
159
|
if (mode === "custom") {
|
|
115
160
|
const host = customHost?.trim();
|
|
116
|
-
if (!host)
|
|
117
|
-
return "0.0.0.0";
|
|
118
|
-
|
|
161
|
+
if (!host) {
|
|
162
|
+
return "0.0.0.0";
|
|
163
|
+
} // invalid config → fall back to all
|
|
164
|
+
if (isValidIPv4(host) && (await canBindToHost(host))) {
|
|
119
165
|
return host;
|
|
166
|
+
}
|
|
120
167
|
// Custom IP failed → fall back to LAN
|
|
121
168
|
return "0.0.0.0";
|
|
122
169
|
}
|
|
123
170
|
if (mode === "auto") {
|
|
124
|
-
if (await canBindToHost("127.0.0.1"))
|
|
171
|
+
if (await canBindToHost("127.0.0.1")) {
|
|
125
172
|
return "127.0.0.1";
|
|
173
|
+
}
|
|
126
174
|
return "0.0.0.0";
|
|
127
175
|
}
|
|
128
176
|
return "0.0.0.0";
|
|
@@ -149,11 +197,13 @@ export async function canBindToHost(host) {
|
|
|
149
197
|
});
|
|
150
198
|
}
|
|
151
199
|
export async function resolveGatewayListenHosts(bindHost, opts) {
|
|
152
|
-
if (bindHost !== "127.0.0.1")
|
|
200
|
+
if (bindHost !== "127.0.0.1") {
|
|
153
201
|
return [bindHost];
|
|
202
|
+
}
|
|
154
203
|
const canBind = opts?.canBindToHost ?? canBindToHost;
|
|
155
|
-
if (await canBind("::1"))
|
|
204
|
+
if (await canBind("::1")) {
|
|
156
205
|
return [bindHost, "::1"];
|
|
206
|
+
}
|
|
157
207
|
return [bindHost];
|
|
158
208
|
}
|
|
159
209
|
/**
|
|
@@ -162,15 +212,30 @@ export async function resolveGatewayListenHosts(bindHost, opts) {
|
|
|
162
212
|
* @param host - The string to validate
|
|
163
213
|
* @returns True if valid IPv4 format
|
|
164
214
|
*/
|
|
165
|
-
function isValidIPv4(host) {
|
|
215
|
+
export function isValidIPv4(host) {
|
|
166
216
|
const parts = host.split(".");
|
|
167
|
-
if (parts.length !== 4)
|
|
217
|
+
if (parts.length !== 4) {
|
|
168
218
|
return false;
|
|
219
|
+
}
|
|
169
220
|
return parts.every((part) => {
|
|
170
221
|
const n = parseInt(part, 10);
|
|
171
222
|
return !Number.isNaN(n) && n >= 0 && n <= 255 && part === String(n);
|
|
172
223
|
});
|
|
173
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Check if a hostname or IP refers to the local machine.
|
|
227
|
+
* Handles: localhost, 127.x.x.x, ::1, [::1], ::ffff:127.x.x.x
|
|
228
|
+
* Note: 0.0.0.0 and :: are NOT loopback - they bind to all interfaces.
|
|
229
|
+
*/
|
|
174
230
|
export function isLoopbackHost(host) {
|
|
175
|
-
|
|
231
|
+
if (!host) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
const h = host.trim().toLowerCase();
|
|
235
|
+
if (h === "localhost") {
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
// Handle bracketed IPv6 addresses like [::1]
|
|
239
|
+
const unbracket = h.startsWith("[") && h.endsWith("]") ? h.slice(1, -1) : h;
|
|
240
|
+
return isLoopbackAddress(unbracket);
|
|
176
241
|
}
|