@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,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Channel Improvements
|
|
3
|
+
*
|
|
4
|
+
* Implements OpenClaw improvements:
|
|
5
|
+
* - Streaming preview with sendMessageDraft for DMs
|
|
6
|
+
* - Reactions as system events (configurable)
|
|
7
|
+
* - Polling offset safety with validation
|
|
8
|
+
* - Webhook mode recovery with cert re-upload
|
|
9
|
+
* - Topic session isolation for DMs
|
|
10
|
+
*
|
|
11
|
+
* OpenClaw #32384, #30951
|
|
12
|
+
*/
|
|
13
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
14
|
+
const log = createSubsystemLogger("telegram-improvements");
|
|
15
|
+
export const DEFAULT_TELEGRAM_STREAMING_CONFIG = {
|
|
16
|
+
enabledForDms: true,
|
|
17
|
+
enabledForGroups: false,
|
|
18
|
+
chunkDelayMs: 500,
|
|
19
|
+
minChunkSize: 50,
|
|
20
|
+
useDraftMethod: true,
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Check if a chat is a DM (private chat)
|
|
24
|
+
*/
|
|
25
|
+
export function isPrivateChat(ctx) {
|
|
26
|
+
return ctx.chat?.type === "private";
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if streaming should be enabled for this chat
|
|
30
|
+
*/
|
|
31
|
+
export function shouldEnableStreaming(ctx, config = DEFAULT_TELEGRAM_STREAMING_CONFIG) {
|
|
32
|
+
if (!config.enabledForDms && !config.enabledForGroups) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (isPrivateChat(ctx)) {
|
|
36
|
+
return config.enabledForDms;
|
|
37
|
+
}
|
|
38
|
+
return config.enabledForGroups;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Split message into chunks for streaming
|
|
42
|
+
*/
|
|
43
|
+
export function splitMessageForStreaming(text, config = DEFAULT_TELEGRAM_STREAMING_CONFIG) {
|
|
44
|
+
const chunks = [];
|
|
45
|
+
let remaining = text;
|
|
46
|
+
while (remaining.length > 0) {
|
|
47
|
+
// Take up to minChunkSize characters
|
|
48
|
+
const chunkSize = Math.min(config.minChunkSize, remaining.length);
|
|
49
|
+
const chunk = remaining.slice(0, chunkSize);
|
|
50
|
+
chunks.push(chunk);
|
|
51
|
+
remaining = remaining.slice(chunkSize);
|
|
52
|
+
}
|
|
53
|
+
return chunks;
|
|
54
|
+
}
|
|
55
|
+
export const DEFAULT_STATUS_REACTION_CONFIG = {
|
|
56
|
+
enabled: true,
|
|
57
|
+
showInDms: false, // Don't spam DMs with reactions by default
|
|
58
|
+
showInGroups: true,
|
|
59
|
+
emojis: {
|
|
60
|
+
queued: ["👀", "👍"],
|
|
61
|
+
thinking: ["🤔", "🤓"],
|
|
62
|
+
tool: ["🔥", "⚡"],
|
|
63
|
+
done: ["👍", "🎉"],
|
|
64
|
+
error: ["😱", "😨"],
|
|
65
|
+
},
|
|
66
|
+
useSystemEvents: true, // Log as system events instead of visible reactions
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Validate polling offset for safety
|
|
70
|
+
* OpenClaw #32384 - prevents offset corruption
|
|
71
|
+
*/
|
|
72
|
+
export function validatePollingOffset(params) {
|
|
73
|
+
const { currentOffset, lastUpdateId, maxAllowedGap } = params;
|
|
74
|
+
// No validation needed if no previous offset
|
|
75
|
+
if (currentOffset === null || currentOffset === 0) {
|
|
76
|
+
return { valid: true };
|
|
77
|
+
}
|
|
78
|
+
// Check for negative offset (corruption)
|
|
79
|
+
if (currentOffset < 0) {
|
|
80
|
+
log.warn("Polling offset is negative (corrupted), resetting to lastUpdateId", {
|
|
81
|
+
currentOffset,
|
|
82
|
+
lastUpdateId,
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
valid: false,
|
|
86
|
+
reason: "Negative offset detected (corruption)",
|
|
87
|
+
suggestedOffset: lastUpdateId,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Check for excessive gap (possible data loss)
|
|
91
|
+
if (lastUpdateId !== null) {
|
|
92
|
+
const gap = lastUpdateId - currentOffset;
|
|
93
|
+
if (gap > maxAllowedGap) {
|
|
94
|
+
log.warn("Polling offset gap too large, possible update loss", {
|
|
95
|
+
currentOffset,
|
|
96
|
+
lastUpdateId,
|
|
97
|
+
gap,
|
|
98
|
+
maxAllowedGap,
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
reason: `Offset gap too large (${gap} > ${maxAllowedGap})`,
|
|
103
|
+
suggestedOffset: lastUpdateId,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { valid: true };
|
|
108
|
+
}
|
|
109
|
+
export const DEFAULT_WEBHOOK_RECOVERY_CONFIG = {
|
|
110
|
+
enabled: true,
|
|
111
|
+
maxRetries: 3,
|
|
112
|
+
retryDelayMs: 5000,
|
|
113
|
+
reuploadCerts: true,
|
|
114
|
+
fallbackToPolling: true,
|
|
115
|
+
};
|
|
116
|
+
const webhookRecoveryStates = new Map();
|
|
117
|
+
/**
|
|
118
|
+
* Get or create webhook recovery state for a bot
|
|
119
|
+
*/
|
|
120
|
+
export function getWebhookRecoveryState(botId) {
|
|
121
|
+
const existing = webhookRecoveryStates.get(botId);
|
|
122
|
+
if (existing) {
|
|
123
|
+
return existing;
|
|
124
|
+
}
|
|
125
|
+
const newState = {
|
|
126
|
+
attempt: 0,
|
|
127
|
+
recovering: false,
|
|
128
|
+
usePolling: false,
|
|
129
|
+
};
|
|
130
|
+
webhookRecoveryStates.set(botId, newState);
|
|
131
|
+
return newState;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Record webhook failure and check if recovery should be attempted
|
|
135
|
+
*/
|
|
136
|
+
export function recordWebhookFailure(params) {
|
|
137
|
+
const { botId, error, config } = params;
|
|
138
|
+
const state = getWebhookRecoveryState(botId);
|
|
139
|
+
state.attempt += 1;
|
|
140
|
+
state.lastError = error;
|
|
141
|
+
if (!config.enabled) {
|
|
142
|
+
return { canRecover: false, shouldFallback: false, retryAfterMs: 0 };
|
|
143
|
+
}
|
|
144
|
+
if (state.attempt > config.maxRetries) {
|
|
145
|
+
state.recovering = false;
|
|
146
|
+
if (config.fallbackToPolling) {
|
|
147
|
+
state.usePolling = true;
|
|
148
|
+
log.warn(`Webhook recovery failed after ${state.attempt} attempts, falling back to polling`, {
|
|
149
|
+
botId,
|
|
150
|
+
});
|
|
151
|
+
return { canRecover: false, shouldFallback: true, retryAfterMs: 0 };
|
|
152
|
+
}
|
|
153
|
+
return { canRecover: false, shouldFallback: false, retryAfterMs: 0 };
|
|
154
|
+
}
|
|
155
|
+
state.recovering = true;
|
|
156
|
+
const retryAfterMs = config.retryDelayMs * Math.pow(2, state.attempt - 1); // Exponential backoff
|
|
157
|
+
log.info(`Webhook failure recorded, attempting recovery in ${retryAfterMs}ms`, {
|
|
158
|
+
botId,
|
|
159
|
+
attempt: state.attempt,
|
|
160
|
+
error,
|
|
161
|
+
});
|
|
162
|
+
return { canRecover: true, shouldFallback: false, retryAfterMs };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Reset webhook recovery state after successful recovery
|
|
166
|
+
*/
|
|
167
|
+
export function resetWebhookRecoveryState(botId) {
|
|
168
|
+
const state = webhookRecoveryStates.get(botId);
|
|
169
|
+
if (state) {
|
|
170
|
+
state.attempt = 0;
|
|
171
|
+
state.lastError = undefined;
|
|
172
|
+
state.recovering = false;
|
|
173
|
+
state.usePolling = false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
export const DEFAULT_TOPIC_SESSION_CONFIG = {
|
|
177
|
+
enabledForDms: false, // DMs typically don't need topic isolation
|
|
178
|
+
enabledForGroups: true,
|
|
179
|
+
separator: ":",
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Build session key with topic isolation
|
|
183
|
+
*/
|
|
184
|
+
export function buildTopicSessionKey(params) {
|
|
185
|
+
const { chatId, userId, threadId, topicId, config } = params;
|
|
186
|
+
const parts = [chatId];
|
|
187
|
+
if (topicId !== undefined && config.enabledForGroups) {
|
|
188
|
+
parts.push(config.separator, "topic", config.separator, topicId);
|
|
189
|
+
}
|
|
190
|
+
if (threadId !== undefined && config.enabledForGroups) {
|
|
191
|
+
parts.push(config.separator, "thread", config.separator, threadId);
|
|
192
|
+
}
|
|
193
|
+
if (userId !== undefined) {
|
|
194
|
+
parts.push(config.separator, "user", config.separator, userId);
|
|
195
|
+
}
|
|
196
|
+
return parts.join("");
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Parse session key to extract topic/thread info
|
|
200
|
+
*/
|
|
201
|
+
export function parseTopicSessionKey(sessionKey) {
|
|
202
|
+
const parts = sessionKey.split(":");
|
|
203
|
+
const result = {
|
|
204
|
+
chatId: parts[0],
|
|
205
|
+
};
|
|
206
|
+
for (let i = 1; i < parts.length; i += 2) {
|
|
207
|
+
const key = parts[i];
|
|
208
|
+
const value = parts[i + 1];
|
|
209
|
+
if (key === "topic") {
|
|
210
|
+
result.topicId = value;
|
|
211
|
+
}
|
|
212
|
+
else if (key === "thread") {
|
|
213
|
+
result.threadId = value;
|
|
214
|
+
}
|
|
215
|
+
else if (key === "user") {
|
|
216
|
+
result.userId = value;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI/UX & Plugins Enhancements
|
|
3
|
+
*
|
|
4
|
+
* Implements OpenClaw improvements:
|
|
5
|
+
* - Cron editor with clone functionality
|
|
6
|
+
* - Sessions cleanup UI
|
|
7
|
+
* - Scoped plugin SDK imports
|
|
8
|
+
* - Hook session lifecycle (session_start/session_end)
|
|
9
|
+
* - HTTP route registration API
|
|
10
|
+
*
|
|
11
|
+
* OpenClaw #30951, #32384
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_CRON_EDITOR_CONFIG = {
|
|
14
|
+
enabled: true,
|
|
15
|
+
enableClone: true,
|
|
16
|
+
enableRichValidation: true,
|
|
17
|
+
validationRules: [
|
|
18
|
+
{
|
|
19
|
+
name: "no-past-schedule",
|
|
20
|
+
pattern: "^(\\*|[0-9]+)\\s+(\\*|[0-9]+)\\s+(\\*|[0-9]+)\\s+(\\*|[0-9]+)\\s+(\\*|[0-9]+)$",
|
|
21
|
+
errorMessage: "Invalid cron expression",
|
|
22
|
+
severity: "error",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "reasonable-frequency",
|
|
26
|
+
pattern: "^(\\*/[1-9][0-9]*|\\*|[0-9]+)\\s+.*$",
|
|
27
|
+
errorMessage: "Schedule runs too frequently (consider increasing interval)",
|
|
28
|
+
severity: "warning",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
defaultTimezone: "UTC",
|
|
32
|
+
showCronExpressions: true,
|
|
33
|
+
showNextRunTimes: true,
|
|
34
|
+
maxNextRunsToShow: 5,
|
|
35
|
+
};
|
|
36
|
+
export const DEFAULT_CRON_CLONE_CONFIG = {
|
|
37
|
+
nameSuffix: " (copy)",
|
|
38
|
+
enabledByDefault: false, // Start disabled to allow configuration
|
|
39
|
+
preserveSchedule: true,
|
|
40
|
+
preserveDelivery: true,
|
|
41
|
+
resetFailureCount: true,
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Validate cron expression
|
|
45
|
+
*/
|
|
46
|
+
export function validateCronExpression(expression, config = DEFAULT_CRON_EDITOR_CONFIG) {
|
|
47
|
+
if (!config.enableRichValidation) {
|
|
48
|
+
// Basic validation only
|
|
49
|
+
const parts = expression.trim().split(/\s+/);
|
|
50
|
+
if (parts.length !== 5) {
|
|
51
|
+
return { valid: false, errors: ["Cron expression must have 5 parts"], warnings: [] };
|
|
52
|
+
}
|
|
53
|
+
return { valid: true, errors: [], warnings: [] };
|
|
54
|
+
}
|
|
55
|
+
const errors = [];
|
|
56
|
+
const warnings = [];
|
|
57
|
+
// Check against validation rules
|
|
58
|
+
for (const rule of config.validationRules) {
|
|
59
|
+
const regex = new RegExp(rule.pattern);
|
|
60
|
+
if (!regex.test(expression)) {
|
|
61
|
+
if (rule.severity === "error") {
|
|
62
|
+
errors.push(rule.errorMessage);
|
|
63
|
+
}
|
|
64
|
+
else if (rule.severity === "warning") {
|
|
65
|
+
warnings.push(rule.errorMessage);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (errors.length === 0) {
|
|
70
|
+
return { valid: true, errors: [], warnings: [] };
|
|
71
|
+
}
|
|
72
|
+
return { valid: false, errors, warnings };
|
|
73
|
+
}
|
|
74
|
+
export const DEFAULT_SESSIONS_CLEANUP_CONFIG = {
|
|
75
|
+
enabled: true,
|
|
76
|
+
showSessionAge: true,
|
|
77
|
+
showSessionSize: true,
|
|
78
|
+
showLastActivity: true,
|
|
79
|
+
enableBulkDelete: true,
|
|
80
|
+
enableSearch: true,
|
|
81
|
+
enableExport: true,
|
|
82
|
+
autoCleanup: false, // Disabled by default for safety
|
|
83
|
+
autoCleanupAgeDays: 30,
|
|
84
|
+
confirmBeforeDelete: true,
|
|
85
|
+
};
|
|
86
|
+
export const DEFAULT_SCOPED_PLUGIN_SDK_CONFIG = {
|
|
87
|
+
enabled: true,
|
|
88
|
+
allowedModules: [
|
|
89
|
+
"@poolbot/plugin-sdk",
|
|
90
|
+
"@poolbot/plugin-sdk/tools",
|
|
91
|
+
"@poolbot/plugin-sdk/providers",
|
|
92
|
+
"@poolbot/plugin-sdk/hooks",
|
|
93
|
+
],
|
|
94
|
+
blockedMethods: [
|
|
95
|
+
"require",
|
|
96
|
+
"eval",
|
|
97
|
+
"Function",
|
|
98
|
+
"vm.runInContext",
|
|
99
|
+
"child_process.exec",
|
|
100
|
+
"child_process.spawn",
|
|
101
|
+
],
|
|
102
|
+
requireExplicitImports: true,
|
|
103
|
+
logViolations: true,
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Validate plugin SDK import
|
|
107
|
+
*/
|
|
108
|
+
export function validatePluginSdkImport(params) {
|
|
109
|
+
const { importPath, config } = params;
|
|
110
|
+
if (!config.enabled) {
|
|
111
|
+
return { allowed: true };
|
|
112
|
+
}
|
|
113
|
+
// Check if module is allowed
|
|
114
|
+
if (!config.allowedModules.some((mod) => importPath.startsWith(mod))) {
|
|
115
|
+
return { allowed: false, reason: `Module "${importPath}" is not in allowed list` };
|
|
116
|
+
}
|
|
117
|
+
return { allowed: true };
|
|
118
|
+
}
|
|
119
|
+
export const DEFAULT_HOOK_SESSION_LIFECYCLE_CONFIG = {
|
|
120
|
+
enabled: true,
|
|
121
|
+
callSessionStart: true,
|
|
122
|
+
callSessionEnd: true,
|
|
123
|
+
includeSessionKey: true,
|
|
124
|
+
includeAgentMetadata: true,
|
|
125
|
+
hookTimeoutMs: 5000,
|
|
126
|
+
continueOnFailure: true,
|
|
127
|
+
};
|
|
128
|
+
export const DEFAULT_HTTP_ROUTE_REGISTRATION_CONFIG = {
|
|
129
|
+
enabled: true,
|
|
130
|
+
routePrefix: "/api/plugins",
|
|
131
|
+
requireAuth: true,
|
|
132
|
+
rateLimitPerMinute: 60,
|
|
133
|
+
maxRoutesPerPlugin: 10,
|
|
134
|
+
allowedMethods: ["GET", "POST"],
|
|
135
|
+
blockedPaths: ["/admin", "/config", "/auth", "/internal", "/gateway", "/system"],
|
|
136
|
+
};
|
|
137
|
+
const registeredRoutes = new Map();
|
|
138
|
+
/**
|
|
139
|
+
* Register HTTP route for plugin
|
|
140
|
+
*/
|
|
141
|
+
export function registerHttpRoute(params) {
|
|
142
|
+
const { path, method, pluginId, handler, config } = params;
|
|
143
|
+
if (!config.enabled) {
|
|
144
|
+
return { success: false, reason: "HTTP route registration is disabled" };
|
|
145
|
+
}
|
|
146
|
+
// Check if path is blocked
|
|
147
|
+
if (config.blockedPaths.some((blocked) => path.startsWith(blocked))) {
|
|
148
|
+
return { success: false, reason: `Path "${path}" is blocked` };
|
|
149
|
+
}
|
|
150
|
+
// Check if method is allowed
|
|
151
|
+
if (!config.allowedMethods.includes(method.toUpperCase())) {
|
|
152
|
+
return { success: false, reason: `Method "${method}" is not allowed` };
|
|
153
|
+
}
|
|
154
|
+
// Check route count for plugin
|
|
155
|
+
const pluginRoutes = registeredRoutes.get(pluginId) ?? [];
|
|
156
|
+
if (pluginRoutes.length >= config.maxRoutesPerPlugin) {
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
reason: `Max routes (${config.maxRoutesPerPlugin}) exceeded for plugin`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const fullPath = `${config.routePrefix}${path}`;
|
|
163
|
+
const route = {
|
|
164
|
+
path: fullPath,
|
|
165
|
+
method: method.toUpperCase(),
|
|
166
|
+
pluginId,
|
|
167
|
+
handler,
|
|
168
|
+
registeredAt: Date.now(),
|
|
169
|
+
};
|
|
170
|
+
pluginRoutes.push(route);
|
|
171
|
+
registeredRoutes.set(pluginId, pluginRoutes);
|
|
172
|
+
return { success: true, fullPath };
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get registered routes for plugin
|
|
176
|
+
*/
|
|
177
|
+
export function getPluginRoutes(pluginId) {
|
|
178
|
+
return registeredRoutes.get(pluginId) ?? [];
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Unregister all routes for plugin
|
|
182
|
+
*/
|
|
183
|
+
export function unregisterPluginRoutes(pluginId) {
|
|
184
|
+
const routes = registeredRoutes.get(pluginId);
|
|
185
|
+
if (!routes) {
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
const count = routes.length;
|
|
189
|
+
registeredRoutes.delete(pluginId);
|
|
190
|
+
return count;
|
|
191
|
+
}
|