@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
|
@@ -59,8 +59,10 @@ export function registerCronAddCommand(cron) {
|
|
|
59
59
|
.option("--wake <mode>", "Wake mode (now|next-heartbeat)", "now")
|
|
60
60
|
.option("--at <when>", "Run once at time (ISO) or +duration (e.g. 20m)")
|
|
61
61
|
.option("--every <duration>", "Run every duration (e.g. 10m, 1h)")
|
|
62
|
-
.option("--cron <expr>", "Cron expression (5-field)")
|
|
62
|
+
.option("--cron <expr>", "Cron expression (5-field or 6-field with seconds)")
|
|
63
63
|
.option("--tz <iana>", "Timezone for cron expressions (IANA)", "")
|
|
64
|
+
.option("--stagger <duration>", "Cron stagger window (e.g. 30s, 5m)")
|
|
65
|
+
.option("--exact", "Disable cron staggering (set stagger to 0)", false)
|
|
64
66
|
.option("--system-event <text>", "System event payload (main session)")
|
|
65
67
|
.option("--message <text>", "Agent message payload")
|
|
66
68
|
.option("--thinking <level>", "Thinking level for agent jobs (off|minimal|low|medium|high)")
|
|
@@ -75,6 +77,11 @@ export function registerCronAddCommand(cron) {
|
|
|
75
77
|
.option("--json", "Output JSON", false)
|
|
76
78
|
.action(async (opts, cmd) => {
|
|
77
79
|
try {
|
|
80
|
+
const staggerRaw = typeof opts.stagger === "string" ? opts.stagger.trim() : "";
|
|
81
|
+
const useExact = Boolean(opts.exact);
|
|
82
|
+
if (staggerRaw && useExact) {
|
|
83
|
+
throw new Error("Choose either --stagger or --exact, not both");
|
|
84
|
+
}
|
|
78
85
|
const schedule = (() => {
|
|
79
86
|
const at = typeof opts.at === "string" ? opts.at : "";
|
|
80
87
|
const every = typeof opts.every === "string" ? opts.every : "";
|
|
@@ -83,6 +90,9 @@ export function registerCronAddCommand(cron) {
|
|
|
83
90
|
if (chosen !== 1) {
|
|
84
91
|
throw new Error("Choose exactly one schedule: --at, --every, or --cron");
|
|
85
92
|
}
|
|
93
|
+
if ((useExact || staggerRaw) && !cronExpr) {
|
|
94
|
+
throw new Error("--stagger/--exact are only valid with --cron");
|
|
95
|
+
}
|
|
86
96
|
if (at) {
|
|
87
97
|
const atIso = parseAt(at);
|
|
88
98
|
if (!atIso) {
|
|
@@ -97,10 +107,24 @@ export function registerCronAddCommand(cron) {
|
|
|
97
107
|
}
|
|
98
108
|
return { kind: "every", everyMs };
|
|
99
109
|
}
|
|
110
|
+
const staggerMs = (() => {
|
|
111
|
+
if (useExact) {
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
if (!staggerRaw) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
const parsed = parseDurationMs(staggerRaw);
|
|
118
|
+
if (!parsed) {
|
|
119
|
+
throw new Error("Invalid --stagger; use e.g. 30s, 1m, 5m");
|
|
120
|
+
}
|
|
121
|
+
return parsed;
|
|
122
|
+
})();
|
|
100
123
|
return {
|
|
101
124
|
kind: "cron",
|
|
102
125
|
expr: cronExpr,
|
|
103
126
|
tz: typeof opts.tz === "string" && opts.tz.trim() ? opts.tz.trim() : undefined,
|
|
127
|
+
staggerMs,
|
|
104
128
|
};
|
|
105
129
|
})();
|
|
106
130
|
const wakeModeRaw = typeof opts.wake === "string" ? opts.wake : "now";
|
|
@@ -27,6 +27,8 @@ export function registerCronEditCommand(cron) {
|
|
|
27
27
|
.option("--every <duration>", "Set interval duration like 10m")
|
|
28
28
|
.option("--cron <expr>", "Set cron expression")
|
|
29
29
|
.option("--tz <iana>", "Timezone for cron expressions (IANA)")
|
|
30
|
+
.option("--stagger <duration>", "Cron stagger window (e.g. 30s, 5m)")
|
|
31
|
+
.option("--exact", "Disable cron staggering (set stagger to 0)")
|
|
30
32
|
.option("--system-event <text>", "Set systemEvent payload")
|
|
31
33
|
.option("--message <text>", "Set agentTurn payload message")
|
|
32
34
|
.option("--thinking <level>", "Thinking level for agent jobs")
|
|
@@ -50,6 +52,24 @@ export function registerCronEditCommand(cron) {
|
|
|
50
52
|
if (opts.announce && typeof opts.deliver === "boolean") {
|
|
51
53
|
throw new Error("Choose --announce or --no-deliver (not multiple).");
|
|
52
54
|
}
|
|
55
|
+
const staggerRaw = typeof opts.stagger === "string" ? opts.stagger.trim() : "";
|
|
56
|
+
const useExact = Boolean(opts.exact);
|
|
57
|
+
if (staggerRaw && useExact) {
|
|
58
|
+
throw new Error("Choose either --stagger or --exact, not both");
|
|
59
|
+
}
|
|
60
|
+
const requestedStaggerMs = (() => {
|
|
61
|
+
if (useExact) {
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
if (!staggerRaw) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
const parsed = parseDurationMs(staggerRaw);
|
|
68
|
+
if (!parsed) {
|
|
69
|
+
throw new Error("Invalid --stagger; use e.g. 30s, 1m, 5m");
|
|
70
|
+
}
|
|
71
|
+
return parsed;
|
|
72
|
+
})();
|
|
53
73
|
const patch = {};
|
|
54
74
|
if (typeof opts.name === "string") {
|
|
55
75
|
patch.name = opts.name;
|
|
@@ -94,6 +114,10 @@ export function registerCronEditCommand(cron) {
|
|
|
94
114
|
if (scheduleChosen > 1) {
|
|
95
115
|
throw new Error("Choose at most one schedule change");
|
|
96
116
|
}
|
|
117
|
+
if ((requestedStaggerMs !== undefined || typeof opts.tz === "string") &&
|
|
118
|
+
(opts.at || opts.every)) {
|
|
119
|
+
throw new Error("--stagger/--exact/--tz are only valid for cron schedules");
|
|
120
|
+
}
|
|
97
121
|
if (opts.at) {
|
|
98
122
|
const atIso = parseAt(String(opts.at));
|
|
99
123
|
if (!atIso) {
|
|
@@ -113,6 +137,26 @@ export function registerCronEditCommand(cron) {
|
|
|
113
137
|
kind: "cron",
|
|
114
138
|
expr: String(opts.cron),
|
|
115
139
|
tz: typeof opts.tz === "string" && opts.tz.trim() ? opts.tz.trim() : undefined,
|
|
140
|
+
staggerMs: requestedStaggerMs,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
else if (requestedStaggerMs !== undefined || typeof opts.tz === "string") {
|
|
144
|
+
const listed = (await callGatewayFromCli("cron.list", opts, {
|
|
145
|
+
includeDisabled: true,
|
|
146
|
+
}));
|
|
147
|
+
const existing = (listed?.jobs ?? []).find((job) => job.id === id);
|
|
148
|
+
if (!existing) {
|
|
149
|
+
throw new Error(`unknown cron job id: ${id}`);
|
|
150
|
+
}
|
|
151
|
+
if (existing.schedule.kind !== "cron") {
|
|
152
|
+
throw new Error("Current job is not a cron schedule; use --cron to convert first");
|
|
153
|
+
}
|
|
154
|
+
const tz = typeof opts.tz === "string" ? opts.tz.trim() || undefined : existing.schedule.tz;
|
|
155
|
+
patch.schedule = {
|
|
156
|
+
kind: "cron",
|
|
157
|
+
expr: existing.schedule.expr,
|
|
158
|
+
tz,
|
|
159
|
+
staggerMs: requestedStaggerMs !== undefined ? requestedStaggerMs : existing.schedule.staggerMs,
|
|
116
160
|
};
|
|
117
161
|
}
|
|
118
162
|
const hasSystemEventPatch = typeof opts.systemEvent === "string";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { listChannelPlugins } from "../../channels/plugins/index.js";
|
|
2
2
|
import { parseAbsoluteTimeMs } from "../../cron/parse.js";
|
|
3
|
+
import { resolveCronStaggerMs } from "../../cron/stagger.js";
|
|
3
4
|
import { formatDurationHuman } from "../../infra/format-time/format-duration.js";
|
|
4
5
|
import { defaultRuntime } from "../../runtime.js";
|
|
5
6
|
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
|
@@ -118,7 +119,12 @@ const formatSchedule = (schedule) => {
|
|
|
118
119
|
if (schedule.kind === "every") {
|
|
119
120
|
return `every ${formatDurationHuman(schedule.everyMs)}`;
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
+
const base = schedule.tz ? `cron ${schedule.expr} @ ${schedule.tz}` : `cron ${schedule.expr}`;
|
|
123
|
+
const staggerMs = resolveCronStaggerMs(schedule);
|
|
124
|
+
if (staggerMs <= 0) {
|
|
125
|
+
return `${base} (exact)`;
|
|
126
|
+
}
|
|
127
|
+
return `${base} (stagger ${formatDurationHuman(staggerMs)})`;
|
|
122
128
|
};
|
|
123
129
|
const formatStatus = (job) => {
|
|
124
130
|
if (!job.enabled) {
|
|
@@ -205,31 +205,33 @@ export async function runServiceRestart(params) {
|
|
|
205
205
|
});
|
|
206
206
|
return false;
|
|
207
207
|
}
|
|
208
|
-
// Check for token drift before restart (service token vs config token)
|
|
209
208
|
const warnings = [];
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (
|
|
226
|
-
defaultRuntime.log(
|
|
209
|
+
if (params.checkTokenDrift) {
|
|
210
|
+
// Check for token drift before restart (service token vs config token)
|
|
211
|
+
try {
|
|
212
|
+
const command = await params.service.readCommand(process.env);
|
|
213
|
+
const serviceToken = command?.environment?.POOLBOT_GATEWAY_TOKEN;
|
|
214
|
+
const cfg = loadConfig();
|
|
215
|
+
const configToken = cfg.gateway?.auth?.token ||
|
|
216
|
+
process.env.POOLBOT_GATEWAY_TOKEN ||
|
|
217
|
+
process.env.CLAWDBOT_GATEWAY_TOKEN;
|
|
218
|
+
const driftIssue = checkTokenDrift({ serviceToken, configToken });
|
|
219
|
+
if (driftIssue) {
|
|
220
|
+
const warning = driftIssue.detail
|
|
221
|
+
? `${driftIssue.message} ${driftIssue.detail}`
|
|
222
|
+
: driftIssue.message;
|
|
223
|
+
warnings.push(warning);
|
|
224
|
+
if (!json) {
|
|
225
|
+
defaultRuntime.log(`\n⚠️ ${driftIssue.message}`);
|
|
226
|
+
if (driftIssue.detail) {
|
|
227
|
+
defaultRuntime.log(` ${driftIssue.detail}\n`);
|
|
228
|
+
}
|
|
227
229
|
}
|
|
228
230
|
}
|
|
229
231
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
catch {
|
|
233
|
+
// Non-fatal: token drift check is best-effort
|
|
234
|
+
}
|
|
233
235
|
}
|
|
234
236
|
try {
|
|
235
237
|
await params.service.restart({ env: process.env, stdout });
|
|
@@ -1,192 +1,28 @@
|
|
|
1
|
-
import { resolveIsNixMode } from "../../config/paths.js";
|
|
2
1
|
import { resolveGatewayService } from "../../daemon/service.js";
|
|
3
|
-
import {
|
|
4
|
-
import { renderSystemdUnavailableHints } from "../../daemon/systemd-hints.js";
|
|
5
|
-
import { isWSL } from "../../infra/wsl.js";
|
|
6
|
-
import { defaultRuntime } from "../../runtime.js";
|
|
7
|
-
import { buildDaemonServiceSnapshot, createNullWriter, emitDaemonActionJson } from "./response.js";
|
|
2
|
+
import { runServiceRestart, runServiceStart, runServiceStop, runServiceUninstall, } from "./lifecycle-core.js";
|
|
8
3
|
import { renderGatewayServiceStartHints } from "./shared.js";
|
|
9
4
|
export async function runDaemonUninstall(opts = {}) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
const fail = (message) => {
|
|
18
|
-
if (json)
|
|
19
|
-
emit({ ok: false, error: message });
|
|
20
|
-
else
|
|
21
|
-
defaultRuntime.error(message);
|
|
22
|
-
defaultRuntime.exit(1);
|
|
23
|
-
};
|
|
24
|
-
if (resolveIsNixMode(process.env)) {
|
|
25
|
-
fail("Nix mode detected; service uninstall is disabled.");
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const service = resolveGatewayService();
|
|
29
|
-
let loaded = false;
|
|
30
|
-
try {
|
|
31
|
-
loaded = await service.isLoaded({ env: process.env });
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
loaded = false;
|
|
35
|
-
}
|
|
36
|
-
if (loaded) {
|
|
37
|
-
try {
|
|
38
|
-
await service.stop({ env: process.env, stdout });
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
// Best-effort stop; final loaded check gates success.
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
await service.uninstall({ env: process.env, stdout });
|
|
46
|
-
}
|
|
47
|
-
catch (err) {
|
|
48
|
-
fail(`Gateway uninstall failed: ${String(err)}`);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
loaded = false;
|
|
52
|
-
try {
|
|
53
|
-
loaded = await service.isLoaded({ env: process.env });
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
loaded = false;
|
|
57
|
-
}
|
|
58
|
-
if (loaded) {
|
|
59
|
-
fail("Gateway service still loaded after uninstall.");
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
emit({
|
|
63
|
-
ok: true,
|
|
64
|
-
result: "uninstalled",
|
|
65
|
-
service: buildDaemonServiceSnapshot(service, loaded),
|
|
5
|
+
return await runServiceUninstall({
|
|
6
|
+
serviceNoun: "Gateway",
|
|
7
|
+
service: resolveGatewayService(),
|
|
8
|
+
opts,
|
|
9
|
+
stopBeforeUninstall: true,
|
|
10
|
+
assertNotLoadedAfterUninstall: true,
|
|
66
11
|
});
|
|
67
12
|
}
|
|
68
13
|
export async function runDaemonStart(opts = {}) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
emitDaemonActionJson({ action: "start", ...payload });
|
|
75
|
-
};
|
|
76
|
-
const fail = (message, hints) => {
|
|
77
|
-
if (json)
|
|
78
|
-
emit({ ok: false, error: message, hints });
|
|
79
|
-
else
|
|
80
|
-
defaultRuntime.error(message);
|
|
81
|
-
defaultRuntime.exit(1);
|
|
82
|
-
};
|
|
83
|
-
const service = resolveGatewayService();
|
|
84
|
-
let loaded = false;
|
|
85
|
-
try {
|
|
86
|
-
loaded = await service.isLoaded({ env: process.env });
|
|
87
|
-
}
|
|
88
|
-
catch (err) {
|
|
89
|
-
fail(`Gateway service check failed: ${String(err)}`);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
if (!loaded) {
|
|
93
|
-
let hints = renderGatewayServiceStartHints();
|
|
94
|
-
if (process.platform === "linux") {
|
|
95
|
-
const systemdAvailable = await isSystemdUserServiceAvailable().catch(() => false);
|
|
96
|
-
if (!systemdAvailable) {
|
|
97
|
-
hints = [...hints, ...renderSystemdUnavailableHints({ wsl: await isWSL() })];
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
emit({
|
|
101
|
-
ok: true,
|
|
102
|
-
result: "not-loaded",
|
|
103
|
-
message: `Gateway service ${service.notLoadedText}.`,
|
|
104
|
-
hints,
|
|
105
|
-
service: buildDaemonServiceSnapshot(service, loaded),
|
|
106
|
-
});
|
|
107
|
-
if (!json) {
|
|
108
|
-
defaultRuntime.log(`Gateway service ${service.notLoadedText}.`);
|
|
109
|
-
for (const hint of hints) {
|
|
110
|
-
defaultRuntime.log(`Start with: ${hint}`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
await service.restart({ env: process.env, stdout });
|
|
117
|
-
}
|
|
118
|
-
catch (err) {
|
|
119
|
-
const hints = renderGatewayServiceStartHints();
|
|
120
|
-
fail(`Gateway start failed: ${String(err)}`, hints);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
let started = true;
|
|
124
|
-
try {
|
|
125
|
-
started = await service.isLoaded({ env: process.env });
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
started = true;
|
|
129
|
-
}
|
|
130
|
-
emit({
|
|
131
|
-
ok: true,
|
|
132
|
-
result: "started",
|
|
133
|
-
service: buildDaemonServiceSnapshot(service, started),
|
|
14
|
+
return await runServiceStart({
|
|
15
|
+
serviceNoun: "Gateway",
|
|
16
|
+
service: resolveGatewayService(),
|
|
17
|
+
renderStartHints: renderGatewayServiceStartHints,
|
|
18
|
+
opts,
|
|
134
19
|
});
|
|
135
20
|
}
|
|
136
21
|
export async function runDaemonStop(opts = {}) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return;
|
|
142
|
-
emitDaemonActionJson({ action: "stop", ...payload });
|
|
143
|
-
};
|
|
144
|
-
const fail = (message) => {
|
|
145
|
-
if (json)
|
|
146
|
-
emit({ ok: false, error: message });
|
|
147
|
-
else
|
|
148
|
-
defaultRuntime.error(message);
|
|
149
|
-
defaultRuntime.exit(1);
|
|
150
|
-
};
|
|
151
|
-
const service = resolveGatewayService();
|
|
152
|
-
let loaded = false;
|
|
153
|
-
try {
|
|
154
|
-
loaded = await service.isLoaded({ env: process.env });
|
|
155
|
-
}
|
|
156
|
-
catch (err) {
|
|
157
|
-
fail(`Gateway service check failed: ${String(err)}`);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
if (!loaded) {
|
|
161
|
-
emit({
|
|
162
|
-
ok: true,
|
|
163
|
-
result: "not-loaded",
|
|
164
|
-
message: `Gateway service ${service.notLoadedText}.`,
|
|
165
|
-
service: buildDaemonServiceSnapshot(service, loaded),
|
|
166
|
-
});
|
|
167
|
-
if (!json) {
|
|
168
|
-
defaultRuntime.log(`Gateway service ${service.notLoadedText}.`);
|
|
169
|
-
}
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
try {
|
|
173
|
-
await service.stop({ env: process.env, stdout });
|
|
174
|
-
}
|
|
175
|
-
catch (err) {
|
|
176
|
-
fail(`Gateway stop failed: ${String(err)}`);
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
let stopped = false;
|
|
180
|
-
try {
|
|
181
|
-
stopped = await service.isLoaded({ env: process.env });
|
|
182
|
-
}
|
|
183
|
-
catch {
|
|
184
|
-
stopped = false;
|
|
185
|
-
}
|
|
186
|
-
emit({
|
|
187
|
-
ok: true,
|
|
188
|
-
result: "stopped",
|
|
189
|
-
service: buildDaemonServiceSnapshot(service, stopped),
|
|
22
|
+
return await runServiceStop({
|
|
23
|
+
serviceNoun: "Gateway",
|
|
24
|
+
service: resolveGatewayService(),
|
|
25
|
+
opts,
|
|
190
26
|
});
|
|
191
27
|
}
|
|
192
28
|
/**
|
|
@@ -195,71 +31,11 @@ export async function runDaemonStop(opts = {}) {
|
|
|
195
31
|
* Throws/exits on check or restart failures.
|
|
196
32
|
*/
|
|
197
33
|
export async function runDaemonRestart(opts = {}) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
};
|
|
205
|
-
const fail = (message, hints) => {
|
|
206
|
-
if (json)
|
|
207
|
-
emit({ ok: false, error: message, hints });
|
|
208
|
-
else
|
|
209
|
-
defaultRuntime.error(message);
|
|
210
|
-
defaultRuntime.exit(1);
|
|
211
|
-
};
|
|
212
|
-
const service = resolveGatewayService();
|
|
213
|
-
let loaded = false;
|
|
214
|
-
try {
|
|
215
|
-
loaded = await service.isLoaded({ env: process.env });
|
|
216
|
-
}
|
|
217
|
-
catch (err) {
|
|
218
|
-
fail(`Gateway service check failed: ${String(err)}`);
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
if (!loaded) {
|
|
222
|
-
let hints = renderGatewayServiceStartHints();
|
|
223
|
-
if (process.platform === "linux") {
|
|
224
|
-
const systemdAvailable = await isSystemdUserServiceAvailable().catch(() => false);
|
|
225
|
-
if (!systemdAvailable) {
|
|
226
|
-
hints = [...hints, ...renderSystemdUnavailableHints({ wsl: await isWSL() })];
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
emit({
|
|
230
|
-
ok: true,
|
|
231
|
-
result: "not-loaded",
|
|
232
|
-
message: `Gateway service ${service.notLoadedText}.`,
|
|
233
|
-
hints,
|
|
234
|
-
service: buildDaemonServiceSnapshot(service, loaded),
|
|
235
|
-
});
|
|
236
|
-
if (!json) {
|
|
237
|
-
defaultRuntime.log(`Gateway service ${service.notLoadedText}.`);
|
|
238
|
-
for (const hint of hints) {
|
|
239
|
-
defaultRuntime.log(`Start with: ${hint}`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
try {
|
|
245
|
-
await service.restart({ env: process.env, stdout });
|
|
246
|
-
let restarted = true;
|
|
247
|
-
try {
|
|
248
|
-
restarted = await service.isLoaded({ env: process.env });
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
restarted = true;
|
|
252
|
-
}
|
|
253
|
-
emit({
|
|
254
|
-
ok: true,
|
|
255
|
-
result: "restarted",
|
|
256
|
-
service: buildDaemonServiceSnapshot(service, restarted),
|
|
257
|
-
});
|
|
258
|
-
return true;
|
|
259
|
-
}
|
|
260
|
-
catch (err) {
|
|
261
|
-
const hints = renderGatewayServiceStartHints();
|
|
262
|
-
fail(`Gateway restart failed: ${String(err)}`, hints);
|
|
263
|
-
return false;
|
|
264
|
-
}
|
|
34
|
+
return await runServiceRestart({
|
|
35
|
+
serviceNoun: "Gateway",
|
|
36
|
+
service: resolveGatewayService(),
|
|
37
|
+
renderStartHints: renderGatewayServiceStartHints,
|
|
38
|
+
opts,
|
|
39
|
+
checkTokenDrift: true,
|
|
40
|
+
});
|
|
265
41
|
}
|
|
@@ -1,4 +1,25 @@
|
|
|
1
|
+
import { inheritOptionFromParent } from "../command-options.js";
|
|
1
2
|
import { runDaemonInstall, runDaemonRestart, runDaemonStart, runDaemonStatus, runDaemonStop, runDaemonUninstall, } from "./runners.js";
|
|
3
|
+
function resolveInstallOptions(cmdOpts, command) {
|
|
4
|
+
const parentForce = inheritOptionFromParent(command, "force");
|
|
5
|
+
const parentPort = inheritOptionFromParent(command, "port");
|
|
6
|
+
const parentToken = inheritOptionFromParent(command, "token");
|
|
7
|
+
return {
|
|
8
|
+
...cmdOpts,
|
|
9
|
+
force: Boolean(cmdOpts.force || parentForce),
|
|
10
|
+
port: cmdOpts.port ?? parentPort,
|
|
11
|
+
token: cmdOpts.token ?? parentToken,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function resolveRpcOptions(cmdOpts, command) {
|
|
15
|
+
const parentToken = inheritOptionFromParent(command, "token");
|
|
16
|
+
const parentPassword = inheritOptionFromParent(command, "password");
|
|
17
|
+
return {
|
|
18
|
+
...cmdOpts,
|
|
19
|
+
token: cmdOpts.token ?? parentToken,
|
|
20
|
+
password: cmdOpts.password ?? parentPassword,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
2
23
|
export function addGatewayServiceCommands(parent, opts) {
|
|
3
24
|
parent
|
|
4
25
|
.command("status")
|
|
@@ -10,9 +31,9 @@ export function addGatewayServiceCommands(parent, opts) {
|
|
|
10
31
|
.option("--no-probe", "Skip RPC probe")
|
|
11
32
|
.option("--deep", "Scan system-level services", false)
|
|
12
33
|
.option("--json", "Output JSON", false)
|
|
13
|
-
.action(async (cmdOpts) => {
|
|
34
|
+
.action(async (cmdOpts, command) => {
|
|
14
35
|
await runDaemonStatus({
|
|
15
|
-
rpc: cmdOpts,
|
|
36
|
+
rpc: resolveRpcOptions(cmdOpts, command),
|
|
16
37
|
probe: Boolean(cmdOpts.probe),
|
|
17
38
|
deep: Boolean(cmdOpts.deep),
|
|
18
39
|
json: Boolean(cmdOpts.json),
|
|
@@ -26,8 +47,8 @@ export function addGatewayServiceCommands(parent, opts) {
|
|
|
26
47
|
.option("--token <token>", "Gateway token (token auth)")
|
|
27
48
|
.option("--force", "Reinstall/overwrite if already installed", false)
|
|
28
49
|
.option("--json", "Output JSON", false)
|
|
29
|
-
.action(async (cmdOpts) => {
|
|
30
|
-
await runDaemonInstall(cmdOpts);
|
|
50
|
+
.action(async (cmdOpts, command) => {
|
|
51
|
+
await runDaemonInstall(resolveInstallOptions(cmdOpts, command));
|
|
31
52
|
});
|
|
32
53
|
parent
|
|
33
54
|
.command("uninstall")
|
package/dist/cli/daemon-cli.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { registerDaemonCli } from "./daemon-cli/register.js";
|
|
2
|
+
export { addGatewayServiceCommands } from "./daemon-cli/register-service-commands.js";
|
|
2
3
|
export { runDaemonInstall, runDaemonRestart, runDaemonStart, runDaemonStatus, runDaemonStop, runDaemonUninstall, } from "./daemon-cli/runners.js";
|
package/dist/cli/devices-cli.js
CHANGED
|
@@ -1,22 +1,10 @@
|
|
|
1
1
|
import { callGateway } from "../gateway/call.js";
|
|
2
|
-
import {
|
|
2
|
+
import { formatTimeAgo } from "../infra/format-time/format-relative.js";
|
|
3
3
|
import { defaultRuntime } from "../runtime.js";
|
|
4
4
|
import { renderTable } from "../terminal/table.js";
|
|
5
5
|
import { theme } from "../terminal/theme.js";
|
|
6
|
+
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
|
|
6
7
|
import { withProgress } from "./progress.js";
|
|
7
|
-
function formatAge(msAgo) {
|
|
8
|
-
const s = Math.max(0, Math.floor(msAgo / 1000));
|
|
9
|
-
if (s < 60)
|
|
10
|
-
return `${s}s`;
|
|
11
|
-
const m = Math.floor(s / 60);
|
|
12
|
-
if (m < 60)
|
|
13
|
-
return `${m}m`;
|
|
14
|
-
const h = Math.floor(m / 60);
|
|
15
|
-
if (h < 24)
|
|
16
|
-
return `${h}h`;
|
|
17
|
-
const d = Math.floor(h / 24);
|
|
18
|
-
return `${d}d`;
|
|
19
|
-
}
|
|
20
8
|
const devicesCallOpts = (cmd, defaults) => cmd
|
|
21
9
|
.option("--url <url>", "Gateway WebSocket URL (defaults to gateway.remote.url when configured)")
|
|
22
10
|
.option("--token <token>", "Gateway token (if required)")
|
|
@@ -44,12 +32,23 @@ function parseDevicePairingList(value) {
|
|
|
44
32
|
paired: Array.isArray(obj.paired) ? obj.paired : [],
|
|
45
33
|
};
|
|
46
34
|
}
|
|
35
|
+
function selectLatestPendingRequest(pending) {
|
|
36
|
+
if (!pending?.length) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return pending.reduce((latest, current) => {
|
|
40
|
+
const latestTs = typeof latest.ts === "number" ? latest.ts : 0;
|
|
41
|
+
const currentTs = typeof current.ts === "number" ? current.ts : 0;
|
|
42
|
+
return currentTs > latestTs ? current : latest;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
47
45
|
function formatTokenSummary(tokens) {
|
|
48
|
-
if (!tokens || tokens.length === 0)
|
|
46
|
+
if (!tokens || tokens.length === 0) {
|
|
49
47
|
return "none";
|
|
48
|
+
}
|
|
50
49
|
const parts = tokens
|
|
51
50
|
.map((t) => `${t.role}${t.revokedAtMs ? " (revoked)" : ""}`)
|
|
52
|
-
.
|
|
51
|
+
.toSorted((a, b) => a.localeCompare(b));
|
|
53
52
|
return parts.join(", ");
|
|
54
53
|
}
|
|
55
54
|
export function registerDevicesCli(program) {
|
|
@@ -82,7 +81,7 @@ export function registerDevicesCli(program) {
|
|
|
82
81
|
Device: req.displayName || req.deviceId,
|
|
83
82
|
Role: req.role ?? "",
|
|
84
83
|
IP: req.remoteIp ?? "",
|
|
85
|
-
Age: typeof req.ts === "number" ?
|
|
84
|
+
Age: typeof req.ts === "number" ? formatTimeAgo(Date.now() - req.ts) : "",
|
|
86
85
|
Flags: req.isRepair ? "repair" : "",
|
|
87
86
|
})),
|
|
88
87
|
}).trimEnd());
|
|
@@ -115,15 +114,29 @@ export function registerDevicesCli(program) {
|
|
|
115
114
|
devicesCallOpts(devices
|
|
116
115
|
.command("approve")
|
|
117
116
|
.description("Approve a pending device pairing request")
|
|
118
|
-
.argument("
|
|
117
|
+
.argument("[requestId]", "Pending request id")
|
|
118
|
+
.option("--latest", "Approve the most recent pending request", false)
|
|
119
119
|
.action(async (requestId, opts) => {
|
|
120
|
-
|
|
120
|
+
let resolvedRequestId = requestId?.trim();
|
|
121
|
+
if (!resolvedRequestId || opts.latest) {
|
|
122
|
+
const listResult = await callGatewayCli("device.pair.list", opts, {});
|
|
123
|
+
const latest = selectLatestPendingRequest(parseDevicePairingList(listResult).pending);
|
|
124
|
+
resolvedRequestId = latest?.requestId?.trim();
|
|
125
|
+
}
|
|
126
|
+
if (!resolvedRequestId) {
|
|
127
|
+
defaultRuntime.error("No pending device pairing requests to approve");
|
|
128
|
+
defaultRuntime.exit(1);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const result = await callGatewayCli("device.pair.approve", opts, {
|
|
132
|
+
requestId: resolvedRequestId,
|
|
133
|
+
});
|
|
121
134
|
if (opts.json) {
|
|
122
135
|
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
123
136
|
return;
|
|
124
137
|
}
|
|
125
138
|
const deviceId = result?.device?.deviceId;
|
|
126
|
-
defaultRuntime.log(`${theme.success("Approved")} ${theme.command(deviceId ?? "ok")}`);
|
|
139
|
+
defaultRuntime.log(`${theme.success("Approved")} ${theme.command(deviceId ?? "ok")} ${theme.muted(`(${resolvedRequestId})`)}`);
|
|
127
140
|
}));
|
|
128
141
|
devicesCallOpts(devices
|
|
129
142
|
.command("reject")
|