@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/plugins/install.js
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import { runCommandWithTimeout } from "../process/exec.js";
|
|
6
|
-
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
|
3
|
+
import { MANIFEST_KEY } from "../compat/legacy-names.js";
|
|
7
4
|
import { extractArchive, fileExists, readJsonFile, resolveArchiveKind, resolvePackedRootDir, } from "../infra/archive.js";
|
|
5
|
+
import { installPackageDir } from "../infra/install-package-dir.js";
|
|
6
|
+
import { resolveSafeInstallDir, safeDirName, unscopedPackageName, } from "../infra/install-safe-path.js";
|
|
7
|
+
import { packNpmSpecToArchive, resolveArchiveSourcePath, withTempDir, } from "../infra/install-source-utils.js";
|
|
8
|
+
import { validateRegistryNpmSpec } from "../infra/npm-registry-spec.js";
|
|
9
|
+
import { extensionUsesSkippedScannerPath, isPathInside } from "../security/scan-paths.js";
|
|
10
|
+
import * as skillScanner from "../security/skill-scanner.js";
|
|
11
|
+
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
|
8
12
|
const defaultLogger = {};
|
|
9
|
-
function unscopedPackageName(name) {
|
|
10
|
-
const trimmed = name.trim();
|
|
11
|
-
if (!trimmed)
|
|
12
|
-
return trimmed;
|
|
13
|
-
return trimmed.includes("/") ? (trimmed.split("/").pop() ?? trimmed) : trimmed;
|
|
14
|
-
}
|
|
15
|
-
function safeDirName(input) {
|
|
16
|
-
const trimmed = input.trim();
|
|
17
|
-
if (!trimmed)
|
|
18
|
-
return trimmed;
|
|
19
|
-
return trimmed.replaceAll("/", "__");
|
|
20
|
-
}
|
|
21
13
|
function safeFileName(input) {
|
|
22
14
|
return safeDirName(input);
|
|
23
15
|
}
|
|
16
|
+
function validatePluginId(pluginId) {
|
|
17
|
+
if (!pluginId) {
|
|
18
|
+
return "invalid plugin name: missing";
|
|
19
|
+
}
|
|
20
|
+
if (pluginId === "." || pluginId === "..") {
|
|
21
|
+
return "invalid plugin name: reserved path segment";
|
|
22
|
+
}
|
|
23
|
+
if (pluginId.includes("/") || pluginId.includes("\\")) {
|
|
24
|
+
return "invalid plugin name: path separators not allowed";
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
24
28
|
async function ensurePoolbotExtensions(manifest) {
|
|
25
|
-
const extensions = manifest
|
|
29
|
+
const extensions = manifest[MANIFEST_KEY]?.extensions;
|
|
26
30
|
if (!Array.isArray(extensions)) {
|
|
27
31
|
throw new Error("package.json missing poolbot.extensions");
|
|
28
32
|
}
|
|
@@ -32,17 +36,49 @@ async function ensurePoolbotExtensions(manifest) {
|
|
|
32
36
|
}
|
|
33
37
|
return list;
|
|
34
38
|
}
|
|
39
|
+
function resolvePluginInstallModeOptions(params) {
|
|
40
|
+
return {
|
|
41
|
+
logger: params.logger ?? defaultLogger,
|
|
42
|
+
mode: params.mode ?? "install",
|
|
43
|
+
dryRun: params.dryRun ?? false,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function resolveTimedPluginInstallModeOptions(params) {
|
|
47
|
+
return {
|
|
48
|
+
...resolvePluginInstallModeOptions(params),
|
|
49
|
+
timeoutMs: params.timeoutMs ?? 120_000,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function buildFileInstallResult(pluginId, targetFile) {
|
|
53
|
+
return {
|
|
54
|
+
ok: true,
|
|
55
|
+
pluginId,
|
|
56
|
+
targetDir: targetFile,
|
|
57
|
+
manifestName: undefined,
|
|
58
|
+
version: undefined,
|
|
59
|
+
extensions: [path.basename(targetFile)],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
35
62
|
export function resolvePluginInstallDir(pluginId, extensionsDir) {
|
|
36
63
|
const extensionsBase = extensionsDir
|
|
37
64
|
? resolveUserPath(extensionsDir)
|
|
38
65
|
: path.join(CONFIG_DIR, "extensions");
|
|
39
|
-
|
|
66
|
+
const pluginIdError = validatePluginId(pluginId);
|
|
67
|
+
if (pluginIdError) {
|
|
68
|
+
throw new Error(pluginIdError);
|
|
69
|
+
}
|
|
70
|
+
const targetDirResult = resolveSafeInstallDir({
|
|
71
|
+
baseDir: extensionsBase,
|
|
72
|
+
id: pluginId,
|
|
73
|
+
invalidNameMessage: "invalid plugin name: path traversal detected",
|
|
74
|
+
});
|
|
75
|
+
if (!targetDirResult.ok) {
|
|
76
|
+
throw new Error(targetDirResult.error);
|
|
77
|
+
}
|
|
78
|
+
return targetDirResult.path;
|
|
40
79
|
}
|
|
41
80
|
async function installPluginFromPackageDir(params) {
|
|
42
|
-
const logger = params
|
|
43
|
-
const timeoutMs = params.timeoutMs ?? 120_000;
|
|
44
|
-
const mode = params.mode ?? "install";
|
|
45
|
-
const dryRun = params.dryRun ?? false;
|
|
81
|
+
const { logger, timeoutMs, mode, dryRun } = resolveTimedPluginInstallModeOptions(params);
|
|
46
82
|
const manifestPath = path.join(params.packageDir, "package.json");
|
|
47
83
|
if (!(await fileExists(manifestPath))) {
|
|
48
84
|
return { ok: false, error: "extracted package missing package.json" };
|
|
@@ -63,17 +99,61 @@ async function installPluginFromPackageDir(params) {
|
|
|
63
99
|
}
|
|
64
100
|
const pkgName = typeof manifest.name === "string" ? manifest.name : "";
|
|
65
101
|
const pluginId = pkgName ? unscopedPackageName(pkgName) : "plugin";
|
|
102
|
+
const pluginIdError = validatePluginId(pluginId);
|
|
103
|
+
if (pluginIdError) {
|
|
104
|
+
return { ok: false, error: pluginIdError };
|
|
105
|
+
}
|
|
66
106
|
if (params.expectedPluginId && params.expectedPluginId !== pluginId) {
|
|
67
107
|
return {
|
|
68
108
|
ok: false,
|
|
69
109
|
error: `plugin id mismatch: expected ${params.expectedPluginId}, got ${pluginId}`,
|
|
70
110
|
};
|
|
71
111
|
}
|
|
112
|
+
const packageDir = path.resolve(params.packageDir);
|
|
113
|
+
const forcedScanEntries = [];
|
|
114
|
+
for (const entry of extensions) {
|
|
115
|
+
const resolvedEntry = path.resolve(packageDir, entry);
|
|
116
|
+
if (!isPathInside(packageDir, resolvedEntry)) {
|
|
117
|
+
logger.warn?.(`extension entry escapes plugin directory and will not be scanned: ${entry}`);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (extensionUsesSkippedScannerPath(entry)) {
|
|
121
|
+
logger.warn?.(`extension entry is in a hidden/node_modules path and will receive targeted scan coverage: ${entry}`);
|
|
122
|
+
}
|
|
123
|
+
forcedScanEntries.push(resolvedEntry);
|
|
124
|
+
}
|
|
125
|
+
// Scan plugin source for dangerous code patterns (warn-only; never blocks install)
|
|
126
|
+
try {
|
|
127
|
+
const scanSummary = await skillScanner.scanDirectoryWithSummary(params.packageDir, {
|
|
128
|
+
includeFiles: forcedScanEntries,
|
|
129
|
+
});
|
|
130
|
+
if (scanSummary.critical > 0) {
|
|
131
|
+
const criticalDetails = scanSummary.findings
|
|
132
|
+
.filter((f) => f.severity === "critical")
|
|
133
|
+
.map((f) => `${f.message} (${f.file}:${f.line})`)
|
|
134
|
+
.join("; ");
|
|
135
|
+
logger.warn?.(`WARNING: Plugin "${pluginId}" contains dangerous code patterns: ${criticalDetails}`);
|
|
136
|
+
}
|
|
137
|
+
else if (scanSummary.warn > 0) {
|
|
138
|
+
logger.warn?.(`Plugin "${pluginId}" has ${scanSummary.warn} suspicious code pattern(s). Run "poolbot security audit --deep" for details.`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
logger.warn?.(`Plugin "${pluginId}" code safety scan failed (${String(err)}). Installation continues; run "poolbot security audit --deep" after install.`);
|
|
143
|
+
}
|
|
72
144
|
const extensionsDir = params.extensionsDir
|
|
73
145
|
? resolveUserPath(params.extensionsDir)
|
|
74
146
|
: path.join(CONFIG_DIR, "extensions");
|
|
75
147
|
await fs.mkdir(extensionsDir, { recursive: true });
|
|
76
|
-
const
|
|
148
|
+
const targetDirResult = resolveSafeInstallDir({
|
|
149
|
+
baseDir: extensionsDir,
|
|
150
|
+
id: pluginId,
|
|
151
|
+
invalidNameMessage: "invalid plugin name: path traversal detected",
|
|
152
|
+
});
|
|
153
|
+
if (!targetDirResult.ok) {
|
|
154
|
+
return { ok: false, error: targetDirResult.error };
|
|
155
|
+
}
|
|
156
|
+
const targetDir = targetDirResult.path;
|
|
77
157
|
if (mode === "install" && (await fileExists(targetDir))) {
|
|
78
158
|
return {
|
|
79
159
|
ok: false,
|
|
@@ -90,49 +170,32 @@ async function installPluginFromPackageDir(params) {
|
|
|
90
170
|
extensions,
|
|
91
171
|
};
|
|
92
172
|
}
|
|
93
|
-
logger.info?.(`Installing to ${targetDir}…`);
|
|
94
|
-
let backupDir = null;
|
|
95
|
-
if (mode === "update" && (await fileExists(targetDir))) {
|
|
96
|
-
backupDir = `${targetDir}.backup-${Date.now()}`;
|
|
97
|
-
await fs.rename(targetDir, backupDir);
|
|
98
|
-
}
|
|
99
|
-
try {
|
|
100
|
-
await fs.cp(params.packageDir, targetDir, { recursive: true });
|
|
101
|
-
}
|
|
102
|
-
catch (err) {
|
|
103
|
-
if (backupDir) {
|
|
104
|
-
await fs.rm(targetDir, { recursive: true, force: true }).catch(() => undefined);
|
|
105
|
-
await fs.rename(backupDir, targetDir).catch(() => undefined);
|
|
106
|
-
}
|
|
107
|
-
return { ok: false, error: `failed to copy plugin: ${String(err)}` };
|
|
108
|
-
}
|
|
109
|
-
for (const entry of extensions) {
|
|
110
|
-
const resolvedEntry = path.resolve(targetDir, entry);
|
|
111
|
-
if (!(await fileExists(resolvedEntry))) {
|
|
112
|
-
logger.warn?.(`extension entry not found: ${entry}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
173
|
const deps = manifest.dependencies ?? {};
|
|
116
174
|
const hasDeps = Object.keys(deps).length > 0;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
175
|
+
const installRes = await installPackageDir({
|
|
176
|
+
sourceDir: params.packageDir,
|
|
177
|
+
targetDir,
|
|
178
|
+
mode,
|
|
179
|
+
timeoutMs,
|
|
180
|
+
logger,
|
|
181
|
+
copyErrorPrefix: "failed to copy plugin",
|
|
182
|
+
hasDeps,
|
|
183
|
+
depsLogMessage: "Installing plugin dependencies…",
|
|
184
|
+
afterCopy: async () => {
|
|
185
|
+
for (const entry of extensions) {
|
|
186
|
+
const resolvedEntry = path.resolve(targetDir, entry);
|
|
187
|
+
if (!isPathInside(targetDir, resolvedEntry)) {
|
|
188
|
+
logger.warn?.(`extension entry escapes plugin directory: ${entry}`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (!(await fileExists(resolvedEntry))) {
|
|
192
|
+
logger.warn?.(`extension entry not found: ${entry}`);
|
|
193
|
+
}
|
|
127
194
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (backupDir) {
|
|
135
|
-
await fs.rm(backupDir, { recursive: true, force: true }).catch(() => undefined);
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
if (!installRes.ok) {
|
|
198
|
+
return installRes;
|
|
136
199
|
}
|
|
137
200
|
return {
|
|
138
201
|
ok: true,
|
|
@@ -147,43 +210,42 @@ export async function installPluginFromArchive(params) {
|
|
|
147
210
|
const logger = params.logger ?? defaultLogger;
|
|
148
211
|
const timeoutMs = params.timeoutMs ?? 120_000;
|
|
149
212
|
const mode = params.mode ?? "install";
|
|
150
|
-
const
|
|
151
|
-
if (!
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
213
|
+
const archivePathResult = await resolveArchiveSourcePath(params.archivePath);
|
|
214
|
+
if (!archivePathResult.ok) {
|
|
215
|
+
return archivePathResult;
|
|
216
|
+
}
|
|
217
|
+
const archivePath = archivePathResult.path;
|
|
218
|
+
return await withTempDir("poolbot-plugin-", async (tmpDir) => {
|
|
219
|
+
const extractDir = path.join(tmpDir, "extract");
|
|
220
|
+
await fs.mkdir(extractDir, { recursive: true });
|
|
221
|
+
logger.info?.(`Extracting ${archivePath}…`);
|
|
222
|
+
try {
|
|
223
|
+
await extractArchive({
|
|
224
|
+
archivePath,
|
|
225
|
+
destDir: extractDir,
|
|
226
|
+
timeoutMs,
|
|
227
|
+
logger,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
return { ok: false, error: `failed to extract archive: ${String(err)}` };
|
|
232
|
+
}
|
|
233
|
+
let packageDir = "";
|
|
234
|
+
try {
|
|
235
|
+
packageDir = await resolvePackedRootDir(extractDir);
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
return { ok: false, error: String(err) };
|
|
239
|
+
}
|
|
240
|
+
return await installPluginFromPackageDir({
|
|
241
|
+
packageDir,
|
|
242
|
+
extensionsDir: params.extensionsDir,
|
|
165
243
|
timeoutMs,
|
|
166
244
|
logger,
|
|
245
|
+
mode,
|
|
246
|
+
dryRun: params.dryRun,
|
|
247
|
+
expectedPluginId: params.expectedPluginId,
|
|
167
248
|
});
|
|
168
|
-
}
|
|
169
|
-
catch (err) {
|
|
170
|
-
return { ok: false, error: `failed to extract archive: ${String(err)}` };
|
|
171
|
-
}
|
|
172
|
-
let packageDir = "";
|
|
173
|
-
try {
|
|
174
|
-
packageDir = await resolvePackedRootDir(extractDir);
|
|
175
|
-
}
|
|
176
|
-
catch (err) {
|
|
177
|
-
return { ok: false, error: String(err) };
|
|
178
|
-
}
|
|
179
|
-
return await installPluginFromPackageDir({
|
|
180
|
-
packageDir,
|
|
181
|
-
extensionsDir: params.extensionsDir,
|
|
182
|
-
timeoutMs,
|
|
183
|
-
logger,
|
|
184
|
-
mode,
|
|
185
|
-
dryRun: params.dryRun,
|
|
186
|
-
expectedPluginId: params.expectedPluginId,
|
|
187
249
|
});
|
|
188
250
|
}
|
|
189
251
|
export async function installPluginFromDir(params) {
|
|
@@ -206,9 +268,7 @@ export async function installPluginFromDir(params) {
|
|
|
206
268
|
});
|
|
207
269
|
}
|
|
208
270
|
export async function installPluginFromFile(params) {
|
|
209
|
-
const logger = params
|
|
210
|
-
const mode = params.mode ?? "install";
|
|
211
|
-
const dryRun = params.dryRun ?? false;
|
|
271
|
+
const { logger, mode, dryRun } = resolvePluginInstallModeOptions(params);
|
|
212
272
|
const filePath = resolveUserPath(params.filePath);
|
|
213
273
|
if (!(await fileExists(filePath))) {
|
|
214
274
|
return { ok: false, error: `file not found: ${filePath}` };
|
|
@@ -219,70 +279,48 @@ export async function installPluginFromFile(params) {
|
|
|
219
279
|
await fs.mkdir(extensionsDir, { recursive: true });
|
|
220
280
|
const base = path.basename(filePath, path.extname(filePath));
|
|
221
281
|
const pluginId = base || "plugin";
|
|
282
|
+
const pluginIdError = validatePluginId(pluginId);
|
|
283
|
+
if (pluginIdError) {
|
|
284
|
+
return { ok: false, error: pluginIdError };
|
|
285
|
+
}
|
|
222
286
|
const targetFile = path.join(extensionsDir, `${safeFileName(pluginId)}${path.extname(filePath)}`);
|
|
223
287
|
if (mode === "install" && (await fileExists(targetFile))) {
|
|
224
288
|
return { ok: false, error: `plugin already exists: ${targetFile} (delete it first)` };
|
|
225
289
|
}
|
|
226
290
|
if (dryRun) {
|
|
227
|
-
return
|
|
228
|
-
ok: true,
|
|
229
|
-
pluginId,
|
|
230
|
-
targetDir: targetFile,
|
|
231
|
-
manifestName: undefined,
|
|
232
|
-
version: undefined,
|
|
233
|
-
extensions: [path.basename(targetFile)],
|
|
234
|
-
};
|
|
291
|
+
return buildFileInstallResult(pluginId, targetFile);
|
|
235
292
|
}
|
|
236
293
|
logger.info?.(`Installing to ${targetFile}…`);
|
|
237
294
|
await fs.copyFile(filePath, targetFile);
|
|
238
|
-
return
|
|
239
|
-
ok: true,
|
|
240
|
-
pluginId,
|
|
241
|
-
targetDir: targetFile,
|
|
242
|
-
manifestName: undefined,
|
|
243
|
-
version: undefined,
|
|
244
|
-
extensions: [path.basename(targetFile)],
|
|
245
|
-
};
|
|
295
|
+
return buildFileInstallResult(pluginId, targetFile);
|
|
246
296
|
}
|
|
247
297
|
export async function installPluginFromNpmSpec(params) {
|
|
248
|
-
const logger = params
|
|
249
|
-
const timeoutMs = params.timeoutMs ?? 120_000;
|
|
250
|
-
const mode = params.mode ?? "install";
|
|
251
|
-
const dryRun = params.dryRun ?? false;
|
|
298
|
+
const { logger, timeoutMs, mode, dryRun } = resolveTimedPluginInstallModeOptions(params);
|
|
252
299
|
const expectedPluginId = params.expectedPluginId;
|
|
253
300
|
const spec = params.spec.trim();
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const archivePath = path.join(tmpDir, packed);
|
|
278
|
-
return await installPluginFromArchive({
|
|
279
|
-
archivePath,
|
|
280
|
-
extensionsDir: params.extensionsDir,
|
|
281
|
-
timeoutMs,
|
|
282
|
-
logger,
|
|
283
|
-
mode,
|
|
284
|
-
dryRun,
|
|
285
|
-
expectedPluginId,
|
|
301
|
+
const specError = validateRegistryNpmSpec(spec);
|
|
302
|
+
if (specError) {
|
|
303
|
+
return { ok: false, error: specError };
|
|
304
|
+
}
|
|
305
|
+
return await withTempDir("poolbot-npm-pack-", async (tmpDir) => {
|
|
306
|
+
logger.info?.(`Downloading ${spec}…`);
|
|
307
|
+
const packedResult = await packNpmSpecToArchive({
|
|
308
|
+
spec,
|
|
309
|
+
timeoutMs,
|
|
310
|
+
cwd: tmpDir,
|
|
311
|
+
});
|
|
312
|
+
if (!packedResult.ok) {
|
|
313
|
+
return packedResult;
|
|
314
|
+
}
|
|
315
|
+
return await installPluginFromArchive({
|
|
316
|
+
archivePath: packedResult.archivePath,
|
|
317
|
+
extensionsDir: params.extensionsDir,
|
|
318
|
+
timeoutMs,
|
|
319
|
+
logger,
|
|
320
|
+
mode,
|
|
321
|
+
dryRun,
|
|
322
|
+
expectedPluginId,
|
|
323
|
+
});
|
|
286
324
|
});
|
|
287
325
|
}
|
|
288
326
|
export async function installPluginFromPath(params) {
|
package/dist/polls.js
CHANGED
|
@@ -21,6 +21,13 @@ export function normalizePollInput(input, options = {}) {
|
|
|
21
21
|
if (maxSelections > cleaned.length) {
|
|
22
22
|
throw new Error("maxSelections cannot exceed option count");
|
|
23
23
|
}
|
|
24
|
+
const durationSecondsRaw = input.durationSeconds;
|
|
25
|
+
const durationSeconds = typeof durationSecondsRaw === "number" && Number.isFinite(durationSecondsRaw)
|
|
26
|
+
? Math.floor(durationSecondsRaw)
|
|
27
|
+
: undefined;
|
|
28
|
+
if (durationSeconds !== undefined && durationSeconds < 1) {
|
|
29
|
+
throw new Error("durationSeconds must be at least 1");
|
|
30
|
+
}
|
|
24
31
|
const durationRaw = input.durationHours;
|
|
25
32
|
const durationHours = typeof durationRaw === "number" && Number.isFinite(durationRaw)
|
|
26
33
|
? Math.floor(durationRaw)
|
|
@@ -28,10 +35,14 @@ export function normalizePollInput(input, options = {}) {
|
|
|
28
35
|
if (durationHours !== undefined && durationHours < 1) {
|
|
29
36
|
throw new Error("durationHours must be at least 1");
|
|
30
37
|
}
|
|
38
|
+
if (durationSeconds !== undefined && durationHours !== undefined) {
|
|
39
|
+
throw new Error("durationSeconds and durationHours are mutually exclusive");
|
|
40
|
+
}
|
|
31
41
|
return {
|
|
32
42
|
question,
|
|
33
43
|
options: cleaned,
|
|
34
44
|
maxSelections,
|
|
45
|
+
durationSeconds,
|
|
35
46
|
durationHours,
|
|
36
47
|
};
|
|
37
48
|
}
|