@poolzin/pool-bot 2026.2.23 → 2026.2.25
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 +29 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-scope.js +10 -0
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/models-config.providers.js +299 -182
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.js +15 -18
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/browser/config.js +26 -0
- package/dist/browser/navigation-guard.js +31 -0
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +25 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +2 -1
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +21 -0
- package/dist/cli/plugins-cli.js +245 -61
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +223 -136
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/route.js +1 -1
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/commands/agent.js +163 -87
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/onboard-helpers.js +4 -4
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/compat/legacy-names.js +2 -2
- package/dist/config/commands.js +3 -0
- package/dist/config/config.js +1 -1
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +9 -0
- package/dist/config/io.js +571 -171
- package/dist/config/merge-patch.js +50 -4
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.js +58 -570
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +8 -7
- package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
- package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
- package/dist/control-ui/index.html +1 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/gateway/auth.js +38 -3
- package/dist/gateway/call.js +149 -68
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +23 -0
- package/dist/gateway/openresponses-http.js +120 -110
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +3 -2
- package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server-http.js +236 -52
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +461 -130
- package/dist/gateway/server-methods/config.js +193 -150
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +5 -0
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/hooks/bundled/session-memory/handler.js +165 -34
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/device-pairing.js +148 -167
- package/dist/infra/exec-approvals-allowlist.js +19 -70
- package/dist/infra/exec-approvals-analysis.js +44 -17
- package/dist/infra/exec-safe-bin-policy.js +269 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/update-startup.js +86 -9
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/memory/test-manager.js +8 -0
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/security/audit-channel.js +8 -19
- package/dist/security/audit-extra.async.js +354 -182
- package/dist/security/audit-extra.js +11 -1
- package/dist/security/audit-extra.sync.js +340 -33
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +145 -371
- package/dist/security/dm-policy-shared.js +24 -0
- package/dist/security/external-content.js +20 -8
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/operator-scope-compat.js +37 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/telegram/bot-message-dispatch.js +366 -164
- package/dist/telegram/draft-stream.js +30 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tui/components/chat-log.js +9 -0
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui.js +181 -45
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/media.js +4 -0
- package/docs/tools/slash-commands.md +5 -1
- 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/feishu/src/external-keys.ts +19 -0
- 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/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- 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/dist/infra/restart.js
CHANGED
|
@@ -1,15 +1,109 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { resolveGatewayLaunchAgentLabel, resolveGatewaySystemdServiceName, } from "../daemon/constants.js";
|
|
3
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
3
4
|
const SPAWN_TIMEOUT_MS = 2000;
|
|
4
5
|
const SIGUSR1_AUTH_GRACE_MS = 5000;
|
|
6
|
+
const DEFAULT_DEFERRAL_POLL_MS = 500;
|
|
7
|
+
const DEFAULT_DEFERRAL_MAX_WAIT_MS = 30_000;
|
|
8
|
+
const RESTART_COOLDOWN_MS = 30_000;
|
|
9
|
+
const restartLog = createSubsystemLogger("restart");
|
|
5
10
|
let sigusr1AuthorizedCount = 0;
|
|
6
11
|
let sigusr1AuthorizedUntil = 0;
|
|
7
12
|
let sigusr1ExternalAllowed = false;
|
|
13
|
+
let preRestartCheck = null;
|
|
14
|
+
let restartCycleToken = 0;
|
|
15
|
+
let emittedRestartToken = 0;
|
|
16
|
+
let consumedRestartToken = 0;
|
|
17
|
+
let lastRestartEmittedAt = 0;
|
|
18
|
+
let pendingRestartTimer = null;
|
|
19
|
+
let pendingRestartDueAt = 0;
|
|
20
|
+
let pendingRestartReason;
|
|
21
|
+
function hasUnconsumedRestartSignal() {
|
|
22
|
+
return emittedRestartToken > consumedRestartToken;
|
|
23
|
+
}
|
|
24
|
+
function clearPendingScheduledRestart() {
|
|
25
|
+
if (pendingRestartTimer) {
|
|
26
|
+
clearTimeout(pendingRestartTimer);
|
|
27
|
+
}
|
|
28
|
+
pendingRestartTimer = null;
|
|
29
|
+
pendingRestartDueAt = 0;
|
|
30
|
+
pendingRestartReason = undefined;
|
|
31
|
+
}
|
|
32
|
+
function summarizeChangedPaths(paths, maxPaths = 6) {
|
|
33
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (paths.length <= maxPaths) {
|
|
37
|
+
return paths.join(",");
|
|
38
|
+
}
|
|
39
|
+
const head = paths.slice(0, maxPaths).join(",");
|
|
40
|
+
return `${head},+${paths.length - maxPaths} more`;
|
|
41
|
+
}
|
|
42
|
+
function formatRestartAudit(audit) {
|
|
43
|
+
const actor = typeof audit?.actor === "string" && audit.actor.trim() ? audit.actor.trim() : null;
|
|
44
|
+
const deviceId = typeof audit?.deviceId === "string" && audit.deviceId.trim() ? audit.deviceId.trim() : null;
|
|
45
|
+
const clientIp = typeof audit?.clientIp === "string" && audit.clientIp.trim() ? audit.clientIp.trim() : null;
|
|
46
|
+
const changed = summarizeChangedPaths(audit?.changedPaths);
|
|
47
|
+
const fields = [];
|
|
48
|
+
if (actor) {
|
|
49
|
+
fields.push(`actor=${actor}`);
|
|
50
|
+
}
|
|
51
|
+
if (deviceId) {
|
|
52
|
+
fields.push(`device=${deviceId}`);
|
|
53
|
+
}
|
|
54
|
+
if (clientIp) {
|
|
55
|
+
fields.push(`ip=${clientIp}`);
|
|
56
|
+
}
|
|
57
|
+
if (changed) {
|
|
58
|
+
fields.push(`changedPaths=${changed}`);
|
|
59
|
+
}
|
|
60
|
+
return fields.length > 0 ? fields.join(" ") : "actor=<unknown>";
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Register a callback that scheduleGatewaySigusr1Restart checks before emitting SIGUSR1.
|
|
64
|
+
* The callback should return the number of pending items (0 = safe to restart).
|
|
65
|
+
*/
|
|
66
|
+
export function setPreRestartDeferralCheck(fn) {
|
|
67
|
+
preRestartCheck = fn;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Emit an authorized SIGUSR1 gateway restart, guarded against duplicate emissions.
|
|
71
|
+
* Returns true if SIGUSR1 was emitted, false if a restart was already emitted.
|
|
72
|
+
* Both scheduleGatewaySigusr1Restart and the config watcher should use this
|
|
73
|
+
* to ensure only one restart fires.
|
|
74
|
+
*/
|
|
75
|
+
export function emitGatewayRestart() {
|
|
76
|
+
if (hasUnconsumedRestartSignal()) {
|
|
77
|
+
clearPendingScheduledRestart();
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
clearPendingScheduledRestart();
|
|
81
|
+
const cycleToken = ++restartCycleToken;
|
|
82
|
+
emittedRestartToken = cycleToken;
|
|
83
|
+
authorizeGatewaySigusr1Restart();
|
|
84
|
+
try {
|
|
85
|
+
if (process.listenerCount("SIGUSR1") > 0) {
|
|
86
|
+
process.emit("SIGUSR1");
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
process.kill(process.pid, "SIGUSR1");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Roll back the cycle marker so future restart requests can still proceed.
|
|
94
|
+
emittedRestartToken = consumedRestartToken;
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
lastRestartEmittedAt = Date.now();
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
8
100
|
function resetSigusr1AuthorizationIfExpired(now = Date.now()) {
|
|
9
|
-
if (sigusr1AuthorizedCount <= 0)
|
|
101
|
+
if (sigusr1AuthorizedCount <= 0) {
|
|
10
102
|
return;
|
|
11
|
-
|
|
103
|
+
}
|
|
104
|
+
if (now <= sigusr1AuthorizedUntil) {
|
|
12
105
|
return;
|
|
106
|
+
}
|
|
13
107
|
sigusr1AuthorizedCount = 0;
|
|
14
108
|
sigusr1AuthorizedUntil = 0;
|
|
15
109
|
}
|
|
@@ -19,7 +113,7 @@ export function setGatewaySigusr1RestartPolicy(opts) {
|
|
|
19
113
|
export function isGatewaySigusr1RestartExternallyAllowed() {
|
|
20
114
|
return sigusr1ExternalAllowed;
|
|
21
115
|
}
|
|
22
|
-
|
|
116
|
+
function authorizeGatewaySigusr1Restart(delayMs = 0) {
|
|
23
117
|
const delay = Math.max(0, Math.floor(delayMs));
|
|
24
118
|
const expiresAt = Date.now() + delay + SIGUSR1_AUTH_GRACE_MS;
|
|
25
119
|
sigusr1AuthorizedCount += 1;
|
|
@@ -29,24 +123,87 @@ export function authorizeGatewaySigusr1Restart(delayMs = 0) {
|
|
|
29
123
|
}
|
|
30
124
|
export function consumeGatewaySigusr1RestartAuthorization() {
|
|
31
125
|
resetSigusr1AuthorizationIfExpired();
|
|
32
|
-
if (sigusr1AuthorizedCount <= 0)
|
|
126
|
+
if (sigusr1AuthorizedCount <= 0) {
|
|
33
127
|
return false;
|
|
128
|
+
}
|
|
34
129
|
sigusr1AuthorizedCount -= 1;
|
|
35
130
|
if (sigusr1AuthorizedCount <= 0) {
|
|
36
131
|
sigusr1AuthorizedUntil = 0;
|
|
37
132
|
}
|
|
38
133
|
return true;
|
|
39
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Mark the currently emitted SIGUSR1 restart cycle as consumed by the run loop.
|
|
137
|
+
* This explicitly advances the cycle state instead of resetting emit guards inside
|
|
138
|
+
* consumeGatewaySigusr1RestartAuthorization().
|
|
139
|
+
*/
|
|
140
|
+
export function markGatewaySigusr1RestartHandled() {
|
|
141
|
+
if (hasUnconsumedRestartSignal()) {
|
|
142
|
+
consumedRestartToken = emittedRestartToken;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Poll pending work until it drains (or times out), then emit one restart signal.
|
|
147
|
+
* Shared by both the direct RPC restart path and the config watcher path.
|
|
148
|
+
*/
|
|
149
|
+
export function deferGatewayRestartUntilIdle(opts) {
|
|
150
|
+
const pollMsRaw = opts.pollMs ?? DEFAULT_DEFERRAL_POLL_MS;
|
|
151
|
+
const pollMs = Math.max(10, Math.floor(pollMsRaw));
|
|
152
|
+
const maxWaitMsRaw = opts.maxWaitMs ?? DEFAULT_DEFERRAL_MAX_WAIT_MS;
|
|
153
|
+
const maxWaitMs = Math.max(pollMs, Math.floor(maxWaitMsRaw));
|
|
154
|
+
let pending;
|
|
155
|
+
try {
|
|
156
|
+
pending = opts.getPendingCount();
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
opts.hooks?.onCheckError?.(err);
|
|
160
|
+
emitGatewayRestart();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (pending <= 0) {
|
|
164
|
+
opts.hooks?.onReady?.();
|
|
165
|
+
emitGatewayRestart();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
opts.hooks?.onDeferring?.(pending);
|
|
169
|
+
const startedAt = Date.now();
|
|
170
|
+
const poll = setInterval(() => {
|
|
171
|
+
let current;
|
|
172
|
+
try {
|
|
173
|
+
current = opts.getPendingCount();
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
clearInterval(poll);
|
|
177
|
+
opts.hooks?.onCheckError?.(err);
|
|
178
|
+
emitGatewayRestart();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (current <= 0) {
|
|
182
|
+
clearInterval(poll);
|
|
183
|
+
opts.hooks?.onReady?.();
|
|
184
|
+
emitGatewayRestart();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const elapsedMs = Date.now() - startedAt;
|
|
188
|
+
if (elapsedMs >= maxWaitMs) {
|
|
189
|
+
clearInterval(poll);
|
|
190
|
+
opts.hooks?.onTimeout?.(current, elapsedMs);
|
|
191
|
+
emitGatewayRestart();
|
|
192
|
+
}
|
|
193
|
+
}, pollMs);
|
|
194
|
+
}
|
|
40
195
|
function formatSpawnDetail(result) {
|
|
41
196
|
const clean = (value) => {
|
|
42
197
|
const text = typeof value === "string" ? value : value ? value.toString() : "";
|
|
43
198
|
return text.replace(/\s+/g, " ").trim();
|
|
44
199
|
};
|
|
45
200
|
if (result.error) {
|
|
46
|
-
if (result.error instanceof Error)
|
|
201
|
+
if (result.error instanceof Error) {
|
|
47
202
|
return result.error.message;
|
|
48
|
-
|
|
203
|
+
}
|
|
204
|
+
if (typeof result.error === "string") {
|
|
49
205
|
return result.error;
|
|
206
|
+
}
|
|
50
207
|
try {
|
|
51
208
|
return JSON.stringify(result.error);
|
|
52
209
|
}
|
|
@@ -55,13 +212,16 @@ function formatSpawnDetail(result) {
|
|
|
55
212
|
}
|
|
56
213
|
}
|
|
57
214
|
const stderr = clean(result.stderr);
|
|
58
|
-
if (stderr)
|
|
215
|
+
if (stderr) {
|
|
59
216
|
return stderr;
|
|
217
|
+
}
|
|
60
218
|
const stdout = clean(result.stdout);
|
|
61
|
-
if (stdout)
|
|
219
|
+
if (stdout) {
|
|
62
220
|
return stdout;
|
|
63
|
-
|
|
221
|
+
}
|
|
222
|
+
if (typeof result.status === "number") {
|
|
64
223
|
return `exit ${result.status}`;
|
|
224
|
+
}
|
|
65
225
|
return "unknown error";
|
|
66
226
|
}
|
|
67
227
|
function normalizeSystemdUnit(raw, profile) {
|
|
@@ -138,29 +298,66 @@ export function scheduleGatewaySigusr1Restart(opts) {
|
|
|
138
298
|
const reason = typeof opts?.reason === "string" && opts.reason.trim()
|
|
139
299
|
? opts.reason.trim().slice(0, 200)
|
|
140
300
|
: undefined;
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
301
|
+
const mode = process.listenerCount("SIGUSR1") > 0 ? "emit" : "signal";
|
|
302
|
+
const nowMs = Date.now();
|
|
303
|
+
const cooldownMsApplied = Math.max(0, lastRestartEmittedAt + RESTART_COOLDOWN_MS - nowMs);
|
|
304
|
+
const requestedDueAt = nowMs + delayMs + cooldownMsApplied;
|
|
305
|
+
if (hasUnconsumedRestartSignal()) {
|
|
306
|
+
restartLog.warn(`restart request coalesced (already in-flight) reason=${reason ?? "unspecified"} ${formatRestartAudit(opts?.audit)}`);
|
|
307
|
+
return {
|
|
308
|
+
ok: true,
|
|
309
|
+
pid: process.pid,
|
|
310
|
+
signal: "SIGUSR1",
|
|
311
|
+
delayMs: 0,
|
|
312
|
+
reason,
|
|
313
|
+
mode,
|
|
314
|
+
coalesced: true,
|
|
315
|
+
cooldownMsApplied,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
if (pendingRestartTimer) {
|
|
319
|
+
const remainingMs = Math.max(0, pendingRestartDueAt - nowMs);
|
|
320
|
+
const shouldPullEarlier = requestedDueAt < pendingRestartDueAt;
|
|
321
|
+
if (shouldPullEarlier) {
|
|
322
|
+
restartLog.warn(`restart request rescheduled earlier reason=${reason ?? "unspecified"} pendingReason=${pendingRestartReason ?? "unspecified"} oldDelayMs=${remainingMs} newDelayMs=${Math.max(0, requestedDueAt - nowMs)} ${formatRestartAudit(opts?.audit)}`);
|
|
323
|
+
clearPendingScheduledRestart();
|
|
152
324
|
}
|
|
153
|
-
|
|
154
|
-
|
|
325
|
+
else {
|
|
326
|
+
restartLog.warn(`restart request coalesced (already scheduled) reason=${reason ?? "unspecified"} pendingReason=${pendingRestartReason ?? "unspecified"} delayMs=${remainingMs} ${formatRestartAudit(opts?.audit)}`);
|
|
327
|
+
return {
|
|
328
|
+
ok: true,
|
|
329
|
+
pid: process.pid,
|
|
330
|
+
signal: "SIGUSR1",
|
|
331
|
+
delayMs: remainingMs,
|
|
332
|
+
reason,
|
|
333
|
+
mode,
|
|
334
|
+
coalesced: true,
|
|
335
|
+
cooldownMsApplied,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
pendingRestartDueAt = requestedDueAt;
|
|
340
|
+
pendingRestartReason = reason;
|
|
341
|
+
pendingRestartTimer = setTimeout(() => {
|
|
342
|
+
pendingRestartTimer = null;
|
|
343
|
+
pendingRestartDueAt = 0;
|
|
344
|
+
pendingRestartReason = undefined;
|
|
345
|
+
const pendingCheck = preRestartCheck;
|
|
346
|
+
if (!pendingCheck) {
|
|
347
|
+
emitGatewayRestart();
|
|
348
|
+
return;
|
|
155
349
|
}
|
|
156
|
-
|
|
350
|
+
deferGatewayRestartUntilIdle({ getPendingCount: pendingCheck });
|
|
351
|
+
}, Math.max(0, requestedDueAt - nowMs));
|
|
157
352
|
return {
|
|
158
353
|
ok: true,
|
|
159
|
-
pid,
|
|
354
|
+
pid: process.pid,
|
|
160
355
|
signal: "SIGUSR1",
|
|
161
|
-
delayMs,
|
|
356
|
+
delayMs: Math.max(0, requestedDueAt - nowMs),
|
|
162
357
|
reason,
|
|
163
|
-
mode
|
|
358
|
+
mode,
|
|
359
|
+
coalesced: false,
|
|
360
|
+
cooldownMsApplied,
|
|
164
361
|
};
|
|
165
362
|
}
|
|
166
363
|
export const __testing = {
|
|
@@ -168,5 +365,11 @@ export const __testing = {
|
|
|
168
365
|
sigusr1AuthorizedCount = 0;
|
|
169
366
|
sigusr1AuthorizedUntil = 0;
|
|
170
367
|
sigusr1ExternalAllowed = false;
|
|
368
|
+
preRestartCheck = null;
|
|
369
|
+
restartCycleToken = 0;
|
|
370
|
+
emittedRestartToken = 0;
|
|
371
|
+
consumedRestartToken = 0;
|
|
372
|
+
lastRestartEmittedAt = 0;
|
|
373
|
+
clearPendingScheduledRestart();
|
|
171
374
|
},
|
|
172
375
|
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const SSH_TOKEN = /^[A-Za-z0-9._-]+$/;
|
|
2
|
+
const BRACKETED_IPV6 = /^\[[0-9A-Fa-f:.%]+\]$/;
|
|
3
|
+
const WHITESPACE = /\s/;
|
|
4
|
+
function hasControlOrWhitespace(value) {
|
|
5
|
+
for (const char of value) {
|
|
6
|
+
const code = char.charCodeAt(0);
|
|
7
|
+
if (code <= 0x1f || code === 0x7f || WHITESPACE.test(char)) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
export function normalizeScpRemoteHost(value) {
|
|
14
|
+
if (typeof value !== "string") {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
if (!trimmed) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
if (hasControlOrWhitespace(trimmed)) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
if (trimmed.startsWith("-") || trimmed.includes("/") || trimmed.includes("\\")) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const firstAt = trimmed.indexOf("@");
|
|
28
|
+
const lastAt = trimmed.lastIndexOf("@");
|
|
29
|
+
let user;
|
|
30
|
+
let host = trimmed;
|
|
31
|
+
if (firstAt !== -1) {
|
|
32
|
+
if (firstAt !== lastAt || firstAt === 0 || firstAt === trimmed.length - 1) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
user = trimmed.slice(0, firstAt);
|
|
36
|
+
host = trimmed.slice(firstAt + 1);
|
|
37
|
+
if (!SSH_TOKEN.test(user)) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!host || host.startsWith("-") || host.includes("@")) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
if (host.includes(":") && !BRACKETED_IPV6.test(host)) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
if (!SSH_TOKEN.test(host) && !BRACKETED_IPV6.test(host)) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return user ? `${user}@${host}` : host;
|
|
51
|
+
}
|
|
52
|
+
export function isSafeScpRemoteHost(value) {
|
|
53
|
+
return normalizeScpRemoteHost(value) !== undefined;
|
|
54
|
+
}
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { formatCliCommand } from "../cli/command-format.js";
|
|
3
4
|
import { resolveStateDir } from "../config/paths.js";
|
|
5
|
+
import { VERSION } from "../version.js";
|
|
4
6
|
import { resolvePoolBotPackageRoot } from "./poolbot-root.js";
|
|
5
|
-
import { compareSemverStrings, resolveNpmChannelTag, checkUpdateStatus } from "./update-check.js";
|
|
6
7
|
import { normalizeUpdateChannel, DEFAULT_PACKAGE_CHANNEL } from "./update-channels.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
8
|
+
import { compareSemverStrings, resolveNpmChannelTag, checkUpdateStatus } from "./update-check.js";
|
|
9
|
+
let updateAvailableCache = null;
|
|
10
|
+
export function getUpdateAvailable() {
|
|
11
|
+
return updateAvailableCache;
|
|
12
|
+
}
|
|
13
|
+
export function resetUpdateAvailableStateForTest() {
|
|
14
|
+
updateAvailableCache = null;
|
|
15
|
+
}
|
|
9
16
|
const UPDATE_CHECK_FILENAME = "update-check.json";
|
|
10
17
|
const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
11
18
|
function shouldSkipCheck(allowInTests) {
|
|
12
|
-
if (allowInTests)
|
|
19
|
+
if (allowInTests) {
|
|
13
20
|
return false;
|
|
14
|
-
|
|
21
|
+
}
|
|
22
|
+
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
15
23
|
return true;
|
|
24
|
+
}
|
|
16
25
|
return false;
|
|
17
26
|
}
|
|
18
27
|
async function readState(statePath) {
|
|
@@ -29,20 +38,63 @@ async function writeState(statePath, state) {
|
|
|
29
38
|
await fs.mkdir(path.dirname(statePath), { recursive: true });
|
|
30
39
|
await fs.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
31
40
|
}
|
|
41
|
+
function sameUpdateAvailable(a, b) {
|
|
42
|
+
if (a === b) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (!a || !b) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return (a.currentVersion === b.currentVersion &&
|
|
49
|
+
a.latestVersion === b.latestVersion &&
|
|
50
|
+
a.channel === b.channel);
|
|
51
|
+
}
|
|
52
|
+
function setUpdateAvailableCache(params) {
|
|
53
|
+
if (sameUpdateAvailable(updateAvailableCache, params.next)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
updateAvailableCache = params.next;
|
|
57
|
+
params.onUpdateAvailableChange?.(params.next);
|
|
58
|
+
}
|
|
59
|
+
function resolvePersistedUpdateAvailable(state) {
|
|
60
|
+
const latestVersion = state.lastAvailableVersion?.trim();
|
|
61
|
+
if (!latestVersion) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const cmp = compareSemverStrings(VERSION, latestVersion);
|
|
65
|
+
if (cmp == null || cmp >= 0) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const channel = state.lastAvailableTag?.trim() || DEFAULT_PACKAGE_CHANNEL;
|
|
69
|
+
return {
|
|
70
|
+
currentVersion: VERSION,
|
|
71
|
+
latestVersion,
|
|
72
|
+
channel,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
32
75
|
export async function runGatewayUpdateCheck(params) {
|
|
33
|
-
if (shouldSkipCheck(Boolean(params.allowInTests)))
|
|
76
|
+
if (shouldSkipCheck(Boolean(params.allowInTests))) {
|
|
34
77
|
return;
|
|
35
|
-
|
|
78
|
+
}
|
|
79
|
+
if (params.isNixMode) {
|
|
36
80
|
return;
|
|
37
|
-
|
|
81
|
+
}
|
|
82
|
+
if (params.cfg.update?.checkOnStart === false) {
|
|
38
83
|
return;
|
|
84
|
+
}
|
|
39
85
|
const statePath = path.join(resolveStateDir(), UPDATE_CHECK_FILENAME);
|
|
40
86
|
const state = await readState(statePath);
|
|
41
87
|
const now = Date.now();
|
|
42
88
|
const lastCheckedAt = state.lastCheckedAt ? Date.parse(state.lastCheckedAt) : null;
|
|
89
|
+
const persistedAvailable = resolvePersistedUpdateAvailable(state);
|
|
90
|
+
setUpdateAvailableCache({
|
|
91
|
+
next: persistedAvailable,
|
|
92
|
+
onUpdateAvailableChange: params.onUpdateAvailableChange,
|
|
93
|
+
});
|
|
43
94
|
if (lastCheckedAt && Number.isFinite(lastCheckedAt)) {
|
|
44
|
-
if (now - lastCheckedAt < UPDATE_CHECK_INTERVAL_MS)
|
|
95
|
+
if (now - lastCheckedAt < UPDATE_CHECK_INTERVAL_MS) {
|
|
45
96
|
return;
|
|
97
|
+
}
|
|
46
98
|
}
|
|
47
99
|
const root = await resolvePoolBotPackageRoot({
|
|
48
100
|
moduleUrl: import.meta.url,
|
|
@@ -60,6 +112,12 @@ export async function runGatewayUpdateCheck(params) {
|
|
|
60
112
|
lastCheckedAt: new Date(now).toISOString(),
|
|
61
113
|
};
|
|
62
114
|
if (status.installKind !== "package") {
|
|
115
|
+
delete nextState.lastAvailableVersion;
|
|
116
|
+
delete nextState.lastAvailableTag;
|
|
117
|
+
setUpdateAvailableCache({
|
|
118
|
+
next: null,
|
|
119
|
+
onUpdateAvailableChange: params.onUpdateAvailableChange,
|
|
120
|
+
});
|
|
63
121
|
await writeState(statePath, nextState);
|
|
64
122
|
return;
|
|
65
123
|
}
|
|
@@ -72,6 +130,17 @@ export async function runGatewayUpdateCheck(params) {
|
|
|
72
130
|
}
|
|
73
131
|
const cmp = compareSemverStrings(VERSION, resolved.version);
|
|
74
132
|
if (cmp != null && cmp < 0) {
|
|
133
|
+
const nextAvailable = {
|
|
134
|
+
currentVersion: VERSION,
|
|
135
|
+
latestVersion: resolved.version,
|
|
136
|
+
channel: tag,
|
|
137
|
+
};
|
|
138
|
+
setUpdateAvailableCache({
|
|
139
|
+
next: nextAvailable,
|
|
140
|
+
onUpdateAvailableChange: params.onUpdateAvailableChange,
|
|
141
|
+
});
|
|
142
|
+
nextState.lastAvailableVersion = resolved.version;
|
|
143
|
+
nextState.lastAvailableTag = tag;
|
|
75
144
|
const shouldNotify = state.lastNotifiedVersion !== resolved.version || state.lastNotifiedTag !== tag;
|
|
76
145
|
if (shouldNotify) {
|
|
77
146
|
params.log.info(`update available (${tag}): v${resolved.version} (current v${VERSION}). Run: ${formatCliCommand("poolbot update")}`);
|
|
@@ -79,6 +148,14 @@ export async function runGatewayUpdateCheck(params) {
|
|
|
79
148
|
nextState.lastNotifiedTag = tag;
|
|
80
149
|
}
|
|
81
150
|
}
|
|
151
|
+
else {
|
|
152
|
+
delete nextState.lastAvailableVersion;
|
|
153
|
+
delete nextState.lastAvailableTag;
|
|
154
|
+
setUpdateAvailableCache({
|
|
155
|
+
next: null,
|
|
156
|
+
onUpdateAvailableChange: params.onUpdateAvailableChange,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
82
159
|
await writeState(statePath, nextState);
|
|
83
160
|
}
|
|
84
161
|
export function scheduleGatewayUpdateCheck(params) {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
const WILDCARD_SEGMENT = "*";
|
|
3
|
+
const WINDOWS_DRIVE_ABS_RE = /^[A-Za-z]:\//;
|
|
4
|
+
const WINDOWS_DRIVE_ROOT_RE = /^[A-Za-z]:$/;
|
|
5
|
+
export const DEFAULT_IMESSAGE_ATTACHMENT_ROOTS = ["/Users/*/Library/Messages/Attachments"];
|
|
6
|
+
function normalizePosixAbsolutePath(value) {
|
|
7
|
+
const trimmed = value.trim();
|
|
8
|
+
if (!trimmed || trimmed.includes("\0")) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
const normalized = path.posix.normalize(trimmed.replaceAll("\\", "/"));
|
|
12
|
+
const isAbsolute = normalized.startsWith("/") || WINDOWS_DRIVE_ABS_RE.test(normalized);
|
|
13
|
+
if (!isAbsolute || normalized === "/") {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const withoutTrailingSlash = normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
|
|
17
|
+
if (WINDOWS_DRIVE_ROOT_RE.test(withoutTrailingSlash)) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
return withoutTrailingSlash;
|
|
21
|
+
}
|
|
22
|
+
function splitPathSegments(value) {
|
|
23
|
+
return value.split("/").filter(Boolean);
|
|
24
|
+
}
|
|
25
|
+
function matchesRootPattern(params) {
|
|
26
|
+
const candidateSegments = splitPathSegments(params.candidatePath);
|
|
27
|
+
const rootSegments = splitPathSegments(params.rootPattern);
|
|
28
|
+
if (candidateSegments.length < rootSegments.length) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
for (let idx = 0; idx < rootSegments.length; idx += 1) {
|
|
32
|
+
const expected = rootSegments[idx];
|
|
33
|
+
const actual = candidateSegments[idx];
|
|
34
|
+
if (expected === WILDCARD_SEGMENT) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (expected !== actual) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
export function isValidInboundPathRootPattern(value) {
|
|
44
|
+
const normalized = normalizePosixAbsolutePath(value);
|
|
45
|
+
if (!normalized) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const segments = splitPathSegments(normalized);
|
|
49
|
+
if (segments.length === 0) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return segments.every((segment) => segment === WILDCARD_SEGMENT || !segment.includes("*"));
|
|
53
|
+
}
|
|
54
|
+
export function normalizeInboundPathRoots(roots) {
|
|
55
|
+
const normalized = [];
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
for (const root of roots ?? []) {
|
|
58
|
+
if (typeof root !== "string") {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (!isValidInboundPathRootPattern(root)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const candidate = normalizePosixAbsolutePath(root);
|
|
65
|
+
if (!candidate || seen.has(candidate)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
seen.add(candidate);
|
|
69
|
+
normalized.push(candidate);
|
|
70
|
+
}
|
|
71
|
+
return normalized;
|
|
72
|
+
}
|
|
73
|
+
export function mergeInboundPathRoots(...rootsLists) {
|
|
74
|
+
const merged = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const roots of rootsLists) {
|
|
77
|
+
const normalized = normalizeInboundPathRoots(roots);
|
|
78
|
+
for (const root of normalized) {
|
|
79
|
+
if (seen.has(root)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
seen.add(root);
|
|
83
|
+
merged.push(root);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return merged;
|
|
87
|
+
}
|
|
88
|
+
export function isInboundPathAllowed(params) {
|
|
89
|
+
const candidatePath = normalizePosixAbsolutePath(params.filePath);
|
|
90
|
+
if (!candidatePath) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const roots = normalizeInboundPathRoots(params.roots);
|
|
94
|
+
const effectiveRoots = roots.length > 0 ? roots : normalizeInboundPathRoots(params.fallbackRoots ?? undefined);
|
|
95
|
+
if (effectiveRoots.length === 0) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return effectiveRoots.some((rootPattern) => matchesRootPattern({ candidatePath, rootPattern }));
|
|
99
|
+
}
|
|
100
|
+
function resolveIMessageAccountConfig(params) {
|
|
101
|
+
const accountId = params.accountId?.trim();
|
|
102
|
+
if (!accountId) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
return params.cfg.channels?.imessage?.accounts?.[accountId];
|
|
106
|
+
}
|
|
107
|
+
export function resolveIMessageAttachmentRoots(params) {
|
|
108
|
+
const accountConfig = resolveIMessageAccountConfig(params);
|
|
109
|
+
return mergeInboundPathRoots(accountConfig?.attachmentRoots, params.cfg.channels?.imessage?.attachmentRoots, DEFAULT_IMESSAGE_ATTACHMENT_ROOTS);
|
|
110
|
+
}
|
|
111
|
+
export function resolveIMessageRemoteAttachmentRoots(params) {
|
|
112
|
+
const accountConfig = resolveIMessageAccountConfig(params);
|
|
113
|
+
return mergeInboundPathRoots(accountConfig?.remoteAttachmentRoots, params.cfg.channels?.imessage?.remoteAttachmentRoots, accountConfig?.attachmentRoots, params.cfg.channels?.imessage?.attachmentRoots, DEFAULT_IMESSAGE_ATTACHMENT_ROOTS);
|
|
114
|
+
}
|
|
@@ -62,6 +62,22 @@ export function normalizeMimeList(values, fallback) {
|
|
|
62
62
|
const input = values && values.length > 0 ? values : fallback;
|
|
63
63
|
return new Set(input.map((value) => normalizeMimeType(value)).filter(Boolean));
|
|
64
64
|
}
|
|
65
|
+
export function resolveInputFileLimits(config) {
|
|
66
|
+
return {
|
|
67
|
+
allowUrl: config?.allowUrl ?? true,
|
|
68
|
+
allowedMimes: normalizeMimeList(config?.allowedMimes, DEFAULT_INPUT_FILE_MIMES),
|
|
69
|
+
maxBytes: config?.maxBytes ?? DEFAULT_INPUT_FILE_MAX_BYTES,
|
|
70
|
+
maxChars: config?.maxChars ?? DEFAULT_INPUT_FILE_MAX_CHARS,
|
|
71
|
+
maxRedirects: config?.maxRedirects ?? DEFAULT_INPUT_MAX_REDIRECTS,
|
|
72
|
+
timeoutMs: config?.timeoutMs ?? DEFAULT_INPUT_TIMEOUT_MS,
|
|
73
|
+
urlAllowlist: config?.urlAllowlist,
|
|
74
|
+
pdf: {
|
|
75
|
+
maxPages: config?.pdf?.maxPages ?? DEFAULT_INPUT_PDF_MAX_PAGES,
|
|
76
|
+
maxPixels: config?.pdf?.maxPixels ?? DEFAULT_INPUT_PDF_MAX_PIXELS,
|
|
77
|
+
minTextChars: config?.pdf?.minTextChars ?? DEFAULT_INPUT_PDF_MIN_TEXT_CHARS,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
65
81
|
export async function fetchWithGuard(params) {
|
|
66
82
|
let currentUrl = params.url;
|
|
67
83
|
let redirectCount = 0;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getMemorySearchManager } from "./index.js";
|
|
2
|
+
export async function createMemoryManagerOrThrow(cfg, agentId = "main") {
|
|
3
|
+
const result = await getMemorySearchManager({ cfg, agentId });
|
|
4
|
+
if (!result.manager) {
|
|
5
|
+
throw new Error("manager missing");
|
|
6
|
+
}
|
|
7
|
+
return result.manager;
|
|
8
|
+
}
|