@poolzin/pool-bot 2026.2.21 → 2026.2.22
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/CHANGELOG.md +17 -0
- package/dist/agents/api-key-rotation.js +47 -0
- package/dist/agents/apply-patch-update.js +19 -9
- package/dist/agents/apply-patch.js +72 -47
- package/dist/agents/bash-tools.exec.js +141 -559
- package/dist/agents/cli-backends.js +49 -6
- package/dist/agents/cli-runner/helpers.js +69 -152
- package/dist/agents/cli-runner.js +70 -19
- package/dist/agents/identity.js +20 -1
- package/dist/agents/image-sanitization.js +9 -0
- package/dist/agents/live-auth-keys.js +123 -26
- package/dist/agents/live-model-filter.js +13 -4
- package/dist/agents/model-catalog.js +40 -9
- package/dist/agents/model-forward-compat.js +60 -23
- package/dist/agents/model-selection.js +134 -41
- package/dist/agents/pi-auth-json.js +2 -2
- package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
- package/dist/agents/pi-embedded-helpers/errors.js +140 -15
- package/dist/agents/pi-embedded-helpers/images.js +22 -12
- package/dist/agents/pi-embedded-helpers.js +2 -2
- package/dist/agents/pi-embedded-runner/abort.js +10 -3
- package/dist/agents/pi-embedded-runner/compact.js +230 -32
- package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
- package/dist/agents/pi-embedded-runner/google.js +109 -19
- package/dist/agents/pi-embedded-runner/history.js +35 -17
- package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
- package/dist/agents/pi-embedded-runner/run/images.js +81 -55
- package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
- package/dist/agents/pi-embedded-runner/run.js +193 -25
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
- package/dist/agents/pi-embedded-runner/runs.js +17 -8
- package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
- package/dist/agents/pi-embedded-runner.js +1 -1
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
- package/dist/agents/pi-embedded-subscribe.js +37 -0
- package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
- package/dist/agents/pi-model-discovery.js +9 -2
- package/dist/agents/pi-tool-definition-adapter.js +60 -8
- package/dist/agents/pi-tools.before-tool-call.js +1 -1
- package/dist/agents/pi-tools.js +113 -94
- package/dist/agents/pi-tools.read.js +337 -38
- package/dist/agents/poolbot-tools.js +14 -5
- package/dist/agents/sandbox/docker.js +10 -5
- package/dist/agents/sandbox/registry.js +96 -46
- package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
- package/dist/agents/sandbox-paths.js +43 -10
- package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
- package/dist/agents/session-tool-result-guard.js +39 -39
- package/dist/agents/session-transcript-repair.js +36 -33
- package/dist/agents/session-write-lock.js +62 -44
- package/dist/agents/skills/frontmatter.js +49 -88
- package/dist/agents/skills/workspace.js +335 -28
- package/dist/agents/subagent-announce.js +508 -174
- package/dist/agents/subagent-registry.js +45 -4
- package/dist/agents/subagent-spawn.js +16 -33
- package/dist/agents/system-prompt-report.js +27 -10
- package/dist/agents/system-prompt.js +26 -32
- package/dist/agents/tool-call-id.js +69 -17
- package/dist/agents/tool-display-common.js +1 -1
- package/dist/agents/tool-images.js +64 -31
- package/dist/agents/tools/canvas-tool.js +17 -11
- package/dist/agents/tools/common.js +37 -19
- package/dist/agents/tools/cron-tool.js +40 -38
- package/dist/agents/tools/gateway.js +70 -2
- package/dist/agents/tools/message-tool.js +181 -40
- package/dist/agents/tools/nodes-tool.js +128 -36
- package/dist/agents/tools/nodes-utils.js +12 -38
- package/dist/agents/tools/session-status-tool.js +24 -71
- package/dist/agents/tools/sessions-helpers.js +38 -210
- package/dist/agents/tools/sessions-spawn-tool.js +28 -198
- package/dist/agents/tools/telegram-actions.js +58 -7
- package/dist/agents/tools/web-fetch-utils.js +112 -7
- package/dist/agents/tools/web-fetch.js +279 -175
- package/dist/agents/tools/web-shared.js +71 -8
- package/dist/agents/usage.js +25 -16
- package/dist/auto-reply/commands-registry.data.js +85 -11
- package/dist/auto-reply/dispatch.js +40 -21
- package/dist/auto-reply/reply/abort.js +102 -33
- package/dist/auto-reply/reply/commands-core.js +82 -33
- package/dist/auto-reply/reply/commands-export-session.js +1 -1
- package/dist/auto-reply/reply/commands-info.js +41 -12
- package/dist/auto-reply/reply/commands-subagents.js +352 -100
- package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
- package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
- package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
- package/dist/auto-reply/reply/inbound-meta.js +12 -1
- package/dist/auto-reply/reply/mentions.js +18 -11
- package/dist/auto-reply/reply/normalize-reply.js +17 -8
- package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
- package/dist/auto-reply/reply/session.js +102 -21
- package/dist/auto-reply/reply/streaming-directives.js +16 -5
- package/dist/auto-reply/status.js +73 -50
- package/dist/browser/extension-relay.js +3 -3
- package/dist/browser/http-auth.js +1 -1
- package/dist/browser/paths.js +2 -2
- package/dist/build-info.json +3 -3
- package/dist/channels/allowlist-match.js +20 -0
- package/dist/channels/allowlists/resolve-utils.js +65 -2
- package/dist/channels/chat-type.js +8 -4
- package/dist/channels/dock.js +127 -35
- package/dist/channels/draft-stream-loop.js +6 -2
- package/dist/channels/plugins/actions/telegram.js +42 -18
- package/dist/channels/plugins/allowlist-match.js +1 -1
- package/dist/channels/plugins/group-mentions.js +51 -41
- package/dist/channels/plugins/message-action-names.js +2 -0
- package/dist/channels/plugins/message-actions.js +24 -5
- package/dist/channels/plugins/normalize/discord.js +26 -4
- package/dist/channels/plugins/normalize/signal.js +35 -22
- package/dist/channels/plugins/onboarding/helpers.js +8 -26
- package/dist/channels/plugins/outbound/imessage.js +15 -14
- package/dist/channels/registry.js +20 -7
- package/dist/cli/acp-cli.js +7 -5
- package/dist/cli/browser-cli-extension.js +25 -12
- package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
- package/dist/cli/browser-cli-state.js +101 -145
- package/dist/cli/command-options.js +28 -0
- package/dist/cli/completion-cli.js +6 -6
- package/dist/cli/cron-cli/register.cron-add.js +25 -1
- package/dist/cli/cron-cli/register.cron-edit.js +44 -0
- package/dist/cli/cron-cli/shared.js +7 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
- package/dist/cli/daemon-cli/lifecycle.js +23 -247
- package/dist/cli/daemon-cli/register-service-commands.js +25 -4
- package/dist/cli/daemon-cli.js +1 -0
- package/dist/cli/devices-cli.js +33 -20
- package/dist/cli/gateway-cli/register.js +37 -105
- package/dist/cli/gateway-cli/run.js +49 -11
- package/dist/cli/nodes-camera.js +59 -4
- package/dist/cli/nodes-cli/register.camera.js +27 -24
- package/dist/cli/nodes-cli/rpc.js +21 -38
- package/dist/cli/qr-cli.js +2 -2
- package/dist/cli/skills-cli.format.js +2 -2
- package/dist/cli/update-cli/progress.js +2 -2
- package/dist/cli/update-cli/restart-helper.js +28 -7
- package/dist/cli/update-cli/shared.js +7 -7
- package/dist/cli/update-cli/status.js +1 -1
- package/dist/cli/update-cli/update-command.js +14 -8
- package/dist/cli/update-cli/wizard.js +2 -2
- package/dist/cli/update-cli.js +21 -1027
- package/dist/commands/auth-choice.apply.anthropic.js +10 -2
- package/dist/commands/channels/add-mutators.js +3 -35
- package/dist/commands/channels/add.js +39 -51
- package/dist/commands/config-validation.js +1 -1
- package/dist/commands/configure.gateway-auth.js +52 -15
- package/dist/commands/configure.gateway.js +84 -40
- package/dist/commands/doctor-completion.js +3 -3
- package/dist/commands/doctor-config-flow.js +536 -16
- package/dist/commands/doctor-gateway-services.js +103 -79
- package/dist/commands/doctor-memory-search.js +9 -9
- package/dist/commands/doctor-platform-notes.js +57 -30
- package/dist/commands/doctor-prompter.js +26 -15
- package/dist/commands/doctor-session-locks.js +1 -1
- package/dist/commands/doctor.js +21 -9
- package/dist/commands/model-picker.js +120 -95
- package/dist/commands/models/set.js +2 -21
- package/dist/commands/models/shared.js +65 -37
- package/dist/commands/onboard-helpers.js +81 -39
- package/dist/commands/openai-codex-oauth.js +1 -1
- package/dist/commands/sessions.js +52 -53
- package/dist/commands/status.summary.js +52 -34
- package/dist/commands/test-wizard-helpers.js +2 -2
- package/dist/config/defaults.js +79 -42
- package/dist/config/group-policy.js +50 -18
- package/dist/config/includes.js +37 -10
- package/dist/config/schema.help.js +5 -4
- package/dist/config/schema.hints.js +2 -2
- package/dist/config/schema.labels.js +1 -0
- package/dist/config/sessions/group.js +12 -11
- package/dist/config/sessions/paths.js +137 -11
- package/dist/config/sessions/store.js +185 -65
- package/dist/config/sessions/types.js +15 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/telegram-custom-commands.js +3 -2
- package/dist/config/types.js +2 -0
- package/dist/config/zod-schema.agent-defaults.js +6 -27
- package/dist/config/zod-schema.agent-runtime.js +171 -79
- package/dist/config/zod-schema.providers-core.js +138 -65
- package/dist/config/zod-schema.session.js +49 -22
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
- package/dist/cron/isolated-agent/run.js +224 -57
- package/dist/cron/normalize.js +48 -45
- package/dist/cron/run-log.js +14 -0
- package/dist/cron/service/jobs.js +190 -28
- package/dist/cron/service/normalize.js +29 -11
- package/dist/cron/service/store.js +30 -44
- package/dist/cron/service/timer.js +182 -96
- package/dist/cron/service.js +3 -0
- package/dist/cron/stagger.js +37 -0
- package/dist/daemon/inspect.js +132 -92
- package/dist/daemon/runtime-paths.js +25 -4
- package/dist/daemon/service-audit.js +47 -16
- package/dist/discord/accounts.js +23 -20
- package/dist/discord/monitor/agent-components.js +1115 -219
- package/dist/discord/monitor/allow-list.js +114 -34
- package/dist/discord/monitor/listeners.js +204 -97
- package/dist/discord/monitor/message-handler.js +21 -10
- package/dist/discord/monitor/message-handler.preflight.js +195 -101
- package/dist/discord/monitor/message-handler.process.js +384 -123
- package/dist/discord/monitor/message-utils.js +86 -23
- package/dist/discord/monitor/native-command.js +77 -57
- package/dist/discord/monitor/provider.js +122 -117
- package/dist/discord/monitor/reply-context.js +20 -16
- package/dist/discord/monitor/reply-delivery.js +40 -8
- package/dist/discord/monitor/rest-fetch.js +22 -0
- package/dist/discord/monitor/threading.js +117 -24
- package/dist/discord/send.js +2 -1
- package/dist/discord/send.outbound.js +124 -11
- package/dist/discord/send.shared.js +112 -72
- package/dist/discord/voice-message.js +3 -3
- package/dist/gateway/auth.js +119 -44
- package/dist/gateway/call.js +76 -34
- package/dist/gateway/channel-health-monitor.js +57 -50
- package/dist/gateway/client.js +63 -29
- package/dist/gateway/control-ui-contract.js +1 -1
- package/dist/gateway/gateway-config-prompts.shared.js +2 -2
- package/dist/gateway/net.js +109 -1
- package/dist/gateway/protocol/index.js +5 -8
- package/dist/gateway/protocol/schema/agent.js +19 -1
- package/dist/gateway/protocol/schema/channels.js +21 -0
- package/dist/gateway/protocol/schema/cron.js +43 -30
- package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
- package/dist/gateway/protocol/schema/sessions.js +5 -1
- package/dist/gateway/protocol/schema.js +0 -1
- package/dist/gateway/server/presence-events.js +12 -0
- package/dist/gateway/server/ws-connection/message-handler.js +203 -212
- package/dist/gateway/server/ws-connection.js +58 -21
- package/dist/gateway/server-broadcast.js +18 -13
- package/dist/gateway/server-cron.js +177 -10
- package/dist/gateway/server-methods/agent-job.js +131 -38
- package/dist/gateway/server-methods/send.js +60 -14
- package/dist/gateway/server-methods/sessions.js +160 -96
- package/dist/gateway/server-methods/system.js +5 -7
- package/dist/gateway/server-methods-list.js +8 -0
- package/dist/gateway/server-methods.js +24 -8
- package/dist/gateway/server-node-events.js +278 -68
- package/dist/gateway/session-utils.fs.js +316 -75
- package/dist/gateway/session-utils.js +224 -70
- package/dist/gateway/sessions-patch.js +63 -20
- package/dist/gateway/test-temp-config.js +1 -1
- package/dist/gateway/tools-invoke-http.js +118 -70
- package/dist/gateway/ws-log.js +135 -107
- package/dist/hooks/frontmatter.js +36 -82
- package/dist/hooks/install.js +149 -139
- package/dist/hooks/internal-hooks.js +29 -4
- package/dist/hooks/plugin-hooks.js +2 -1
- package/dist/imessage/monitor/deliver.js +10 -4
- package/dist/imessage/monitor/monitor-provider.js +138 -375
- package/dist/imessage/monitor/runtime.js +4 -8
- package/dist/imessage/send.js +65 -19
- package/dist/infra/exec-approvals-allowlist.js +7 -0
- package/dist/infra/exec-approvals.js +35 -920
- package/dist/infra/exec-safe-bin-trust.js +64 -0
- package/dist/infra/heartbeat-runner.js +207 -134
- package/dist/infra/heartbeat-wake.js +183 -22
- package/dist/infra/install-source-utils.js +47 -0
- package/dist/infra/net/ssrf.js +170 -36
- package/dist/infra/outbound/deliver.js +224 -58
- package/dist/infra/outbound/message-action-spec.js +12 -5
- package/dist/infra/outbound/outbound-session.js +27 -25
- package/dist/infra/poolbot-root.js +32 -22
- package/dist/infra/ports.js +14 -11
- package/dist/infra/skills-remote.js +48 -37
- package/dist/infra/system-events.js +25 -11
- package/dist/infra/system-presence.js +26 -33
- package/dist/infra/tmp-poolbot-dir.js +81 -2
- package/dist/infra/wsl.js +37 -1
- package/dist/line/bot-message-context.js +163 -191
- package/dist/logging/subsystem.js +59 -22
- package/dist/markdown/ir.js +124 -50
- package/dist/media/store.js +1 -1
- package/dist/media-understanding/runner.entries.js +42 -25
- package/dist/media-understanding/runner.js +53 -488
- package/dist/memory/embeddings-gemini.js +53 -38
- package/dist/memory/manager-embedding-ops.js +48 -69
- package/dist/pairing/pairing-store.js +178 -119
- package/dist/plugin-sdk/index.js +34 -6
- package/dist/plugins/hooks.js +135 -14
- package/dist/plugins/install.js +190 -152
- package/dist/polls.js +11 -0
- package/dist/routing/resolve-route.js +190 -56
- package/dist/routing/session-key.js +38 -22
- package/dist/runtime.js +35 -9
- package/dist/security/audit-channel.js +1 -1
- package/dist/sessions/session-key-utils.js +29 -11
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-types.js +1 -0
- package/dist/shared/string-normalization.js +15 -0
- package/dist/signal/monitor/event-handler.js +68 -36
- package/dist/signal/send.js +29 -37
- package/dist/slack/monitor/allow-list.js +10 -11
- package/dist/slack/monitor/commands.js +14 -3
- package/dist/slack/monitor/events/interactions.js +4 -4
- package/dist/slack/monitor/media.js +224 -16
- package/dist/slack/monitor/message-handler/dispatch.js +247 -13
- package/dist/slack/monitor/message-handler/prepare.js +128 -45
- package/dist/slack/monitor/slash.js +357 -144
- package/dist/slack/streaming.js +77 -0
- package/dist/telegram/accounts.js +40 -13
- package/dist/telegram/allowed-updates.js +3 -0
- package/dist/telegram/bot/delivery.js +129 -66
- package/dist/telegram/bot/helpers.js +136 -122
- package/dist/telegram/bot-handlers.js +600 -339
- package/dist/telegram/bot-message-context.js +115 -73
- package/dist/telegram/bot-message-dispatch.js +235 -104
- package/dist/telegram/bot-native-command-menu.js +3 -1
- package/dist/telegram/bot-native-commands.js +213 -193
- package/dist/telegram/bot.js +24 -132
- package/dist/telegram/draft-stream.js +84 -75
- package/dist/telegram/format.js +150 -6
- package/dist/telegram/send.js +415 -255
- package/dist/telegram/targets.js +21 -2
- package/dist/telegram/update-offset-store.js +19 -3
- package/dist/terminal/restore.js +5 -2
- package/dist/test-utils/fetch-mock.js +5 -0
- package/dist/version.js +18 -5
- package/dist/web/auto-reply/monitor/broadcast.js +7 -3
- package/dist/web/auto-reply/monitor/on-message.js +6 -3
- package/dist/web/inbound/media.js +34 -8
- package/dist/web/inbound/monitor.js +34 -17
- package/dist/web/inbound/send-api.js +18 -17
- package/dist/web/outbound.js +12 -5
- package/dist/wizard/clack-prompter.js +40 -7
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
- package/skills/apple-reminders/SKILL.md +100 -49
- package/skills/coding-agent/SKILL.md +34 -28
- package/skills/github/SKILL.md +131 -16
- package/skills/imsg/SKILL.md +112 -15
- package/skills/openhue/SKILL.md +101 -19
- package/skills/tmux/SKILL.md +111 -79
- package/skills/weather/SKILL.md +88 -25
|
@@ -4,50 +4,18 @@ import { loadConfig } from "../../config/config.js";
|
|
|
4
4
|
import { discoverGatewayBeacons } from "../../infra/bonjour-discovery.js";
|
|
5
5
|
import { resolveWideAreaDiscoveryDomain } from "../../infra/widearea-dns.js";
|
|
6
6
|
import { defaultRuntime } from "../../runtime.js";
|
|
7
|
+
import { styleHealthChannelLine } from "../../terminal/health-style.js";
|
|
7
8
|
import { formatDocsLink } from "../../terminal/links.js";
|
|
8
9
|
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
|
9
10
|
import { formatTokenCount, formatUsd } from "../../utils/usage-format.js";
|
|
10
11
|
import { runCommandWithRuntime } from "../cli-utils.js";
|
|
11
|
-
import {
|
|
12
|
+
import { inheritOptionFromParent } from "../command-options.js";
|
|
13
|
+
import { addGatewayServiceCommands } from "../daemon-cli.js";
|
|
14
|
+
import { formatHelpExamples } from "../help-format.js";
|
|
12
15
|
import { withProgress } from "../progress.js";
|
|
13
16
|
import { callGatewayCli, gatewayCallOpts } from "./call.js";
|
|
14
17
|
import { dedupeBeacons, parseDiscoverTimeoutMs, pickBeaconHost, pickGatewayPort, renderBeaconLines, } from "./discover.js";
|
|
15
18
|
import { addGatewayRunCommand } from "./run.js";
|
|
16
|
-
function styleHealthChannelLine(line, rich) {
|
|
17
|
-
if (!rich) {
|
|
18
|
-
return line;
|
|
19
|
-
}
|
|
20
|
-
const colon = line.indexOf(":");
|
|
21
|
-
if (colon === -1) {
|
|
22
|
-
return line;
|
|
23
|
-
}
|
|
24
|
-
const label = line.slice(0, colon + 1);
|
|
25
|
-
const detail = line.slice(colon + 1).trimStart();
|
|
26
|
-
const normalized = detail.toLowerCase();
|
|
27
|
-
const applyPrefix = (prefix, color) => `${label} ${color(detail.slice(0, prefix.length))}${detail.slice(prefix.length)}`;
|
|
28
|
-
if (normalized.startsWith("failed")) {
|
|
29
|
-
return applyPrefix("failed", theme.error);
|
|
30
|
-
}
|
|
31
|
-
if (normalized.startsWith("ok")) {
|
|
32
|
-
return applyPrefix("ok", theme.success);
|
|
33
|
-
}
|
|
34
|
-
if (normalized.startsWith("linked")) {
|
|
35
|
-
return applyPrefix("linked", theme.success);
|
|
36
|
-
}
|
|
37
|
-
if (normalized.startsWith("configured")) {
|
|
38
|
-
return applyPrefix("configured", theme.success);
|
|
39
|
-
}
|
|
40
|
-
if (normalized.startsWith("not linked")) {
|
|
41
|
-
return applyPrefix("not linked", theme.warn);
|
|
42
|
-
}
|
|
43
|
-
if (normalized.startsWith("not configured")) {
|
|
44
|
-
return applyPrefix("not configured", theme.muted);
|
|
45
|
-
}
|
|
46
|
-
if (normalized.startsWith("unknown")) {
|
|
47
|
-
return applyPrefix("unknown", theme.warn);
|
|
48
|
-
}
|
|
49
|
-
return line;
|
|
50
|
-
}
|
|
51
19
|
function runGatewayCommand(action, label) {
|
|
52
20
|
return runCommandWithRuntime(defaultRuntime, action, (err) => {
|
|
53
21
|
const message = String(err);
|
|
@@ -67,6 +35,15 @@ function parseDaysOption(raw, fallback = 30) {
|
|
|
67
35
|
}
|
|
68
36
|
return fallback;
|
|
69
37
|
}
|
|
38
|
+
function resolveGatewayRpcOptions(opts, command) {
|
|
39
|
+
const parentToken = inheritOptionFromParent(command, "token");
|
|
40
|
+
const parentPassword = inheritOptionFromParent(command, "password");
|
|
41
|
+
return {
|
|
42
|
+
...opts,
|
|
43
|
+
token: opts.token ?? parentToken,
|
|
44
|
+
password: opts.password ?? parentPassword,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
70
47
|
function renderCostUsageSummary(summary, days, rich) {
|
|
71
48
|
const totalCost = formatUsd(summary.totals.totalCost) ?? "$0.00";
|
|
72
49
|
const totalTokens = formatTokenCount(summary.totals.totalTokens) ?? "0";
|
|
@@ -88,76 +65,28 @@ function renderCostUsageSummary(summary, days, rich) {
|
|
|
88
65
|
export function registerGatewayCli(program) {
|
|
89
66
|
const gateway = addGatewayRunCommand(program
|
|
90
67
|
.command("gateway")
|
|
91
|
-
.description("Run the WebSocket Gateway")
|
|
92
|
-
.addHelpText("after", () => `\n${theme.
|
|
68
|
+
.description("Run, inspect, and query the WebSocket Gateway")
|
|
69
|
+
.addHelpText("after", () => `\n${theme.heading("Examples:")}\n${formatHelpExamples([
|
|
70
|
+
["poolbot gateway run", "Run the gateway in the foreground."],
|
|
71
|
+
["poolbot gateway status", "Show service status and probe reachability."],
|
|
72
|
+
["poolbot gateway discover", "Find local and wide-area gateway beacons."],
|
|
73
|
+
["poolbot gateway call health", "Call a gateway RPC method directly."],
|
|
74
|
+
])}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/gateway", "docs.poolbot.ai/cli/gateway")}\n`));
|
|
93
75
|
addGatewayRunCommand(gateway.command("run").description("Run the WebSocket Gateway (foreground)"));
|
|
94
|
-
gateway
|
|
95
|
-
|
|
96
|
-
.description("Show gateway service status + probe the Gateway")
|
|
97
|
-
.option("--url <url>", "Gateway WebSocket URL (defaults to config/remote/local)")
|
|
98
|
-
.option("--token <token>", "Gateway token (if required)")
|
|
99
|
-
.option("--password <password>", "Gateway password (password auth)")
|
|
100
|
-
.option("--timeout <ms>", "Timeout in ms", "10000")
|
|
101
|
-
.option("--no-probe", "Skip RPC probe")
|
|
102
|
-
.option("--deep", "Scan system-level services", false)
|
|
103
|
-
.option("--json", "Output JSON", false)
|
|
104
|
-
.action(async (opts) => {
|
|
105
|
-
await runDaemonStatus({
|
|
106
|
-
rpc: opts,
|
|
107
|
-
probe: Boolean(opts.probe),
|
|
108
|
-
deep: Boolean(opts.deep),
|
|
109
|
-
json: Boolean(opts.json),
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
gateway
|
|
113
|
-
.command("install")
|
|
114
|
-
.description("Install the Gateway service (launchd/systemd/schtasks)")
|
|
115
|
-
.option("--port <port>", "Gateway port")
|
|
116
|
-
.option("--runtime <runtime>", "Daemon runtime (node|bun). Default: node")
|
|
117
|
-
.option("--token <token>", "Gateway token (token auth)")
|
|
118
|
-
.option("--force", "Reinstall/overwrite if already installed", false)
|
|
119
|
-
.option("--json", "Output JSON", false)
|
|
120
|
-
.action(async (opts) => {
|
|
121
|
-
await runDaemonInstall(opts);
|
|
122
|
-
});
|
|
123
|
-
gateway
|
|
124
|
-
.command("uninstall")
|
|
125
|
-
.description("Uninstall the Gateway service (launchd/systemd/schtasks)")
|
|
126
|
-
.option("--json", "Output JSON", false)
|
|
127
|
-
.action(async (opts) => {
|
|
128
|
-
await runDaemonUninstall(opts);
|
|
129
|
-
});
|
|
130
|
-
gateway
|
|
131
|
-
.command("start")
|
|
132
|
-
.description("Start the Gateway service (launchd/systemd/schtasks)")
|
|
133
|
-
.option("--json", "Output JSON", false)
|
|
134
|
-
.action(async (opts) => {
|
|
135
|
-
await runDaemonStart(opts);
|
|
136
|
-
});
|
|
137
|
-
gateway
|
|
138
|
-
.command("stop")
|
|
139
|
-
.description("Stop the Gateway service (launchd/systemd/schtasks)")
|
|
140
|
-
.option("--json", "Output JSON", false)
|
|
141
|
-
.action(async (opts) => {
|
|
142
|
-
await runDaemonStop(opts);
|
|
143
|
-
});
|
|
144
|
-
gateway
|
|
145
|
-
.command("restart")
|
|
146
|
-
.description("Restart the Gateway service (launchd/systemd/schtasks)")
|
|
147
|
-
.option("--json", "Output JSON", false)
|
|
148
|
-
.action(async (opts) => {
|
|
149
|
-
await runDaemonRestart(opts);
|
|
76
|
+
addGatewayServiceCommands(gateway, {
|
|
77
|
+
statusDescription: "Show gateway service status + probe the Gateway",
|
|
150
78
|
});
|
|
151
79
|
gatewayCallOpts(gateway
|
|
152
80
|
.command("call")
|
|
153
81
|
.description("Call a Gateway method")
|
|
154
82
|
.argument("<method>", "Method name (health/status/system-presence/cron.*)")
|
|
155
83
|
.option("--params <json>", "JSON object string for params", "{}")
|
|
156
|
-
.action(async (method, opts) => {
|
|
84
|
+
.action(async (method, opts, command) => {
|
|
157
85
|
await runGatewayCommand(async () => {
|
|
86
|
+
const rpcOpts = resolveGatewayRpcOptions(opts, command);
|
|
158
87
|
const params = JSON.parse(String(opts.params ?? "{}"));
|
|
159
|
-
const result = await callGatewayCli(method,
|
|
160
|
-
if (
|
|
88
|
+
const result = await callGatewayCli(method, rpcOpts, params);
|
|
89
|
+
if (rpcOpts.json) {
|
|
161
90
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
162
91
|
return;
|
|
163
92
|
}
|
|
@@ -170,11 +99,12 @@ export function registerGatewayCli(program) {
|
|
|
170
99
|
.command("usage-cost")
|
|
171
100
|
.description("Fetch usage cost summary from session logs")
|
|
172
101
|
.option("--days <days>", "Number of days to include", "30")
|
|
173
|
-
.action(async (opts) => {
|
|
102
|
+
.action(async (opts, command) => {
|
|
174
103
|
await runGatewayCommand(async () => {
|
|
104
|
+
const rpcOpts = resolveGatewayRpcOptions(opts, command);
|
|
175
105
|
const days = parseDaysOption(opts.days);
|
|
176
|
-
const result = await callGatewayCli("usage.cost",
|
|
177
|
-
if (
|
|
106
|
+
const result = await callGatewayCli("usage.cost", rpcOpts, { days });
|
|
107
|
+
if (rpcOpts.json) {
|
|
178
108
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
179
109
|
return;
|
|
180
110
|
}
|
|
@@ -188,10 +118,11 @@ export function registerGatewayCli(program) {
|
|
|
188
118
|
gatewayCallOpts(gateway
|
|
189
119
|
.command("health")
|
|
190
120
|
.description("Fetch Gateway health")
|
|
191
|
-
.action(async (opts) => {
|
|
121
|
+
.action(async (opts, command) => {
|
|
192
122
|
await runGatewayCommand(async () => {
|
|
193
|
-
const
|
|
194
|
-
|
|
123
|
+
const rpcOpts = resolveGatewayRpcOptions(opts, command);
|
|
124
|
+
const result = await callGatewayCli("health", rpcOpts);
|
|
125
|
+
if (rpcOpts.json) {
|
|
195
126
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
196
127
|
return;
|
|
197
128
|
}
|
|
@@ -218,9 +149,10 @@ export function registerGatewayCli(program) {
|
|
|
218
149
|
.option("--password <password>", "Gateway password (applies to all probes)")
|
|
219
150
|
.option("--timeout <ms>", "Overall probe budget in ms", "3000")
|
|
220
151
|
.option("--json", "Output JSON", false)
|
|
221
|
-
.action(async (opts) => {
|
|
152
|
+
.action(async (opts, command) => {
|
|
222
153
|
await runGatewayCommand(async () => {
|
|
223
|
-
|
|
154
|
+
const rpcOpts = resolveGatewayRpcOptions(opts, command);
|
|
155
|
+
await gatewayStatusCommand(rpcOpts, defaultRuntime);
|
|
224
156
|
});
|
|
225
157
|
});
|
|
226
158
|
gateway
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CONFIG_PATH, loadConfig, readConfigFileSnapshot, resolveStateDir, resolveGatewayPort, } from "../../config/config.js";
|
|
3
4
|
import { resolveGatewayAuth } from "../../gateway/auth.js";
|
|
4
5
|
import { startGatewayServer } from "../../gateway/server.js";
|
|
5
6
|
import { setGatewayWsLogStyle } from "../../gateway/ws-logging.js";
|
|
@@ -10,13 +11,52 @@ import { setConsoleSubsystemFilter, setConsoleTimestampPrefix } from "../../logg
|
|
|
10
11
|
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
|
11
12
|
import { defaultRuntime } from "../../runtime.js";
|
|
12
13
|
import { formatCliCommand } from "../command-format.js";
|
|
14
|
+
import { inheritOptionFromParent } from "../command-options.js";
|
|
13
15
|
import { forceFreePortAndWait } from "../ports.js";
|
|
14
16
|
import { ensureDevGatewayConfig } from "./dev.js";
|
|
15
17
|
import { runGatewayLoop } from "./run-loop.js";
|
|
16
18
|
import { describeUnknownError, extractGatewayMiskeys, maybeExplainGatewayServiceStop, parsePort, toOptionString, } from "./shared.js";
|
|
17
19
|
const gatewayLog = createSubsystemLogger("gateway");
|
|
20
|
+
const GATEWAY_RUN_VALUE_KEYS = [
|
|
21
|
+
"port",
|
|
22
|
+
"bind",
|
|
23
|
+
"token",
|
|
24
|
+
"auth",
|
|
25
|
+
"password",
|
|
26
|
+
"tailscale",
|
|
27
|
+
"wsLog",
|
|
28
|
+
"rawStreamPath",
|
|
29
|
+
];
|
|
30
|
+
const GATEWAY_RUN_BOOLEAN_KEYS = [
|
|
31
|
+
"tailscaleResetOnExit",
|
|
32
|
+
"allowUnconfigured",
|
|
33
|
+
"dev",
|
|
34
|
+
"reset",
|
|
35
|
+
"force",
|
|
36
|
+
"verbose",
|
|
37
|
+
"claudeCliLogs",
|
|
38
|
+
"compact",
|
|
39
|
+
"rawStream",
|
|
40
|
+
];
|
|
41
|
+
function resolveGatewayRunOptions(opts, command) {
|
|
42
|
+
const resolved = { ...opts };
|
|
43
|
+
for (const key of GATEWAY_RUN_VALUE_KEYS) {
|
|
44
|
+
const inherited = inheritOptionFromParent(command, key);
|
|
45
|
+
if (key === "wsLog") {
|
|
46
|
+
// wsLog has a child default ("auto"), so prefer inherited parent CLI value when present.
|
|
47
|
+
resolved[key] = inherited ?? resolved[key];
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
resolved[key] = resolved[key] ?? inherited;
|
|
51
|
+
}
|
|
52
|
+
for (const key of GATEWAY_RUN_BOOLEAN_KEYS) {
|
|
53
|
+
const inherited = inheritOptionFromParent(command, key);
|
|
54
|
+
resolved[key] = Boolean(resolved[key] || inherited);
|
|
55
|
+
}
|
|
56
|
+
return resolved;
|
|
57
|
+
}
|
|
18
58
|
async function runGatewayCommand(opts) {
|
|
19
|
-
const isDevProfile =
|
|
59
|
+
const isDevProfile = process.env.POOLBOT_PROFILE?.trim().toLowerCase() === "dev";
|
|
20
60
|
const devMode = Boolean(opts.dev) || isDevProfile;
|
|
21
61
|
if (opts.reset && !devMode) {
|
|
22
62
|
defaultRuntime.error("Use --reset with --dev.");
|
|
@@ -28,7 +68,6 @@ async function runGatewayCommand(opts) {
|
|
|
28
68
|
if (opts.claudeCliLogs) {
|
|
29
69
|
setConsoleSubsystemFilter(["agent/claude-cli"]);
|
|
30
70
|
process.env.POOLBOT_CLAUDE_CLI_LOG_OUTPUT = "1";
|
|
31
|
-
process.env.CLAWDBOT_CLAUDE_CLI_LOG_OUTPUT = "1";
|
|
32
71
|
}
|
|
33
72
|
const wsLogRaw = (opts.compact ? "compact" : opts.wsLog);
|
|
34
73
|
const wsLogStyle = wsLogRaw === "compact" ? "compact" : wsLogRaw === "full" ? "full" : "auto";
|
|
@@ -42,12 +81,10 @@ async function runGatewayCommand(opts) {
|
|
|
42
81
|
setGatewayWsLogStyle(wsLogStyle);
|
|
43
82
|
if (opts.rawStream) {
|
|
44
83
|
process.env.POOLBOT_RAW_STREAM = "1";
|
|
45
|
-
process.env.CLAWDBOT_RAW_STREAM = "1";
|
|
46
84
|
}
|
|
47
85
|
const rawStreamPath = toOptionString(opts.rawStreamPath);
|
|
48
86
|
if (rawStreamPath) {
|
|
49
87
|
process.env.POOLBOT_RAW_STREAM_PATH = rawStreamPath;
|
|
50
|
-
process.env.CLAWDBOT_RAW_STREAM_PATH = rawStreamPath;
|
|
51
88
|
}
|
|
52
89
|
if (devMode) {
|
|
53
90
|
await ensureDevGatewayConfig({ reset: Boolean(opts.reset) });
|
|
@@ -95,7 +132,6 @@ async function runGatewayCommand(opts) {
|
|
|
95
132
|
const token = toOptionString(opts.token);
|
|
96
133
|
if (token) {
|
|
97
134
|
process.env.POOLBOT_GATEWAY_TOKEN = token;
|
|
98
|
-
process.env.CLAWDBOT_GATEWAY_TOKEN = token;
|
|
99
135
|
}
|
|
100
136
|
}
|
|
101
137
|
const authModeRaw = toOptionString(opts.auth);
|
|
@@ -116,7 +152,9 @@ async function runGatewayCommand(opts) {
|
|
|
116
152
|
}
|
|
117
153
|
const passwordRaw = toOptionString(opts.password);
|
|
118
154
|
const tokenRaw = toOptionString(opts.token);
|
|
119
|
-
const
|
|
155
|
+
const snapshot = await readConfigFileSnapshot().catch(() => null);
|
|
156
|
+
const configExists = snapshot?.exists ?? fs.existsSync(CONFIG_PATH);
|
|
157
|
+
const configAuditPath = path.join(resolveStateDir(process.env), "logs", "config-audit.jsonl");
|
|
120
158
|
const mode = cfg.gateway?.mode;
|
|
121
159
|
if (!opts.allowUnconfigured && mode !== "local") {
|
|
122
160
|
if (!configExists) {
|
|
@@ -124,6 +162,7 @@ async function runGatewayCommand(opts) {
|
|
|
124
162
|
}
|
|
125
163
|
else {
|
|
126
164
|
defaultRuntime.error(`Gateway start blocked: set gateway.mode=local (current: ${mode ?? "unset"}) or pass --allow-unconfigured.`);
|
|
165
|
+
defaultRuntime.error(`Config write audit: ${configAuditPath}`);
|
|
127
166
|
}
|
|
128
167
|
defaultRuntime.exit(1);
|
|
129
168
|
return;
|
|
@@ -141,7 +180,6 @@ async function runGatewayCommand(opts) {
|
|
|
141
180
|
defaultRuntime.exit(1);
|
|
142
181
|
return;
|
|
143
182
|
}
|
|
144
|
-
const snapshot = await readConfigFileSnapshot().catch(() => null);
|
|
145
183
|
const miskeys = extractGatewayMiskeys(snapshot?.parsed);
|
|
146
184
|
const authConfig = {
|
|
147
185
|
...cfg.gateway?.auth,
|
|
@@ -189,7 +227,7 @@ async function runGatewayCommand(opts) {
|
|
|
189
227
|
defaultRuntime.exit(1);
|
|
190
228
|
return;
|
|
191
229
|
}
|
|
192
|
-
if (bind !== "loopback" && !hasSharedSecret) {
|
|
230
|
+
if (bind !== "loopback" && !hasSharedSecret && resolvedAuthMode !== "trusted-proxy") {
|
|
193
231
|
defaultRuntime.error([
|
|
194
232
|
`Refusing to bind gateway to ${bind} without auth.`,
|
|
195
233
|
"Set gateway.auth.token/password (or POOLBOT_GATEWAY_TOKEN/POOLBOT_GATEWAY_PASSWORD) or pass --token/--password.",
|
|
@@ -264,7 +302,7 @@ export function addGatewayRunCommand(cmd) {
|
|
|
264
302
|
.option("--compact", 'Alias for "--ws-log compact"', false)
|
|
265
303
|
.option("--raw-stream", "Log raw model stream events to jsonl", false)
|
|
266
304
|
.option("--raw-stream-path <path>", "Raw stream jsonl path")
|
|
267
|
-
.action(async (opts) => {
|
|
268
|
-
await runGatewayCommand(opts);
|
|
305
|
+
.action(async (opts, command) => {
|
|
306
|
+
await runGatewayCommand(resolveGatewayRunOptions(opts, command));
|
|
269
307
|
});
|
|
270
308
|
}
|
package/dist/cli/nodes-camera.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as fs from "node:fs/promises";
|
|
|
3
3
|
import * as os from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { resolveCliName } from "./cli-name.js";
|
|
6
|
+
const MAX_CAMERA_URL_DOWNLOAD_BYTES = 250 * 1024 * 1024;
|
|
6
7
|
function asRecord(value) {
|
|
7
8
|
return typeof value === "object" && value !== null ? value : {};
|
|
8
9
|
}
|
|
@@ -19,23 +20,25 @@ export function parseCameraSnapPayload(value) {
|
|
|
19
20
|
const obj = asRecord(value);
|
|
20
21
|
const format = asString(obj.format);
|
|
21
22
|
const base64 = asString(obj.base64);
|
|
23
|
+
const url = asString(obj.url);
|
|
22
24
|
const width = asNumber(obj.width);
|
|
23
25
|
const height = asNumber(obj.height);
|
|
24
|
-
if (!format || !base64 || width === undefined || height === undefined) {
|
|
26
|
+
if (!format || (!base64 && !url) || width === undefined || height === undefined) {
|
|
25
27
|
throw new Error("invalid camera.snap payload");
|
|
26
28
|
}
|
|
27
|
-
return { format, base64, width, height };
|
|
29
|
+
return { format, ...(base64 ? { base64 } : {}), ...(url ? { url } : {}), width, height };
|
|
28
30
|
}
|
|
29
31
|
export function parseCameraClipPayload(value) {
|
|
30
32
|
const obj = asRecord(value);
|
|
31
33
|
const format = asString(obj.format);
|
|
32
34
|
const base64 = asString(obj.base64);
|
|
35
|
+
const url = asString(obj.url);
|
|
33
36
|
const durationMs = asNumber(obj.durationMs);
|
|
34
37
|
const hasAudio = asBoolean(obj.hasAudio);
|
|
35
|
-
if (!format || !base64 || durationMs === undefined || hasAudio === undefined) {
|
|
38
|
+
if (!format || (!base64 && !url) || durationMs === undefined || hasAudio === undefined) {
|
|
36
39
|
throw new Error("invalid camera.clip payload");
|
|
37
40
|
}
|
|
38
|
-
return { format, base64, durationMs, hasAudio };
|
|
41
|
+
return { format, ...(base64 ? { base64 } : {}), ...(url ? { url } : {}), durationMs, hasAudio };
|
|
39
42
|
}
|
|
40
43
|
export function cameraTempPath(opts) {
|
|
41
44
|
const tmpDir = opts.tmpDir ?? os.tmpdir();
|
|
@@ -45,6 +48,58 @@ export function cameraTempPath(opts) {
|
|
|
45
48
|
const cliName = resolveCliName();
|
|
46
49
|
return path.join(tmpDir, `${cliName}-camera-${opts.kind}${facingPart}-${id}${ext}`);
|
|
47
50
|
}
|
|
51
|
+
export async function writeUrlToFile(filePath, url) {
|
|
52
|
+
const parsed = new URL(url);
|
|
53
|
+
if (parsed.protocol !== "https:") {
|
|
54
|
+
throw new Error(`writeUrlToFile: only https URLs are allowed, got ${parsed.protocol}`);
|
|
55
|
+
}
|
|
56
|
+
const res = await fetch(url);
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
throw new Error(`failed to download ${url}: ${res.status} ${res.statusText}`);
|
|
59
|
+
}
|
|
60
|
+
const contentLengthRaw = res.headers.get("content-length");
|
|
61
|
+
const contentLength = contentLengthRaw ? Number.parseInt(contentLengthRaw, 10) : undefined;
|
|
62
|
+
if (typeof contentLength === "number" &&
|
|
63
|
+
Number.isFinite(contentLength) &&
|
|
64
|
+
contentLength > MAX_CAMERA_URL_DOWNLOAD_BYTES) {
|
|
65
|
+
throw new Error(`writeUrlToFile: content-length ${contentLength} exceeds max ${MAX_CAMERA_URL_DOWNLOAD_BYTES}`);
|
|
66
|
+
}
|
|
67
|
+
const body = res.body;
|
|
68
|
+
if (!body) {
|
|
69
|
+
throw new Error(`failed to download ${url}: empty response body`);
|
|
70
|
+
}
|
|
71
|
+
const fileHandle = await fs.open(filePath, "w");
|
|
72
|
+
let bytes = 0;
|
|
73
|
+
let thrown;
|
|
74
|
+
try {
|
|
75
|
+
const reader = body.getReader();
|
|
76
|
+
while (true) {
|
|
77
|
+
const { done, value } = await reader.read();
|
|
78
|
+
if (done) {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
if (!value || value.byteLength === 0) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
bytes += value.byteLength;
|
|
85
|
+
if (bytes > MAX_CAMERA_URL_DOWNLOAD_BYTES) {
|
|
86
|
+
throw new Error(`writeUrlToFile: downloaded ${bytes} bytes, exceeds max ${MAX_CAMERA_URL_DOWNLOAD_BYTES}`);
|
|
87
|
+
}
|
|
88
|
+
await fileHandle.write(value);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
thrown = err;
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
await fileHandle.close();
|
|
96
|
+
}
|
|
97
|
+
if (thrown) {
|
|
98
|
+
await fs.unlink(filePath).catch(() => { });
|
|
99
|
+
throw thrown;
|
|
100
|
+
}
|
|
101
|
+
return { path: filePath, bytes };
|
|
102
|
+
}
|
|
48
103
|
export async function writeBase64ToFile(filePath, base64) {
|
|
49
104
|
const buf = Buffer.from(base64, "base64");
|
|
50
105
|
await fs.writeFile(filePath, buf);
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { randomIdempotencyKey } from "../../gateway/call.js";
|
|
2
1
|
import { defaultRuntime } from "../../runtime.js";
|
|
3
|
-
import { cameraTempPath, parseCameraClipPayload, parseCameraSnapPayload, writeBase64ToFile, } from "../nodes-camera.js";
|
|
4
|
-
import { parseDurationMs } from "../parse-duration.js";
|
|
5
|
-
import { getNodesTheme, runNodesCommand } from "./cli-utils.js";
|
|
6
|
-
import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js";
|
|
7
2
|
import { renderTable } from "../../terminal/table.js";
|
|
8
3
|
import { shortenHomePath } from "../../utils.js";
|
|
4
|
+
import { cameraTempPath, parseCameraClipPayload, parseCameraSnapPayload, writeBase64ToFile, writeUrlToFile, } from "../nodes-camera.js";
|
|
5
|
+
import { parseDurationMs } from "../parse-duration.js";
|
|
6
|
+
import { getNodesTheme, runNodesCommand } from "./cli-utils.js";
|
|
7
|
+
import { buildNodeInvokeParams, callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js";
|
|
9
8
|
const parseFacing = (value) => {
|
|
10
9
|
const v = String(value ?? "")
|
|
11
10
|
.trim()
|
|
12
11
|
.toLowerCase();
|
|
13
|
-
if (v === "front" || v === "back")
|
|
12
|
+
if (v === "front" || v === "back") {
|
|
14
13
|
return v;
|
|
14
|
+
}
|
|
15
15
|
throw new Error(`invalid facing: ${value} (expected front|back)`);
|
|
16
16
|
};
|
|
17
17
|
export function registerNodesCameraCommands(nodes) {
|
|
@@ -23,11 +23,10 @@ export function registerNodesCameraCommands(nodes) {
|
|
|
23
23
|
.action(async (opts) => {
|
|
24
24
|
await runNodesCommand("camera list", async () => {
|
|
25
25
|
const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
|
|
26
|
-
const raw =
|
|
26
|
+
const raw = await callGatewayCli("node.invoke", opts, buildNodeInvokeParams({
|
|
27
27
|
nodeId,
|
|
28
28
|
command: "camera.list",
|
|
29
29
|
params: {},
|
|
30
|
-
idempotencyKey: randomIdempotencyKey(),
|
|
31
30
|
}));
|
|
32
31
|
const res = typeof raw === "object" && raw !== null ? raw : {};
|
|
33
32
|
const payload = typeof res.payload === "object" && res.payload !== null
|
|
@@ -94,7 +93,7 @@ export function registerNodesCameraCommands(nodes) {
|
|
|
94
93
|
: undefined;
|
|
95
94
|
const results = [];
|
|
96
95
|
for (const facing of facings) {
|
|
97
|
-
const invokeParams = {
|
|
96
|
+
const invokeParams = buildNodeInvokeParams({
|
|
98
97
|
nodeId,
|
|
99
98
|
command: "camera.snap",
|
|
100
99
|
params: {
|
|
@@ -105,12 +104,9 @@ export function registerNodesCameraCommands(nodes) {
|
|
|
105
104
|
delayMs: Number.isFinite(delayMs) ? delayMs : undefined,
|
|
106
105
|
deviceId: deviceId || undefined,
|
|
107
106
|
},
|
|
108
|
-
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
invokeParams.timeoutMs = timeoutMs;
|
|
112
|
-
}
|
|
113
|
-
const raw = (await callGatewayCli("node.invoke", opts, invokeParams));
|
|
107
|
+
timeoutMs,
|
|
108
|
+
});
|
|
109
|
+
const raw = await callGatewayCli("node.invoke", opts, invokeParams);
|
|
114
110
|
const res = typeof raw === "object" && raw !== null ? raw : {};
|
|
115
111
|
const payload = parseCameraSnapPayload(res.payload);
|
|
116
112
|
const filePath = cameraTempPath({
|
|
@@ -118,7 +114,12 @@ export function registerNodesCameraCommands(nodes) {
|
|
|
118
114
|
facing,
|
|
119
115
|
ext: payload.format === "jpeg" ? "jpg" : payload.format,
|
|
120
116
|
});
|
|
121
|
-
|
|
117
|
+
if (payload.url) {
|
|
118
|
+
await writeUrlToFile(filePath, payload.url);
|
|
119
|
+
}
|
|
120
|
+
else if (payload.base64) {
|
|
121
|
+
await writeBase64ToFile(filePath, payload.base64);
|
|
122
|
+
}
|
|
122
123
|
results.push({
|
|
123
124
|
facing,
|
|
124
125
|
path: filePath,
|
|
@@ -152,7 +153,7 @@ export function registerNodesCameraCommands(nodes) {
|
|
|
152
153
|
? Number.parseInt(String(opts.invokeTimeout), 10)
|
|
153
154
|
: undefined;
|
|
154
155
|
const deviceId = opts.deviceId ? String(opts.deviceId).trim() : undefined;
|
|
155
|
-
const invokeParams = {
|
|
156
|
+
const invokeParams = buildNodeInvokeParams({
|
|
156
157
|
nodeId,
|
|
157
158
|
command: "camera.clip",
|
|
158
159
|
params: {
|
|
@@ -162,12 +163,9 @@ export function registerNodesCameraCommands(nodes) {
|
|
|
162
163
|
format: "mp4",
|
|
163
164
|
deviceId: deviceId || undefined,
|
|
164
165
|
},
|
|
165
|
-
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
invokeParams.timeoutMs = timeoutMs;
|
|
169
|
-
}
|
|
170
|
-
const raw = (await callGatewayCli("node.invoke", opts, invokeParams));
|
|
166
|
+
timeoutMs,
|
|
167
|
+
});
|
|
168
|
+
const raw = await callGatewayCli("node.invoke", opts, invokeParams);
|
|
171
169
|
const res = typeof raw === "object" && raw !== null ? raw : {};
|
|
172
170
|
const payload = parseCameraClipPayload(res.payload);
|
|
173
171
|
const filePath = cameraTempPath({
|
|
@@ -175,7 +173,12 @@ export function registerNodesCameraCommands(nodes) {
|
|
|
175
173
|
facing,
|
|
176
174
|
ext: payload.format,
|
|
177
175
|
});
|
|
178
|
-
|
|
176
|
+
if (payload.url) {
|
|
177
|
+
await writeUrlToFile(filePath, payload.url);
|
|
178
|
+
}
|
|
179
|
+
else if (payload.base64) {
|
|
180
|
+
await writeBase64ToFile(filePath, payload.base64);
|
|
181
|
+
}
|
|
179
182
|
if (opts.json) {
|
|
180
183
|
defaultRuntime.log(JSON.stringify({
|
|
181
184
|
file: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { callGateway } from "../../gateway/call.js";
|
|
1
|
+
import { callGateway, randomIdempotencyKey } from "../../gateway/call.js";
|
|
2
|
+
import { resolveNodeIdFromCandidates } from "../../shared/node-match.js";
|
|
2
3
|
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../../utils/message-channel.js";
|
|
3
4
|
import { withProgress } from "../progress.js";
|
|
4
5
|
import { parseNodeList, parsePairingList } from "./format.js";
|
|
@@ -7,7 +8,7 @@ export const nodesCallOpts = (cmd, defaults) => cmd
|
|
|
7
8
|
.option("--token <token>", "Gateway token (if required)")
|
|
8
9
|
.option("--timeout <ms>", "Timeout in ms", String(defaults?.timeoutMs ?? 10_000))
|
|
9
10
|
.option("--json", "Output JSON", false);
|
|
10
|
-
export const callGatewayCli = async (method, opts, params) => withProgress({
|
|
11
|
+
export const callGatewayCli = async (method, opts, params, callOpts) => withProgress({
|
|
11
12
|
label: `Nodes ${method}`,
|
|
12
13
|
indeterminate: true,
|
|
13
14
|
enabled: opts.json !== true,
|
|
@@ -16,10 +17,22 @@ export const callGatewayCli = async (method, opts, params) => withProgress({
|
|
|
16
17
|
token: opts.token,
|
|
17
18
|
method,
|
|
18
19
|
params,
|
|
19
|
-
timeoutMs: Number(opts.timeout ?? 10_000),
|
|
20
|
+
timeoutMs: callOpts?.transportTimeoutMs ?? Number(opts.timeout ?? 10_000),
|
|
20
21
|
clientName: GATEWAY_CLIENT_NAMES.CLI,
|
|
21
22
|
mode: GATEWAY_CLIENT_MODES.CLI,
|
|
22
23
|
}));
|
|
24
|
+
export function buildNodeInvokeParams(params) {
|
|
25
|
+
const invokeParams = {
|
|
26
|
+
nodeId: params.nodeId,
|
|
27
|
+
command: params.command,
|
|
28
|
+
params: params.params,
|
|
29
|
+
idempotencyKey: params.idempotencyKey ?? randomIdempotencyKey(),
|
|
30
|
+
};
|
|
31
|
+
if (typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs)) {
|
|
32
|
+
invokeParams.timeoutMs = params.timeoutMs;
|
|
33
|
+
}
|
|
34
|
+
return invokeParams;
|
|
35
|
+
}
|
|
23
36
|
export function unauthorizedHintForMessage(message) {
|
|
24
37
|
const haystack = message.toLowerCase();
|
|
25
38
|
if (haystack.includes("unauthorizedclient") ||
|
|
@@ -33,24 +46,18 @@ export function unauthorizedHintForMessage(message) {
|
|
|
33
46
|
}
|
|
34
47
|
return null;
|
|
35
48
|
}
|
|
36
|
-
function normalizeNodeKey(value) {
|
|
37
|
-
return value
|
|
38
|
-
.toLowerCase()
|
|
39
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
40
|
-
.replace(/^-+/, "")
|
|
41
|
-
.replace(/-+$/, "");
|
|
42
|
-
}
|
|
43
49
|
export async function resolveNodeId(opts, query) {
|
|
44
50
|
const q = String(query ?? "").trim();
|
|
45
|
-
if (!q)
|
|
51
|
+
if (!q) {
|
|
46
52
|
throw new Error("node required");
|
|
53
|
+
}
|
|
47
54
|
let nodes = [];
|
|
48
55
|
try {
|
|
49
|
-
const res =
|
|
56
|
+
const res = await callGatewayCli("node.list", opts, {});
|
|
50
57
|
nodes = parseNodeList(res);
|
|
51
58
|
}
|
|
52
59
|
catch {
|
|
53
|
-
const res =
|
|
60
|
+
const res = await callGatewayCli("node.pair.list", opts, {});
|
|
54
61
|
const { paired } = parsePairingList(res);
|
|
55
62
|
nodes = paired.map((n) => ({
|
|
56
63
|
nodeId: n.nodeId,
|
|
@@ -60,29 +67,5 @@ export async function resolveNodeId(opts, query) {
|
|
|
60
67
|
remoteIp: n.remoteIp,
|
|
61
68
|
}));
|
|
62
69
|
}
|
|
63
|
-
|
|
64
|
-
const matches = nodes.filter((n) => {
|
|
65
|
-
if (n.nodeId === q)
|
|
66
|
-
return true;
|
|
67
|
-
if (typeof n.remoteIp === "string" && n.remoteIp === q)
|
|
68
|
-
return true;
|
|
69
|
-
const name = typeof n.displayName === "string" ? n.displayName : "";
|
|
70
|
-
if (name && normalizeNodeKey(name) === qNorm)
|
|
71
|
-
return true;
|
|
72
|
-
if (q.length >= 6 && n.nodeId.startsWith(q))
|
|
73
|
-
return true;
|
|
74
|
-
return false;
|
|
75
|
-
});
|
|
76
|
-
if (matches.length === 1)
|
|
77
|
-
return matches[0].nodeId;
|
|
78
|
-
if (matches.length === 0) {
|
|
79
|
-
const known = nodes
|
|
80
|
-
.map((n) => n.displayName || n.remoteIp || n.nodeId)
|
|
81
|
-
.filter(Boolean)
|
|
82
|
-
.join(", ");
|
|
83
|
-
throw new Error(`unknown node: ${q}${known ? ` (known: ${known})` : ""}`);
|
|
84
|
-
}
|
|
85
|
-
throw new Error(`ambiguous node: ${q} (matches: ${matches
|
|
86
|
-
.map((n) => n.displayName || n.remoteIp || n.nodeId)
|
|
87
|
-
.join(", ")})`);
|
|
70
|
+
return resolveNodeIdFromCandidates(nodes, q);
|
|
88
71
|
}
|
package/dist/cli/qr-cli.js
CHANGED
|
@@ -114,14 +114,14 @@ export function registerQrCli(program) {
|
|
|
114
114
|
}
|
|
115
115
|
const lines = [
|
|
116
116
|
theme.heading("Pairing QR"),
|
|
117
|
-
"Scan this with the
|
|
117
|
+
"Scan this with the Pool Bot iOS app (Onboarding -> Scan QR).",
|
|
118
118
|
"",
|
|
119
119
|
];
|
|
120
120
|
if (opts.ascii !== false) {
|
|
121
121
|
const qrAscii = await renderQrAscii(setupCode);
|
|
122
122
|
lines.push(qrAscii.trimEnd(), "");
|
|
123
123
|
}
|
|
124
|
-
lines.push(`${theme.muted("Setup code:")} ${setupCode}`, `${theme.muted("Gateway:")} ${resolved.payload.url}`, `${theme.muted("Auth:")} ${resolved.authLabel}`, `${theme.muted("Source:")} ${resolved.urlSource}`, "", "Approve after scan with:", ` ${theme.command("
|
|
124
|
+
lines.push(`${theme.muted("Setup code:")} ${setupCode}`, `${theme.muted("Gateway:")} ${resolved.payload.url}`, `${theme.muted("Auth:")} ${resolved.authLabel}`, `${theme.muted("Source:")} ${resolved.urlSource}`, "", "Approve after scan with:", ` ${theme.command("poolbot devices list")}`, ` ${theme.command("poolbot devices approve <requestId>")}`);
|
|
125
125
|
defaultRuntime.log(lines.join("\n"));
|
|
126
126
|
}
|
|
127
127
|
catch (err) {
|