@poolzin/pool-bot 2026.3.13 → 2026.3.15
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 +87 -0
- package/dist/agents/checkpoint-manager.js +291 -0
- package/dist/agents/poolbot-tools.js +5 -0
- package/dist/agents/subagent-announce-reliability.js +160 -0
- package/dist/agents/tool-result-truncation.js +299 -0
- package/dist/agents/tools/nodes-file-tool.js +197 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/config-cli.js +60 -0
- package/dist/cron/cron-improvements.js +195 -0
- package/dist/discord/discord-improvements.js +167 -0
- package/dist/gateway/auth-rate-limit.js +19 -0
- package/dist/gateway/auth.js +41 -0
- package/dist/gateway/gateway-improvements.js +294 -0
- package/dist/gateway/node-command-policy.js +7 -2
- package/dist/infra/net/ssrf.js +15 -2
- package/dist/infra/shell-security.js +201 -0
- package/dist/memory/memory-improvements.js +239 -0
- package/dist/node-host/runner.js +146 -79
- package/dist/security/prototype-pollution.js +141 -0
- package/dist/security/webhook-security.js +253 -0
- package/dist/shared/net/ip.js +52 -1
- package/dist/slack/slack-improvements.js +225 -0
- package/dist/telegram/telegram-improvements.js +220 -0
- package/dist/ui-plugins/ui-plugins-improvements.js +191 -0
- package/docs/ANALISE_OPENCLAW_PROFISSIONAL.md +520 -0
- package/docs/competitive-analysis.md +421 -0
- package/docs/implementation-analysis.md +393 -0
- package/docs/plans/2026-03-11-file-operations-security-hardening.md +307 -0
- package/docs/plans/2026-03-11-integracao-projetos-poolbot.md +666 -0
- package/extensions/agency-agents/README.md +301 -0
- package/extensions/agency-agents/agents/CONTRIBUTING.md +353 -0
- package/extensions/agency-agents/agents/README.md +602 -0
- package/extensions/agency-agents/agents/design/design-brand-guardian.md +320 -0
- package/extensions/agency-agents/agents/design/design-image-prompt-engineer.md +234 -0
- package/extensions/agency-agents/agents/design/design-ui-designer.md +381 -0
- package/extensions/agency-agents/agents/design/design-ux-architect.md +467 -0
- package/extensions/agency-agents/agents/design/design-ux-researcher.md +327 -0
- package/extensions/agency-agents/agents/design/design-visual-storyteller.md +147 -0
- package/extensions/agency-agents/agents/design/design-whimsy-injector.md +436 -0
- package/extensions/agency-agents/agents/engineering/engineering-ai-engineer.md +144 -0
- package/extensions/agency-agents/agents/engineering/engineering-backend-architect.md +233 -0
- package/extensions/agency-agents/agents/engineering/engineering-devops-automator.md +374 -0
- package/extensions/agency-agents/agents/engineering/engineering-frontend-developer.md +223 -0
- package/extensions/agency-agents/agents/engineering/engineering-mobile-app-builder.md +491 -0
- package/extensions/agency-agents/agents/engineering/engineering-rapid-prototyper.md +460 -0
- package/extensions/agency-agents/agents/engineering/engineering-security-engineer.md +275 -0
- package/extensions/agency-agents/agents/engineering/engineering-senior-developer.md +174 -0
- package/extensions/agency-agents/agents/examples/README.md +48 -0
- package/extensions/agency-agents/agents/examples/nexus-spatial-discovery.md +852 -0
- package/extensions/agency-agents/agents/examples/workflow-landing-page.md +119 -0
- package/extensions/agency-agents/agents/examples/workflow-startup-mvp.md +155 -0
- package/extensions/agency-agents/agents/integrations/README.md +117 -0
- package/extensions/agency-agents/agents/integrations/aider/README.md +38 -0
- package/extensions/agency-agents/agents/integrations/antigravity/README.md +49 -0
- package/extensions/agency-agents/agents/integrations/claude-code/README.md +31 -0
- package/extensions/agency-agents/agents/integrations/cursor/README.md +38 -0
- package/extensions/agency-agents/agents/integrations/gemini-cli/README.md +36 -0
- package/extensions/agency-agents/agents/integrations/opencode/README.md +58 -0
- package/extensions/agency-agents/agents/integrations/windsurf/README.md +26 -0
- package/extensions/agency-agents/agents/marketing/marketing-app-store-optimizer.md +319 -0
- package/extensions/agency-agents/agents/marketing/marketing-content-creator.md +52 -0
- package/extensions/agency-agents/agents/marketing/marketing-growth-hacker.md +52 -0
- package/extensions/agency-agents/agents/marketing/marketing-instagram-curator.md +111 -0
- package/extensions/agency-agents/agents/marketing/marketing-reddit-community-builder.md +121 -0
- package/extensions/agency-agents/agents/marketing/marketing-social-media-strategist.md +123 -0
- package/extensions/agency-agents/agents/marketing/marketing-tiktok-strategist.md +123 -0
- package/extensions/agency-agents/agents/marketing/marketing-twitter-engager.md +124 -0
- package/extensions/agency-agents/agents/marketing/marketing-wechat-official-account.md +143 -0
- package/extensions/agency-agents/agents/marketing/marketing-xiaohongshu-specialist.md +136 -0
- package/extensions/agency-agents/agents/marketing/marketing-zhihu-strategist.md +160 -0
- package/extensions/agency-agents/agents/product/product-feedback-synthesizer.md +117 -0
- package/extensions/agency-agents/agents/product/product-sprint-prioritizer.md +152 -0
- package/extensions/agency-agents/agents/product/product-trend-researcher.md +157 -0
- package/extensions/agency-agents/agents/project-management/project-management-experiment-tracker.md +196 -0
- package/extensions/agency-agents/agents/project-management/project-management-project-shepherd.md +192 -0
- package/extensions/agency-agents/agents/project-management/project-management-studio-operations.md +198 -0
- package/extensions/agency-agents/agents/project-management/project-management-studio-producer.md +201 -0
- package/extensions/agency-agents/agents/project-management/project-manager-senior.md +133 -0
- package/extensions/agency-agents/agents/scripts/convert.sh +362 -0
- package/extensions/agency-agents/agents/scripts/install.sh +465 -0
- package/extensions/agency-agents/agents/scripts/lint-agents.sh +115 -0
- package/extensions/agency-agents/agents/spatial-computing/macos-spatial-metal-engineer.md +335 -0
- package/extensions/agency-agents/agents/spatial-computing/terminal-integration-specialist.md +68 -0
- package/extensions/agency-agents/agents/spatial-computing/visionos-spatial-engineer.md +52 -0
- package/extensions/agency-agents/agents/spatial-computing/xr-cockpit-interaction-specialist.md +30 -0
- package/extensions/agency-agents/agents/spatial-computing/xr-immersive-developer.md +30 -0
- package/extensions/agency-agents/agents/spatial-computing/xr-interface-architect.md +30 -0
- package/extensions/agency-agents/agents/specialized/agentic-identity-trust.md +367 -0
- package/extensions/agency-agents/agents/specialized/agents-orchestrator.md +365 -0
- package/extensions/agency-agents/agents/specialized/data-analytics-reporter.md +52 -0
- package/extensions/agency-agents/agents/specialized/data-consolidation-agent.md +58 -0
- package/extensions/agency-agents/agents/specialized/lsp-index-engineer.md +312 -0
- package/extensions/agency-agents/agents/specialized/report-distribution-agent.md +63 -0
- package/extensions/agency-agents/agents/specialized/sales-data-extraction-agent.md +65 -0
- package/extensions/agency-agents/agents/strategy/EXECUTIVE-BRIEF.md +95 -0
- package/extensions/agency-agents/agents/strategy/QUICKSTART.md +194 -0
- package/extensions/agency-agents/agents/strategy/coordination/agent-activation-prompts.md +401 -0
- package/extensions/agency-agents/agents/strategy/coordination/handoff-templates.md +357 -0
- package/extensions/agency-agents/agents/strategy/nexus-strategy.md +1110 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-0-discovery.md +178 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-1-strategy.md +238 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-2-foundation.md +278 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-3-build.md +286 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-4-hardening.md +332 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-5-launch.md +277 -0
- package/extensions/agency-agents/agents/strategy/playbooks/phase-6-operate.md +318 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-enterprise-feature.md +157 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-incident-response.md +217 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-marketing-campaign.md +187 -0
- package/extensions/agency-agents/agents/strategy/runbooks/scenario-startup-mvp.md +154 -0
- package/extensions/agency-agents/agents/support/support-analytics-reporter.md +363 -0
- package/extensions/agency-agents/agents/support/support-executive-summary-generator.md +210 -0
- package/extensions/agency-agents/agents/support/support-finance-tracker.md +440 -0
- package/extensions/agency-agents/agents/support/support-infrastructure-maintainer.md +616 -0
- package/extensions/agency-agents/agents/support/support-legal-compliance-checker.md +586 -0
- package/extensions/agency-agents/agents/support/support-support-responder.md +583 -0
- package/extensions/agency-agents/agents/testing/testing-accessibility-auditor.md +313 -0
- package/extensions/agency-agents/agents/testing/testing-api-tester.md +304 -0
- package/extensions/agency-agents/agents/testing/testing-evidence-collector.md +208 -0
- package/extensions/agency-agents/agents/testing/testing-performance-benchmarker.md +266 -0
- package/extensions/agency-agents/agents/testing/testing-reality-checker.md +236 -0
- package/extensions/agency-agents/agents/testing/testing-test-results-analyzer.md +303 -0
- package/extensions/agency-agents/agents/testing/testing-tool-evaluator.md +392 -0
- package/extensions/agency-agents/agents/testing/testing-workflow-optimizer.md +448 -0
- package/extensions/agency-agents/index.ts +733 -0
- package/extensions/agency-agents/node_modules/.bin/jiti +21 -0
- package/extensions/agency-agents/node_modules/.bin/tsc +21 -0
- package/extensions/agency-agents/node_modules/.bin/tsserver +21 -0
- package/extensions/agency-agents/node_modules/.bin/tsx +21 -0
- package/extensions/agency-agents/node_modules/.bin/vite +21 -0
- package/extensions/agency-agents/node_modules/.bin/vitest +21 -0
- package/extensions/agency-agents/node_modules/.bin/yaml +21 -0
- package/extensions/agency-agents/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/extensions/agency-agents/package.json +25 -0
- package/extensions/agency-agents/poolbot.plugin.json +11 -0
- package/extensions/agency-agents/src/AgencyAgentsService.test.ts +443 -0
- package/extensions/agency-agents/src/AgencyAgentsService.ts +288 -0
- package/extensions/agency-agents/src/types.ts +147 -0
- package/extensions/agency-agents/vitest.config.ts +8 -0
- package/extensions/hexstrike-ai/README.md +98 -0
- package/extensions/hexstrike-ai/node_modules/.bin/tsc +21 -0
- package/extensions/hexstrike-ai/node_modules/.bin/tsserver +21 -0
- package/extensions/hexstrike-ai/package.json +29 -0
- package/extensions/hexstrike-ai/poolbot.plugin.json +31 -0
- package/extensions/hexstrike-ai/src/client.ts +91 -0
- package/extensions/hexstrike-ai/src/index.ts +170 -0
- package/extensions/hexstrike-ai/src/server/hexstrike_mcp.py +5470 -0
- package/extensions/hexstrike-ai/src/server/hexstrike_server.py +17289 -0
- package/extensions/hexstrike-ai/src/server/requirements.txt +84 -0
- package/extensions/hexstrike-ai/src/server-manager.ts +83 -0
- package/extensions/hexstrike-ai/tsconfig.json +20 -0
- package/extensions/page-agent/README.md +159 -0
- package/extensions/page-agent/index.ts +595 -0
- package/extensions/page-agent/node_modules/.bin/jiti +21 -0
- package/extensions/page-agent/node_modules/.bin/playwright +21 -0
- package/extensions/page-agent/node_modules/.bin/tsc +21 -0
- package/extensions/page-agent/node_modules/.bin/tsserver +21 -0
- package/extensions/page-agent/node_modules/.bin/tsx +21 -0
- package/extensions/page-agent/node_modules/.bin/vitest +21 -0
- package/extensions/page-agent/node_modules/.bin/yaml +21 -0
- package/extensions/page-agent/package.json +43 -0
- package/extensions/page-agent/poolbot.plugin.json +24 -0
- package/extensions/page-agent/src/PageAgentService.test.ts +517 -0
- package/extensions/page-agent/src/PageAgentService.ts +636 -0
- package/extensions/page-agent/src/PoolBotPageController.test.ts +358 -0
- package/extensions/page-agent/src/PoolBotPageController.ts +245 -0
- package/extensions/page-agent/src/index.ts +20 -0
- package/extensions/page-agent/src/tools.test.ts +231 -0
- package/extensions/page-agent/src/tools.ts +167 -0
- package/extensions/page-agent/src/types.ts +198 -0
- package/extensions/xyops/README.md +227 -0
- package/extensions/xyops/index.ts +342 -0
- package/extensions/xyops/node_modules/.bin/jiti +21 -0
- package/extensions/xyops/node_modules/.bin/tsc +21 -0
- package/extensions/xyops/node_modules/.bin/tsserver +21 -0
- package/extensions/xyops/node_modules/.bin/tsx +21 -0
- package/extensions/xyops/node_modules/.bin/vitest +21 -0
- package/extensions/xyops/node_modules/.bin/yaml +21 -0
- package/extensions/xyops/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/extensions/xyops/package.json +39 -0
- package/extensions/xyops/poolbot.plugin.json +21 -0
- package/extensions/xyops/src/client.test.ts +467 -0
- package/extensions/xyops/src/client.ts +157 -0
- package/extensions/xyops/src/types.ts +147 -0
- package/extensions/xyops/vitest.config.ts +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron Improvements
|
|
3
|
+
*
|
|
4
|
+
* Implements OpenClaw improvements:
|
|
5
|
+
* - Isolated delivery with delivery.mode: "none" | "webhook" | "announce"
|
|
6
|
+
* - Failure alerts configurable (thresholds, cooldowns)
|
|
7
|
+
* - Session target guardrail (reject sessionTarget: "main" for non-default agents)
|
|
8
|
+
* - Schedule errors with auto-disable and notification
|
|
9
|
+
* - One-shot retry with backoff configurable
|
|
10
|
+
*
|
|
11
|
+
* OpenClaw #32384, #30951
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_CRON_DELIVERY_CONFIG = {
|
|
14
|
+
mode: "none", // Default to no delivery (silent)
|
|
15
|
+
timeoutMs: 30_000, // 30 seconds
|
|
16
|
+
retryOnFailure: true,
|
|
17
|
+
maxRetries: 3,
|
|
18
|
+
retryDelayMs: 5000,
|
|
19
|
+
};
|
|
20
|
+
export const DEFAULT_CRON_FAILURE_ALERT_CONFIG = {
|
|
21
|
+
enabled: true,
|
|
22
|
+
failureThreshold: 3, // Alert after 3 consecutive failures
|
|
23
|
+
cooldownMs: 60 * 60 * 1000, // 1 hour cooldown
|
|
24
|
+
channels: ["log"], // Default to log only
|
|
25
|
+
includeErrorDetails: true,
|
|
26
|
+
includeJobMetadata: true,
|
|
27
|
+
};
|
|
28
|
+
const cronJobFailureStates = new Map();
|
|
29
|
+
/**
|
|
30
|
+
* Get or create cron job failure state
|
|
31
|
+
*/
|
|
32
|
+
export function getOrCreateCronJobFailureState(jobId) {
|
|
33
|
+
const existing = cronJobFailureStates.get(jobId);
|
|
34
|
+
if (existing) {
|
|
35
|
+
return existing;
|
|
36
|
+
}
|
|
37
|
+
const newState = {
|
|
38
|
+
consecutiveFailures: 0,
|
|
39
|
+
alertSuppressed: false,
|
|
40
|
+
};
|
|
41
|
+
cronJobFailureStates.set(jobId, newState);
|
|
42
|
+
return newState;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Record cron job failure and check if alert should be triggered
|
|
46
|
+
*/
|
|
47
|
+
export function recordCronJobFailure(params) {
|
|
48
|
+
const { jobId, error, config } = params;
|
|
49
|
+
const state = getOrCreateCronJobFailureState(jobId);
|
|
50
|
+
state.consecutiveFailures += 1;
|
|
51
|
+
state.lastFailureAt = Date.now();
|
|
52
|
+
state.lastError = error;
|
|
53
|
+
if (!config.enabled) {
|
|
54
|
+
return { shouldAlert: false, suppressed: false };
|
|
55
|
+
}
|
|
56
|
+
// Check cooldown
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
if (state.lastAlertAt && now - state.lastAlertAt < config.cooldownMs) {
|
|
59
|
+
state.alertSuppressed = true;
|
|
60
|
+
return { shouldAlert: false, suppressed: true };
|
|
61
|
+
}
|
|
62
|
+
// Check threshold
|
|
63
|
+
if (state.consecutiveFailures < config.failureThreshold) {
|
|
64
|
+
return { shouldAlert: false, suppressed: false };
|
|
65
|
+
}
|
|
66
|
+
state.alertSuppressed = false;
|
|
67
|
+
state.lastAlertAt = now;
|
|
68
|
+
return { shouldAlert: true, suppressed: false };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Record cron job success (resets failure count)
|
|
72
|
+
*/
|
|
73
|
+
export function recordCronJobSuccess(jobId) {
|
|
74
|
+
const state = cronJobFailureStates.get(jobId);
|
|
75
|
+
if (state) {
|
|
76
|
+
state.consecutiveFailures = 0;
|
|
77
|
+
state.alertSuppressed = false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export const DEFAULT_SESSION_TARGET_GUARDRAIL_CONFIG = {
|
|
81
|
+
enabled: true,
|
|
82
|
+
rejectMainForNonDefault: true,
|
|
83
|
+
allowExplicitOverride: true,
|
|
84
|
+
warnOnly: false,
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Validate session target for cron job
|
|
88
|
+
*/
|
|
89
|
+
export function validateSessionTarget(params) {
|
|
90
|
+
const { agentId, sessionTarget, defaultAgentId, config } = params;
|
|
91
|
+
if (!config.enabled) {
|
|
92
|
+
return { valid: true };
|
|
93
|
+
}
|
|
94
|
+
// Check if targeting "main" for non-default agent
|
|
95
|
+
if (config.rejectMainForNonDefault && sessionTarget === "main" && agentId !== defaultAgentId) {
|
|
96
|
+
const reason = `Cannot use sessionTarget "main" for non-default agent "${agentId}"`;
|
|
97
|
+
const suggestion = `Use explicit session key or target default agent "${defaultAgentId}"`;
|
|
98
|
+
if (config.warnOnly) {
|
|
99
|
+
// Log warning but allow
|
|
100
|
+
return { valid: true };
|
|
101
|
+
}
|
|
102
|
+
return { valid: false, reason, suggestion };
|
|
103
|
+
}
|
|
104
|
+
return { valid: true };
|
|
105
|
+
}
|
|
106
|
+
export const DEFAULT_SCHEDULE_ERROR_CONFIG = {
|
|
107
|
+
enabled: true,
|
|
108
|
+
autoDisableAfterErrors: 5,
|
|
109
|
+
errorWindowMs: 60 * 60 * 1000, // 1 hour window
|
|
110
|
+
notifyOnDisable: true,
|
|
111
|
+
notificationChannels: ["log"],
|
|
112
|
+
};
|
|
113
|
+
const scheduleErrorStates = new Map();
|
|
114
|
+
/**
|
|
115
|
+
* Get or create schedule error state
|
|
116
|
+
*/
|
|
117
|
+
export function getOrCreateScheduleErrorState(scheduleId) {
|
|
118
|
+
const existing = scheduleErrorStates.get(scheduleId);
|
|
119
|
+
if (existing) {
|
|
120
|
+
return existing;
|
|
121
|
+
}
|
|
122
|
+
const newState = {
|
|
123
|
+
errorTimestamps: [],
|
|
124
|
+
disabled: false,
|
|
125
|
+
};
|
|
126
|
+
scheduleErrorStates.set(scheduleId, newState);
|
|
127
|
+
return newState;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Record schedule error and check if auto-disable should trigger
|
|
131
|
+
*/
|
|
132
|
+
export function recordScheduleError(params) {
|
|
133
|
+
const { scheduleId, error: _error, config } = params;
|
|
134
|
+
const state = getOrCreateScheduleErrorState(scheduleId);
|
|
135
|
+
if (state.disabled) {
|
|
136
|
+
return { shouldDisable: false, shouldNotify: false };
|
|
137
|
+
}
|
|
138
|
+
const now = Date.now();
|
|
139
|
+
// Clean old errors outside window
|
|
140
|
+
state.errorTimestamps = state.errorTimestamps.filter((ts) => now - ts < config.errorWindowMs);
|
|
141
|
+
// Add new error
|
|
142
|
+
state.errorTimestamps.push(now);
|
|
143
|
+
if (!config.enabled) {
|
|
144
|
+
return { shouldDisable: false, shouldNotify: false };
|
|
145
|
+
}
|
|
146
|
+
// Check if should auto-disable
|
|
147
|
+
if (state.errorTimestamps.length >= config.autoDisableAfterErrors) {
|
|
148
|
+
state.disabled = true;
|
|
149
|
+
state.disabledAt = now;
|
|
150
|
+
state.disableReason = `Auto-disabled after ${state.errorTimestamps.length} errors in ${config.errorWindowMs / 1000}s`;
|
|
151
|
+
return { shouldDisable: true, shouldNotify: config.notifyOnDisable };
|
|
152
|
+
}
|
|
153
|
+
return { shouldDisable: false, shouldNotify: false };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Reset schedule error state (manual re-enable)
|
|
157
|
+
*/
|
|
158
|
+
export function resetScheduleErrorState(scheduleId) {
|
|
159
|
+
const state = scheduleErrorStates.get(scheduleId);
|
|
160
|
+
if (state) {
|
|
161
|
+
state.errorTimestamps = [];
|
|
162
|
+
state.disabled = false;
|
|
163
|
+
state.disabledAt = undefined;
|
|
164
|
+
state.disableReason = undefined;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export const DEFAULT_ONESHOT_RETRY_CONFIG = {
|
|
168
|
+
enabled: true,
|
|
169
|
+
maxRetries: 3,
|
|
170
|
+
baseDelayMs: 1000,
|
|
171
|
+
maxDelayMs: 60_000, // 1 minute
|
|
172
|
+
backoffMultiplier: 2,
|
|
173
|
+
jitterFactor: 0.1, // 10% jitter
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Calculate retry delay with exponential backoff and jitter
|
|
177
|
+
*/
|
|
178
|
+
export function calculateRetryDelay(attempt, config = DEFAULT_ONESHOT_RETRY_CONFIG) {
|
|
179
|
+
if (!config.enabled || attempt >= config.maxRetries) {
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
// Exponential backoff
|
|
183
|
+
const exponentialDelay = config.baseDelayMs * Math.pow(config.backoffMultiplier, attempt);
|
|
184
|
+
// Cap at max delay
|
|
185
|
+
const cappedDelay = Math.min(exponentialDelay, config.maxDelayMs);
|
|
186
|
+
// Add jitter
|
|
187
|
+
const jitter = cappedDelay * config.jitterFactor * (Math.random() * 2 - 1);
|
|
188
|
+
return Math.max(0, Math.floor(cappedDelay + jitter));
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Check if retry is allowed
|
|
192
|
+
*/
|
|
193
|
+
export function canRetry(attempt, config = DEFAULT_ONESHOT_RETRY_CONFIG) {
|
|
194
|
+
return config.enabled && attempt < config.maxRetries;
|
|
195
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord Channel Improvements
|
|
3
|
+
*
|
|
4
|
+
* Implements OpenClaw improvements:
|
|
5
|
+
* - Thread-bound subagent sessions (enhanced)
|
|
6
|
+
* - Status reactions configuráveis
|
|
7
|
+
* - Voice channel join/leave via /vc
|
|
8
|
+
* - Native slash command auth
|
|
9
|
+
* - Component interactions with wildcard handlers
|
|
10
|
+
*
|
|
11
|
+
* OpenClaw #32384, #30951
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_VOICE_CHANNEL_CONFIG = {
|
|
14
|
+
enabled: true,
|
|
15
|
+
autoJoin: false,
|
|
16
|
+
autoLeaveAfterSilenceMs: 5 * 60 * 1000, // 5 minutes
|
|
17
|
+
maxChannelsPerGuild: 5,
|
|
18
|
+
enableTts: false,
|
|
19
|
+
};
|
|
20
|
+
const voiceChannelSessions = new Map();
|
|
21
|
+
/**
|
|
22
|
+
* Get or create voice channel session
|
|
23
|
+
*/
|
|
24
|
+
export function getOrCreateVoiceChannelSession(params) {
|
|
25
|
+
const { channelId, guildId, ttsEnabled = false } = params;
|
|
26
|
+
const key = `${guildId}:${channelId}`;
|
|
27
|
+
const existing = voiceChannelSessions.get(key);
|
|
28
|
+
if (existing) {
|
|
29
|
+
return existing;
|
|
30
|
+
}
|
|
31
|
+
const session = {
|
|
32
|
+
channelId,
|
|
33
|
+
guildId,
|
|
34
|
+
joinedAt: Date.now(),
|
|
35
|
+
lastActivityAt: Date.now(),
|
|
36
|
+
ttsEnabled,
|
|
37
|
+
};
|
|
38
|
+
voiceChannelSessions.set(key, session);
|
|
39
|
+
return session;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Update voice channel session activity
|
|
43
|
+
*/
|
|
44
|
+
export function updateVoiceChannelActivity(channelId, guildId) {
|
|
45
|
+
const key = `${guildId}:${channelId}`;
|
|
46
|
+
const session = voiceChannelSessions.get(key);
|
|
47
|
+
if (session) {
|
|
48
|
+
session.lastActivityAt = Date.now();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Remove voice channel session
|
|
53
|
+
*/
|
|
54
|
+
export function removeVoiceChannelSession(channelId, guildId) {
|
|
55
|
+
const key = `${guildId}:${channelId}`;
|
|
56
|
+
return voiceChannelSessions.delete(key);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Check for stale voice channel sessions
|
|
60
|
+
*/
|
|
61
|
+
export function getStaleVoiceChannelSessions(config = DEFAULT_VOICE_CHANNEL_CONFIG) {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const stale = [];
|
|
64
|
+
for (const session of voiceChannelSessions.values()) {
|
|
65
|
+
if (now - session.lastActivityAt > config.autoLeaveAfterSilenceMs) {
|
|
66
|
+
stale.push(session);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return stale;
|
|
70
|
+
}
|
|
71
|
+
export const DEFAULT_SLASH_COMMAND_AUTH_CONFIG = {
|
|
72
|
+
enabled: true,
|
|
73
|
+
requireAuthForAll: false,
|
|
74
|
+
protectedCommands: ["admin", "config", "pair", "auth"],
|
|
75
|
+
allowLoopback: true,
|
|
76
|
+
authTimeoutMs: 5 * 60 * 1000, // 5 minutes
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Check if a slash command requires authentication
|
|
80
|
+
*/
|
|
81
|
+
export function isSlashCommandProtected(commandName, config = DEFAULT_SLASH_COMMAND_AUTH_CONFIG) {
|
|
82
|
+
if (!config.enabled) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (config.requireAuthForAll) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
return config.protectedCommands.includes(commandName.toLowerCase());
|
|
89
|
+
}
|
|
90
|
+
export const DEFAULT_COMPONENT_INTERACTION_CONFIG = {
|
|
91
|
+
enabled: true,
|
|
92
|
+
wildcardPatterns: ["action:*", "button:*", "select:*", "modal:*"],
|
|
93
|
+
rateLimitPerMinute: 60,
|
|
94
|
+
responseTimeoutMs: 3000, // Discord requires response within 3 seconds
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Match custom ID against wildcard patterns
|
|
98
|
+
*/
|
|
99
|
+
export function matchWildcardPattern(customId, config = DEFAULT_COMPONENT_INTERACTION_CONFIG) {
|
|
100
|
+
if (!config.enabled) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
for (const pattern of config.wildcardPatterns) {
|
|
104
|
+
// Convert wildcard pattern to regex
|
|
105
|
+
// * matches any characters except :
|
|
106
|
+
const regexPattern = pattern.replace(/\*/g, "[^:]*");
|
|
107
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
108
|
+
if (regex.test(customId)) {
|
|
109
|
+
return pattern;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Component interaction rate limiting
|
|
116
|
+
*/
|
|
117
|
+
const interactionCounts = new Map();
|
|
118
|
+
/**
|
|
119
|
+
* Check if component interaction is rate limited
|
|
120
|
+
*/
|
|
121
|
+
export function checkComponentInteractionRateLimit(userId, config = DEFAULT_COMPONENT_INTERACTION_CONFIG) {
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
const windowMs = 60_000; // 1 minute
|
|
124
|
+
const timestamps = interactionCounts.get(userId) ?? [];
|
|
125
|
+
const recentTimestamps = timestamps.filter((ts) => now - ts < windowMs);
|
|
126
|
+
if (recentTimestamps.length >= config.rateLimitPerMinute) {
|
|
127
|
+
const oldestTimestamp = recentTimestamps[0];
|
|
128
|
+
const retryAfterMs = windowMs - (now - oldestTimestamp);
|
|
129
|
+
return { allowed: false, retryAfterMs };
|
|
130
|
+
}
|
|
131
|
+
recentTimestamps.push(now);
|
|
132
|
+
interactionCounts.set(userId, recentTimestamps);
|
|
133
|
+
return { allowed: true };
|
|
134
|
+
}
|
|
135
|
+
export const DEFAULT_THREAD_BINDING_ENHANCEMENT = {
|
|
136
|
+
autoBindSubagents: true,
|
|
137
|
+
threadTtlMs: 60 * 60 * 1000, // 1 hour
|
|
138
|
+
autoUnbindOnSessionEnd: true,
|
|
139
|
+
maxThreadsPerSession: 10,
|
|
140
|
+
};
|
|
141
|
+
export const DEFAULT_DISCORD_STATUS_REACTION_CONFIG = {
|
|
142
|
+
enabled: true,
|
|
143
|
+
showInThreads: true,
|
|
144
|
+
showInChannels: false, // Don't spam channels by default
|
|
145
|
+
useEmojiReactions: true,
|
|
146
|
+
useEmbedStatus: false,
|
|
147
|
+
emojis: {
|
|
148
|
+
queued: "👀",
|
|
149
|
+
thinking: "🤔",
|
|
150
|
+
tool: "⚡",
|
|
151
|
+
coding: "👨💻",
|
|
152
|
+
web: "🌐",
|
|
153
|
+
done: "✅",
|
|
154
|
+
error: "❌",
|
|
155
|
+
stallSoft: "⏳",
|
|
156
|
+
stallHard: "⚠️",
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Get status reaction emoji for a given status
|
|
161
|
+
*/
|
|
162
|
+
export function getStatusReactionEmoji(status, config = DEFAULT_DISCORD_STATUS_REACTION_CONFIG) {
|
|
163
|
+
if (!config.enabled) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
return config.emojis[status.toLowerCase()] ?? null;
|
|
167
|
+
}
|
|
@@ -26,6 +26,12 @@ const DEFAULT_MAX_ATTEMPTS = 10;
|
|
|
26
26
|
const DEFAULT_WINDOW_MS = 60_000; // 1 minute
|
|
27
27
|
const DEFAULT_LOCKOUT_MS = 300_000; // 5 minutes
|
|
28
28
|
const PRUNE_INTERVAL_MS = 60_000; // prune stale entries every minute
|
|
29
|
+
// Control-plane rate limiting: 3 requests per minute per device+IP
|
|
30
|
+
// OpenClaw #32384 - prevents abuse of control-plane endpoints
|
|
31
|
+
export const CONTROL_PLANE_MAX_ATTEMPTS = 3;
|
|
32
|
+
export const CONTROL_PLANE_WINDOW_MS = 60_000; // 1 minute
|
|
33
|
+
export const CONTROL_PLANE_LOCKOUT_MS = 300_000; // 5 minutes
|
|
34
|
+
export const AUTH_RATE_LIMIT_SCOPE_CONTROL_PLANE = "control-plane";
|
|
29
35
|
// ---------------------------------------------------------------------------
|
|
30
36
|
// Implementation
|
|
31
37
|
// ---------------------------------------------------------------------------
|
|
@@ -134,3 +140,16 @@ export function createAuthRateLimiter(config) {
|
|
|
134
140
|
}
|
|
135
141
|
return { check, recordFailure, reset, size, prune, dispose };
|
|
136
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Create a rate limiter specifically for control-plane operations.
|
|
145
|
+
* Uses stricter limits (3 req/min) to prevent abuse of sensitive endpoints.
|
|
146
|
+
* OpenClaw #32384
|
|
147
|
+
*/
|
|
148
|
+
export function createControlPlaneRateLimiter() {
|
|
149
|
+
return createAuthRateLimiter({
|
|
150
|
+
maxAttempts: CONTROL_PLANE_MAX_ATTEMPTS,
|
|
151
|
+
windowMs: CONTROL_PLANE_WINDOW_MS,
|
|
152
|
+
lockoutMs: CONTROL_PLANE_LOCKOUT_MS,
|
|
153
|
+
exemptLoopback: true, // Always exempt loopback for local development
|
|
154
|
+
});
|
|
155
|
+
}
|
package/dist/gateway/auth.js
CHANGED
|
@@ -222,6 +222,47 @@ function authorizeTrustedProxy(params) {
|
|
|
222
222
|
function shouldAllowTailscaleHeaderAuth(authSurface) {
|
|
223
223
|
return authSurface === "ws-control-ui";
|
|
224
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Check if a request is from loopback (localhost) for auto-approve scenarios.
|
|
227
|
+
* OpenClaw #32384 - loopback requests get auto-approved for pairing scope upgrades.
|
|
228
|
+
*/
|
|
229
|
+
export function isLoopbackRequest(req) {
|
|
230
|
+
if (!req?.socket?.remoteAddress) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
return isLoopbackAddress(req.socket.remoteAddress);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Auto-approve scope upgrades for loopback requests.
|
|
237
|
+
* This allows tokenless local development while maintaining security for remote requests.
|
|
238
|
+
* OpenClaw #32384
|
|
239
|
+
*/
|
|
240
|
+
export function shouldAutoApproveScopeUpgrade(params) {
|
|
241
|
+
const { req, requestedScopes, currentScopes } = params;
|
|
242
|
+
// Only auto-approve for loopback requests
|
|
243
|
+
if (!isLoopbackRequest(req)) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
// Check if requested scopes are strictly greater than current scopes
|
|
247
|
+
const currentSet = new Set(currentScopes.map((s) => s.trim().toLowerCase()));
|
|
248
|
+
const newScopes = requestedScopes.filter((s) => !currentSet.has(s.trim().toLowerCase()));
|
|
249
|
+
// Auto-approve if all new scopes are "safe" (non-destructive operations)
|
|
250
|
+
const safeScopes = new Set([
|
|
251
|
+
"read",
|
|
252
|
+
"read:config",
|
|
253
|
+
"read:status",
|
|
254
|
+
"write",
|
|
255
|
+
"write:config",
|
|
256
|
+
"execute",
|
|
257
|
+
"execute:tool",
|
|
258
|
+
]);
|
|
259
|
+
return newScopes.every((scope) => {
|
|
260
|
+
const normalized = scope.trim().toLowerCase();
|
|
261
|
+
return (safeScopes.has(normalized) ||
|
|
262
|
+
normalized.startsWith("read:") ||
|
|
263
|
+
normalized.startsWith("execute:"));
|
|
264
|
+
});
|
|
265
|
+
}
|
|
225
266
|
export async function authorizeGatewayConnect(params) {
|
|
226
267
|
const { auth, connectAuth, req, trustedProxies } = params;
|
|
227
268
|
const tailscaleWhois = params.tailscaleWhois ?? readTailscaleWhoisIdentity;
|