@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
|
@@ -27,11 +27,13 @@ async function findPackageRoot(startDir, maxDepth = 12) {
|
|
|
27
27
|
let current = path.resolve(startDir);
|
|
28
28
|
for (let i = 0; i < maxDepth; i += 1) {
|
|
29
29
|
const name = await readPackageName(current);
|
|
30
|
-
if (name && CORE_PACKAGE_NAMES.has(name))
|
|
30
|
+
if (name && CORE_PACKAGE_NAMES.has(name)) {
|
|
31
31
|
return current;
|
|
32
|
+
}
|
|
32
33
|
const parent = path.dirname(current);
|
|
33
|
-
if (parent === current)
|
|
34
|
+
if (parent === current) {
|
|
34
35
|
break;
|
|
36
|
+
}
|
|
35
37
|
current = parent;
|
|
36
38
|
}
|
|
37
39
|
return null;
|
|
@@ -40,11 +42,13 @@ function findPackageRootSync(startDir, maxDepth = 12) {
|
|
|
40
42
|
let current = path.resolve(startDir);
|
|
41
43
|
for (let i = 0; i < maxDepth; i += 1) {
|
|
42
44
|
const name = readPackageNameSync(current);
|
|
43
|
-
if (name && CORE_PACKAGE_NAMES.has(name))
|
|
45
|
+
if (name && CORE_PACKAGE_NAMES.has(name)) {
|
|
44
46
|
return current;
|
|
47
|
+
}
|
|
45
48
|
const parent = path.dirname(current);
|
|
46
|
-
if (parent === current)
|
|
49
|
+
if (parent === current) {
|
|
47
50
|
break;
|
|
51
|
+
}
|
|
48
52
|
current = parent;
|
|
49
53
|
}
|
|
50
54
|
return null;
|
|
@@ -52,6 +56,17 @@ function findPackageRootSync(startDir, maxDepth = 12) {
|
|
|
52
56
|
function candidateDirsFromArgv1(argv1) {
|
|
53
57
|
const normalized = path.resolve(argv1);
|
|
54
58
|
const candidates = [path.dirname(normalized)];
|
|
59
|
+
// Resolve symlinks for version managers (nvm, fnm, n, Homebrew/Linuxbrew)
|
|
60
|
+
// that create symlinks in bin/ pointing to the real package location.
|
|
61
|
+
try {
|
|
62
|
+
const resolved = fsSync.realpathSync(normalized);
|
|
63
|
+
if (resolved !== normalized) {
|
|
64
|
+
candidates.push(path.dirname(resolved));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// realpathSync throws if path doesn't exist; keep original candidates
|
|
69
|
+
}
|
|
55
70
|
const parts = normalized.split(path.sep);
|
|
56
71
|
const binIndex = parts.lastIndexOf(".bin");
|
|
57
72
|
if (binIndex > 0 && parts[binIndex - 1] === "node_modules") {
|
|
@@ -62,24 +77,24 @@ function candidateDirsFromArgv1(argv1) {
|
|
|
62
77
|
return candidates;
|
|
63
78
|
}
|
|
64
79
|
export async function resolvePoolBotPackageRoot(opts) {
|
|
65
|
-
const
|
|
66
|
-
if (opts.moduleUrl) {
|
|
67
|
-
candidates.push(path.dirname(fileURLToPath(opts.moduleUrl)));
|
|
68
|
-
}
|
|
69
|
-
if (opts.argv1) {
|
|
70
|
-
candidates.push(...candidateDirsFromArgv1(opts.argv1));
|
|
71
|
-
}
|
|
72
|
-
if (opts.cwd) {
|
|
73
|
-
candidates.push(opts.cwd);
|
|
74
|
-
}
|
|
75
|
-
for (const candidate of candidates) {
|
|
80
|
+
for (const candidate of buildCandidates(opts)) {
|
|
76
81
|
const found = await findPackageRoot(candidate);
|
|
77
|
-
if (found)
|
|
82
|
+
if (found) {
|
|
78
83
|
return found;
|
|
84
|
+
}
|
|
79
85
|
}
|
|
80
86
|
return null;
|
|
81
87
|
}
|
|
82
88
|
export function resolvePoolBotPackageRootSync(opts) {
|
|
89
|
+
for (const candidate of buildCandidates(opts)) {
|
|
90
|
+
const found = findPackageRootSync(candidate);
|
|
91
|
+
if (found) {
|
|
92
|
+
return found;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
function buildCandidates(opts) {
|
|
83
98
|
const candidates = [];
|
|
84
99
|
if (opts.moduleUrl) {
|
|
85
100
|
candidates.push(path.dirname(fileURLToPath(opts.moduleUrl)));
|
|
@@ -90,10 +105,5 @@ export function resolvePoolBotPackageRootSync(opts) {
|
|
|
90
105
|
if (opts.cwd) {
|
|
91
106
|
candidates.push(opts.cwd);
|
|
92
107
|
}
|
|
93
|
-
|
|
94
|
-
const found = findPackageRootSync(candidate);
|
|
95
|
-
if (found)
|
|
96
|
-
return found;
|
|
97
|
-
}
|
|
98
|
-
return null;
|
|
108
|
+
return candidates;
|
|
99
109
|
}
|
package/dist/infra/ports.js
CHANGED
|
@@ -2,6 +2,7 @@ import net from "node:net";
|
|
|
2
2
|
import { danger, info, shouldLogVerbose, warn } from "../globals.js";
|
|
3
3
|
import { logDebug } from "../logger.js";
|
|
4
4
|
import { defaultRuntime } from "../runtime.js";
|
|
5
|
+
import { isErrno } from "./errors.js";
|
|
5
6
|
import { formatPortDiagnostics } from "./ports-format.js";
|
|
6
7
|
import { inspectPortUsage } from "./ports-inspect.js";
|
|
7
8
|
class PortInUseError extends Error {
|
|
@@ -14,13 +15,11 @@ class PortInUseError extends Error {
|
|
|
14
15
|
this.details = details;
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
|
-
function isErrno(err) {
|
|
18
|
-
return Boolean(err && typeof err === "object" && "code" in err);
|
|
19
|
-
}
|
|
20
18
|
export async function describePortOwner(port) {
|
|
21
19
|
const diagnostics = await inspectPortUsage(port);
|
|
22
|
-
if (diagnostics.listeners.length === 0)
|
|
20
|
+
if (diagnostics.listeners.length === 0) {
|
|
23
21
|
return undefined;
|
|
22
|
+
}
|
|
24
23
|
return formatPortDiagnostics(diagnostics).join("\n");
|
|
25
24
|
}
|
|
26
25
|
export async function ensurePortAvailable(port) {
|
|
@@ -38,8 +37,7 @@ export async function ensurePortAvailable(port) {
|
|
|
38
37
|
}
|
|
39
38
|
catch (err) {
|
|
40
39
|
if (isErrno(err) && err.code === "EADDRINUSE") {
|
|
41
|
-
|
|
42
|
-
throw new PortInUseError(port, details);
|
|
40
|
+
throw new PortInUseError(port);
|
|
43
41
|
}
|
|
44
42
|
throw err;
|
|
45
43
|
}
|
|
@@ -47,13 +45,15 @@ export async function ensurePortAvailable(port) {
|
|
|
47
45
|
export async function handlePortError(err, port, context, runtime = defaultRuntime) {
|
|
48
46
|
// Uniform messaging for EADDRINUSE with optional owner details.
|
|
49
47
|
if (err instanceof PortInUseError || (isErrno(err) && err.code === "EADDRINUSE")) {
|
|
50
|
-
const details = err instanceof PortInUseError
|
|
48
|
+
const details = err instanceof PortInUseError
|
|
49
|
+
? (err.details ?? (await describePortOwner(port)))
|
|
50
|
+
: await describePortOwner(port);
|
|
51
51
|
runtime.error(danger(`${context} failed: port ${port} is already in use.`));
|
|
52
52
|
if (details) {
|
|
53
53
|
runtime.error(info("Port listener details:"));
|
|
54
54
|
runtime.error(details);
|
|
55
55
|
if (/poolbot|src\/index\.ts|dist\/index\.js/.test(details)) {
|
|
56
|
-
runtime.error(warn("It looks like another
|
|
56
|
+
runtime.error(warn("It looks like another Pool Bot instance is already running. Stop it or pick a different port."));
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
runtime.error(info("Resolve by stopping the process using the port or passing --port <free-port>."));
|
|
@@ -63,12 +63,15 @@ export async function handlePortError(err, port, context, runtime = defaultRunti
|
|
|
63
63
|
if (shouldLogVerbose()) {
|
|
64
64
|
const stdout = err?.stdout;
|
|
65
65
|
const stderr = err?.stderr;
|
|
66
|
-
if (stdout?.trim())
|
|
66
|
+
if (stdout?.trim()) {
|
|
67
67
|
logDebug(`stdout: ${stdout.trim()}`);
|
|
68
|
-
|
|
68
|
+
}
|
|
69
|
+
if (stderr?.trim()) {
|
|
69
70
|
logDebug(`stderr: ${stderr.trim()}`);
|
|
71
|
+
}
|
|
70
72
|
}
|
|
71
|
-
|
|
73
|
+
runtime.exit(1);
|
|
74
|
+
throw new Error("unreachable");
|
|
72
75
|
}
|
|
73
76
|
export { PortInUseError };
|
|
74
77
|
export { buildPortHints, classifyPortListener, formatPortDiagnostics } from "./ports-format.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { loadWorkspaceSkillEntries } from "../agents/skills.js";
|
|
2
|
-
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
|
3
|
-
import { listNodePairing, updatePairedNodeMetadata } from "./node-pairing.js";
|
|
4
|
-
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
5
2
|
import { bumpSkillsSnapshotVersion } from "../agents/skills/refresh.js";
|
|
3
|
+
import { listAgentWorkspaceDirs } from "../agents/workspace-dirs.js";
|
|
4
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
5
|
+
import { listNodePairing, updatePairedNodeMetadata } from "./node-pairing.js";
|
|
6
6
|
const log = createSubsystemLogger("gateway/skills-remote");
|
|
7
7
|
const remoteNodes = new Map();
|
|
8
8
|
let remoteRegistry = null;
|
|
@@ -14,12 +14,15 @@ function describeNode(nodeId) {
|
|
|
14
14
|
return ip ? `${base} @ ${ip}` : base;
|
|
15
15
|
}
|
|
16
16
|
function extractErrorMessage(err) {
|
|
17
|
-
if (!err)
|
|
17
|
+
if (!err) {
|
|
18
18
|
return undefined;
|
|
19
|
-
|
|
19
|
+
}
|
|
20
|
+
if (typeof err === "string") {
|
|
20
21
|
return err;
|
|
21
|
-
|
|
22
|
+
}
|
|
23
|
+
if (err instanceof Error) {
|
|
22
24
|
return err.message;
|
|
25
|
+
}
|
|
23
26
|
if (typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
24
27
|
return err.message;
|
|
25
28
|
}
|
|
@@ -61,12 +64,15 @@ function isMacPlatform(platform, deviceFamily) {
|
|
|
61
64
|
const familyNorm = String(deviceFamily ?? "")
|
|
62
65
|
.trim()
|
|
63
66
|
.toLowerCase();
|
|
64
|
-
if (platformNorm.includes("mac"))
|
|
67
|
+
if (platformNorm.includes("mac")) {
|
|
65
68
|
return true;
|
|
66
|
-
|
|
69
|
+
}
|
|
70
|
+
if (platformNorm.includes("darwin")) {
|
|
67
71
|
return true;
|
|
68
|
-
|
|
72
|
+
}
|
|
73
|
+
if (familyNorm === "mac") {
|
|
69
74
|
return true;
|
|
75
|
+
}
|
|
70
76
|
return false;
|
|
71
77
|
}
|
|
72
78
|
function supportsSystemRun(commands) {
|
|
@@ -123,34 +129,27 @@ export function recordRemoteNodeInfo(node) {
|
|
|
123
129
|
export function recordRemoteNodeBins(nodeId, bins) {
|
|
124
130
|
upsertNode({ nodeId, bins });
|
|
125
131
|
}
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
const list = cfg.agents?.list;
|
|
129
|
-
if (Array.isArray(list)) {
|
|
130
|
-
for (const entry of list) {
|
|
131
|
-
if (entry && typeof entry === "object" && typeof entry.id === "string") {
|
|
132
|
-
dirs.add(resolveAgentWorkspaceDir(cfg, entry.id));
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
dirs.add(resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)));
|
|
137
|
-
return [...dirs];
|
|
132
|
+
export function removeRemoteNodeInfo(nodeId) {
|
|
133
|
+
remoteNodes.delete(nodeId);
|
|
138
134
|
}
|
|
139
135
|
function collectRequiredBins(entries, targetPlatform) {
|
|
140
136
|
const bins = new Set();
|
|
141
137
|
for (const entry of entries) {
|
|
142
138
|
const os = entry.metadata?.os ?? [];
|
|
143
|
-
if (os.length > 0 && !os.includes(targetPlatform))
|
|
139
|
+
if (os.length > 0 && !os.includes(targetPlatform)) {
|
|
144
140
|
continue;
|
|
141
|
+
}
|
|
145
142
|
const required = entry.metadata?.requires?.bins ?? [];
|
|
146
143
|
const anyBins = entry.metadata?.requires?.anyBins ?? [];
|
|
147
144
|
for (const bin of required) {
|
|
148
|
-
if (bin.trim())
|
|
145
|
+
if (bin.trim()) {
|
|
149
146
|
bins.add(bin.trim());
|
|
147
|
+
}
|
|
150
148
|
}
|
|
151
149
|
for (const bin of anyBins) {
|
|
152
|
-
if (bin.trim())
|
|
150
|
+
if (bin.trim()) {
|
|
153
151
|
bins.add(bin.trim());
|
|
152
|
+
}
|
|
154
153
|
}
|
|
155
154
|
}
|
|
156
155
|
return [...bins];
|
|
@@ -160,8 +159,9 @@ function buildBinProbeScript(bins) {
|
|
|
160
159
|
return `for b in ${escaped}; do if command -v "$b" >/dev/null 2>&1; then echo "$b"; fi; done`;
|
|
161
160
|
}
|
|
162
161
|
function parseBinProbePayload(payloadJSON, payload) {
|
|
163
|
-
if (!payloadJSON && !payload)
|
|
162
|
+
if (!payloadJSON && !payload) {
|
|
164
163
|
return [];
|
|
164
|
+
}
|
|
165
165
|
try {
|
|
166
166
|
const parsed = payloadJSON
|
|
167
167
|
? JSON.parse(payloadJSON)
|
|
@@ -182,26 +182,32 @@ function parseBinProbePayload(payloadJSON, payload) {
|
|
|
182
182
|
return [];
|
|
183
183
|
}
|
|
184
184
|
function areBinSetsEqual(a, b) {
|
|
185
|
-
if (!a)
|
|
185
|
+
if (!a) {
|
|
186
186
|
return false;
|
|
187
|
-
|
|
187
|
+
}
|
|
188
|
+
if (a.size !== b.size) {
|
|
188
189
|
return false;
|
|
190
|
+
}
|
|
189
191
|
for (const bin of b) {
|
|
190
|
-
if (!a.has(bin))
|
|
192
|
+
if (!a.has(bin)) {
|
|
191
193
|
return false;
|
|
194
|
+
}
|
|
192
195
|
}
|
|
193
196
|
return true;
|
|
194
197
|
}
|
|
195
198
|
export async function refreshRemoteNodeBins(params) {
|
|
196
|
-
if (!remoteRegistry)
|
|
199
|
+
if (!remoteRegistry) {
|
|
197
200
|
return;
|
|
198
|
-
|
|
201
|
+
}
|
|
202
|
+
if (!isMacPlatform(params.platform, params.deviceFamily)) {
|
|
199
203
|
return;
|
|
204
|
+
}
|
|
200
205
|
const canWhich = supportsSystemWhich(params.commands);
|
|
201
206
|
const canRun = supportsSystemRun(params.commands);
|
|
202
|
-
if (!canWhich && !canRun)
|
|
207
|
+
if (!canWhich && !canRun) {
|
|
203
208
|
return;
|
|
204
|
-
|
|
209
|
+
}
|
|
210
|
+
const workspaceDirs = listAgentWorkspaceDirs(params.cfg);
|
|
205
211
|
const requiredBins = new Set();
|
|
206
212
|
for (const workspaceDir of workspaceDirs) {
|
|
207
213
|
const entries = loadWorkspaceSkillEntries(workspaceDir, { config: params.cfg });
|
|
@@ -209,8 +215,9 @@ export async function refreshRemoteNodeBins(params) {
|
|
|
209
215
|
requiredBins.add(bin);
|
|
210
216
|
}
|
|
211
217
|
}
|
|
212
|
-
if (requiredBins.size === 0)
|
|
218
|
+
if (requiredBins.size === 0) {
|
|
213
219
|
return;
|
|
220
|
+
}
|
|
214
221
|
try {
|
|
215
222
|
const binsList = [...requiredBins];
|
|
216
223
|
const res = await remoteRegistry.invoke(canWhich
|
|
@@ -237,8 +244,9 @@ export async function refreshRemoteNodeBins(params) {
|
|
|
237
244
|
const nextBins = new Set(bins);
|
|
238
245
|
const hasChanged = !areBinSetsEqual(existingBins, nextBins);
|
|
239
246
|
recordRemoteNodeBins(params.nodeId, bins);
|
|
240
|
-
if (!hasChanged)
|
|
247
|
+
if (!hasChanged) {
|
|
241
248
|
return;
|
|
249
|
+
}
|
|
242
250
|
await updatePairedNodeMetadata(params.nodeId, { bins });
|
|
243
251
|
bumpSkillsSnapshotVersion({ reason: "remote-node" });
|
|
244
252
|
}
|
|
@@ -248,12 +256,14 @@ export async function refreshRemoteNodeBins(params) {
|
|
|
248
256
|
}
|
|
249
257
|
export function getRemoteSkillEligibility() {
|
|
250
258
|
const macNodes = [...remoteNodes.values()].filter((node) => isMacPlatform(node.platform, node.deviceFamily) && supportsSystemRun(node.commands));
|
|
251
|
-
if (macNodes.length === 0)
|
|
259
|
+
if (macNodes.length === 0) {
|
|
252
260
|
return undefined;
|
|
261
|
+
}
|
|
253
262
|
const bins = new Set();
|
|
254
263
|
for (const node of macNodes) {
|
|
255
|
-
for (const bin of node.bins)
|
|
264
|
+
for (const bin of node.bins) {
|
|
256
265
|
bins.add(bin);
|
|
266
|
+
}
|
|
257
267
|
}
|
|
258
268
|
const labels = macNodes.map((node) => node.displayName ?? node.nodeId).filter(Boolean);
|
|
259
269
|
const note = labels.length > 0
|
|
@@ -267,8 +277,9 @@ export function getRemoteSkillEligibility() {
|
|
|
267
277
|
};
|
|
268
278
|
}
|
|
269
279
|
export async function refreshRemoteBinsForConnectedNodes(cfg) {
|
|
270
|
-
if (!remoteRegistry)
|
|
280
|
+
if (!remoteRegistry) {
|
|
271
281
|
return;
|
|
282
|
+
}
|
|
272
283
|
const connected = remoteRegistry.listConnected();
|
|
273
284
|
for (const node of connected) {
|
|
274
285
|
await refreshRemoteNodeBins({
|
|
@@ -11,11 +11,13 @@ function requireSessionKey(key) {
|
|
|
11
11
|
return trimmed;
|
|
12
12
|
}
|
|
13
13
|
function normalizeContextKey(key) {
|
|
14
|
-
if (!key)
|
|
14
|
+
if (!key) {
|
|
15
15
|
return null;
|
|
16
|
+
}
|
|
16
17
|
const trimmed = key.trim();
|
|
17
|
-
if (!trimmed)
|
|
18
|
+
if (!trimmed) {
|
|
18
19
|
return null;
|
|
20
|
+
}
|
|
19
21
|
return trimmed.toLowerCase();
|
|
20
22
|
}
|
|
21
23
|
export function isSystemEventContextChanged(sessionKey, contextKey) {
|
|
@@ -37,21 +39,30 @@ export function enqueueSystemEvent(text, options) {
|
|
|
37
39
|
return created;
|
|
38
40
|
})();
|
|
39
41
|
const cleaned = text.trim();
|
|
40
|
-
if (!cleaned)
|
|
42
|
+
if (!cleaned) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const normalizedContextKey = normalizeContextKey(options?.contextKey);
|
|
46
|
+
entry.lastContextKey = normalizedContextKey;
|
|
47
|
+
if (entry.lastText === cleaned) {
|
|
41
48
|
return;
|
|
42
|
-
|
|
43
|
-
if (entry.lastText === cleaned)
|
|
44
|
-
return; // skip consecutive duplicates
|
|
49
|
+
} // skip consecutive duplicates
|
|
45
50
|
entry.lastText = cleaned;
|
|
46
|
-
entry.queue.push({
|
|
47
|
-
|
|
51
|
+
entry.queue.push({
|
|
52
|
+
text: cleaned,
|
|
53
|
+
ts: Date.now(),
|
|
54
|
+
contextKey: normalizedContextKey,
|
|
55
|
+
});
|
|
56
|
+
if (entry.queue.length > MAX_EVENTS) {
|
|
48
57
|
entry.queue.shift();
|
|
58
|
+
}
|
|
49
59
|
}
|
|
50
60
|
export function drainSystemEventEntries(sessionKey) {
|
|
51
61
|
const key = requireSessionKey(sessionKey);
|
|
52
62
|
const entry = queues.get(key);
|
|
53
|
-
if (!entry || entry.queue.length === 0)
|
|
63
|
+
if (!entry || entry.queue.length === 0) {
|
|
54
64
|
return [];
|
|
65
|
+
}
|
|
55
66
|
const out = entry.queue.slice();
|
|
56
67
|
entry.queue.length = 0;
|
|
57
68
|
entry.lastText = null;
|
|
@@ -62,9 +73,12 @@ export function drainSystemEventEntries(sessionKey) {
|
|
|
62
73
|
export function drainSystemEvents(sessionKey) {
|
|
63
74
|
return drainSystemEventEntries(sessionKey).map((event) => event.text);
|
|
64
75
|
}
|
|
65
|
-
export function
|
|
76
|
+
export function peekSystemEventEntries(sessionKey) {
|
|
66
77
|
const key = requireSessionKey(sessionKey);
|
|
67
|
-
return queues.get(key)?.queue.map((
|
|
78
|
+
return queues.get(key)?.queue.map((event) => ({ ...event })) ?? [];
|
|
79
|
+
}
|
|
80
|
+
export function peekSystemEvents(sessionKey) {
|
|
81
|
+
return peekSystemEventEntries(sessionKey).map((event) => event.text);
|
|
68
82
|
}
|
|
69
83
|
export function hasSystemEvents(sessionKey) {
|
|
70
84
|
const key = requireSessionKey(sessionKey);
|
|
@@ -1,42 +1,27 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import os from "node:os";
|
|
3
|
+
import { pickPrimaryLanIPv4 } from "../gateway/net.js";
|
|
4
|
+
import { resolveRuntimeServiceVersion } from "../version.js";
|
|
3
5
|
const entries = new Map();
|
|
4
6
|
const TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
5
7
|
const MAX_ENTRIES = 200;
|
|
6
8
|
function normalizePresenceKey(key) {
|
|
7
|
-
if (!key)
|
|
9
|
+
if (!key) {
|
|
8
10
|
return undefined;
|
|
11
|
+
}
|
|
9
12
|
const trimmed = key.trim();
|
|
10
|
-
if (!trimmed)
|
|
13
|
+
if (!trimmed) {
|
|
11
14
|
return undefined;
|
|
15
|
+
}
|
|
12
16
|
return trimmed.toLowerCase();
|
|
13
17
|
}
|
|
14
18
|
function resolvePrimaryIPv4() {
|
|
15
|
-
|
|
16
|
-
const prefer = ["en0", "eth0"];
|
|
17
|
-
const pick = (names) => {
|
|
18
|
-
for (const name of names) {
|
|
19
|
-
const list = nets[name];
|
|
20
|
-
const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
|
|
21
|
-
if (entry?.address)
|
|
22
|
-
return entry.address;
|
|
23
|
-
}
|
|
24
|
-
for (const list of Object.values(nets)) {
|
|
25
|
-
const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
|
|
26
|
-
if (entry?.address)
|
|
27
|
-
return entry.address;
|
|
28
|
-
}
|
|
29
|
-
return undefined;
|
|
30
|
-
};
|
|
31
|
-
return pick(prefer) ?? os.hostname();
|
|
19
|
+
return pickPrimaryLanIPv4() ?? os.hostname();
|
|
32
20
|
}
|
|
33
21
|
function initSelfPresence() {
|
|
34
22
|
const host = os.hostname();
|
|
35
23
|
const ip = resolvePrimaryIPv4() ?? undefined;
|
|
36
|
-
const version = process.env
|
|
37
|
-
process.env.CLAWDBOT_VERSION ??
|
|
38
|
-
process.env.npm_package_version ??
|
|
39
|
-
"unknown";
|
|
24
|
+
const version = resolveRuntimeServiceVersion(process.env, "unknown");
|
|
40
25
|
const modelIdentifier = (() => {
|
|
41
26
|
const p = os.platform();
|
|
42
27
|
if (p === "darwin") {
|
|
@@ -58,20 +43,25 @@ function initSelfPresence() {
|
|
|
58
43
|
const platform = (() => {
|
|
59
44
|
const p = os.platform();
|
|
60
45
|
const rel = os.release();
|
|
61
|
-
if (p === "darwin")
|
|
46
|
+
if (p === "darwin") {
|
|
62
47
|
return `macos ${macOSVersion()}`;
|
|
63
|
-
|
|
48
|
+
}
|
|
49
|
+
if (p === "win32") {
|
|
64
50
|
return `windows ${rel}`;
|
|
51
|
+
}
|
|
65
52
|
return `${p} ${rel}`;
|
|
66
53
|
})();
|
|
67
54
|
const deviceFamily = (() => {
|
|
68
55
|
const p = os.platform();
|
|
69
|
-
if (p === "darwin")
|
|
56
|
+
if (p === "darwin") {
|
|
70
57
|
return "Mac";
|
|
71
|
-
|
|
58
|
+
}
|
|
59
|
+
if (p === "win32") {
|
|
72
60
|
return "Windows";
|
|
73
|
-
|
|
61
|
+
}
|
|
62
|
+
if (p === "linux") {
|
|
74
63
|
return "Linux";
|
|
64
|
+
}
|
|
75
65
|
return p;
|
|
76
66
|
})();
|
|
77
67
|
const text = `Gateway: ${host}${ip ? ` (${ip})` : ""} · app ${version} · mode gateway · reason self`;
|
|
@@ -134,12 +124,14 @@ function parsePresence(text) {
|
|
|
134
124
|
function mergeStringList(...values) {
|
|
135
125
|
const out = new Set();
|
|
136
126
|
for (const list of values) {
|
|
137
|
-
if (!Array.isArray(list))
|
|
127
|
+
if (!Array.isArray(list)) {
|
|
138
128
|
continue;
|
|
129
|
+
}
|
|
139
130
|
for (const item of list) {
|
|
140
131
|
const trimmed = String(item).trim();
|
|
141
|
-
if (trimmed)
|
|
132
|
+
if (trimmed) {
|
|
142
133
|
out.add(trimmed);
|
|
134
|
+
}
|
|
143
135
|
}
|
|
144
136
|
}
|
|
145
137
|
return out.size > 0 ? [...out] : undefined;
|
|
@@ -218,17 +210,18 @@ export function listSystemPresence() {
|
|
|
218
210
|
// prune expired
|
|
219
211
|
const now = Date.now();
|
|
220
212
|
for (const [k, v] of entries) {
|
|
221
|
-
if (now - v.ts > TTL_MS)
|
|
213
|
+
if (now - v.ts > TTL_MS) {
|
|
222
214
|
entries.delete(k);
|
|
215
|
+
}
|
|
223
216
|
}
|
|
224
217
|
// enforce max size (LRU by ts)
|
|
225
218
|
if (entries.size > MAX_ENTRIES) {
|
|
226
|
-
const sorted = [...entries.entries()].
|
|
219
|
+
const sorted = [...entries.entries()].toSorted((a, b) => a[1].ts - b[1].ts);
|
|
227
220
|
const toDrop = entries.size - MAX_ENTRIES;
|
|
228
221
|
for (let i = 0; i < toDrop; i++) {
|
|
229
222
|
entries.delete(sorted[i][0]);
|
|
230
223
|
}
|
|
231
224
|
}
|
|
232
225
|
touchSelfPresence();
|
|
233
|
-
return [...entries.values()].
|
|
226
|
+
return [...entries.values()].toSorted((a, b) => b.ts - a.ts);
|
|
234
227
|
}
|
|
@@ -1,2 +1,81 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export const POSIX_POOLBOT_TMP_DIR = "/tmp/poolbot";
|
|
5
|
+
function isNodeErrorWithCode(err, code) {
|
|
6
|
+
return (typeof err === "object" &&
|
|
7
|
+
err !== null &&
|
|
8
|
+
"code" in err &&
|
|
9
|
+
err.code === code);
|
|
10
|
+
}
|
|
11
|
+
export function resolvePreferredPoolbotTmpDir(options = {}) {
|
|
12
|
+
const accessSync = options.accessSync ?? fs.accessSync;
|
|
13
|
+
const lstatSync = options.lstatSync ?? fs.lstatSync;
|
|
14
|
+
const mkdirSync = options.mkdirSync ?? fs.mkdirSync;
|
|
15
|
+
const getuid = options.getuid ??
|
|
16
|
+
(() => {
|
|
17
|
+
try {
|
|
18
|
+
return typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const tmpdir = options.tmpdir ?? os.tmpdir;
|
|
25
|
+
const uid = getuid();
|
|
26
|
+
const isSecureDirForUser = (st) => {
|
|
27
|
+
if (uid === undefined) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (typeof st.uid === "number" && st.uid !== uid) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
// Avoid group/other writable dirs when running on multi-user hosts.
|
|
34
|
+
if (typeof st.mode === "number" && (st.mode & 0o022) !== 0) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
};
|
|
39
|
+
const fallback = () => {
|
|
40
|
+
const base = tmpdir();
|
|
41
|
+
const suffix = uid === undefined ? "poolbot" : `poolbot-${uid}`;
|
|
42
|
+
return path.join(base, suffix);
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
const preferred = lstatSync(POSIX_POOLBOT_TMP_DIR);
|
|
46
|
+
if (!preferred.isDirectory() || preferred.isSymbolicLink()) {
|
|
47
|
+
return fallback();
|
|
48
|
+
}
|
|
49
|
+
accessSync(POSIX_POOLBOT_TMP_DIR, fs.constants.W_OK | fs.constants.X_OK);
|
|
50
|
+
if (!isSecureDirForUser(preferred)) {
|
|
51
|
+
return fallback();
|
|
52
|
+
}
|
|
53
|
+
return POSIX_POOLBOT_TMP_DIR;
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
if (!isNodeErrorWithCode(err, "ENOENT")) {
|
|
57
|
+
return fallback();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
accessSync("/tmp", fs.constants.W_OK | fs.constants.X_OK);
|
|
62
|
+
// Create with a safe default; subsequent callers expect it exists.
|
|
63
|
+
mkdirSync(POSIX_POOLBOT_TMP_DIR, { recursive: true, mode: 0o700 });
|
|
64
|
+
try {
|
|
65
|
+
const preferred = lstatSync(POSIX_POOLBOT_TMP_DIR);
|
|
66
|
+
if (!preferred.isDirectory() || preferred.isSymbolicLink()) {
|
|
67
|
+
return fallback();
|
|
68
|
+
}
|
|
69
|
+
if (!isSecureDirForUser(preferred)) {
|
|
70
|
+
return fallback();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return fallback();
|
|
75
|
+
}
|
|
76
|
+
return POSIX_POOLBOT_TMP_DIR;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return fallback();
|
|
80
|
+
}
|
|
81
|
+
}
|
package/dist/infra/wsl.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
let wslCached = null;
|
|
3
4
|
export function isWSLEnv() {
|
|
@@ -6,9 +7,44 @@ export function isWSLEnv() {
|
|
|
6
7
|
}
|
|
7
8
|
return false;
|
|
8
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Synchronously check if running in WSL.
|
|
12
|
+
* Checks env vars first, then /proc/version.
|
|
13
|
+
*/
|
|
14
|
+
export function isWSLSync() {
|
|
15
|
+
if (process.platform !== "linux") {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (isWSLEnv()) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const release = readFileSync("/proc/version", "utf8").toLowerCase();
|
|
23
|
+
return release.includes("microsoft") || release.includes("wsl");
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Synchronously check if running in WSL2.
|
|
31
|
+
*/
|
|
32
|
+
export function isWSL2Sync() {
|
|
33
|
+
if (!isWSLSync()) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const version = readFileSync("/proc/version", "utf8").toLowerCase();
|
|
38
|
+
return version.includes("wsl2") || version.includes("microsoft-standard");
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
9
44
|
export async function isWSL() {
|
|
10
|
-
if (wslCached !== null)
|
|
45
|
+
if (wslCached !== null) {
|
|
11
46
|
return wslCached;
|
|
47
|
+
}
|
|
12
48
|
if (isWSLEnv()) {
|
|
13
49
|
wslCached = true;
|
|
14
50
|
return wslCached;
|