@poolzin/pool-bot 2026.3.22 → 2026.3.24
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 +111 -0
- package/dist/.buildstamp +1 -1
- package/dist/acp/bindings-store.js +209 -0
- package/dist/acp/control-plane/runtime-cache.js +54 -0
- package/dist/acp/control-plane/runtime-options.js +215 -0
- package/dist/acp/control-plane/session-actor-queue.js +36 -0
- package/dist/acp/policy.js +52 -0
- package/dist/acp/runtime/errors.js +47 -0
- package/dist/acp/runtime/registry.js +86 -0
- package/dist/acp/runtime/types.js +1 -0
- package/dist/acp/translator.js +97 -0
- package/dist/agents/btw.js +280 -0
- package/dist/agents/failover-error.js +145 -47
- package/dist/agents/fast-mode.js +24 -0
- package/dist/agents/live-model-errors.js +23 -0
- package/dist/agents/model-auth-env-vars.js +44 -0
- package/dist/agents/model-auth-markers.js +69 -0
- package/dist/agents/models-config.providers.discovery.js +180 -0
- package/dist/agents/models-config.providers.static.js +480 -0
- package/dist/auto-reply/reply/typing-policy.js +15 -0
- package/dist/browser/browser-profile-manager.js +319 -0
- package/dist/browser/cdp-proxy-bypass.js +129 -0
- package/dist/browser/cdp-timeouts.js +41 -0
- package/dist/browser/chrome-extension-validator.js +406 -0
- package/dist/browser/chrome-mcp-snapshot.js +222 -0
- package/dist/browser/chrome-mcp.js +421 -0
- package/dist/browser/chrome-mcp.snapshot.js +133 -0
- package/dist/browser/errors.js +67 -0
- package/dist/browser/form-fields.js +22 -0
- package/dist/browser/output-atomic.js +44 -0
- package/dist/browser/profile-capabilities.js +47 -0
- package/dist/browser/safe-filename.js +25 -0
- package/dist/browser/snapshot-roles.js +60 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/account-snapshot-fields.js +176 -0
- package/dist/channels/draft-stream-controls.js +89 -0
- package/dist/channels/inbound-debounce-policy.js +28 -0
- package/dist/channels/typing-lifecycle.js +39 -0
- package/dist/cli/program/command-registry.js +52 -0
- package/dist/commands/agent-binding.js +123 -0
- package/dist/commands/agents.commands.bind.js +280 -0
- package/dist/commands/backup-shared.js +186 -0
- package/dist/commands/backup-verify.js +236 -0
- package/dist/commands/backup.js +166 -0
- package/dist/commands/channel-account-context.js +15 -0
- package/dist/commands/channel-account.js +190 -0
- package/dist/commands/gateway-install-token.js +117 -0
- package/dist/commands/oauth-tls-preflight.js +121 -0
- package/dist/commands/ollama-setup.js +402 -0
- package/dist/commands/security-owner-only.js +86 -0
- package/dist/commands/self-hosted-provider-setup.js +207 -0
- package/dist/commands/session-store-targets.js +12 -0
- package/dist/commands/sessions-cleanup.js +97 -0
- package/dist/control-ui/assets/{index-Dvkl4Xlx.js → index-D7shnQwQ.js} +404 -388
- package/dist/control-ui/assets/index-D7shnQwQ.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/cron/cron-filters.js +150 -0
- package/dist/cron/heartbeat-policy.js +26 -0
- package/dist/gateway/device-pairing-security.js +197 -0
- package/dist/gateway/event-deduplication.js +167 -0
- package/dist/gateway/hooks-mapping.js +46 -7
- package/dist/gateway/run-tracker.js +253 -0
- package/dist/gateway/server-methods/nodes.js +14 -0
- package/dist/gateway/websocket-preauth-security.js +188 -0
- package/dist/hooks/module-loader.js +28 -0
- package/dist/infra/agent-command-binding.js +144 -0
- package/dist/infra/backup.js +328 -0
- package/dist/infra/channel-account-context.js +173 -0
- package/dist/infra/errors.js +53 -13
- package/dist/infra/exec-approvals-security.js +217 -0
- package/dist/infra/security/command-analyzer.js +257 -0
- package/dist/infra/session-cleanup.js +143 -0
- package/dist/plugins/loader.js +16 -8
- package/dist/security/external-content.js +51 -1
- package/dist/sessions/session-costs.js +228 -0
- package/dist/shared/param-key.js +16 -0
- package/dist/shared/poll-params.js +58 -0
- package/dist/shared/polls.js +55 -0
- package/docs/DASHBOARD-GAP-ANALYSIS-AND-PLAN.md +430 -0
- package/docs/FEATURES.md +523 -0
- package/docs/FINAL-IMPLEMENTATION-REVIEW.md +274 -0
- package/docs/FINAL-IMPLEMENTATION-SUMMARY.md +356 -0
- package/docs/FINAL-PROFESSIONAL-EVALUATION.md +312 -0
- package/docs/IMPLEMENTATION-PRIORITY-EVALUATION.md +298 -0
- package/docs/IMPLEMENTATION-PROGRESS.md +237 -0
- package/docs/IMPLEMENTATION-REVIEW-PHASE1-2.md +381 -0
- package/docs/IMPLEMENTATION-REVIEW-PHASE4.md +389 -0
- package/docs/IMPLEMENTATION-REVIEW-PHASE5.md +420 -0
- package/docs/IMPLEMENTATION-REVIEW-PHASE6.md +422 -0
- package/docs/IMPLEMENTATION-REVIEW-PHASE7-FINAL.md +184 -0
- package/docs/MIKRODASH-ANALYSIS.md +412 -0
- package/docs/OPENCLAW-GAP-ANALYSIS-FINAL.md +431 -0
- package/docs/OPENCLAW-VS-POOLBOT-ANALYSIS.md +351 -0
- package/docs/PHASE-7-SUMMARY.md +144 -0
- package/docs/POOLBOT-OFFICE-PLAN.md +697 -0
- package/docs/PROJECT-FINAL-STATUS.md +237 -0
- package/docs/README.md +116 -0
- package/docs/REAL-IMPROVEMENTS-EVALUATION.md +477 -0
- package/docs/SECURITY-HARDENING-IMPLEMENTATION.md +161 -0
- package/docs/channels/googlechat.md +235 -206
- package/docs/channels/irc.md +332 -0
- package/docs/channels/nostr.md +255 -168
- package/docs/components/command-palette.md +166 -0
- package/docs/components/login-gate.md +219 -0
- package/docs/getting-started/installation.md +191 -0
- package/docs/getting-started/introduction.md +120 -0
- package/docs/improvements/USAGE-GUIDE.md +359 -0
- package/docs/plans/2026-03-15-openclaw-features-implementation.md +1632 -0
- package/docs/reference/deadcode-detection.md +72 -0
- package/extensions/acpx/node_modules/.bin/acpx +21 -0
- package/extensions/agency-agents/node_modules/.bin/vite +4 -4
- package/extensions/agency-agents/node_modules/.bin/vitest +2 -2
- package/extensions/googlechat/node_modules/.bin/tsc +21 -0
- package/extensions/googlechat/node_modules/.bin/tsserver +21 -0
- package/extensions/googlechat/node_modules/.bin/vitest +21 -0
- package/extensions/googlechat/package.json +11 -28
- package/extensions/googlechat/src/googlechat-channel.test.ts +60 -0
- package/extensions/googlechat/src/googlechat-channel.ts +120 -0
- package/extensions/googlechat/src/index.ts +14 -0
- package/extensions/irc/node_modules/.bin/tsc +21 -0
- package/extensions/irc/node_modules/.bin/tsserver +21 -0
- package/extensions/irc/node_modules/.bin/vitest +21 -0
- package/extensions/irc/package.json +16 -8
- package/extensions/irc/src/index.ts +14 -0
- package/extensions/irc/src/irc-channel.test.ts +43 -0
- package/extensions/irc/src/irc-channel.ts +191 -0
- package/extensions/keyed-async-queue/node_modules/.bin/tsc +21 -0
- package/extensions/keyed-async-queue/node_modules/.bin/tsserver +21 -0
- package/extensions/keyed-async-queue/node_modules/.bin/vitest +21 -0
- package/extensions/keyed-async-queue/package.json +20 -0
- package/extensions/keyed-async-queue/src/index.ts +14 -0
- package/extensions/keyed-async-queue/src/queue.test.ts +135 -0
- package/extensions/keyed-async-queue/src/queue.ts +200 -0
- package/extensions/memory-core/node_modules/.bin/tsc +21 -0
- package/extensions/memory-core/node_modules/.bin/tsserver +21 -0
- package/extensions/memory-core/node_modules/.bin/vitest +21 -0
- package/extensions/memory-core/package.json +11 -8
- package/extensions/memory-core/src/index.ts +14 -0
- package/extensions/memory-core/src/memory-manager.test.ts +124 -0
- package/extensions/memory-core/src/memory-manager.ts +186 -0
- package/extensions/nostr/node_modules/.bin/tsc +2 -2
- package/extensions/nostr/node_modules/.bin/tsserver +2 -2
- package/extensions/nostr/node_modules/.bin/vitest +21 -0
- package/extensions/nostr/package.json +15 -24
- package/extensions/nostr/src/index.ts +14 -0
- package/extensions/nostr/src/nostr-channel.test.ts +55 -0
- package/extensions/nostr/src/nostr-channel.ts +228 -0
- package/extensions/page-agent/node_modules/.bin/vitest +2 -2
- package/extensions/test-utils/node_modules/.bin/jiti +21 -0
- package/extensions/test-utils/node_modules/.bin/playwright +21 -0
- package/extensions/test-utils/node_modules/.bin/tsx +21 -0
- package/extensions/test-utils/node_modules/.bin/vite +21 -0
- package/extensions/test-utils/node_modules/.bin/vitest +21 -0
- package/extensions/test-utils/node_modules/.bin/yaml +21 -0
- package/extensions/xyops/node_modules/.bin/vitest +2 -2
- package/package.json +2 -1
- package/dist/control-ui/assets/index-Dvkl4Xlx.js.map +0 -1
- package/extensions/googlechat/node_modules/.bin/poolbot +0 -21
- package/extensions/memory-core/node_modules/.bin/poolbot +0 -21
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function getBrowserProfileCapabilities(profile) {
|
|
2
|
+
if (profile.driver === "extension") {
|
|
3
|
+
return {
|
|
4
|
+
mode: "local-extension-relay",
|
|
5
|
+
isRemote: false,
|
|
6
|
+
requiresRelay: true,
|
|
7
|
+
requiresAttachedTab: true,
|
|
8
|
+
usesPersistentPlaywright: false,
|
|
9
|
+
supportsPerTabWs: false,
|
|
10
|
+
supportsJsonTabEndpoints: true,
|
|
11
|
+
supportsReset: true,
|
|
12
|
+
supportsManagedTabLimit: false,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
mode: "local-managed",
|
|
17
|
+
isRemote: !profile.cdpIsLoopback,
|
|
18
|
+
requiresRelay: false,
|
|
19
|
+
requiresAttachedTab: false,
|
|
20
|
+
usesPersistentPlaywright: false,
|
|
21
|
+
supportsPerTabWs: profile.cdpIsLoopback,
|
|
22
|
+
supportsJsonTabEndpoints: profile.cdpIsLoopback,
|
|
23
|
+
supportsReset: profile.cdpIsLoopback,
|
|
24
|
+
supportsManagedTabLimit: profile.cdpIsLoopback,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function resolveDefaultSnapshotFormat(params) {
|
|
28
|
+
if (params.explicitFormat) {
|
|
29
|
+
return params.explicitFormat;
|
|
30
|
+
}
|
|
31
|
+
if (params.mode === "efficient") {
|
|
32
|
+
return "ai";
|
|
33
|
+
}
|
|
34
|
+
const capabilities = getBrowserProfileCapabilities(params.profile);
|
|
35
|
+
if (capabilities.mode === "local-extension-relay") {
|
|
36
|
+
return "aria";
|
|
37
|
+
}
|
|
38
|
+
return params.hasPlaywright ? "ai" : "aria";
|
|
39
|
+
}
|
|
40
|
+
export function shouldUsePlaywrightForScreenshot(params) {
|
|
41
|
+
const capabilities = getBrowserProfileCapabilities(params.profile);
|
|
42
|
+
return (capabilities.requiresRelay || !params.wsUrl || Boolean(params.ref) || Boolean(params.element));
|
|
43
|
+
}
|
|
44
|
+
export function shouldUsePlaywrightForAriaSnapshot(params) {
|
|
45
|
+
const capabilities = getBrowserProfileCapabilities(params.profile);
|
|
46
|
+
return capabilities.requiresRelay || !params.wsUrl;
|
|
47
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export function sanitizeUntrustedFileName(fileName, fallbackName) {
|
|
3
|
+
const trimmed = String(fileName ?? "").trim();
|
|
4
|
+
if (!trimmed) {
|
|
5
|
+
return fallbackName;
|
|
6
|
+
}
|
|
7
|
+
let base = path.posix.basename(trimmed);
|
|
8
|
+
base = path.win32.basename(base);
|
|
9
|
+
let cleaned = "";
|
|
10
|
+
for (let i = 0; i < base.length; i++) {
|
|
11
|
+
const code = base.charCodeAt(i);
|
|
12
|
+
if (code < 0x20 || code === 0x7f) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
cleaned += base[i];
|
|
16
|
+
}
|
|
17
|
+
base = cleaned.trim();
|
|
18
|
+
if (!base || base === "." || base === "..") {
|
|
19
|
+
return fallbackName;
|
|
20
|
+
}
|
|
21
|
+
if (base.length > 200) {
|
|
22
|
+
base = base.slice(0, 200);
|
|
23
|
+
}
|
|
24
|
+
return base;
|
|
25
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared ARIA role classification sets used by both the Playwright and Chrome MCP
|
|
3
|
+
* snapshot paths. Keep these in sync — divergence causes the two drivers to produce
|
|
4
|
+
* different snapshot output for the same page.
|
|
5
|
+
*/
|
|
6
|
+
/** Roles that represent user-interactive elements and always get a ref. */
|
|
7
|
+
export const INTERACTIVE_ROLES = new Set([
|
|
8
|
+
"button",
|
|
9
|
+
"checkbox",
|
|
10
|
+
"combobox",
|
|
11
|
+
"link",
|
|
12
|
+
"listbox",
|
|
13
|
+
"menuitem",
|
|
14
|
+
"menuitemcheckbox",
|
|
15
|
+
"menuitemradio",
|
|
16
|
+
"option",
|
|
17
|
+
"radio",
|
|
18
|
+
"searchbox",
|
|
19
|
+
"slider",
|
|
20
|
+
"spinbutton",
|
|
21
|
+
"switch",
|
|
22
|
+
"tab",
|
|
23
|
+
"textbox",
|
|
24
|
+
"treeitem",
|
|
25
|
+
]);
|
|
26
|
+
/** Roles that carry meaningful content and get a ref when named. */
|
|
27
|
+
export const CONTENT_ROLES = new Set([
|
|
28
|
+
"article",
|
|
29
|
+
"cell",
|
|
30
|
+
"columnheader",
|
|
31
|
+
"gridcell",
|
|
32
|
+
"heading",
|
|
33
|
+
"listitem",
|
|
34
|
+
"main",
|
|
35
|
+
"navigation",
|
|
36
|
+
"region",
|
|
37
|
+
"rowheader",
|
|
38
|
+
]);
|
|
39
|
+
/** Structural/container roles — typically skipped in compact mode. */
|
|
40
|
+
export const STRUCTURAL_ROLES = new Set([
|
|
41
|
+
"application",
|
|
42
|
+
"directory",
|
|
43
|
+
"document",
|
|
44
|
+
"generic",
|
|
45
|
+
"grid",
|
|
46
|
+
"group",
|
|
47
|
+
"ignored",
|
|
48
|
+
"list",
|
|
49
|
+
"menu",
|
|
50
|
+
"menubar",
|
|
51
|
+
"none",
|
|
52
|
+
"presentation",
|
|
53
|
+
"row",
|
|
54
|
+
"rowgroup",
|
|
55
|
+
"table",
|
|
56
|
+
"tablist",
|
|
57
|
+
"toolbar",
|
|
58
|
+
"tree",
|
|
59
|
+
"treegrid",
|
|
60
|
+
]);
|
package/dist/build-info.json
CHANGED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// Read-only status commands project a safe subset of account fields into snapshots
|
|
2
|
+
// so renderers can preserve "configured but unavailable" state without touching
|
|
3
|
+
// strict runtime-only credential helpers.
|
|
4
|
+
const CREDENTIAL_STATUS_KEYS = [
|
|
5
|
+
"tokenStatus",
|
|
6
|
+
"botTokenStatus",
|
|
7
|
+
"appTokenStatus",
|
|
8
|
+
"signingSecretStatus",
|
|
9
|
+
"userTokenStatus",
|
|
10
|
+
];
|
|
11
|
+
function asRecord(value) {
|
|
12
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function readTrimmedString(record, key) {
|
|
18
|
+
const value = record[key];
|
|
19
|
+
if (typeof value !== "string") {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const trimmed = value.trim();
|
|
23
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
24
|
+
}
|
|
25
|
+
function readBoolean(record, key) {
|
|
26
|
+
return typeof record[key] === "boolean" ? record[key] : undefined;
|
|
27
|
+
}
|
|
28
|
+
function readNumber(record, key) {
|
|
29
|
+
const value = record[key];
|
|
30
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
31
|
+
}
|
|
32
|
+
function readStringArray(record, key) {
|
|
33
|
+
const value = record[key];
|
|
34
|
+
if (!Array.isArray(value)) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const normalized = value
|
|
38
|
+
.map((entry) => (typeof entry === "string" || typeof entry === "number" ? String(entry) : ""))
|
|
39
|
+
.map((entry) => entry.trim())
|
|
40
|
+
.filter(Boolean);
|
|
41
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
42
|
+
}
|
|
43
|
+
function readCredentialStatus(record, key) {
|
|
44
|
+
const value = record[key];
|
|
45
|
+
return value === "available" || value === "configured_unavailable" || value === "missing"
|
|
46
|
+
? value
|
|
47
|
+
: undefined;
|
|
48
|
+
}
|
|
49
|
+
export function resolveConfiguredFromCredentialStatuses(account) {
|
|
50
|
+
const record = asRecord(account);
|
|
51
|
+
if (!record) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
let sawCredentialStatus = false;
|
|
55
|
+
for (const key of CREDENTIAL_STATUS_KEYS) {
|
|
56
|
+
const status = readCredentialStatus(record, key);
|
|
57
|
+
if (!status) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
sawCredentialStatus = true;
|
|
61
|
+
if (status !== "missing") {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return sawCredentialStatus ? false : undefined;
|
|
66
|
+
}
|
|
67
|
+
export function resolveConfiguredFromRequiredCredentialStatuses(account, requiredKeys) {
|
|
68
|
+
const record = asRecord(account);
|
|
69
|
+
if (!record) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
let sawCredentialStatus = false;
|
|
73
|
+
for (const key of requiredKeys) {
|
|
74
|
+
const status = readCredentialStatus(record, key);
|
|
75
|
+
if (!status) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
sawCredentialStatus = true;
|
|
79
|
+
if (status === "missing") {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return sawCredentialStatus ? true : undefined;
|
|
84
|
+
}
|
|
85
|
+
export function hasConfiguredUnavailableCredentialStatus(account) {
|
|
86
|
+
const record = asRecord(account);
|
|
87
|
+
if (!record) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return CREDENTIAL_STATUS_KEYS.some((key) => readCredentialStatus(record, key) === "configured_unavailable");
|
|
91
|
+
}
|
|
92
|
+
export function hasResolvedCredentialValue(account) {
|
|
93
|
+
const record = asRecord(account);
|
|
94
|
+
if (!record) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return (["token", "botToken", "appToken", "signingSecret", "userToken"].some((key) => {
|
|
98
|
+
const value = record[key];
|
|
99
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
100
|
+
}) || CREDENTIAL_STATUS_KEYS.some((key) => readCredentialStatus(record, key) === "available"));
|
|
101
|
+
}
|
|
102
|
+
export function projectCredentialSnapshotFields(account) {
|
|
103
|
+
const record = asRecord(account);
|
|
104
|
+
if (!record) {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
...(readTrimmedString(record, "tokenSource")
|
|
109
|
+
? { tokenSource: readTrimmedString(record, "tokenSource") }
|
|
110
|
+
: {}),
|
|
111
|
+
...(readTrimmedString(record, "botTokenSource")
|
|
112
|
+
? { botTokenSource: readTrimmedString(record, "botTokenSource") }
|
|
113
|
+
: {}),
|
|
114
|
+
...(readTrimmedString(record, "appTokenSource")
|
|
115
|
+
? { appTokenSource: readTrimmedString(record, "appTokenSource") }
|
|
116
|
+
: {}),
|
|
117
|
+
...(readTrimmedString(record, "signingSecretSource")
|
|
118
|
+
? { signingSecretSource: readTrimmedString(record, "signingSecretSource") }
|
|
119
|
+
: {}),
|
|
120
|
+
...(readCredentialStatus(record, "tokenStatus")
|
|
121
|
+
? { tokenStatus: readCredentialStatus(record, "tokenStatus") }
|
|
122
|
+
: {}),
|
|
123
|
+
...(readCredentialStatus(record, "botTokenStatus")
|
|
124
|
+
? { botTokenStatus: readCredentialStatus(record, "botTokenStatus") }
|
|
125
|
+
: {}),
|
|
126
|
+
...(readCredentialStatus(record, "appTokenStatus")
|
|
127
|
+
? { appTokenStatus: readCredentialStatus(record, "appTokenStatus") }
|
|
128
|
+
: {}),
|
|
129
|
+
...(readCredentialStatus(record, "signingSecretStatus")
|
|
130
|
+
? { signingSecretStatus: readCredentialStatus(record, "signingSecretStatus") }
|
|
131
|
+
: {}),
|
|
132
|
+
...(readCredentialStatus(record, "userTokenStatus")
|
|
133
|
+
? { userTokenStatus: readCredentialStatus(record, "userTokenStatus") }
|
|
134
|
+
: {}),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
export function projectSafeChannelAccountSnapshotFields(account) {
|
|
138
|
+
const record = asRecord(account);
|
|
139
|
+
if (!record) {
|
|
140
|
+
return {};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
...(readTrimmedString(record, "name") ? { name: readTrimmedString(record, "name") } : {}),
|
|
144
|
+
...(readBoolean(record, "linked") !== undefined
|
|
145
|
+
? { linked: readBoolean(record, "linked") }
|
|
146
|
+
: {}),
|
|
147
|
+
...(readBoolean(record, "running") !== undefined
|
|
148
|
+
? { running: readBoolean(record, "running") }
|
|
149
|
+
: {}),
|
|
150
|
+
...(readBoolean(record, "connected") !== undefined
|
|
151
|
+
? { connected: readBoolean(record, "connected") }
|
|
152
|
+
: {}),
|
|
153
|
+
...(readNumber(record, "reconnectAttempts") !== undefined
|
|
154
|
+
? { reconnectAttempts: readNumber(record, "reconnectAttempts") }
|
|
155
|
+
: {}),
|
|
156
|
+
...(readTrimmedString(record, "mode") ? { mode: readTrimmedString(record, "mode") } : {}),
|
|
157
|
+
...(readTrimmedString(record, "dmPolicy")
|
|
158
|
+
? { dmPolicy: readTrimmedString(record, "dmPolicy") }
|
|
159
|
+
: {}),
|
|
160
|
+
...(readStringArray(record, "allowFrom")
|
|
161
|
+
? { allowFrom: readStringArray(record, "allowFrom") }
|
|
162
|
+
: {}),
|
|
163
|
+
...projectCredentialSnapshotFields(account),
|
|
164
|
+
...(readTrimmedString(record, "baseUrl")
|
|
165
|
+
? { baseUrl: readTrimmedString(record, "baseUrl") }
|
|
166
|
+
: {}),
|
|
167
|
+
...(readBoolean(record, "allowUnmentionedGroups") !== undefined
|
|
168
|
+
? { allowUnmentionedGroups: readBoolean(record, "allowUnmentionedGroups") }
|
|
169
|
+
: {}),
|
|
170
|
+
...(readTrimmedString(record, "cliPath")
|
|
171
|
+
? { cliPath: readTrimmedString(record, "cliPath") }
|
|
172
|
+
: {}),
|
|
173
|
+
...(readTrimmedString(record, "dbPath") ? { dbPath: readTrimmedString(record, "dbPath") } : {}),
|
|
174
|
+
...(readNumber(record, "port") !== undefined ? { port: readNumber(record, "port") } : {}),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { createDraftStreamLoop } from "./draft-stream-loop.js";
|
|
2
|
+
export function createFinalizableDraftStreamControls(params) {
|
|
3
|
+
const loop = createDraftStreamLoop({
|
|
4
|
+
throttleMs: params.throttleMs,
|
|
5
|
+
isStopped: params.isStopped,
|
|
6
|
+
sendOrEditStreamMessage: params.sendOrEditStreamMessage,
|
|
7
|
+
});
|
|
8
|
+
const update = (text) => {
|
|
9
|
+
if (params.isStopped() || params.isFinal()) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
loop.update(text);
|
|
13
|
+
};
|
|
14
|
+
const stop = async () => {
|
|
15
|
+
params.markFinal();
|
|
16
|
+
await loop.flush();
|
|
17
|
+
};
|
|
18
|
+
const stopForClear = async () => {
|
|
19
|
+
params.markStopped();
|
|
20
|
+
loop.stop();
|
|
21
|
+
await loop.waitForInFlight();
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
loop,
|
|
25
|
+
update,
|
|
26
|
+
stop,
|
|
27
|
+
stopForClear,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function createFinalizableDraftStreamControlsForState(params) {
|
|
31
|
+
return createFinalizableDraftStreamControls({
|
|
32
|
+
throttleMs: params.throttleMs,
|
|
33
|
+
isStopped: () => params.state.stopped,
|
|
34
|
+
isFinal: () => params.state.final,
|
|
35
|
+
markStopped: () => {
|
|
36
|
+
params.state.stopped = true;
|
|
37
|
+
},
|
|
38
|
+
markFinal: () => {
|
|
39
|
+
params.state.final = true;
|
|
40
|
+
},
|
|
41
|
+
sendOrEditStreamMessage: params.sendOrEditStreamMessage,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export async function takeMessageIdAfterStop(params) {
|
|
45
|
+
await params.stopForClear();
|
|
46
|
+
const messageId = params.readMessageId();
|
|
47
|
+
params.clearMessageId();
|
|
48
|
+
return messageId;
|
|
49
|
+
}
|
|
50
|
+
export async function clearFinalizableDraftMessage(params) {
|
|
51
|
+
const messageId = await takeMessageIdAfterStop({
|
|
52
|
+
stopForClear: params.stopForClear,
|
|
53
|
+
readMessageId: params.readMessageId,
|
|
54
|
+
clearMessageId: params.clearMessageId,
|
|
55
|
+
});
|
|
56
|
+
if (!params.isValidMessageId(messageId)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
await params.deleteMessage(messageId);
|
|
61
|
+
params.onDeleteSuccess?.(messageId);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
params.warn?.(`${params.warnPrefix}: ${err instanceof Error ? err.message : String(err)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function createFinalizableDraftLifecycle(params) {
|
|
68
|
+
const controls = createFinalizableDraftStreamControlsForState({
|
|
69
|
+
throttleMs: params.throttleMs,
|
|
70
|
+
state: params.state,
|
|
71
|
+
sendOrEditStreamMessage: params.sendOrEditStreamMessage,
|
|
72
|
+
});
|
|
73
|
+
const clear = async () => {
|
|
74
|
+
await clearFinalizableDraftMessage({
|
|
75
|
+
stopForClear: controls.stopForClear,
|
|
76
|
+
readMessageId: params.readMessageId,
|
|
77
|
+
clearMessageId: params.clearMessageId,
|
|
78
|
+
isValidMessageId: params.isValidMessageId,
|
|
79
|
+
deleteMessage: params.deleteMessage,
|
|
80
|
+
onDeleteSuccess: params.onDeleteSuccess,
|
|
81
|
+
warn: params.warn,
|
|
82
|
+
warnPrefix: params.warnPrefix,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
...controls,
|
|
87
|
+
clear,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { hasControlCommand } from "../auto-reply/command-detection.js";
|
|
2
|
+
import { createInboundDebouncer, resolveInboundDebounceMs, } from "../auto-reply/inbound-debounce.js";
|
|
3
|
+
export function shouldDebounceTextInbound(params) {
|
|
4
|
+
if (params.allowDebounce === false) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
if (params.hasMedia) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
const text = params.text?.trim() ?? "";
|
|
11
|
+
if (!text) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return !hasControlCommand(text, params.cfg, params.commandOptions);
|
|
15
|
+
}
|
|
16
|
+
export function createChannelInboundDebouncer(params) {
|
|
17
|
+
const debounceMs = resolveInboundDebounceMs({
|
|
18
|
+
cfg: params.cfg,
|
|
19
|
+
channel: params.channel,
|
|
20
|
+
overrideMs: params.debounceMsOverride,
|
|
21
|
+
});
|
|
22
|
+
const { cfg: _cfg, channel: _channel, debounceMsOverride: _override, ...rest } = params;
|
|
23
|
+
const debouncer = createInboundDebouncer({
|
|
24
|
+
debounceMs,
|
|
25
|
+
...rest,
|
|
26
|
+
});
|
|
27
|
+
return { debounceMs, debouncer };
|
|
28
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function createTypingKeepaliveLoop(params) {
|
|
2
|
+
let timer;
|
|
3
|
+
let tickInFlight = false;
|
|
4
|
+
const tick = async () => {
|
|
5
|
+
if (tickInFlight) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
tickInFlight = true;
|
|
9
|
+
try {
|
|
10
|
+
await params.onTick();
|
|
11
|
+
}
|
|
12
|
+
finally {
|
|
13
|
+
tickInFlight = false;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const start = () => {
|
|
17
|
+
if (params.intervalMs <= 0 || timer) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
timer = setInterval(() => {
|
|
21
|
+
void tick();
|
|
22
|
+
}, params.intervalMs);
|
|
23
|
+
};
|
|
24
|
+
const stop = () => {
|
|
25
|
+
if (!timer) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
clearInterval(timer);
|
|
29
|
+
timer = undefined;
|
|
30
|
+
tickInFlight = false;
|
|
31
|
+
};
|
|
32
|
+
const isRunning = () => timer !== undefined;
|
|
33
|
+
return {
|
|
34
|
+
tick,
|
|
35
|
+
start,
|
|
36
|
+
stop,
|
|
37
|
+
isRunning,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -92,6 +92,19 @@ const coreEntries = [
|
|
|
92
92
|
mod.registerMaintenanceCommands(program);
|
|
93
93
|
},
|
|
94
94
|
},
|
|
95
|
+
{
|
|
96
|
+
commands: [
|
|
97
|
+
{
|
|
98
|
+
name: "sessions-cleanup",
|
|
99
|
+
description: "Clean up old sessions to stay within disk budget",
|
|
100
|
+
hasSubcommands: true,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
register: async ({ program }) => {
|
|
104
|
+
const mod = await import("../../commands/sessions-cleanup.js");
|
|
105
|
+
mod.registerSessionCleanupCommand(program);
|
|
106
|
+
},
|
|
107
|
+
},
|
|
95
108
|
{
|
|
96
109
|
commands: [
|
|
97
110
|
{
|
|
@@ -138,6 +151,19 @@ const coreEntries = [
|
|
|
138
151
|
});
|
|
139
152
|
},
|
|
140
153
|
},
|
|
154
|
+
{
|
|
155
|
+
commands: [
|
|
156
|
+
{
|
|
157
|
+
name: "agent-binding",
|
|
158
|
+
description: "Manage agent command bindings (bind/unbind commands to agents)",
|
|
159
|
+
hasSubcommands: true,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
register: async ({ program }) => {
|
|
163
|
+
const mod = await import("../../commands/agent-binding.js");
|
|
164
|
+
mod.registerAgentCommandBindingCommand(program);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
141
167
|
{
|
|
142
168
|
commands: [
|
|
143
169
|
{
|
|
@@ -161,6 +187,32 @@ const coreEntries = [
|
|
|
161
187
|
mod.registerStatusHealthSessionsCommands(program);
|
|
162
188
|
},
|
|
163
189
|
},
|
|
190
|
+
{
|
|
191
|
+
commands: [
|
|
192
|
+
{
|
|
193
|
+
name: "backup",
|
|
194
|
+
description: "Manage Pool Bot backups (create, restore, list, delete)",
|
|
195
|
+
hasSubcommands: true,
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
register: async ({ program }) => {
|
|
199
|
+
const mod = await import("../../commands/backup.js");
|
|
200
|
+
mod.registerBackupCommand(program);
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
commands: [
|
|
205
|
+
{
|
|
206
|
+
name: "channel-account",
|
|
207
|
+
description: "Manage channel accounts (multi-account support)",
|
|
208
|
+
hasSubcommands: true,
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
register: async ({ program }) => {
|
|
212
|
+
const mod = await import("../../commands/channel-account.js");
|
|
213
|
+
mod.registerChannelAccountCommand(program);
|
|
214
|
+
},
|
|
215
|
+
},
|
|
164
216
|
{
|
|
165
217
|
commands: [
|
|
166
218
|
{
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Command Binding CLI
|
|
3
|
+
*
|
|
4
|
+
* Bind specific commands to agents.
|
|
5
|
+
*/
|
|
6
|
+
import { bindCommandsToAgent, unbindCommandsFromAgent, getAgentBindings, getAllBindings, addCommandsToAgent, removeCommandsFromAgent, toggleBindingMode, isCommandAllowedForAgent, } from "../infra/agent-command-binding.js";
|
|
7
|
+
export function registerAgentCommandBindingCommand(program) {
|
|
8
|
+
const bindingCmd = program.command("agent-binding").description("Manage agent command bindings");
|
|
9
|
+
// List all bindings
|
|
10
|
+
bindingCmd
|
|
11
|
+
.command("list")
|
|
12
|
+
.description("List all agent command bindings")
|
|
13
|
+
.action(async () => {
|
|
14
|
+
console.log("🎱 Pool Bot - Agent Command Bindings\n");
|
|
15
|
+
const bindings = await getAllBindings();
|
|
16
|
+
if (bindings.length === 0) {
|
|
17
|
+
console.log("No bindings found.\n");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
console.log("┌──────────────┬──────────────┬──────────────┬──────────────┐");
|
|
21
|
+
console.log("│ Agent ID │ Mode │ Commands │ Updated │");
|
|
22
|
+
console.log("├──────────────┼──────────────┼──────────────┼──────────────┤");
|
|
23
|
+
bindings.forEach((binding) => {
|
|
24
|
+
const mode = binding.allowlist ? "Allowlist" : "Denylist";
|
|
25
|
+
const commandCount = binding.commands.length;
|
|
26
|
+
const updated = new Date(binding.updatedAt).toLocaleDateString();
|
|
27
|
+
console.log(`│ ${binding.agentId.padEnd(12)} │ ${mode.padEnd(12)} │ ${String(commandCount).padEnd(12)} │ ${updated.padEnd(12)} │`);
|
|
28
|
+
});
|
|
29
|
+
console.log("└──────────────┴──────────────┴──────────────┴──────────────┘\n");
|
|
30
|
+
console.log(`Total: ${bindings.length} binding(s)\n`);
|
|
31
|
+
});
|
|
32
|
+
// Show binding for specific agent
|
|
33
|
+
bindingCmd
|
|
34
|
+
.command("show <agentId>")
|
|
35
|
+
.description("Show bindings for a specific agent")
|
|
36
|
+
.action(async (agentId) => {
|
|
37
|
+
console.log(`🎱 Pool Bot - Agent Binding for ${agentId}\n`);
|
|
38
|
+
const binding = await getAgentBindings(agentId);
|
|
39
|
+
if (!binding) {
|
|
40
|
+
console.log(`No binding found for agent ${agentId}\n`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console.log(`Agent ID: ${binding.agentId}`);
|
|
44
|
+
console.log(`Mode: ${binding.allowlist ? "Allowlist" : "Denylist"}`);
|
|
45
|
+
console.log(`Commands (${binding.commands.length}):`);
|
|
46
|
+
binding.commands.forEach((cmd) => console.log(` - ${cmd}`));
|
|
47
|
+
console.log(`Created: ${new Date(binding.createdAt).toLocaleString()}`);
|
|
48
|
+
console.log(`Updated: ${new Date(binding.updatedAt).toLocaleString()}\n`);
|
|
49
|
+
});
|
|
50
|
+
// Bind commands
|
|
51
|
+
bindingCmd
|
|
52
|
+
.command("bind <agentId> <commands...>")
|
|
53
|
+
.description("Bind commands to an agent (allowlist mode)")
|
|
54
|
+
.option("--denylist", "Use denylist mode instead of allowlist")
|
|
55
|
+
.action(async (agentId, commands, options) => {
|
|
56
|
+
console.log("🎱 Pool Bot - Binding Commands\n");
|
|
57
|
+
const binding = await bindCommandsToAgent({
|
|
58
|
+
agentId,
|
|
59
|
+
commands,
|
|
60
|
+
allowlist: !options.denylist,
|
|
61
|
+
});
|
|
62
|
+
console.log(`✅ Commands bound to agent ${agentId}\n`);
|
|
63
|
+
console.log(`Mode: ${binding.allowlist ? "Allowlist" : "Denylist"}`);
|
|
64
|
+
console.log(`Commands: ${binding.commands.join(", ")}\n`);
|
|
65
|
+
});
|
|
66
|
+
// Add commands
|
|
67
|
+
bindingCmd
|
|
68
|
+
.command("add <agentId> <commands...>")
|
|
69
|
+
.description("Add commands to an agent's binding")
|
|
70
|
+
.action(async (agentId, commands) => {
|
|
71
|
+
console.log("🎱 Pool Bot - Adding Commands\n");
|
|
72
|
+
const binding = await addCommandsToAgent({ agentId, commands });
|
|
73
|
+
console.log(`✅ Commands added to agent ${agentId}\n`);
|
|
74
|
+
console.log(`Total commands: ${binding.commands.length}\n`);
|
|
75
|
+
});
|
|
76
|
+
// Remove commands
|
|
77
|
+
bindingCmd
|
|
78
|
+
.command("remove <agentId> <commands...>")
|
|
79
|
+
.description("Remove commands from an agent's binding")
|
|
80
|
+
.action(async (agentId, commands) => {
|
|
81
|
+
console.log("🎱 Pool Bot - Removing Commands\n");
|
|
82
|
+
const binding = await removeCommandsFromAgent({ agentId, commands });
|
|
83
|
+
console.log(`✅ Commands removed from agent ${agentId}\n`);
|
|
84
|
+
console.log(`Remaining commands: ${binding.commands.length}\n`);
|
|
85
|
+
});
|
|
86
|
+
// Unbind
|
|
87
|
+
bindingCmd
|
|
88
|
+
.command("unbind <agentId>")
|
|
89
|
+
.description("Remove all bindings from an agent")
|
|
90
|
+
.action(async (agentId) => {
|
|
91
|
+
console.log("🎱 Pool Bot - Unbinding Commands\n");
|
|
92
|
+
const success = await unbindCommandsFromAgent(agentId);
|
|
93
|
+
if (success) {
|
|
94
|
+
console.log(`✅ Bindings removed for agent ${agentId}\n`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.log(`No bindings found for agent ${agentId}\n`);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// Toggle mode
|
|
101
|
+
bindingCmd
|
|
102
|
+
.command("toggle <agentId>")
|
|
103
|
+
.description("Toggle between allowlist and denylist mode")
|
|
104
|
+
.action(async (agentId) => {
|
|
105
|
+
console.log("🎱 Pool Bot - Toggling Binding Mode\n");
|
|
106
|
+
const binding = await toggleBindingMode(agentId);
|
|
107
|
+
console.log(`✅ Mode toggled for agent ${agentId}\n`);
|
|
108
|
+
console.log(`New mode: ${binding.allowlist ? "Allowlist" : "Denylist"}\n`);
|
|
109
|
+
});
|
|
110
|
+
// Test command
|
|
111
|
+
bindingCmd
|
|
112
|
+
.command("test <agentId> <command>")
|
|
113
|
+
.description("Test if a command is allowed for an agent")
|
|
114
|
+
.action(async (agentId, command) => {
|
|
115
|
+
console.log("🎱 Pool Bot - Testing Command\n");
|
|
116
|
+
const result = await isCommandAllowedForAgent({ agentId, command });
|
|
117
|
+
console.log(`Agent: ${agentId}`);
|
|
118
|
+
console.log(`Command: ${command}`);
|
|
119
|
+
console.log(`Allowed: ${result.allowed ? "Yes ✅" : "No ❌"}`);
|
|
120
|
+
console.log(`Reason: ${result.reason}\n`);
|
|
121
|
+
});
|
|
122
|
+
return bindingCmd;
|
|
123
|
+
}
|