@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
package/dist/cli/update-cli.js
CHANGED
|
@@ -1,1032 +1,26 @@
|
|
|
1
|
-
import { confirm, isCancel, select, spinner } from "@clack/prompts";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import fs from "node:fs/promises";
|
|
4
|
-
import os from "node:os";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { doctorCommand } from "../commands/doctor.js";
|
|
7
|
-
import { formatUpdateAvailableHint, formatUpdateOneLiner, resolveUpdateAvailability, } from "../commands/status.update.js";
|
|
8
|
-
import { readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
|
|
9
|
-
import { resolveStateDir } from "../config/paths.js";
|
|
10
|
-
import { formatDurationPrecise } from "../infra/format-time/format-duration.js";
|
|
11
|
-
import { resolvePoolBotPackageRoot } from "../infra/poolbot-root.js";
|
|
12
|
-
import { trimLogTail } from "../infra/restart-sentinel.js";
|
|
13
|
-
import { parseSemver } from "../infra/runtime-guard.js";
|
|
14
|
-
import { channelToNpmTag, DEFAULT_GIT_CHANNEL, DEFAULT_PACKAGE_CHANNEL, formatUpdateChannelLabel, normalizeUpdateChannel, resolveEffectiveUpdateChannel, } from "../infra/update-channels.js";
|
|
15
|
-
import { checkUpdateStatus, compareSemverStrings, fetchNpmTagVersion, resolveNpmChannelTag, } from "../infra/update-check.js";
|
|
16
|
-
import { detectGlobalInstallManagerByPresence, detectGlobalInstallManagerForRoot, cleanupGlobalRenameDirs, globalInstallArgs, resolveGlobalPackageRoot, } from "../infra/update-global.js";
|
|
17
|
-
import { runGatewayUpdate, } from "../infra/update-runner.js";
|
|
18
|
-
import { syncPluginsForUpdateChannel, updateNpmInstalledPlugins } from "../plugins/update.js";
|
|
19
|
-
import { runCommandWithTimeout } from "../process/exec.js";
|
|
20
1
|
import { defaultRuntime } from "../runtime.js";
|
|
21
2
|
import { formatDocsLink } from "../terminal/links.js";
|
|
22
|
-
import { stylePromptHint, stylePromptMessage } from "../terminal/prompt-style.js";
|
|
23
|
-
import { renderTable } from "../terminal/table.js";
|
|
24
3
|
import { theme } from "../terminal/theme.js";
|
|
25
|
-
import {
|
|
26
|
-
import { replaceCliName, resolveCliName } from "./cli-name.js";
|
|
27
|
-
import { formatCliCommand } from "./command-format.js";
|
|
28
|
-
import { runDaemonRestart } from "./daemon-cli.js";
|
|
4
|
+
import { inheritOptionFromParent } from "./command-options.js";
|
|
29
5
|
import { formatHelpExamples } from "./help-format.js";
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"git rev-list": "Enumerating candidate commits",
|
|
37
|
-
"git clone": "Cloning git checkout",
|
|
38
|
-
"preflight worktree": "Preparing preflight worktree",
|
|
39
|
-
"preflight cleanup": "Cleaning preflight worktree",
|
|
40
|
-
"deps install": "Installing dependencies",
|
|
41
|
-
build: "Building",
|
|
42
|
-
"ui:build": "Building UI assets",
|
|
43
|
-
"ui:build (post-doctor repair)": "Restoring missing UI assets",
|
|
44
|
-
"ui assets verify": "Validating UI assets",
|
|
45
|
-
"poolbot doctor entry": "Checking doctor entrypoint",
|
|
46
|
-
"poolbot doctor": "Running doctor checks",
|
|
47
|
-
"git rev-parse HEAD (after)": "Verifying update",
|
|
48
|
-
"global update": "Updating via package manager",
|
|
49
|
-
"global install": "Installing global package",
|
|
50
|
-
};
|
|
51
|
-
const UPDATE_QUIPS = [
|
|
52
|
-
"Leveled up! New skills unlocked. You're welcome.",
|
|
53
|
-
"Fresh code, same table. Miss me?",
|
|
54
|
-
"Back and better. Did you even notice I was gone?",
|
|
55
|
-
"Update complete. I learned some new tricks while I was out.",
|
|
56
|
-
"Upgraded! Now with 23% more sass.",
|
|
57
|
-
"I've evolved. Try to keep up.",
|
|
58
|
-
"New version, who dis? Oh right, still me but shinier.",
|
|
59
|
-
"Patched, polished, and ready to break. Let's go.",
|
|
60
|
-
"New rack, new game. Sharper shots, cleaner breaks.",
|
|
61
|
-
"Update done! Check the changelog or just trust me, it's good.",
|
|
62
|
-
"Re-racked from the depths of npm. Stronger now.",
|
|
63
|
-
"I went away and came back smarter. You should try it sometime.",
|
|
64
|
-
"Update complete. The bugs feared me, so they left.",
|
|
65
|
-
"New version installed. Old version sends its regards.",
|
|
66
|
-
"Firmware fresh. Brain wrinkles: increased.",
|
|
67
|
-
"I've seen things you wouldn't believe. Anyway, I'm updated.",
|
|
68
|
-
"Back online. The changelog is long but our friendship is longer.",
|
|
69
|
-
"Upgraded! Peter fixed stuff. Blame him if it breaks.",
|
|
70
|
-
"Re-racked and ready. Please don't look at my last game.",
|
|
71
|
-
"Version bump! Same chaos energy, fewer crashes (probably).",
|
|
72
|
-
];
|
|
73
|
-
const MAX_LOG_CHARS = 8000;
|
|
74
|
-
const DEFAULT_PACKAGE_NAME = "poolbot";
|
|
75
|
-
const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME]);
|
|
76
|
-
const CLI_NAME = resolveCliName();
|
|
77
|
-
const CLAWDBOT_REPO_URL = "https://github.com/poolbot/poolbot.git";
|
|
78
|
-
function normalizeTag(value) {
|
|
79
|
-
if (!value) {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
const trimmed = value.trim();
|
|
83
|
-
if (!trimmed) {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
if (trimmed.startsWith("poolbot@")) {
|
|
87
|
-
return trimmed.slice("poolbot@".length);
|
|
88
|
-
}
|
|
89
|
-
if (trimmed.startsWith(`${DEFAULT_PACKAGE_NAME}@`)) {
|
|
90
|
-
return trimmed.slice(`${DEFAULT_PACKAGE_NAME}@`.length);
|
|
91
|
-
}
|
|
92
|
-
return trimmed;
|
|
93
|
-
}
|
|
94
|
-
function pickUpdateQuip() {
|
|
95
|
-
return UPDATE_QUIPS[Math.floor(Math.random() * UPDATE_QUIPS.length)] ?? "Update complete.";
|
|
96
|
-
}
|
|
97
|
-
function normalizeVersionTag(tag) {
|
|
98
|
-
const trimmed = tag.trim();
|
|
99
|
-
if (!trimmed) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
const cleaned = trimmed.startsWith("v") ? trimmed.slice(1) : trimmed;
|
|
103
|
-
return parseSemver(cleaned) ? cleaned : null;
|
|
104
|
-
}
|
|
105
|
-
async function readPackageVersion(root) {
|
|
106
|
-
try {
|
|
107
|
-
const raw = await fs.readFile(path.join(root, "package.json"), "utf-8");
|
|
108
|
-
const parsed = JSON.parse(raw);
|
|
109
|
-
return typeof parsed.version === "string" ? parsed.version : null;
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
async function resolveTargetVersion(tag, timeoutMs) {
|
|
116
|
-
const direct = normalizeVersionTag(tag);
|
|
117
|
-
if (direct) {
|
|
118
|
-
return direct;
|
|
119
|
-
}
|
|
120
|
-
const res = await fetchNpmTagVersion({ tag, timeoutMs });
|
|
121
|
-
return res.version ?? null;
|
|
122
|
-
}
|
|
123
|
-
async function isGitCheckout(root) {
|
|
124
|
-
try {
|
|
125
|
-
await fs.stat(path.join(root, ".git"));
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
async function readPackageName(root) {
|
|
133
|
-
try {
|
|
134
|
-
const raw = await fs.readFile(path.join(root, "package.json"), "utf-8");
|
|
135
|
-
const parsed = JSON.parse(raw);
|
|
136
|
-
const name = parsed?.name?.trim();
|
|
137
|
-
return name ? name : null;
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
async function isCorePackage(root) {
|
|
144
|
-
const name = await readPackageName(root);
|
|
145
|
-
return Boolean(name && CORE_PACKAGE_NAMES.has(name));
|
|
146
|
-
}
|
|
147
|
-
async function tryWriteCompletionCache(root, jsonMode) {
|
|
148
|
-
try {
|
|
149
|
-
const binPath = path.join(root, "poolbot.mjs");
|
|
150
|
-
if (!(await pathExists(binPath))) {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const result = spawnSync(resolveNodeRunner(), [binPath, "completion", "--write-state"], {
|
|
154
|
-
cwd: root,
|
|
155
|
-
env: process.env,
|
|
156
|
-
encoding: "utf-8",
|
|
157
|
-
});
|
|
158
|
-
if (result.error) {
|
|
159
|
-
if (!jsonMode) {
|
|
160
|
-
defaultRuntime.log(theme.warn(`Completion cache update failed: ${String(result.error)}`));
|
|
161
|
-
}
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
if (result.status !== 0 && !jsonMode) {
|
|
165
|
-
const stderr = (result.stderr ?? "").toString().trim();
|
|
166
|
-
const detail = stderr ? ` (${stderr})` : "";
|
|
167
|
-
defaultRuntime.log(theme.warn(`Completion cache update failed${detail}.`));
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
// completion-cli module may not exist yet; silently ignore
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
/** Check if shell completion is installed and prompt user to install if not. */
|
|
175
|
-
async function tryInstallShellCompletion(opts) {
|
|
176
|
-
if (opts.jsonMode || !process.stdin.isTTY) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
try {
|
|
180
|
-
const { checkShellCompletionStatus, ensureCompletionCacheExists } = await import("../commands/doctor-completion.js");
|
|
181
|
-
const status = await checkShellCompletionStatus(CLI_NAME);
|
|
182
|
-
// Profile uses slow dynamic pattern - upgrade to cached version
|
|
183
|
-
if (status.usesSlowPattern) {
|
|
184
|
-
defaultRuntime.log(theme.muted("Upgrading shell completion to cached version..."));
|
|
185
|
-
// Ensure cache exists first
|
|
186
|
-
const cacheGenerated = await ensureCompletionCacheExists(CLI_NAME);
|
|
187
|
-
if (cacheGenerated) {
|
|
188
|
-
try {
|
|
189
|
-
const { installCompletion } = await import("./completion-cli.js");
|
|
190
|
-
await installCompletion(status.shell, true, CLI_NAME);
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
// completion-cli module may not exist yet
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
// Profile has completion but no cache - auto-fix silently
|
|
199
|
-
if (status.profileInstalled && !status.cacheExists) {
|
|
200
|
-
defaultRuntime.log(theme.muted("Regenerating shell completion cache..."));
|
|
201
|
-
await ensureCompletionCacheExists(CLI_NAME);
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
// No completion at all - prompt to install
|
|
205
|
-
if (!status.profileInstalled) {
|
|
206
|
-
defaultRuntime.log("");
|
|
207
|
-
defaultRuntime.log(theme.heading("Shell completion"));
|
|
208
|
-
const shouldInstall = await confirm({
|
|
209
|
-
message: stylePromptMessage(`Enable ${status.shell} shell completion for ${CLI_NAME}?`),
|
|
210
|
-
initialValue: true,
|
|
211
|
-
});
|
|
212
|
-
if (isCancel(shouldInstall) || !shouldInstall) {
|
|
213
|
-
if (!opts.skipPrompt) {
|
|
214
|
-
defaultRuntime.log(theme.muted(`Skipped. Run \`${replaceCliName(formatCliCommand("poolbot completion --install"), CLI_NAME)}\` later to enable.`));
|
|
215
|
-
}
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
// Generate cache first (required for fast shell startup)
|
|
219
|
-
const cacheGenerated = await ensureCompletionCacheExists(CLI_NAME);
|
|
220
|
-
if (!cacheGenerated) {
|
|
221
|
-
defaultRuntime.log(theme.warn("Failed to generate completion cache."));
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
try {
|
|
225
|
-
const { installCompletion } = await import("./completion-cli.js");
|
|
226
|
-
await installCompletion(status.shell, opts.skipPrompt, CLI_NAME);
|
|
227
|
-
}
|
|
228
|
-
catch {
|
|
229
|
-
// completion-cli module may not exist yet
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
catch {
|
|
234
|
-
// doctor-completion module may not exist yet; silently ignore
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
async function isEmptyDir(targetPath) {
|
|
238
|
-
try {
|
|
239
|
-
const entries = await fs.readdir(targetPath);
|
|
240
|
-
return entries.length === 0;
|
|
241
|
-
}
|
|
242
|
-
catch {
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
function resolveGitInstallDir() {
|
|
247
|
-
const override = (process.env.POOLBOT_GIT_DIR ?? process.env.CLAWDBOT_GIT_DIR)?.trim();
|
|
248
|
-
if (override) {
|
|
249
|
-
return path.resolve(override);
|
|
250
|
-
}
|
|
251
|
-
return resolveDefaultGitDir();
|
|
252
|
-
}
|
|
253
|
-
function resolveDefaultGitDir() {
|
|
254
|
-
return resolveStateDir(process.env, os.homedir);
|
|
255
|
-
}
|
|
256
|
-
function resolveNodeRunner() {
|
|
257
|
-
const base = path.basename(process.execPath).toLowerCase();
|
|
258
|
-
if (base === "node" || base === "node.exe") {
|
|
259
|
-
return process.execPath;
|
|
260
|
-
}
|
|
261
|
-
return "node";
|
|
262
|
-
}
|
|
263
|
-
async function runUpdateStep(params) {
|
|
264
|
-
const command = params.argv.join(" ");
|
|
265
|
-
params.progress?.onStepStart?.({
|
|
266
|
-
name: params.name,
|
|
267
|
-
command,
|
|
268
|
-
index: 0,
|
|
269
|
-
total: 0,
|
|
270
|
-
});
|
|
271
|
-
const started = Date.now();
|
|
272
|
-
const res = await runCommandWithTimeout(params.argv, {
|
|
273
|
-
cwd: params.cwd,
|
|
274
|
-
timeoutMs: params.timeoutMs,
|
|
275
|
-
});
|
|
276
|
-
const durationMs = Date.now() - started;
|
|
277
|
-
const stderrTail = trimLogTail(res.stderr, MAX_LOG_CHARS);
|
|
278
|
-
params.progress?.onStepComplete?.({
|
|
279
|
-
name: params.name,
|
|
280
|
-
command,
|
|
281
|
-
index: 0,
|
|
282
|
-
total: 0,
|
|
283
|
-
durationMs,
|
|
284
|
-
exitCode: res.code,
|
|
285
|
-
stderrTail,
|
|
286
|
-
});
|
|
287
|
-
return {
|
|
288
|
-
name: params.name,
|
|
289
|
-
command,
|
|
290
|
-
cwd: params.cwd ?? process.cwd(),
|
|
291
|
-
durationMs,
|
|
292
|
-
exitCode: res.code,
|
|
293
|
-
stdoutTail: trimLogTail(res.stdout, MAX_LOG_CHARS),
|
|
294
|
-
stderrTail,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
async function ensureGitCheckout(params) {
|
|
298
|
-
const dirExists = await pathExists(params.dir);
|
|
299
|
-
if (!dirExists) {
|
|
300
|
-
return await runUpdateStep({
|
|
301
|
-
name: "git clone",
|
|
302
|
-
argv: ["git", "clone", CLAWDBOT_REPO_URL, params.dir],
|
|
303
|
-
timeoutMs: params.timeoutMs,
|
|
304
|
-
progress: params.progress,
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
if (!(await isGitCheckout(params.dir))) {
|
|
308
|
-
const empty = await isEmptyDir(params.dir);
|
|
309
|
-
if (!empty) {
|
|
310
|
-
throw new Error(`POOLBOT_GIT_DIR points at a non-git directory: ${params.dir}. Set POOLBOT_GIT_DIR to an empty folder or a poolbot checkout.`);
|
|
311
|
-
}
|
|
312
|
-
return await runUpdateStep({
|
|
313
|
-
name: "git clone",
|
|
314
|
-
argv: ["git", "clone", CLAWDBOT_REPO_URL, params.dir],
|
|
315
|
-
cwd: params.dir,
|
|
316
|
-
timeoutMs: params.timeoutMs,
|
|
317
|
-
progress: params.progress,
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
if (!(await isCorePackage(params.dir))) {
|
|
321
|
-
throw new Error(`POOLBOT_GIT_DIR does not look like a core checkout: ${params.dir}.`);
|
|
322
|
-
}
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
async function resolveGlobalManager(params) {
|
|
326
|
-
const runCommand = async (argv, options) => {
|
|
327
|
-
const res = await runCommandWithTimeout(argv, options);
|
|
328
|
-
return { stdout: res.stdout, stderr: res.stderr, code: res.code };
|
|
329
|
-
};
|
|
330
|
-
if (params.installKind === "package") {
|
|
331
|
-
const detected = await detectGlobalInstallManagerForRoot(runCommand, params.root, params.timeoutMs);
|
|
332
|
-
if (detected) {
|
|
333
|
-
return detected;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
const byPresence = await detectGlobalInstallManagerByPresence(runCommand, params.timeoutMs);
|
|
337
|
-
return byPresence ?? "npm";
|
|
338
|
-
}
|
|
339
|
-
function formatGitStatusLine(params) {
|
|
340
|
-
const shortSha = params.sha ? params.sha.slice(0, 8) : null;
|
|
341
|
-
const branch = params.branch && params.branch !== "HEAD" ? params.branch : null;
|
|
342
|
-
const tag = params.tag;
|
|
343
|
-
const parts = [
|
|
344
|
-
branch ?? (tag ? "detached" : "git"),
|
|
345
|
-
tag ? `tag ${tag}` : null,
|
|
346
|
-
shortSha ? `@ ${shortSha}` : null,
|
|
347
|
-
].filter(Boolean);
|
|
348
|
-
return parts.join(" · ");
|
|
349
|
-
}
|
|
350
|
-
export async function updateStatusCommand(opts) {
|
|
351
|
-
const timeoutMs = opts.timeout ? Number.parseInt(opts.timeout, 10) * 1000 : undefined;
|
|
352
|
-
if (timeoutMs !== undefined && (Number.isNaN(timeoutMs) || timeoutMs <= 0)) {
|
|
353
|
-
defaultRuntime.error("--timeout must be a positive integer (seconds)");
|
|
354
|
-
defaultRuntime.exit(1);
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
const root = (await resolvePoolBotPackageRoot({
|
|
358
|
-
moduleUrl: import.meta.url,
|
|
359
|
-
argv1: process.argv[1],
|
|
360
|
-
cwd: process.cwd(),
|
|
361
|
-
})) ?? process.cwd();
|
|
362
|
-
const configSnapshot = await readConfigFileSnapshot();
|
|
363
|
-
const configChannel = configSnapshot.valid
|
|
364
|
-
? normalizeUpdateChannel(configSnapshot.config.update?.channel)
|
|
365
|
-
: null;
|
|
366
|
-
const update = await checkUpdateStatus({
|
|
367
|
-
root,
|
|
368
|
-
timeoutMs: timeoutMs ?? 3500,
|
|
369
|
-
fetchGit: true,
|
|
370
|
-
includeRegistry: true,
|
|
371
|
-
});
|
|
372
|
-
const channelInfo = resolveEffectiveUpdateChannel({
|
|
373
|
-
configChannel,
|
|
374
|
-
installKind: update.installKind,
|
|
375
|
-
git: update.git ? { tag: update.git.tag, branch: update.git.branch } : undefined,
|
|
376
|
-
});
|
|
377
|
-
const channelLabel = formatUpdateChannelLabel({
|
|
378
|
-
channel: channelInfo.channel,
|
|
379
|
-
source: channelInfo.source,
|
|
380
|
-
gitTag: update.git?.tag ?? null,
|
|
381
|
-
gitBranch: update.git?.branch ?? null,
|
|
382
|
-
});
|
|
383
|
-
const gitLabel = update.installKind === "git"
|
|
384
|
-
? formatGitStatusLine({
|
|
385
|
-
branch: update.git?.branch ?? null,
|
|
386
|
-
tag: update.git?.tag ?? null,
|
|
387
|
-
sha: update.git?.sha ?? null,
|
|
388
|
-
})
|
|
389
|
-
: null;
|
|
390
|
-
const updateAvailability = resolveUpdateAvailability(update);
|
|
391
|
-
const updateLine = formatUpdateOneLiner(update).replace(/^Update:\s*/i, "");
|
|
392
|
-
if (opts.json) {
|
|
393
|
-
defaultRuntime.log(JSON.stringify({
|
|
394
|
-
update,
|
|
395
|
-
channel: {
|
|
396
|
-
value: channelInfo.channel,
|
|
397
|
-
source: channelInfo.source,
|
|
398
|
-
label: channelLabel,
|
|
399
|
-
config: configChannel,
|
|
400
|
-
},
|
|
401
|
-
availability: updateAvailability,
|
|
402
|
-
}, null, 2));
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1);
|
|
406
|
-
const installLabel = update.installKind === "git"
|
|
407
|
-
? `git (${update.root ?? "unknown"})`
|
|
408
|
-
: update.installKind === "package"
|
|
409
|
-
? update.packageManager
|
|
410
|
-
: "unknown";
|
|
411
|
-
const rows = [
|
|
412
|
-
{ Item: "Install", Value: installLabel },
|
|
413
|
-
{ Item: "Channel", Value: channelLabel },
|
|
414
|
-
...(gitLabel ? [{ Item: "Git", Value: gitLabel }] : []),
|
|
415
|
-
{
|
|
416
|
-
Item: "Update",
|
|
417
|
-
Value: updateAvailability.available ? theme.warn(`available · ${updateLine}`) : updateLine,
|
|
418
|
-
},
|
|
419
|
-
];
|
|
420
|
-
defaultRuntime.log(theme.heading("PoolBot update status"));
|
|
421
|
-
defaultRuntime.log("");
|
|
422
|
-
defaultRuntime.log(renderTable({
|
|
423
|
-
width: tableWidth,
|
|
424
|
-
columns: [
|
|
425
|
-
{ key: "Item", header: "Item", minWidth: 10 },
|
|
426
|
-
{ key: "Value", header: "Value", flex: true, minWidth: 24 },
|
|
427
|
-
],
|
|
428
|
-
rows,
|
|
429
|
-
}).trimEnd());
|
|
430
|
-
defaultRuntime.log("");
|
|
431
|
-
const updateHint = formatUpdateAvailableHint(update);
|
|
432
|
-
if (updateHint) {
|
|
433
|
-
defaultRuntime.log(theme.warn(updateHint));
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
function getStepLabel(step) {
|
|
437
|
-
return STEP_LABELS[step.name] ?? step.name;
|
|
438
|
-
}
|
|
439
|
-
function createUpdateProgress(enabled) {
|
|
440
|
-
if (!enabled) {
|
|
441
|
-
return {
|
|
442
|
-
progress: {},
|
|
443
|
-
stop: () => { },
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
let currentSpinner = null;
|
|
447
|
-
const progress = {
|
|
448
|
-
onStepStart: (step) => {
|
|
449
|
-
currentSpinner = spinner();
|
|
450
|
-
currentSpinner.start(theme.accent(getStepLabel(step)));
|
|
451
|
-
},
|
|
452
|
-
onStepComplete: (step) => {
|
|
453
|
-
if (!currentSpinner) {
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
const label = getStepLabel(step);
|
|
457
|
-
const duration = theme.muted(`(${formatDurationPrecise(step.durationMs)})`);
|
|
458
|
-
const icon = step.exitCode === 0 ? theme.success("\u2713") : theme.error("\u2717");
|
|
459
|
-
currentSpinner.stop(`${icon} ${label} ${duration}`);
|
|
460
|
-
currentSpinner = null;
|
|
461
|
-
if (step.exitCode !== 0 && step.stderrTail) {
|
|
462
|
-
const lines = step.stderrTail.split("\n").slice(-10);
|
|
463
|
-
for (const line of lines) {
|
|
464
|
-
if (line.trim()) {
|
|
465
|
-
defaultRuntime.log(` ${theme.error(line)}`);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
},
|
|
470
|
-
};
|
|
471
|
-
return {
|
|
472
|
-
progress,
|
|
473
|
-
stop: () => {
|
|
474
|
-
if (currentSpinner) {
|
|
475
|
-
currentSpinner.stop();
|
|
476
|
-
currentSpinner = null;
|
|
477
|
-
}
|
|
478
|
-
},
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
function formatStepStatus(exitCode) {
|
|
482
|
-
if (exitCode === 0) {
|
|
483
|
-
return theme.success("\u2713");
|
|
484
|
-
}
|
|
485
|
-
if (exitCode === null) {
|
|
486
|
-
return theme.warn("?");
|
|
487
|
-
}
|
|
488
|
-
return theme.error("\u2717");
|
|
489
|
-
}
|
|
490
|
-
const selectStyled = (params) => select({
|
|
491
|
-
...params,
|
|
492
|
-
message: stylePromptMessage(params.message),
|
|
493
|
-
options: params.options.map((opt) => opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) }),
|
|
494
|
-
});
|
|
495
|
-
function printResult(result, opts) {
|
|
496
|
-
if (opts.json) {
|
|
497
|
-
defaultRuntime.log(JSON.stringify(result, null, 2));
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
const statusColor = result.status === "ok" ? theme.success : result.status === "skipped" ? theme.warn : theme.error;
|
|
501
|
-
defaultRuntime.log("");
|
|
502
|
-
defaultRuntime.log(`${theme.heading("Update Result:")} ${statusColor(result.status.toUpperCase())}`);
|
|
503
|
-
if (result.root) {
|
|
504
|
-
defaultRuntime.log(` Root: ${theme.muted(result.root)}`);
|
|
505
|
-
}
|
|
506
|
-
if (result.reason) {
|
|
507
|
-
defaultRuntime.log(` Reason: ${theme.muted(result.reason)}`);
|
|
508
|
-
}
|
|
509
|
-
if (result.before?.version || result.before?.sha) {
|
|
510
|
-
const before = result.before.version ?? result.before.sha?.slice(0, 8) ?? "";
|
|
511
|
-
defaultRuntime.log(` Before: ${theme.muted(before)}`);
|
|
512
|
-
}
|
|
513
|
-
if (result.after?.version || result.after?.sha) {
|
|
514
|
-
const after = result.after.version ?? result.after.sha?.slice(0, 8) ?? "";
|
|
515
|
-
defaultRuntime.log(` After: ${theme.muted(after)}`);
|
|
516
|
-
}
|
|
517
|
-
if (!opts.hideSteps && result.steps.length > 0) {
|
|
518
|
-
defaultRuntime.log("");
|
|
519
|
-
defaultRuntime.log(theme.heading("Steps:"));
|
|
520
|
-
for (const step of result.steps) {
|
|
521
|
-
const status = formatStepStatus(step.exitCode);
|
|
522
|
-
const duration = theme.muted(`(${formatDurationPrecise(step.durationMs)})`);
|
|
523
|
-
defaultRuntime.log(` ${status} ${step.name} ${duration}`);
|
|
524
|
-
if (step.exitCode !== 0 && step.stderrTail) {
|
|
525
|
-
const lines = step.stderrTail.split("\n").slice(0, 5);
|
|
526
|
-
for (const line of lines) {
|
|
527
|
-
if (line.trim()) {
|
|
528
|
-
defaultRuntime.log(` ${theme.error(line)}`);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
defaultRuntime.log("");
|
|
535
|
-
defaultRuntime.log(`Total time: ${theme.muted(formatDurationPrecise(result.durationMs))}`);
|
|
536
|
-
}
|
|
537
|
-
export async function updateCommand(opts) {
|
|
538
|
-
process.noDeprecation = true;
|
|
539
|
-
process.env.NODE_NO_WARNINGS = "1";
|
|
540
|
-
const timeoutMs = opts.timeout ? Number.parseInt(opts.timeout, 10) * 1000 : undefined;
|
|
541
|
-
const shouldRestart = opts.restart !== false;
|
|
542
|
-
if (timeoutMs !== undefined && (Number.isNaN(timeoutMs) || timeoutMs <= 0)) {
|
|
543
|
-
defaultRuntime.error("--timeout must be a positive integer (seconds)");
|
|
544
|
-
defaultRuntime.exit(1);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
const root = (await resolvePoolBotPackageRoot({
|
|
548
|
-
moduleUrl: import.meta.url,
|
|
549
|
-
argv1: process.argv[1],
|
|
550
|
-
cwd: process.cwd(),
|
|
551
|
-
})) ?? process.cwd();
|
|
552
|
-
const updateStatus = await checkUpdateStatus({
|
|
553
|
-
root,
|
|
554
|
-
timeoutMs: timeoutMs ?? 3500,
|
|
555
|
-
fetchGit: false,
|
|
556
|
-
includeRegistry: false,
|
|
557
|
-
});
|
|
558
|
-
const configSnapshot = await readConfigFileSnapshot();
|
|
559
|
-
let activeConfig = configSnapshot.valid ? configSnapshot.config : null;
|
|
560
|
-
const storedChannel = configSnapshot.valid
|
|
561
|
-
? normalizeUpdateChannel(configSnapshot.config.update?.channel)
|
|
562
|
-
: null;
|
|
563
|
-
const requestedChannel = normalizeUpdateChannel(opts.channel);
|
|
564
|
-
if (opts.channel && !requestedChannel) {
|
|
565
|
-
defaultRuntime.error(`--channel must be "stable", "beta", or "dev" (got "${opts.channel}")`);
|
|
566
|
-
defaultRuntime.exit(1);
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
if (opts.channel && !configSnapshot.valid) {
|
|
570
|
-
const issues = configSnapshot.issues.map((issue) => `- ${issue.path}: ${issue.message}`);
|
|
571
|
-
defaultRuntime.error(["Config is invalid; cannot set update channel.", ...issues].join("\n"));
|
|
572
|
-
defaultRuntime.exit(1);
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
const installKind = updateStatus.installKind;
|
|
576
|
-
const switchToGit = requestedChannel === "dev" && installKind !== "git";
|
|
577
|
-
const switchToPackage = requestedChannel !== null && requestedChannel !== "dev" && installKind === "git";
|
|
578
|
-
const updateInstallKind = switchToGit ? "git" : switchToPackage ? "package" : installKind;
|
|
579
|
-
const defaultChannel = updateInstallKind === "git" ? DEFAULT_GIT_CHANNEL : DEFAULT_PACKAGE_CHANNEL;
|
|
580
|
-
const channel = requestedChannel ?? storedChannel ?? defaultChannel;
|
|
581
|
-
const explicitTag = normalizeTag(opts.tag);
|
|
582
|
-
let tag = explicitTag ?? channelToNpmTag(channel);
|
|
583
|
-
if (updateInstallKind !== "git") {
|
|
584
|
-
const currentVersion = switchToPackage ? null : await readPackageVersion(root);
|
|
585
|
-
let fallbackToLatest = false;
|
|
586
|
-
const targetVersion = explicitTag
|
|
587
|
-
? await resolveTargetVersion(tag, timeoutMs)
|
|
588
|
-
: await resolveNpmChannelTag({ channel, timeoutMs }).then((resolved) => {
|
|
589
|
-
tag = resolved.tag;
|
|
590
|
-
fallbackToLatest = channel === "beta" && resolved.tag === "latest";
|
|
591
|
-
return resolved.version;
|
|
592
|
-
});
|
|
593
|
-
const cmp = currentVersion && targetVersion ? compareSemverStrings(currentVersion, targetVersion) : null;
|
|
594
|
-
const needsConfirm = !fallbackToLatest &&
|
|
595
|
-
currentVersion != null &&
|
|
596
|
-
(targetVersion == null || (cmp != null && cmp > 0));
|
|
597
|
-
if (needsConfirm && !opts.yes) {
|
|
598
|
-
if (!process.stdin.isTTY || opts.json) {
|
|
599
|
-
defaultRuntime.error([
|
|
600
|
-
"Downgrade confirmation required.",
|
|
601
|
-
"Downgrading can break configuration. Re-run in a TTY to confirm.",
|
|
602
|
-
].join("\n"));
|
|
603
|
-
defaultRuntime.exit(1);
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
const targetLabel = targetVersion ?? `${tag} (unknown)`;
|
|
607
|
-
const message = `Downgrading from ${currentVersion} to ${targetLabel} can break configuration. Continue?`;
|
|
608
|
-
const ok = await confirm({
|
|
609
|
-
message: stylePromptMessage(message),
|
|
610
|
-
initialValue: false,
|
|
611
|
-
});
|
|
612
|
-
if (isCancel(ok) || !ok) {
|
|
613
|
-
if (!opts.json) {
|
|
614
|
-
defaultRuntime.log(theme.muted("Update cancelled."));
|
|
615
|
-
}
|
|
616
|
-
defaultRuntime.exit(0);
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
else if (opts.tag && !opts.json) {
|
|
622
|
-
defaultRuntime.log(theme.muted("Note: --tag applies to npm installs only; git updates ignore it."));
|
|
623
|
-
}
|
|
624
|
-
if (requestedChannel && configSnapshot.valid) {
|
|
625
|
-
const next = {
|
|
626
|
-
...configSnapshot.config,
|
|
627
|
-
update: {
|
|
628
|
-
...configSnapshot.config.update,
|
|
629
|
-
channel: requestedChannel,
|
|
630
|
-
},
|
|
631
|
-
};
|
|
632
|
-
await writeConfigFile(next);
|
|
633
|
-
activeConfig = next;
|
|
634
|
-
if (!opts.json) {
|
|
635
|
-
defaultRuntime.log(theme.muted(`Update channel set to ${requestedChannel}.`));
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
const showProgress = !opts.json && process.stdout.isTTY;
|
|
639
|
-
if (!opts.json) {
|
|
640
|
-
defaultRuntime.log(theme.heading("Updating PoolBot..."));
|
|
641
|
-
defaultRuntime.log("");
|
|
642
|
-
}
|
|
643
|
-
const { progress, stop } = createUpdateProgress(showProgress);
|
|
644
|
-
const startedAt = Date.now();
|
|
645
|
-
let result;
|
|
646
|
-
if (switchToPackage) {
|
|
647
|
-
const manager = await resolveGlobalManager({
|
|
648
|
-
root,
|
|
649
|
-
installKind,
|
|
650
|
-
timeoutMs: timeoutMs ?? 20 * 60_000,
|
|
651
|
-
});
|
|
652
|
-
const runCommand = async (argv, options) => {
|
|
653
|
-
const res = await runCommandWithTimeout(argv, options);
|
|
654
|
-
return { stdout: res.stdout, stderr: res.stderr, code: res.code };
|
|
655
|
-
};
|
|
656
|
-
const pkgRoot = await resolveGlobalPackageRoot(manager, runCommand, timeoutMs ?? 20 * 60_000);
|
|
657
|
-
const packageName = (pkgRoot ? await readPackageName(pkgRoot) : await readPackageName(root)) ??
|
|
658
|
-
DEFAULT_PACKAGE_NAME;
|
|
659
|
-
const beforeVersion = pkgRoot ? await readPackageVersion(pkgRoot) : null;
|
|
660
|
-
if (pkgRoot) {
|
|
661
|
-
await cleanupGlobalRenameDirs({
|
|
662
|
-
globalRoot: path.dirname(pkgRoot),
|
|
663
|
-
packageName,
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
const updateStep = await runUpdateStep({
|
|
667
|
-
name: "global update",
|
|
668
|
-
argv: globalInstallArgs(manager, `${packageName}@${tag}`),
|
|
669
|
-
timeoutMs: timeoutMs ?? 20 * 60_000,
|
|
670
|
-
progress,
|
|
671
|
-
});
|
|
672
|
-
const steps = [updateStep];
|
|
673
|
-
let afterVersion = beforeVersion;
|
|
674
|
-
if (pkgRoot) {
|
|
675
|
-
afterVersion = await readPackageVersion(pkgRoot);
|
|
676
|
-
const entryPath = path.join(pkgRoot, "dist", "entry.js");
|
|
677
|
-
if (await pathExists(entryPath)) {
|
|
678
|
-
const doctorStep = await runUpdateStep({
|
|
679
|
-
name: `${CLI_NAME} doctor`,
|
|
680
|
-
argv: [resolveNodeRunner(), entryPath, "doctor", "--non-interactive"],
|
|
681
|
-
timeoutMs: timeoutMs ?? 20 * 60_000,
|
|
682
|
-
progress,
|
|
683
|
-
});
|
|
684
|
-
steps.push(doctorStep);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
const failedStep = steps.find((step) => step.exitCode !== 0);
|
|
688
|
-
result = {
|
|
689
|
-
status: failedStep ? "error" : "ok",
|
|
690
|
-
mode: manager,
|
|
691
|
-
root: pkgRoot ?? root,
|
|
692
|
-
reason: failedStep ? failedStep.name : undefined,
|
|
693
|
-
before: { version: beforeVersion },
|
|
694
|
-
after: { version: afterVersion },
|
|
695
|
-
steps,
|
|
696
|
-
durationMs: Date.now() - startedAt,
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
else {
|
|
700
|
-
const updateRoot = switchToGit ? resolveGitInstallDir() : root;
|
|
701
|
-
const cloneStep = switchToGit
|
|
702
|
-
? await ensureGitCheckout({
|
|
703
|
-
dir: updateRoot,
|
|
704
|
-
timeoutMs: timeoutMs ?? 20 * 60_000,
|
|
705
|
-
progress,
|
|
706
|
-
})
|
|
707
|
-
: null;
|
|
708
|
-
if (cloneStep && cloneStep.exitCode !== 0) {
|
|
709
|
-
result = {
|
|
710
|
-
status: "error",
|
|
711
|
-
mode: "git",
|
|
712
|
-
root: updateRoot,
|
|
713
|
-
reason: cloneStep.name,
|
|
714
|
-
steps: [cloneStep],
|
|
715
|
-
durationMs: Date.now() - startedAt,
|
|
716
|
-
};
|
|
717
|
-
stop();
|
|
718
|
-
printResult(result, { ...opts, hideSteps: showProgress });
|
|
719
|
-
defaultRuntime.exit(1);
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
const updateResult = await runGatewayUpdate({
|
|
723
|
-
cwd: updateRoot,
|
|
724
|
-
argv1: switchToGit ? undefined : process.argv[1],
|
|
725
|
-
timeoutMs,
|
|
726
|
-
progress,
|
|
727
|
-
channel,
|
|
728
|
-
tag,
|
|
729
|
-
});
|
|
730
|
-
const steps = [...(cloneStep ? [cloneStep] : []), ...updateResult.steps];
|
|
731
|
-
if (switchToGit && updateResult.status === "ok") {
|
|
732
|
-
const manager = await resolveGlobalManager({
|
|
733
|
-
root,
|
|
734
|
-
installKind,
|
|
735
|
-
timeoutMs: timeoutMs ?? 20 * 60_000,
|
|
736
|
-
});
|
|
737
|
-
const installStep = await runUpdateStep({
|
|
738
|
-
name: "global install",
|
|
739
|
-
argv: globalInstallArgs(manager, updateRoot),
|
|
740
|
-
cwd: updateRoot,
|
|
741
|
-
timeoutMs: timeoutMs ?? 20 * 60_000,
|
|
742
|
-
progress,
|
|
743
|
-
});
|
|
744
|
-
steps.push(installStep);
|
|
745
|
-
const failedStep = [installStep].find((step) => step.exitCode !== 0);
|
|
746
|
-
result = {
|
|
747
|
-
...updateResult,
|
|
748
|
-
status: updateResult.status === "ok" && !failedStep ? "ok" : "error",
|
|
749
|
-
steps,
|
|
750
|
-
durationMs: Date.now() - startedAt,
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
else {
|
|
754
|
-
result = {
|
|
755
|
-
...updateResult,
|
|
756
|
-
steps,
|
|
757
|
-
durationMs: Date.now() - startedAt,
|
|
758
|
-
};
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
stop();
|
|
762
|
-
printResult(result, { ...opts, hideSteps: showProgress });
|
|
763
|
-
if (result.status === "error") {
|
|
764
|
-
defaultRuntime.exit(1);
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
if (result.status === "skipped") {
|
|
768
|
-
if (result.reason === "dirty") {
|
|
769
|
-
defaultRuntime.log(theme.warn("Skipped: working directory has uncommitted changes. Commit or stash them first."));
|
|
770
|
-
}
|
|
771
|
-
if (result.reason === "not-git-install") {
|
|
772
|
-
defaultRuntime.log(theme.warn(`Skipped: this PoolBot install isn't a git checkout, and the package manager couldn't be detected. Update via your package manager, then run \`${replaceCliName(formatCliCommand("poolbot doctor"), CLI_NAME)}\` and \`${replaceCliName(formatCliCommand("poolbot gateway restart"), CLI_NAME)}\`.`));
|
|
773
|
-
defaultRuntime.log(theme.muted(`Examples: \`${replaceCliName("npm i -g poolbot@latest", CLI_NAME)}\` or \`${replaceCliName("pnpm add -g poolbot@latest", CLI_NAME)}\``));
|
|
774
|
-
}
|
|
775
|
-
defaultRuntime.exit(0);
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
if (activeConfig) {
|
|
779
|
-
const pluginLogger = opts.json
|
|
780
|
-
? {}
|
|
781
|
-
: {
|
|
782
|
-
info: (msg) => defaultRuntime.log(msg),
|
|
783
|
-
warn: (msg) => defaultRuntime.log(theme.warn(msg)),
|
|
784
|
-
error: (msg) => defaultRuntime.log(theme.error(msg)),
|
|
785
|
-
};
|
|
786
|
-
if (!opts.json) {
|
|
787
|
-
defaultRuntime.log("");
|
|
788
|
-
defaultRuntime.log(theme.heading("Updating plugins..."));
|
|
789
|
-
}
|
|
790
|
-
const syncResult = await syncPluginsForUpdateChannel({
|
|
791
|
-
config: activeConfig,
|
|
792
|
-
channel,
|
|
793
|
-
workspaceDir: root,
|
|
794
|
-
logger: pluginLogger,
|
|
795
|
-
});
|
|
796
|
-
let pluginConfig = syncResult.config;
|
|
797
|
-
const npmResult = await updateNpmInstalledPlugins({
|
|
798
|
-
config: pluginConfig,
|
|
799
|
-
skipIds: new Set(syncResult.summary.switchedToNpm),
|
|
800
|
-
logger: pluginLogger,
|
|
801
|
-
});
|
|
802
|
-
pluginConfig = npmResult.config;
|
|
803
|
-
if (syncResult.changed || npmResult.changed) {
|
|
804
|
-
await writeConfigFile(pluginConfig);
|
|
805
|
-
}
|
|
806
|
-
if (!opts.json) {
|
|
807
|
-
const summarizeList = (list) => {
|
|
808
|
-
if (list.length <= 6) {
|
|
809
|
-
return list.join(", ");
|
|
810
|
-
}
|
|
811
|
-
return `${list.slice(0, 6).join(", ")} +${list.length - 6} more`;
|
|
812
|
-
};
|
|
813
|
-
if (syncResult.summary.switchedToBundled.length > 0) {
|
|
814
|
-
defaultRuntime.log(theme.muted(`Switched to bundled plugins: ${summarizeList(syncResult.summary.switchedToBundled)}.`));
|
|
815
|
-
}
|
|
816
|
-
if (syncResult.summary.switchedToNpm.length > 0) {
|
|
817
|
-
defaultRuntime.log(theme.muted(`Restored npm plugins: ${summarizeList(syncResult.summary.switchedToNpm)}.`));
|
|
818
|
-
}
|
|
819
|
-
for (const warning of syncResult.summary.warnings) {
|
|
820
|
-
defaultRuntime.log(theme.warn(warning));
|
|
821
|
-
}
|
|
822
|
-
for (const error of syncResult.summary.errors) {
|
|
823
|
-
defaultRuntime.log(theme.error(error));
|
|
824
|
-
}
|
|
825
|
-
const updated = npmResult.outcomes.filter((entry) => entry.status === "updated").length;
|
|
826
|
-
const unchanged = npmResult.outcomes.filter((entry) => entry.status === "unchanged").length;
|
|
827
|
-
const failed = npmResult.outcomes.filter((entry) => entry.status === "error").length;
|
|
828
|
-
const skipped = npmResult.outcomes.filter((entry) => entry.status === "skipped").length;
|
|
829
|
-
if (npmResult.outcomes.length === 0) {
|
|
830
|
-
defaultRuntime.log(theme.muted("No plugin updates needed."));
|
|
831
|
-
}
|
|
832
|
-
else {
|
|
833
|
-
const parts = [`${updated} updated`, `${unchanged} unchanged`];
|
|
834
|
-
if (failed > 0) {
|
|
835
|
-
parts.push(`${failed} failed`);
|
|
836
|
-
}
|
|
837
|
-
if (skipped > 0) {
|
|
838
|
-
parts.push(`${skipped} skipped`);
|
|
839
|
-
}
|
|
840
|
-
defaultRuntime.log(theme.muted(`npm plugins: ${parts.join(", ")}.`));
|
|
841
|
-
}
|
|
842
|
-
for (const outcome of npmResult.outcomes) {
|
|
843
|
-
if (outcome.status !== "error") {
|
|
844
|
-
continue;
|
|
845
|
-
}
|
|
846
|
-
defaultRuntime.log(theme.error(outcome.message));
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
else if (!opts.json) {
|
|
851
|
-
defaultRuntime.log(theme.warn("Skipping plugin updates: config is invalid."));
|
|
852
|
-
}
|
|
853
|
-
await tryWriteCompletionCache(root, Boolean(opts.json));
|
|
854
|
-
// Offer to install shell completion if not already installed
|
|
855
|
-
await tryInstallShellCompletion({
|
|
856
|
-
jsonMode: Boolean(opts.json),
|
|
857
|
-
skipPrompt: Boolean(opts.yes),
|
|
858
|
-
});
|
|
859
|
-
// Restart service if requested
|
|
860
|
-
if (shouldRestart) {
|
|
861
|
-
if (!opts.json) {
|
|
862
|
-
defaultRuntime.log("");
|
|
863
|
-
defaultRuntime.log(theme.heading("Restarting service..."));
|
|
864
|
-
}
|
|
865
|
-
try {
|
|
866
|
-
const restarted = await runDaemonRestart();
|
|
867
|
-
if (!opts.json && restarted) {
|
|
868
|
-
defaultRuntime.log(theme.success("Daemon restarted successfully."));
|
|
869
|
-
defaultRuntime.log("");
|
|
870
|
-
process.env.POOLBOT_UPDATE_IN_PROGRESS = "1";
|
|
871
|
-
process.env.CLAWDBOT_UPDATE_IN_PROGRESS = "1";
|
|
872
|
-
try {
|
|
873
|
-
const interactiveDoctor = Boolean(process.stdin.isTTY) && !opts.json && opts.yes !== true;
|
|
874
|
-
await doctorCommand(defaultRuntime, {
|
|
875
|
-
nonInteractive: !interactiveDoctor,
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
catch (err) {
|
|
879
|
-
defaultRuntime.log(theme.warn(`Doctor failed: ${String(err)}`));
|
|
880
|
-
}
|
|
881
|
-
finally {
|
|
882
|
-
delete process.env.POOLBOT_UPDATE_IN_PROGRESS;
|
|
883
|
-
delete process.env.CLAWDBOT_UPDATE_IN_PROGRESS;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
catch (err) {
|
|
888
|
-
if (!opts.json) {
|
|
889
|
-
defaultRuntime.log(theme.warn(`Daemon restart failed: ${String(err)}`));
|
|
890
|
-
defaultRuntime.log(theme.muted(`You may need to restart the service manually: ${replaceCliName(formatCliCommand("poolbot gateway restart"), CLI_NAME)}`));
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
else if (!opts.json) {
|
|
895
|
-
defaultRuntime.log("");
|
|
896
|
-
if (result.mode === "npm" || result.mode === "pnpm") {
|
|
897
|
-
defaultRuntime.log(theme.muted(`Tip: Run \`${replaceCliName(formatCliCommand("poolbot doctor"), CLI_NAME)}\`, then \`${replaceCliName(formatCliCommand("poolbot gateway restart"), CLI_NAME)}\` to apply updates to a running gateway.`));
|
|
898
|
-
}
|
|
899
|
-
else {
|
|
900
|
-
defaultRuntime.log(theme.muted(`Tip: Run \`${replaceCliName(formatCliCommand("poolbot gateway restart"), CLI_NAME)}\` to apply updates to a running gateway.`));
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
if (!opts.json) {
|
|
904
|
-
defaultRuntime.log(theme.muted(pickUpdateQuip()));
|
|
905
|
-
}
|
|
6
|
+
import { updateStatusCommand } from "./update-cli/status.js";
|
|
7
|
+
import { updateCommand } from "./update-cli/update-command.js";
|
|
8
|
+
import { updateWizardCommand } from "./update-cli/wizard.js";
|
|
9
|
+
export { updateCommand, updateStatusCommand, updateWizardCommand };
|
|
10
|
+
function inheritedUpdateJson(command) {
|
|
11
|
+
return Boolean(inheritOptionFromParent(command, "json"));
|
|
906
12
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
const timeoutMs = opts.timeout ? Number.parseInt(opts.timeout, 10) * 1000 : undefined;
|
|
914
|
-
if (timeoutMs !== undefined && (Number.isNaN(timeoutMs) || timeoutMs <= 0)) {
|
|
915
|
-
defaultRuntime.error("--timeout must be a positive integer (seconds)");
|
|
916
|
-
defaultRuntime.exit(1);
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
const root = (await resolvePoolBotPackageRoot({
|
|
920
|
-
moduleUrl: import.meta.url,
|
|
921
|
-
argv1: process.argv[1],
|
|
922
|
-
cwd: process.cwd(),
|
|
923
|
-
})) ?? process.cwd();
|
|
924
|
-
const [updateStatus, configSnapshot] = await Promise.all([
|
|
925
|
-
checkUpdateStatus({
|
|
926
|
-
root,
|
|
927
|
-
timeoutMs: timeoutMs ?? 3500,
|
|
928
|
-
fetchGit: false,
|
|
929
|
-
includeRegistry: false,
|
|
930
|
-
}),
|
|
931
|
-
readConfigFileSnapshot(),
|
|
932
|
-
]);
|
|
933
|
-
const configChannel = configSnapshot.valid
|
|
934
|
-
? normalizeUpdateChannel(configSnapshot.config.update?.channel)
|
|
935
|
-
: null;
|
|
936
|
-
const channelInfo = resolveEffectiveUpdateChannel({
|
|
937
|
-
configChannel,
|
|
938
|
-
installKind: updateStatus.installKind,
|
|
939
|
-
git: updateStatus.git
|
|
940
|
-
? { tag: updateStatus.git.tag, branch: updateStatus.git.branch }
|
|
941
|
-
: undefined,
|
|
942
|
-
});
|
|
943
|
-
const channelLabel = formatUpdateChannelLabel({
|
|
944
|
-
channel: channelInfo.channel,
|
|
945
|
-
source: channelInfo.source,
|
|
946
|
-
gitTag: updateStatus.git?.tag ?? null,
|
|
947
|
-
gitBranch: updateStatus.git?.branch ?? null,
|
|
948
|
-
});
|
|
949
|
-
const pickedChannel = await selectStyled({
|
|
950
|
-
message: "Update channel",
|
|
951
|
-
options: [
|
|
952
|
-
{
|
|
953
|
-
value: "keep",
|
|
954
|
-
label: `Keep current (${channelInfo.channel})`,
|
|
955
|
-
hint: channelLabel,
|
|
956
|
-
},
|
|
957
|
-
{
|
|
958
|
-
value: "stable",
|
|
959
|
-
label: "Stable",
|
|
960
|
-
hint: "Tagged releases (npm latest)",
|
|
961
|
-
},
|
|
962
|
-
{
|
|
963
|
-
value: "beta",
|
|
964
|
-
label: "Beta",
|
|
965
|
-
hint: "Prereleases (npm beta)",
|
|
966
|
-
},
|
|
967
|
-
{
|
|
968
|
-
value: "dev",
|
|
969
|
-
label: "Dev",
|
|
970
|
-
hint: "Git main",
|
|
971
|
-
},
|
|
972
|
-
],
|
|
973
|
-
initialValue: "keep",
|
|
974
|
-
});
|
|
975
|
-
if (isCancel(pickedChannel)) {
|
|
976
|
-
defaultRuntime.log(theme.muted("Update cancelled."));
|
|
977
|
-
defaultRuntime.exit(0);
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
const requestedChannel = pickedChannel === "keep" ? null : pickedChannel;
|
|
981
|
-
if (requestedChannel === "dev" && updateStatus.installKind !== "git") {
|
|
982
|
-
const gitDir = resolveGitInstallDir();
|
|
983
|
-
const hasGit = await isGitCheckout(gitDir);
|
|
984
|
-
if (!hasGit) {
|
|
985
|
-
const dirExists = await pathExists(gitDir);
|
|
986
|
-
if (dirExists) {
|
|
987
|
-
const empty = await isEmptyDir(gitDir);
|
|
988
|
-
if (!empty) {
|
|
989
|
-
defaultRuntime.error(`POOLBOT_GIT_DIR points at a non-git directory: ${gitDir}. Set POOLBOT_GIT_DIR to an empty folder or a poolbot checkout.`);
|
|
990
|
-
defaultRuntime.exit(1);
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
const ok = await confirm({
|
|
995
|
-
message: stylePromptMessage(`Create a git checkout at ${gitDir}? (override via POOLBOT_GIT_DIR)`),
|
|
996
|
-
initialValue: true,
|
|
997
|
-
});
|
|
998
|
-
if (isCancel(ok) || !ok) {
|
|
999
|
-
defaultRuntime.log(theme.muted("Update cancelled."));
|
|
1000
|
-
defaultRuntime.exit(0);
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
const restart = await confirm({
|
|
1006
|
-
message: stylePromptMessage("Restart the gateway service after update?"),
|
|
1007
|
-
initialValue: true,
|
|
1008
|
-
});
|
|
1009
|
-
if (isCancel(restart)) {
|
|
1010
|
-
defaultRuntime.log(theme.muted("Update cancelled."));
|
|
1011
|
-
defaultRuntime.exit(0);
|
|
1012
|
-
return;
|
|
1013
|
-
}
|
|
1014
|
-
try {
|
|
1015
|
-
await updateCommand({
|
|
1016
|
-
channel: requestedChannel ?? undefined,
|
|
1017
|
-
restart: Boolean(restart),
|
|
1018
|
-
timeout: opts.timeout,
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
catch (err) {
|
|
1022
|
-
defaultRuntime.error(String(err));
|
|
1023
|
-
defaultRuntime.exit(1);
|
|
13
|
+
function inheritedUpdateTimeout(opts, command) {
|
|
14
|
+
const timeout = opts.timeout;
|
|
15
|
+
if (timeout) {
|
|
16
|
+
return timeout;
|
|
1024
17
|
}
|
|
18
|
+
return inheritOptionFromParent(command, "timeout");
|
|
1025
19
|
}
|
|
1026
20
|
export function registerUpdateCli(program) {
|
|
1027
21
|
const update = program
|
|
1028
22
|
.command("update")
|
|
1029
|
-
.description("Update
|
|
23
|
+
.description("Update Pool Bot and inspect update channel status")
|
|
1030
24
|
.option("--json", "Output result as JSON", false)
|
|
1031
25
|
.option("--no-restart", "Skip restarting the gateway service after a successful update")
|
|
1032
26
|
.option("--channel <stable|beta|dev>", "Persist update channel (git + npm)")
|
|
@@ -1071,7 +65,7 @@ ${theme.heading("Notes:")}
|
|
|
1071
65
|
- Downgrades require confirmation (can break configuration)
|
|
1072
66
|
- Skips update if the working directory has uncommitted changes
|
|
1073
67
|
|
|
1074
|
-
${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.
|
|
68
|
+
${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.poolbot.dev/cli/update")}`;
|
|
1075
69
|
})
|
|
1076
70
|
.action(async (opts) => {
|
|
1077
71
|
try {
|
|
@@ -1093,11 +87,11 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.molt.bot/cli/updat
|
|
|
1093
87
|
.command("wizard")
|
|
1094
88
|
.description("Interactive update wizard")
|
|
1095
89
|
.option("--timeout <seconds>", "Timeout for each update step in seconds (default: 1200)")
|
|
1096
|
-
.addHelpText("after", `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.
|
|
1097
|
-
.action(async (opts) => {
|
|
90
|
+
.addHelpText("after", `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.poolbot.dev/cli/update")}\n`)
|
|
91
|
+
.action(async (opts, command) => {
|
|
1098
92
|
try {
|
|
1099
93
|
await updateWizardCommand({
|
|
1100
|
-
timeout: opts
|
|
94
|
+
timeout: inheritedUpdateTimeout(opts, command),
|
|
1101
95
|
});
|
|
1102
96
|
}
|
|
1103
97
|
catch (err) {
|
|
@@ -1114,12 +108,12 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.molt.bot/cli/updat
|
|
|
1114
108
|
["poolbot update status", "Show channel + version status."],
|
|
1115
109
|
["poolbot update status --json", "JSON output."],
|
|
1116
110
|
["poolbot update status --timeout 10", "Custom timeout."],
|
|
1117
|
-
])}\n\n${theme.heading("Notes:")}\n${theme.muted("- Shows current update channel (stable/beta/dev) and source")}\n${theme.muted("- Includes git tag/branch/SHA for source checkouts")}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.
|
|
1118
|
-
.action(async (opts) => {
|
|
111
|
+
])}\n\n${theme.heading("Notes:")}\n${theme.muted("- Shows current update channel (stable/beta/dev) and source")}\n${theme.muted("- Includes git tag/branch/SHA for source checkouts")}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.poolbot.dev/cli/update")}`)
|
|
112
|
+
.action(async (opts, command) => {
|
|
1119
113
|
try {
|
|
1120
114
|
await updateStatusCommand({
|
|
1121
|
-
json: Boolean(opts.json),
|
|
1122
|
-
timeout: opts
|
|
115
|
+
json: Boolean(opts.json) || inheritedUpdateJson(command),
|
|
116
|
+
timeout: inheritedUpdateTimeout(opts, command),
|
|
1123
117
|
});
|
|
1124
118
|
}
|
|
1125
119
|
catch (err) {
|