@poolzin/pool-bot 2026.3.11 → 2026.3.14

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.
Files changed (195) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/dist/.buildstamp +1 -1
  3. package/dist/agents/checkpoint-manager.js +291 -0
  4. package/dist/agents/poolbot-tools.js +5 -0
  5. package/dist/agents/subagent-announce-reliability.js +160 -0
  6. package/dist/agents/tool-result-truncation.js +299 -0
  7. package/dist/agents/tools/nodes-file-tool.js +197 -0
  8. package/dist/build-info.json +3 -3
  9. package/dist/cli/config-cli.js +60 -0
  10. package/dist/cron/cron-improvements.js +195 -0
  11. package/dist/discord/discord-improvements.js +167 -0
  12. package/dist/gateway/auth-rate-limit.js +19 -0
  13. package/dist/gateway/auth.js +41 -0
  14. package/dist/gateway/gateway-improvements.js +294 -0
  15. package/dist/gateway/node-command-policy.js +7 -2
  16. package/dist/infra/net/ssrf.js +15 -2
  17. package/dist/infra/shell-security.js +201 -0
  18. package/dist/memory/memory-improvements.js +239 -0
  19. package/dist/node-host/runner.js +146 -79
  20. package/dist/security/prototype-pollution.js +141 -0
  21. package/dist/security/webhook-security.js +253 -0
  22. package/dist/shared/net/ip.js +52 -1
  23. package/dist/slack/slack-improvements.js +225 -0
  24. package/dist/telegram/telegram-improvements.js +220 -0
  25. package/dist/ui-plugins/ui-plugins-improvements.js +191 -0
  26. package/docs/ANALISE_OPENCLAW_PROFISSIONAL.md +520 -0
  27. package/docs/competitive-analysis.md +421 -0
  28. package/docs/implementation-analysis.md +393 -0
  29. package/docs/plans/2026-03-11-file-operations-security-hardening.md +307 -0
  30. package/docs/plans/2026-03-11-integracao-projetos-poolbot.md +666 -0
  31. package/docs/refactor/plugin-development-guide.md +281 -0
  32. package/extensions/agency-agents/README.md +301 -0
  33. package/extensions/agency-agents/agents/CONTRIBUTING.md +353 -0
  34. package/extensions/agency-agents/agents/README.md +602 -0
  35. package/extensions/agency-agents/agents/design/design-brand-guardian.md +320 -0
  36. package/extensions/agency-agents/agents/design/design-image-prompt-engineer.md +234 -0
  37. package/extensions/agency-agents/agents/design/design-ui-designer.md +381 -0
  38. package/extensions/agency-agents/agents/design/design-ux-architect.md +467 -0
  39. package/extensions/agency-agents/agents/design/design-ux-researcher.md +327 -0
  40. package/extensions/agency-agents/agents/design/design-visual-storyteller.md +147 -0
  41. package/extensions/agency-agents/agents/design/design-whimsy-injector.md +436 -0
  42. package/extensions/agency-agents/agents/engineering/engineering-ai-engineer.md +144 -0
  43. package/extensions/agency-agents/agents/engineering/engineering-backend-architect.md +233 -0
  44. package/extensions/agency-agents/agents/engineering/engineering-devops-automator.md +374 -0
  45. package/extensions/agency-agents/agents/engineering/engineering-frontend-developer.md +223 -0
  46. package/extensions/agency-agents/agents/engineering/engineering-mobile-app-builder.md +491 -0
  47. package/extensions/agency-agents/agents/engineering/engineering-rapid-prototyper.md +460 -0
  48. package/extensions/agency-agents/agents/engineering/engineering-security-engineer.md +275 -0
  49. package/extensions/agency-agents/agents/engineering/engineering-senior-developer.md +174 -0
  50. package/extensions/agency-agents/agents/examples/README.md +48 -0
  51. package/extensions/agency-agents/agents/examples/nexus-spatial-discovery.md +852 -0
  52. package/extensions/agency-agents/agents/examples/workflow-landing-page.md +119 -0
  53. package/extensions/agency-agents/agents/examples/workflow-startup-mvp.md +155 -0
  54. package/extensions/agency-agents/agents/integrations/README.md +117 -0
  55. package/extensions/agency-agents/agents/integrations/aider/README.md +38 -0
  56. package/extensions/agency-agents/agents/integrations/antigravity/README.md +49 -0
  57. package/extensions/agency-agents/agents/integrations/claude-code/README.md +31 -0
  58. package/extensions/agency-agents/agents/integrations/cursor/README.md +38 -0
  59. package/extensions/agency-agents/agents/integrations/gemini-cli/README.md +36 -0
  60. package/extensions/agency-agents/agents/integrations/opencode/README.md +58 -0
  61. package/extensions/agency-agents/agents/integrations/windsurf/README.md +26 -0
  62. package/extensions/agency-agents/agents/marketing/marketing-app-store-optimizer.md +319 -0
  63. package/extensions/agency-agents/agents/marketing/marketing-content-creator.md +52 -0
  64. package/extensions/agency-agents/agents/marketing/marketing-growth-hacker.md +52 -0
  65. package/extensions/agency-agents/agents/marketing/marketing-instagram-curator.md +111 -0
  66. package/extensions/agency-agents/agents/marketing/marketing-reddit-community-builder.md +121 -0
  67. package/extensions/agency-agents/agents/marketing/marketing-social-media-strategist.md +123 -0
  68. package/extensions/agency-agents/agents/marketing/marketing-tiktok-strategist.md +123 -0
  69. package/extensions/agency-agents/agents/marketing/marketing-twitter-engager.md +124 -0
  70. package/extensions/agency-agents/agents/marketing/marketing-wechat-official-account.md +143 -0
  71. package/extensions/agency-agents/agents/marketing/marketing-xiaohongshu-specialist.md +136 -0
  72. package/extensions/agency-agents/agents/marketing/marketing-zhihu-strategist.md +160 -0
  73. package/extensions/agency-agents/agents/product/product-feedback-synthesizer.md +117 -0
  74. package/extensions/agency-agents/agents/product/product-sprint-prioritizer.md +152 -0
  75. package/extensions/agency-agents/agents/product/product-trend-researcher.md +157 -0
  76. package/extensions/agency-agents/agents/project-management/project-management-experiment-tracker.md +196 -0
  77. package/extensions/agency-agents/agents/project-management/project-management-project-shepherd.md +192 -0
  78. package/extensions/agency-agents/agents/project-management/project-management-studio-operations.md +198 -0
  79. package/extensions/agency-agents/agents/project-management/project-management-studio-producer.md +201 -0
  80. package/extensions/agency-agents/agents/project-management/project-manager-senior.md +133 -0
  81. package/extensions/agency-agents/agents/scripts/convert.sh +362 -0
  82. package/extensions/agency-agents/agents/scripts/install.sh +465 -0
  83. package/extensions/agency-agents/agents/scripts/lint-agents.sh +115 -0
  84. package/extensions/agency-agents/agents/spatial-computing/macos-spatial-metal-engineer.md +335 -0
  85. package/extensions/agency-agents/agents/spatial-computing/terminal-integration-specialist.md +68 -0
  86. package/extensions/agency-agents/agents/spatial-computing/visionos-spatial-engineer.md +52 -0
  87. package/extensions/agency-agents/agents/spatial-computing/xr-cockpit-interaction-specialist.md +30 -0
  88. package/extensions/agency-agents/agents/spatial-computing/xr-immersive-developer.md +30 -0
  89. package/extensions/agency-agents/agents/spatial-computing/xr-interface-architect.md +30 -0
  90. package/extensions/agency-agents/agents/specialized/agentic-identity-trust.md +367 -0
  91. package/extensions/agency-agents/agents/specialized/agents-orchestrator.md +365 -0
  92. package/extensions/agency-agents/agents/specialized/data-analytics-reporter.md +52 -0
  93. package/extensions/agency-agents/agents/specialized/data-consolidation-agent.md +58 -0
  94. package/extensions/agency-agents/agents/specialized/lsp-index-engineer.md +312 -0
  95. package/extensions/agency-agents/agents/specialized/report-distribution-agent.md +63 -0
  96. package/extensions/agency-agents/agents/specialized/sales-data-extraction-agent.md +65 -0
  97. package/extensions/agency-agents/agents/strategy/EXECUTIVE-BRIEF.md +95 -0
  98. package/extensions/agency-agents/agents/strategy/QUICKSTART.md +194 -0
  99. package/extensions/agency-agents/agents/strategy/coordination/agent-activation-prompts.md +401 -0
  100. package/extensions/agency-agents/agents/strategy/coordination/handoff-templates.md +357 -0
  101. package/extensions/agency-agents/agents/strategy/nexus-strategy.md +1110 -0
  102. package/extensions/agency-agents/agents/strategy/playbooks/phase-0-discovery.md +178 -0
  103. package/extensions/agency-agents/agents/strategy/playbooks/phase-1-strategy.md +238 -0
  104. package/extensions/agency-agents/agents/strategy/playbooks/phase-2-foundation.md +278 -0
  105. package/extensions/agency-agents/agents/strategy/playbooks/phase-3-build.md +286 -0
  106. package/extensions/agency-agents/agents/strategy/playbooks/phase-4-hardening.md +332 -0
  107. package/extensions/agency-agents/agents/strategy/playbooks/phase-5-launch.md +277 -0
  108. package/extensions/agency-agents/agents/strategy/playbooks/phase-6-operate.md +318 -0
  109. package/extensions/agency-agents/agents/strategy/runbooks/scenario-enterprise-feature.md +157 -0
  110. package/extensions/agency-agents/agents/strategy/runbooks/scenario-incident-response.md +217 -0
  111. package/extensions/agency-agents/agents/strategy/runbooks/scenario-marketing-campaign.md +187 -0
  112. package/extensions/agency-agents/agents/strategy/runbooks/scenario-startup-mvp.md +154 -0
  113. package/extensions/agency-agents/agents/support/support-analytics-reporter.md +363 -0
  114. package/extensions/agency-agents/agents/support/support-executive-summary-generator.md +210 -0
  115. package/extensions/agency-agents/agents/support/support-finance-tracker.md +440 -0
  116. package/extensions/agency-agents/agents/support/support-infrastructure-maintainer.md +616 -0
  117. package/extensions/agency-agents/agents/support/support-legal-compliance-checker.md +586 -0
  118. package/extensions/agency-agents/agents/support/support-support-responder.md +583 -0
  119. package/extensions/agency-agents/agents/testing/testing-accessibility-auditor.md +313 -0
  120. package/extensions/agency-agents/agents/testing/testing-api-tester.md +304 -0
  121. package/extensions/agency-agents/agents/testing/testing-evidence-collector.md +208 -0
  122. package/extensions/agency-agents/agents/testing/testing-performance-benchmarker.md +266 -0
  123. package/extensions/agency-agents/agents/testing/testing-reality-checker.md +236 -0
  124. package/extensions/agency-agents/agents/testing/testing-test-results-analyzer.md +303 -0
  125. package/extensions/agency-agents/agents/testing/testing-tool-evaluator.md +392 -0
  126. package/extensions/agency-agents/agents/testing/testing-workflow-optimizer.md +448 -0
  127. package/extensions/agency-agents/index.ts +733 -0
  128. package/extensions/agency-agents/node_modules/.bin/jiti +21 -0
  129. package/extensions/agency-agents/node_modules/.bin/tsc +21 -0
  130. package/extensions/agency-agents/node_modules/.bin/tsserver +21 -0
  131. package/extensions/agency-agents/node_modules/.bin/tsx +21 -0
  132. package/extensions/agency-agents/node_modules/.bin/vite +21 -0
  133. package/extensions/agency-agents/node_modules/.bin/vitest +21 -0
  134. package/extensions/agency-agents/node_modules/.bin/yaml +21 -0
  135. package/extensions/agency-agents/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  136. package/extensions/agency-agents/package.json +25 -0
  137. package/extensions/agency-agents/src/AgencyAgentsService.test.ts +443 -0
  138. package/extensions/agency-agents/src/AgencyAgentsService.ts +288 -0
  139. package/extensions/agency-agents/src/types.ts +147 -0
  140. package/extensions/agency-agents/vitest.config.ts +8 -0
  141. package/extensions/hexstrike-ai/README.md +98 -0
  142. package/extensions/hexstrike-ai/node_modules/.bin/tsc +21 -0
  143. package/extensions/hexstrike-ai/node_modules/.bin/tsserver +21 -0
  144. package/extensions/hexstrike-ai/package.json +29 -0
  145. package/extensions/hexstrike-ai/poolbot.plugin.json +31 -0
  146. package/extensions/hexstrike-ai/src/client.ts +91 -0
  147. package/extensions/hexstrike-ai/src/index.ts +170 -0
  148. package/extensions/hexstrike-ai/src/server/hexstrike_mcp.py +5470 -0
  149. package/extensions/hexstrike-ai/src/server/hexstrike_server.py +17289 -0
  150. package/extensions/hexstrike-ai/src/server/requirements.txt +84 -0
  151. package/extensions/hexstrike-ai/src/server-manager.ts +83 -0
  152. package/extensions/hexstrike-ai/tsconfig.json +20 -0
  153. package/extensions/hexstrike-bridge/package.json +1 -1
  154. package/extensions/hexstrike-bridge/poolbot.plugin.json +23 -0
  155. package/extensions/mcp-server/poolbot.plugin.json +10 -0
  156. package/extensions/page-agent/README.md +159 -0
  157. package/extensions/page-agent/index.ts +595 -0
  158. package/extensions/page-agent/node_modules/.bin/jiti +21 -0
  159. package/extensions/page-agent/node_modules/.bin/playwright +21 -0
  160. package/extensions/page-agent/node_modules/.bin/tsc +21 -0
  161. package/extensions/page-agent/node_modules/.bin/tsserver +21 -0
  162. package/extensions/page-agent/node_modules/.bin/tsx +21 -0
  163. package/extensions/page-agent/node_modules/.bin/vitest +21 -0
  164. package/extensions/page-agent/node_modules/.bin/yaml +21 -0
  165. package/extensions/page-agent/package.json +43 -0
  166. package/extensions/page-agent/src/PageAgentService.test.ts +517 -0
  167. package/extensions/page-agent/src/PageAgentService.ts +636 -0
  168. package/extensions/page-agent/src/PoolBotPageController.test.ts +358 -0
  169. package/extensions/page-agent/src/PoolBotPageController.ts +245 -0
  170. package/extensions/page-agent/src/index.ts +20 -0
  171. package/extensions/page-agent/src/tools.test.ts +231 -0
  172. package/extensions/page-agent/src/tools.ts +167 -0
  173. package/extensions/page-agent/src/types.ts +198 -0
  174. package/extensions/template/README.md +101 -0
  175. package/extensions/template/index.ts +38 -0
  176. package/extensions/template/package.json +15 -0
  177. package/extensions/template/poolbot.plugin.json +10 -0
  178. package/extensions/xyops/README.md +227 -0
  179. package/extensions/xyops/index.ts +342 -0
  180. package/extensions/xyops/node_modules/.bin/jiti +21 -0
  181. package/extensions/xyops/node_modules/.bin/tsc +21 -0
  182. package/extensions/xyops/node_modules/.bin/tsserver +21 -0
  183. package/extensions/xyops/node_modules/.bin/tsx +21 -0
  184. package/extensions/xyops/node_modules/.bin/vitest +21 -0
  185. package/extensions/xyops/node_modules/.bin/yaml +21 -0
  186. package/extensions/xyops/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  187. package/extensions/xyops/package.json +39 -0
  188. package/extensions/xyops/src/client.test.ts +467 -0
  189. package/extensions/xyops/src/client.ts +157 -0
  190. package/extensions/xyops/src/types.ts +147 -0
  191. package/extensions/xyops/vitest.config.ts +8 -0
  192. package/package.json +1 -1
  193. package/extensions/mavalie/README.md +0 -97
  194. package/extensions/mavalie/package.json +0 -15
  195. package/extensions/mavalie/src/index.ts +0 -62
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/tsx@4.21.0/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/tsx@4.21.0/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/cli.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../../../../node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/cli.mjs" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/vitest@3.2.4_@types+node@22.19.15_@vitest+browser@4.0.18_vite@7.3.1_@types+node@25.2.3__6cb5e51e836830cd2cffacfc8438d429/node_modules/vitest/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/vitest@3.2.4_@types+node@22.19.15_@vitest+browser@4.0.18_vite@7.3.1_@types+node@25.2.3__6cb5e51e836830cd2cffacfc8438d429/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/vitest@3.2.4_@types+node@22.19.15_@vitest+browser@4.0.18_vite@7.3.1_@types+node@25.2.3__6cb5e51e836830cd2cffacfc8438d429/node_modules/vitest/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/vitest@3.2.4_@types+node@22.19.15_@vitest+browser@4.0.18_vite@7.3.1_@types+node@25.2.3__6cb5e51e836830cd2cffacfc8438d429/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../vitest/vitest.mjs" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/yaml@2.8.2/node_modules/yaml/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/yaml@2.8.2/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/yaml@2.8.2/node_modules/yaml/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/yaml@2.8.2/node_modules:/Users/pool/Documents/GitHub/pool-bot/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/yaml@2.8.2/node_modules/yaml/bin.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../../../../node_modules/.pnpm/yaml@2.8.2/node_modules/yaml/bin.mjs" "$@"
21
+ fi
@@ -0,0 +1 @@
1
+ {"version":"4.0.18","results":[[":src/client.test.ts",{"duration":29.100499999999982,"failed":false}]]}
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@poolbot/xyops",
3
+ "version": "2026.3.13",
4
+ "description": "XYOps integration for PoolBot - Workflow automation and job scheduling",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": "./dist/index.js",
10
+ "./client": "./dist/client.js",
11
+ "./types": "./dist/types.js"
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsc --watch",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest"
18
+ },
19
+ "dependencies": {
20
+ "@sinclair/typebox": "^0.34.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.0.0",
24
+ "typescript": "^5.0.0",
25
+ "vitest": "^3.0.0"
26
+ },
27
+ "peerDependencies": {
28
+ "poolbot": "workspace:*"
29
+ },
30
+ "poolbot": {
31
+ "plugin": true,
32
+ "config": {
33
+ "schema": "./dist/config/schema.js"
34
+ }
35
+ },
36
+ "keywords": ["xyops", "workflow", "automation", "scheduler", "jobs"],
37
+ "author": "PoolBot Team",
38
+ "license": "MIT"
39
+ }
@@ -0,0 +1,467 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
2
+ import { XYOpsClient } from './client.js'
3
+ import type { XYOpsConfig, XYOpsJob, XYOpsSchedule, XYOpsEvent, XYOpsStatus, XYOpsPlugin } from './types.js'
4
+
5
+ describe('XYOpsClient', () => {
6
+ let client: XYOpsClient
7
+ let mockFetch: ReturnType<typeof vi.fn>
8
+
9
+ const config: XYOpsConfig = {
10
+ baseUrl: 'http://localhost:8080',
11
+ apiKey: 'test-api-key',
12
+ timeout: 5000,
13
+ }
14
+
15
+ beforeEach(() => {
16
+ vi.clearAllMocks()
17
+ client = new XYOpsClient(config)
18
+ mockFetch = vi.fn()
19
+ global.fetch = mockFetch as unknown as typeof fetch
20
+ })
21
+
22
+ describe('constructor', () => {
23
+ it('should create client with config', () => {
24
+ expect(client).toBeDefined()
25
+ })
26
+
27
+ it('should remove trailing slash from baseUrl', () => {
28
+ const clientWithSlash = new XYOpsClient({
29
+ ...config,
30
+ baseUrl: 'http://localhost:8080/',
31
+ })
32
+ expect(clientWithSlash).toBeDefined()
33
+ })
34
+
35
+ it('should use default timeout', () => {
36
+ const clientWithoutTimeout = new XYOpsClient({
37
+ baseUrl: 'http://localhost:8080',
38
+ })
39
+ expect(clientWithoutTimeout).toBeDefined()
40
+ })
41
+ })
42
+
43
+ describe('request', () => {
44
+ it('should make GET request with auth header', async () => {
45
+ const mockJob: XYOpsJob = {
46
+ id: 'job-1',
47
+ title: 'Test Job',
48
+ category: 'test',
49
+ plugin: 'shell',
50
+ params: { command: 'echo test' },
51
+ enabled: true,
52
+ created: Date.now(),
53
+ modified: Date.now(),
54
+ }
55
+
56
+ mockFetch.mockResolvedValue({
57
+ ok: true,
58
+ headers: new Headers({ 'content-type': 'application/json' }),
59
+ json: () => Promise.resolve(mockJob),
60
+ })
61
+
62
+ const result = await client.getJob('job-1')
63
+
64
+ expect(mockFetch).toHaveBeenCalledWith(
65
+ 'http://localhost:8080/api/jobs/job-1',
66
+ expect.objectContaining({
67
+ method: 'GET',
68
+ headers: expect.objectContaining({
69
+ 'Authorization': 'Bearer test-api-key',
70
+ 'Content-Type': 'application/json',
71
+ }),
72
+ })
73
+ )
74
+ expect(result).toEqual(mockJob)
75
+ })
76
+
77
+ it('should make POST request with body', async () => {
78
+ const newJob = {
79
+ title: 'New Job',
80
+ category: 'test',
81
+ plugin: 'shell',
82
+ params: { command: 'echo hello' },
83
+ enabled: true,
84
+ }
85
+
86
+ const mockResponse: XYOpsJob = {
87
+ id: 'job-2',
88
+ ...newJob,
89
+ created: Date.now(),
90
+ modified: Date.now(),
91
+ }
92
+
93
+ mockFetch.mockResolvedValue({
94
+ ok: true,
95
+ headers: new Headers({ 'content-type': 'application/json' }),
96
+ json: () => Promise.resolve(mockResponse),
97
+ })
98
+
99
+ const result = await client.createJob(newJob)
100
+
101
+ expect(mockFetch).toHaveBeenCalledWith(
102
+ 'http://localhost:8080/api/jobs',
103
+ expect.objectContaining({
104
+ method: 'POST',
105
+ body: JSON.stringify(newJob),
106
+ })
107
+ )
108
+ expect(result).toEqual(mockResponse)
109
+ })
110
+
111
+ it('should handle API errors', async () => {
112
+ mockFetch.mockResolvedValue({
113
+ ok: false,
114
+ status: 404,
115
+ statusText: 'Not Found',
116
+ text: () => Promise.resolve('Job not found'),
117
+ })
118
+
119
+ await expect(client.getJob('nonexistent')).rejects.toThrow('XYOps API error')
120
+ })
121
+
122
+ it('should handle timeout', async () => {
123
+ mockFetch.mockImplementation(() => {
124
+ const error = new Error('AbortError')
125
+ error.name = 'AbortError'
126
+ return Promise.reject(error)
127
+ })
128
+
129
+ await expect(client.getJob('job-1')).rejects.toThrow('timeout')
130
+ })
131
+
132
+ it('should handle non-JSON responses', async () => {
133
+ mockFetch.mockResolvedValue({
134
+ ok: true,
135
+ headers: new Headers({ 'content-type': 'text/plain' }),
136
+ })
137
+
138
+ const result = await client.deleteJob('job-1')
139
+ expect(result).toBeUndefined()
140
+ })
141
+
142
+ it('should work without API key', async () => {
143
+ const clientNoAuth = new XYOpsClient({
144
+ baseUrl: 'http://localhost:8080',
145
+ })
146
+
147
+ mockFetch.mockResolvedValue({
148
+ ok: true,
149
+ headers: new Headers({ 'content-type': 'application/json' }),
150
+ json: () => Promise.resolve([]),
151
+ })
152
+
153
+ await clientNoAuth.listJobs()
154
+
155
+ expect(mockFetch).toHaveBeenCalledWith(
156
+ expect.any(String),
157
+ expect.objectContaining({
158
+ headers: expect.not.objectContaining({
159
+ 'Authorization': expect.any(String),
160
+ }),
161
+ })
162
+ )
163
+ })
164
+ })
165
+
166
+ describe('Jobs', () => {
167
+ const mockJob: XYOpsJob = {
168
+ id: 'job-1',
169
+ title: 'Test Job',
170
+ category: 'test',
171
+ plugin: 'shell',
172
+ params: { command: 'echo test' },
173
+ enabled: true,
174
+ created: Date.now(),
175
+ modified: Date.now(),
176
+ }
177
+
178
+ it('should list jobs', async () => {
179
+ mockFetch.mockResolvedValue({
180
+ ok: true,
181
+ headers: new Headers({ 'content-type': 'application/json' }),
182
+ json: () => Promise.resolve([mockJob]),
183
+ })
184
+
185
+ const result = await client.listJobs(0, 50)
186
+
187
+ expect(mockFetch).toHaveBeenCalledWith(
188
+ 'http://localhost:8080/api/jobs?offset=0&limit=50',
189
+ expect.any(Object)
190
+ )
191
+ expect(result).toEqual([mockJob])
192
+ })
193
+
194
+ it('should get job by id', async () => {
195
+ mockFetch.mockResolvedValue({
196
+ ok: true,
197
+ headers: new Headers({ 'content-type': 'application/json' }),
198
+ json: () => Promise.resolve(mockJob),
199
+ })
200
+
201
+ const result = await client.getJob('job-1')
202
+
203
+ expect(result).toEqual(mockJob)
204
+ })
205
+
206
+ it('should create job', async () => {
207
+ const jobData = {
208
+ title: 'New Job',
209
+ category: 'test',
210
+ plugin: 'shell',
211
+ params: { command: 'echo hello' },
212
+ enabled: true,
213
+ }
214
+
215
+ mockFetch.mockResolvedValue({
216
+ ok: true,
217
+ headers: new Headers({ 'content-type': 'application/json' }),
218
+ json: () => Promise.resolve({ id: 'job-2', ...jobData, created: Date.now(), modified: Date.now() }),
219
+ })
220
+
221
+ const result = await client.createJob(jobData)
222
+
223
+ expect(result.title).toBe('New Job')
224
+ })
225
+
226
+ it('should update job', async () => {
227
+ const updates = { title: 'Updated Job' }
228
+
229
+ mockFetch.mockResolvedValue({
230
+ ok: true,
231
+ headers: new Headers({ 'content-type': 'application/json' }),
232
+ json: () => Promise.resolve({ ...mockJob, ...updates }),
233
+ })
234
+
235
+ const result = await client.updateJob('job-1', updates)
236
+
237
+ expect(result.title).toBe('Updated Job')
238
+ })
239
+
240
+ it('should delete job', async () => {
241
+ mockFetch.mockResolvedValue({
242
+ ok: true,
243
+ headers: new Headers({ 'content-type': 'application/json' }),
244
+ json: () => Promise.resolve(undefined),
245
+ })
246
+
247
+ await client.deleteJob('job-1')
248
+
249
+ expect(mockFetch).toHaveBeenCalledWith(
250
+ 'http://localhost:8080/api/jobs/job-1',
251
+ expect.objectContaining({ method: 'DELETE' })
252
+ )
253
+ })
254
+
255
+ it('should run job', async () => {
256
+ mockFetch.mockResolvedValue({
257
+ ok: true,
258
+ headers: new Headers({ 'content-type': 'application/json' }),
259
+ json: () => Promise.resolve({ eventId: 'event-1' }),
260
+ })
261
+
262
+ const result = await client.runJob('job-1', { param1: 'value1' })
263
+
264
+ expect(result.eventId).toBe('event-1')
265
+ expect(mockFetch).toHaveBeenCalledWith(
266
+ 'http://localhost:8080/api/jobs/job-1/run',
267
+ expect.objectContaining({
268
+ method: 'POST',
269
+ body: JSON.stringify({ params: { param1: 'value1' } }),
270
+ })
271
+ )
272
+ })
273
+
274
+ it('should run job without params', async () => {
275
+ mockFetch.mockResolvedValue({
276
+ ok: true,
277
+ headers: new Headers({ 'content-type': 'application/json' }),
278
+ json: () => Promise.resolve({ eventId: 'event-1' }),
279
+ })
280
+
281
+ await client.runJob('job-1')
282
+
283
+ expect(mockFetch).toHaveBeenCalledWith(
284
+ expect.any(String),
285
+ expect.objectContaining({
286
+ body: undefined,
287
+ })
288
+ )
289
+ })
290
+ })
291
+
292
+ describe('Schedules', () => {
293
+ const mockSchedule: XYOpsSchedule = {
294
+ id: 'schedule-1',
295
+ jobId: 'job-1',
296
+ cron: '0 0 * * *',
297
+ enabled: true,
298
+ created: Date.now(),
299
+ }
300
+
301
+ it('should list schedules', async () => {
302
+ mockFetch.mockResolvedValue({
303
+ ok: true,
304
+ headers: new Headers({ 'content-type': 'application/json' }),
305
+ json: () => Promise.resolve([mockSchedule]),
306
+ })
307
+
308
+ const result = await client.listSchedules()
309
+
310
+ expect(result).toEqual([mockSchedule])
311
+ })
312
+
313
+ it('should list schedules for specific job', async () => {
314
+ mockFetch.mockResolvedValue({
315
+ ok: true,
316
+ headers: new Headers({ 'content-type': 'application/json' }),
317
+ json: () => Promise.resolve([mockSchedule]),
318
+ })
319
+
320
+ await client.listSchedules('job-1')
321
+
322
+ expect(mockFetch).toHaveBeenCalledWith(
323
+ 'http://localhost:8080/api/schedules?jobId=job-1',
324
+ expect.any(Object)
325
+ )
326
+ })
327
+
328
+ it('should create schedule', async () => {
329
+ const scheduleData = {
330
+ jobId: 'job-1',
331
+ cron: '0 0 * * *',
332
+ enabled: true,
333
+ }
334
+
335
+ mockFetch.mockResolvedValue({
336
+ ok: true,
337
+ headers: new Headers({ 'content-type': 'application/json' }),
338
+ json: () => Promise.resolve({ id: 'schedule-2', ...scheduleData, created: Date.now() }),
339
+ })
340
+
341
+ const result = await client.createSchedule(scheduleData)
342
+
343
+ expect(result.jobId).toBe('job-1')
344
+ })
345
+
346
+ it('should delete schedule', async () => {
347
+ mockFetch.mockResolvedValue({
348
+ ok: true,
349
+ headers: new Headers({ 'content-type': 'application/json' }),
350
+ json: () => Promise.resolve(undefined),
351
+ })
352
+
353
+ await client.deleteSchedule('schedule-1')
354
+
355
+ expect(mockFetch).toHaveBeenCalledWith(
356
+ 'http://localhost:8080/api/schedules/schedule-1',
357
+ expect.objectContaining({ method: 'DELETE' })
358
+ )
359
+ })
360
+ })
361
+
362
+ describe('Events', () => {
363
+ const mockEvent: XYOpsEvent = {
364
+ id: 'event-1',
365
+ jobId: 'job-1',
366
+ status: 'running',
367
+ startTime: Date.now(),
368
+ }
369
+
370
+ it('should list events', async () => {
371
+ mockFetch.mockResolvedValue({
372
+ ok: true,
373
+ headers: new Headers({ 'content-type': 'application/json' }),
374
+ json: () => Promise.resolve([mockEvent]),
375
+ })
376
+
377
+ const result = await client.listEvents()
378
+
379
+ expect(mockFetch).toHaveBeenCalledWith(
380
+ 'http://localhost:8080/api/events?limit=50&offset=0',
381
+ expect.any(Object)
382
+ )
383
+ expect(result).toEqual([mockEvent])
384
+ })
385
+
386
+ it('should list events with filters', async () => {
387
+ mockFetch.mockResolvedValue({
388
+ ok: true,
389
+ headers: new Headers({ 'content-type': 'application/json' }),
390
+ json: () => Promise.resolve([mockEvent]),
391
+ })
392
+
393
+ await client.listEvents('job-1', 10, 20)
394
+
395
+ expect(mockFetch).toHaveBeenCalledWith(
396
+ 'http://localhost:8080/api/events?jobId=job-1&limit=10&offset=20',
397
+ expect.any(Object)
398
+ )
399
+ })
400
+
401
+ it('should get event by id', async () => {
402
+ mockFetch.mockResolvedValue({
403
+ ok: true,
404
+ headers: new Headers({ 'content-type': 'application/json' }),
405
+ json: () => Promise.resolve(mockEvent),
406
+ })
407
+
408
+ const result = await client.getEvent('event-1')
409
+
410
+ expect(result).toEqual(mockEvent)
411
+ })
412
+
413
+ it('should abort event', async () => {
414
+ mockFetch.mockResolvedValue({
415
+ ok: true,
416
+ headers: new Headers({ 'content-type': 'application/json' }),
417
+ json: () => Promise.resolve(undefined),
418
+ })
419
+
420
+ await client.abortEvent('event-1')
421
+
422
+ expect(mockFetch).toHaveBeenCalledWith(
423
+ 'http://localhost:8080/api/events/event-1/abort',
424
+ expect.objectContaining({ method: 'POST' })
425
+ )
426
+ })
427
+ })
428
+
429
+ describe('System', () => {
430
+ it('should get status', async () => {
431
+ const mockStatus: XYOpsStatus = {
432
+ version: '1.0.0',
433
+ uptime: 3600,
434
+ jobCount: 5,
435
+ scheduleCount: 3,
436
+ activeEvents: 1,
437
+ }
438
+
439
+ mockFetch.mockResolvedValue({
440
+ ok: true,
441
+ headers: new Headers({ 'content-type': 'application/json' }),
442
+ json: () => Promise.resolve(mockStatus),
443
+ })
444
+
445
+ const result = await client.getStatus()
446
+
447
+ expect(result).toEqual(mockStatus)
448
+ })
449
+
450
+ it('should list plugins', async () => {
451
+ const mockPlugins: XYOpsPlugin[] = [
452
+ { name: 'plugin-1', title: 'Plugin 1', description: 'Test plugin 1' },
453
+ { name: 'plugin-2', title: 'Plugin 2', description: 'Test plugin 2' },
454
+ ]
455
+
456
+ mockFetch.mockResolvedValue({
457
+ ok: true,
458
+ headers: new Headers({ 'content-type': 'application/json' }),
459
+ json: () => Promise.resolve(mockPlugins),
460
+ })
461
+
462
+ const result = await client.listPlugins()
463
+
464
+ expect(result).toEqual(mockPlugins)
465
+ })
466
+ })
467
+ })