@jaimevalasek/aioson 1.4.0 → 1.5.1

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 (199) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/LICENSE +661 -21
  3. package/README.md +3 -1
  4. package/docs/en/squad-dashboard.md +372 -0
  5. package/docs/openclaw-bridge.md +308 -0
  6. package/docs/pt/agentes.md +124 -10
  7. package/docs/pt/cenarios.md +46 -2
  8. package/docs/pt/comandos-cli.md +60 -1
  9. package/docs/pt/inicio-rapido.md +18 -2
  10. package/docs/pt/squad-dashboard.md +373 -0
  11. package/docs/testing/genome-2.0-matrix.md +5 -5
  12. package/docs/testing/genome-2.0-rollout.md +9 -9
  13. package/package.json +2 -2
  14. package/src/backup-local.js +74 -0
  15. package/src/cli.js +98 -0
  16. package/src/commands/backup-local-cmd.js +25 -0
  17. package/src/commands/runtime.js +242 -0
  18. package/src/commands/setup-context.js +7 -2
  19. package/src/commands/squad-daemon.js +209 -0
  20. package/src/commands/squad-dashboard.js +39 -0
  21. package/src/commands/squad-deploy.js +64 -0
  22. package/src/commands/squad-doctor.js +52 -0
  23. package/src/commands/squad-mcp.js +270 -0
  24. package/src/commands/squad-processes.js +56 -0
  25. package/src/commands/squad-recovery.js +42 -0
  26. package/src/commands/squad-roi.js +291 -0
  27. package/src/commands/squad-score.js +250 -0
  28. package/src/commands/squad-status.js +37 -1
  29. package/src/commands/squad-validate.js +62 -1
  30. package/src/commands/squad-webhook.js +160 -0
  31. package/src/commands/squad-worker.js +191 -0
  32. package/src/commands/squad-worktrees.js +75 -0
  33. package/src/commands/web-map.js +70 -0
  34. package/src/commands/web-scrape.js +71 -0
  35. package/src/constants.js +8 -0
  36. package/src/context-writer.js +45 -1
  37. package/src/i18n/messages/en.js +127 -1
  38. package/src/i18n/messages/es.js +117 -0
  39. package/src/i18n/messages/fr.js +117 -0
  40. package/src/i18n/messages/pt-BR.js +126 -1
  41. package/src/lib/webhook-server.js +328 -0
  42. package/src/mcp-connectors/registry.js +602 -0
  43. package/src/runtime-store.js +259 -2
  44. package/src/squad/external-session.js +180 -0
  45. package/src/squad/inter-squad.js +74 -0
  46. package/src/squad/recovery-context.js +201 -0
  47. package/src/squad/worktree-manager.js +114 -0
  48. package/src/squad-daemon.js +490 -0
  49. package/src/squad-dashboard/api.js +223 -0
  50. package/src/squad-dashboard/attachment-handler.js +93 -0
  51. package/src/squad-dashboard/context-monitor.js +157 -0
  52. package/src/squad-dashboard/execution-logs.js +115 -0
  53. package/src/squad-dashboard/hunk-review.js +209 -0
  54. package/src/squad-dashboard/metrics.js +133 -0
  55. package/src/squad-dashboard/process-monitor.js +125 -0
  56. package/src/squad-dashboard/renderer.js +858 -0
  57. package/src/squad-dashboard/server.js +232 -0
  58. package/src/squad-dashboard/styles.js +525 -0
  59. package/src/squad-dashboard/token-tracker.js +99 -0
  60. package/src/web.js +284 -0
  61. package/src/worker-runner.js +339 -0
  62. package/template/.aioson/agents/analyst.md +4 -0
  63. package/template/.aioson/agents/architect.md +4 -0
  64. package/template/.aioson/agents/dev.md +120 -11
  65. package/template/.aioson/agents/deyvin.md +8 -0
  66. package/template/.aioson/agents/neo.md +152 -0
  67. package/template/.aioson/agents/orache.md +17 -0
  68. package/template/.aioson/agents/orchestrator.md +26 -0
  69. package/template/.aioson/agents/product.md +60 -12
  70. package/template/.aioson/agents/qa.md +1 -0
  71. package/template/.aioson/agents/setup.md +63 -19
  72. package/template/.aioson/agents/sheldon.md +603 -0
  73. package/template/.aioson/agents/squad.md +191 -0
  74. package/template/.aioson/agents/tester.md +254 -0
  75. package/template/.aioson/agents/ux-ui.md +12 -0
  76. package/template/.aioson/config.md +6 -0
  77. package/template/.aioson/locales/en/agents/analyst.md +8 -0
  78. package/template/.aioson/locales/en/agents/architect.md +8 -0
  79. package/template/.aioson/locales/en/agents/dev.md +66 -7
  80. package/template/.aioson/locales/en/agents/deyvin.md +8 -0
  81. package/template/.aioson/locales/en/agents/neo.md +8 -0
  82. package/template/.aioson/locales/en/agents/orchestrator.md +26 -0
  83. package/template/.aioson/locales/en/agents/qa.md +49 -0
  84. package/template/.aioson/locales/en/agents/setup.md +2 -1
  85. package/template/.aioson/locales/en/agents/sheldon.md +340 -0
  86. package/template/.aioson/locales/en/agents/ux-ui.md +8 -0
  87. package/template/.aioson/locales/es/agents/analyst.md +8 -0
  88. package/template/.aioson/locales/es/agents/architect.md +8 -0
  89. package/template/.aioson/locales/es/agents/dev.md +66 -7
  90. package/template/.aioson/locales/es/agents/deyvin.md +8 -0
  91. package/template/.aioson/locales/es/agents/neo.md +48 -0
  92. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  93. package/template/.aioson/locales/es/agents/qa.md +26 -0
  94. package/template/.aioson/locales/es/agents/setup.md +2 -1
  95. package/template/.aioson/locales/es/agents/sheldon.md +192 -0
  96. package/template/.aioson/locales/es/agents/squad.md +63 -0
  97. package/template/.aioson/locales/es/agents/ux-ui.md +8 -0
  98. package/template/.aioson/locales/fr/agents/analyst.md +8 -0
  99. package/template/.aioson/locales/fr/agents/architect.md +8 -0
  100. package/template/.aioson/locales/fr/agents/dev.md +66 -7
  101. package/template/.aioson/locales/fr/agents/deyvin.md +8 -0
  102. package/template/.aioson/locales/fr/agents/neo.md +48 -0
  103. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  104. package/template/.aioson/locales/fr/agents/qa.md +26 -0
  105. package/template/.aioson/locales/fr/agents/setup.md +2 -1
  106. package/template/.aioson/locales/fr/agents/sheldon.md +192 -0
  107. package/template/.aioson/locales/fr/agents/squad.md +63 -0
  108. package/template/.aioson/locales/fr/agents/ux-ui.md +8 -0
  109. package/template/.aioson/locales/pt-BR/agents/analyst.md +19 -0
  110. package/template/.aioson/locales/pt-BR/agents/architect.md +19 -0
  111. package/template/.aioson/locales/pt-BR/agents/dev.md +75 -12
  112. package/template/.aioson/locales/pt-BR/agents/deyvin.md +8 -0
  113. package/template/.aioson/locales/pt-BR/agents/neo.md +147 -0
  114. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +26 -0
  115. package/template/.aioson/locales/pt-BR/agents/product.md +8 -3
  116. package/template/.aioson/locales/pt-BR/agents/qa.md +60 -0
  117. package/template/.aioson/locales/pt-BR/agents/setup.md +2 -1
  118. package/template/.aioson/locales/pt-BR/agents/sheldon.md +192 -0
  119. package/template/.aioson/locales/pt-BR/agents/squad.md +105 -0
  120. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +8 -0
  121. package/template/.aioson/schemas/squad-blueprint.schema.json +21 -0
  122. package/template/.aioson/schemas/squad-manifest.schema.json +178 -1
  123. package/template/.aioson/skills/design/bold-editorial-ui/SKILL.md +205 -0
  124. package/template/.aioson/skills/design/bold-editorial-ui/references/art-direction.md +338 -0
  125. package/template/.aioson/skills/design/bold-editorial-ui/references/components.md +977 -0
  126. package/template/.aioson/skills/design/bold-editorial-ui/references/dashboards.md +218 -0
  127. package/template/.aioson/skills/design/bold-editorial-ui/references/design-tokens.md +326 -0
  128. package/template/.aioson/skills/design/bold-editorial-ui/references/motion.md +461 -0
  129. package/template/.aioson/skills/design/bold-editorial-ui/references/patterns.md +293 -0
  130. package/template/.aioson/skills/design/bold-editorial-ui/references/websites.md +352 -0
  131. package/template/.aioson/skills/design/clean-saas-ui/SKILL.md +210 -0
  132. package/template/.aioson/skills/design/clean-saas-ui/references/art-direction.md +319 -0
  133. package/template/.aioson/skills/design/clean-saas-ui/references/components.md +365 -0
  134. package/template/.aioson/skills/design/clean-saas-ui/references/dashboards.md +196 -0
  135. package/template/.aioson/skills/design/clean-saas-ui/references/design-tokens.md +244 -0
  136. package/template/.aioson/skills/design/clean-saas-ui/references/motion.md +235 -0
  137. package/template/.aioson/skills/design/clean-saas-ui/references/patterns.md +215 -0
  138. package/template/.aioson/skills/design/clean-saas-ui/references/websites.md +295 -0
  139. package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +55 -9
  140. package/template/.aioson/skills/design/cognitive-core-ui/references/art-direction.md +339 -0
  141. package/template/.aioson/skills/design/cognitive-core-ui/references/components.md +1 -1
  142. package/template/.aioson/skills/design/cognitive-core-ui/references/dashboards.md +100 -0
  143. package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +43 -9
  144. package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +40 -0
  145. package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +1 -1
  146. package/template/.aioson/skills/design/cognitive-core-ui/references/websites.md +99 -12
  147. package/template/.aioson/skills/design/warm-craft-ui/SKILL.md +209 -0
  148. package/template/.aioson/skills/design/warm-craft-ui/references/art-direction.md +324 -0
  149. package/template/.aioson/skills/design/warm-craft-ui/references/components.md +508 -0
  150. package/template/.aioson/skills/design/warm-craft-ui/references/dashboards.md +223 -0
  151. package/template/.aioson/skills/design/warm-craft-ui/references/design-tokens.md +374 -0
  152. package/template/.aioson/skills/design/warm-craft-ui/references/motion.md +356 -0
  153. package/template/.aioson/skills/design/warm-craft-ui/references/patterns.md +288 -0
  154. package/template/.aioson/skills/design/warm-craft-ui/references/websites.md +289 -0
  155. package/template/.aioson/skills/premium-visual-design/SKILL.md +83 -0
  156. package/template/.aioson/skills/premium-visual-design/components/agent-badge.md +92 -0
  157. package/template/.aioson/skills/premium-visual-design/components/dependency-node.md +102 -0
  158. package/template/.aioson/skills/premium-visual-design/components/mention-autocomplete.md +136 -0
  159. package/template/.aioson/skills/premium-visual-design/components/notification-center.md +136 -0
  160. package/template/.aioson/skills/premium-visual-design/components/review-action-bar.md +188 -0
  161. package/template/.aioson/skills/premium-visual-design/components/team-switcher.md +131 -0
  162. package/template/.aioson/skills/premium-visual-design/patterns/agent-message-thread.md +198 -0
  163. package/template/.aioson/skills/premium-visual-design/patterns/notification-panel.md +275 -0
  164. package/template/.aioson/skills/premium-visual-design/patterns/review-workflow-ui.md +234 -0
  165. package/template/.aioson/skills/premium-visual-design/patterns/task-dependency-graph.md +147 -0
  166. package/template/.aioson/skills/premium-visual-design/tokens/status-extended.md +142 -0
  167. package/template/.aioson/skills/squad/formats/catalog.json +15 -0
  168. package/template/.aioson/skills/squad/formats/content/blog-post.md +47 -0
  169. package/template/.aioson/skills/squad/formats/content/newsletter.md +47 -0
  170. package/template/.aioson/skills/squad/formats/creative/podcast-script.md +43 -0
  171. package/template/.aioson/skills/squad/formats/creative/video-script.md +41 -0
  172. package/template/.aioson/skills/squad/formats/social/instagram-feed.md +42 -0
  173. package/template/.aioson/skills/squad/formats/social/linkedin-post.md +42 -0
  174. package/template/.aioson/skills/squad/formats/social/tiktok.md +39 -0
  175. package/template/.aioson/skills/squad/formats/social/twitter-thread.md +39 -0
  176. package/template/.aioson/skills/squad/formats/social/youtube-long.md +47 -0
  177. package/template/.aioson/skills/squad/formats/social/youtube-shorts.md +39 -0
  178. package/template/.aioson/skills/squad/patterns/multi-platform-pattern.md +108 -0
  179. package/template/.aioson/skills/squad/patterns/persona-based-pattern.md +98 -0
  180. package/template/.aioson/skills/squad/patterns/pipeline-pattern.md +106 -0
  181. package/template/.aioson/skills/squad/patterns/review-loop-pattern.md +81 -0
  182. package/template/.aioson/skills/squad/references/checklist-templates.md +122 -0
  183. package/template/.aioson/skills/squad/references/executor-archetypes.md +123 -0
  184. package/template/.aioson/skills/squad/references/workflow-templates.md +169 -0
  185. package/template/.aioson/skills/static/debugging-protocol.md +42 -0
  186. package/template/.aioson/skills/static/git-worktrees.md +36 -0
  187. package/template/.aioson/tasks/implementation-plan.md +19 -0
  188. package/template/.aioson/tasks/squad-design.md +28 -0
  189. package/template/.aioson/tasks/squad-profile.md +48 -0
  190. package/template/.aioson/tasks/squad-review.md +61 -0
  191. package/template/.aioson/tasks/squad-task-decompose.md +66 -0
  192. package/template/.claude/commands/aioson/agent/neo.md +5 -0
  193. package/template/.claude/commands/aioson/agent/tester.md +5 -0
  194. package/template/.gemini/GEMINI.md +1 -0
  195. package/template/.gemini/commands/aios-neo.toml +4 -0
  196. package/template/.gemini/commands/aios-tester.toml +6 -0
  197. package/template/AGENTS.md +3 -0
  198. package/template/CLAUDE.md +5 -2
  199. package/template/OPENCODE.md +2 -0
@@ -59,6 +59,10 @@ module.exports = {
59
59
  'aioson qa:scan [path] [--url=<app-url>] [--depth=3] [--max-pages=50] [--headed] [--html] [--json] [--locale=pt-BR]',
60
60
  help_qa_report:
61
61
  'aioson qa:report [path] [--html] [--json] [--locale=pt-BR]',
62
+ help_web_map:
63
+ 'aioson web:map [path] --url=<url> [--depth=<N>] [--max-pages=<N>] [--include-external] [--json] [--locale=pt-BR]',
64
+ help_web_scrape:
65
+ 'aioson web:scrape [path] --url=<url> [--format=markdown|text|html|links] [--json] [--locale=pt-BR]',
62
66
  help_scan_project:
63
67
  'aioson scan:project [path] --folder=<pasta[,pasta2]> [--summary-mode=titles|summaries|raw] [--context-mode=merge|rewrite] [--with-llm] [--provider=<name>] [--llm-model=<name>] [--dry-run] [--json] [--locale=pt-BR]',
64
68
  help_config:
@@ -85,6 +89,18 @@ module.exports = {
85
89
  'aioson squad:investigate [path] [--sub=list|show|score|link|register] [--investigation=<slug>] [--squad=<slug>] [--locale=pt-BR]',
86
90
  help_squad_learning:
87
91
  'aioson squad:learning [path] [--sub=list|stats|archive|promote|export] [--squad=<slug>] [--status=<status>] [--locale=pt-BR]',
92
+ help_squad_dashboard:
93
+ 'aioson squad:dashboard [path] [--port=4180] [--squad=<slug>] [--locale=pt-BR]',
94
+ help_squad_worker:
95
+ 'aioson squad:worker [path] [--sub=list|run|test|logs|scaffold] [--squad=<slug>] [--worker=<slug>] [--input=<json>] [--locale=pt-BR]',
96
+ help_squad_daemon:
97
+ 'aioson squad:daemon [path] [--sub=start|status|stop|logs] [--squad=<slug>] [--port=<N>] [--locale=pt-BR]',
98
+ help_squad_mcp:
99
+ 'aioson squad:mcp [path] [--sub=status|connectors|configure|test] [--squad=<slug>] [--mcp=<slug>] [--connector=<id>]',
100
+ help_squad_roi:
101
+ 'aioson squad:roi [path] [--sub=config|metric|report|export] [--squad=<slug>] [--key=<metrica>] [--value=<N>]',
102
+ help_squad_score:
103
+ 'aioson squad:score [path] --squad=<slug> [--locale=pt-BR]',
88
104
  help_learning:
89
105
  'aioson learning [path] [--sub=list|stats|promote] [--status=<status>] [--id=<learning-id>] [--locale=pt-BR]',
90
106
  help_runtime_init:
@@ -758,6 +774,23 @@ module.exports = {
758
774
  not_found: 'Nenhum relatorio QA encontrado. Execute: aioson qa:run ou aioson qa:scan',
759
775
  html_report_written: 'Relatorio HTML escrito: {path}'
760
776
  },
777
+ web_map: {
778
+ url_missing: 'Opcao obrigatoria ausente: --url=<url>.',
779
+ starting: 'Mapeando site: {url}',
780
+ pages_found: 'Paginas descobertas: {count}',
781
+ page_line: '- {url} | profundidade={depth} | status={status} | links={links}',
782
+ done: 'Mapa web concluido.',
783
+ failed: 'Falha no mapa web: {error}'
784
+ },
785
+ web_scrape: {
786
+ url_missing: 'Opcao obrigatoria ausente: --url=<url>.',
787
+ invalid_format: 'Valor invalido para --format: {format}. Use markdown, text, html ou links.',
788
+ fetching: 'Buscando pagina: {url}',
789
+ title_line: 'Titulo: {title}',
790
+ status_line: 'Status: {status} | Content-Type: {type}',
791
+ done: 'Web scrape concluido ({format}).',
792
+ failed: 'Falha no web scrape: {error}'
793
+ },
761
794
  config: {
762
795
  usage_error:
763
796
  'Uso: aioson config <set KEY=value|show|get KEY> [--json] [--locale=pt-BR]',
@@ -841,7 +874,9 @@ module.exports = {
841
874
  sessions: ' Sessoes : {count} ({path})',
842
875
  latest_html: ' Latest HTML : {value}',
843
876
  logs: ' Logs : {count} ({path})',
844
- genomes: ' Genomes : {count} no squad / {agent_count} vinculos por agente'
877
+ genomes: ' Genomes : {count} no squad / {agent_count} vinculos por agente',
878
+ model_tiers: ' Model Tiers : {value}',
879
+ estimated_cost: ' Custo Est. : ~${value}/run'
845
880
  },
846
881
  squad_agent_create: {
847
882
  no_name: 'Uso: aioson squad:agent-create [path] --name=<nome-agente> [--type=agent|assistant|clone|worker] [--scope=my-agents|squad] [--squad=<slug>]',
@@ -965,6 +1000,96 @@ module.exports = {
965
1000
  registered: 'Investigacao registrada: {slug} ({path})',
966
1001
  unknown_sub: 'Subcomando desconhecido: {sub}. Use: list, show, score, link, register.'
967
1002
  },
1003
+ squad_daemon: {
1004
+ squad_required: 'Slug do squad e obrigatorio. Use --squad=<slug>.',
1005
+ started: 'Daemon iniciado para squad "{squad}" na porta {port} ({workers} workers, {cron} cron jobs)',
1006
+ webhook_hint: 'Endpoint webhook: POST http://127.0.0.1:{port}/webhook/<worker-slug>',
1007
+ stop_hint: 'Pressione Ctrl+C para parar.',
1008
+ stopping: 'Parando daemon...',
1009
+ start_failed: 'Falha ao iniciar daemon: {error}',
1010
+ no_runtime: 'Runtime store nao encontrado. Execute aioson runtime:init primeiro.',
1011
+ no_daemons: 'Nenhum registro de daemon encontrado.',
1012
+ not_found: 'Nenhum registro de daemon para o squad: {squad}',
1013
+ not_running: 'O daemon do squad "{squad}" nao esta rodando.',
1014
+ signal_sent: 'SIGTERM enviado ao daemon de "{squad}" (pid {pid}).',
1015
+ process_gone: 'O processo do daemon de "{squad}" nao esta mais rodando.',
1016
+ no_logs: 'Nenhum log de atividade do daemon encontrado.',
1017
+ unknown_sub: 'Subcomando desconhecido: {sub}. Use: start, status, stop, logs.'
1018
+ },
1019
+
1020
+ squad_mcp: {
1021
+ squad_required: 'Slug do squad e obrigatorio. Use --squad=<slug>.',
1022
+ connectors_title: 'Conectores MCP Integrados:',
1023
+ actions: 'Acoes',
1024
+ required_config: 'Obrigatorio',
1025
+ no_integrations: 'Nenhuma integracao configurada para o squad "{squad}".',
1026
+ missing_config: 'Config ausente',
1027
+ calls: 'Chamadas',
1028
+ mcp_required: 'Slug do MCP e obrigatorio. Use --mcp=<slug>.',
1029
+ connector_required: 'ID do conector e obrigatorio. Use --connector=<id>.',
1030
+ unknown_connector: 'Conector desconhecido: {connector}. Use --sub=connectors para listar.',
1031
+ configured: 'Integracao "{mcp}" configurada com conector "{connector}" (status: {status}).',
1032
+ still_missing: 'Ainda faltam env/config: {keys}',
1033
+ not_configured: 'Integracao "{mcp}" nao esta configurada.',
1034
+ test_missing: 'Integracao "{mcp}" tem config ausente: {keys}',
1035
+ test_ok: 'Integracao "{mcp}" ({connector}) — config OK.',
1036
+ health_url: 'URL de health check: {url}',
1037
+ testing_connection: 'Testando conexao...',
1038
+ health_ok: 'Conexao OK (HTTP {statusCode})',
1039
+ health_error: 'Erro na conexao: {error}',
1040
+ health_skipped: 'Verificacao de saude nao disponivel para este connector',
1041
+ action_required: 'Slug da acao e obrigatorio. Use --action=<slug>.',
1042
+ invalid_input: 'JSON invalido. Forneca JSON valido com --input.',
1043
+ unknown_sub: 'Subcomando desconhecido: {sub}. Use: status, connectors, configure, test, call.'
1044
+ },
1045
+
1046
+ squad_roi: {
1047
+ squad_required: 'Slug do squad e obrigatorio. Use --squad=<slug>.',
1048
+ config_saved: 'Config de ROI salva para o squad "{squad}".',
1049
+ pricing_model: 'Modelo de precificacao',
1050
+ setup_fee: 'Taxa de implantacao',
1051
+ monthly_fee: 'Mensalidade',
1052
+ percentage: 'Percentual',
1053
+ contract: 'Contrato',
1054
+ metric_required: 'Chave e valor da metrica sao obrigatorios. Use --key=<nome> --value=<N>.',
1055
+ metric_saved: 'Metrica "{key}" = {value} salva para o squad "{squad}".',
1056
+ no_metrics: 'Nenhuma metrica encontrada para o squad "{squad}".',
1057
+ report_title: 'Relatorio de ROI — {squad}',
1058
+ baseline: 'Baseline',
1059
+ actual: 'Atual',
1060
+ target: 'Meta',
1061
+ period: 'Periodo',
1062
+ cost_section: 'Resumo de Custos:',
1063
+ monthly_cost: 'Custo mensal efetivo',
1064
+ exported: 'Relatorio exportado para {file} ({format}).',
1065
+ unknown_sub: 'Subcomando desconhecido: {sub}. Use: config, metric, report, export.'
1066
+ },
1067
+
1068
+ squad_worker: {
1069
+ squad_required: 'Slug do squad e obrigatorio. Use --squad=<slug>.',
1070
+ no_workers: 'Nenhum worker encontrado para este squad.',
1071
+ run_usage: 'Uso: aioson squad:worker --sub=run --squad=<slug> --worker=<slug> [--input=<json>]',
1072
+ test_usage: 'Uso: aioson squad:worker --sub=test --squad=<slug> --worker=<slug>',
1073
+ scaffold_usage: 'Uso: aioson squad:worker --sub=scaffold --squad=<slug> --worker=<slug> [--trigger=manual|event|scheduled]',
1074
+ not_found: 'Worker nao encontrado: {worker}',
1075
+ invalid_input: 'JSON invalido. Forneca JSON valido com --input.',
1076
+ run_success: 'Worker "{worker}" concluido com sucesso.',
1077
+ run_failed: 'Worker "{worker}" falhou: {error}',
1078
+ test_passed: 'Worker "{worker}" teste aprovado.',
1079
+ test_failed: 'Worker "{worker}" teste falhou: {error}',
1080
+ scaffold_created: 'Worker "{worker}" criado em {path}',
1081
+ no_runtime: 'Runtime store nao encontrado. Execute aioson runtime:init primeiro.',
1082
+ no_logs: 'Nenhuma execucao de worker encontrada.',
1083
+ unknown_sub: 'Subcomando desconhecido: {sub}. Use: list, run, test, logs, scaffold.'
1084
+ },
1085
+
1086
+ squad_dashboard: {
1087
+ started: 'Squad Dashboard rodando em {url} (porta {port})',
1088
+ filtered: 'Filtrando para squad: {squad}',
1089
+ stop_hint: 'Pressione Ctrl+C para parar.',
1090
+ stopping: 'Parando Squad Dashboard...',
1091
+ port_in_use: 'Porta {port} ja esta em uso. Tente --port=<outra>'
1092
+ },
968
1093
  implementation_plan: {
969
1094
  not_found: 'Plano de implementacao nao encontrado: {file}',
970
1095
  no_runtime: 'Runtime store nao encontrado. Execute aioson runtime:init primeiro.',
@@ -0,0 +1,328 @@
1
+ 'use strict';
2
+
3
+ const http = require('node:http');
4
+ const https = require('node:https');
5
+ const { randomBytes } = require('node:crypto');
6
+
7
+ function generateRunId() {
8
+ return randomBytes(8).toString('hex');
9
+ }
10
+
11
+ function sendJSON(res, statusCode, data) {
12
+ const body = JSON.stringify(data);
13
+ res.writeHead(statusCode, {
14
+ 'Content-Type': 'application/json; charset=utf-8',
15
+ 'Content-Length': Buffer.byteLength(body)
16
+ });
17
+ res.end(body);
18
+ }
19
+
20
+ function parseBody(req) {
21
+ return new Promise((resolve, reject) => {
22
+ let data = '';
23
+ req.on('data', chunk => { data += chunk; });
24
+ req.on('end', () => {
25
+ try {
26
+ resolve(data ? JSON.parse(data) : {});
27
+ } catch {
28
+ reject(new Error('Invalid JSON body'));
29
+ }
30
+ });
31
+ req.on('error', reject);
32
+ });
33
+ }
34
+
35
+ async function postCallback(url, payload, maxRetries = 3) {
36
+ const delays = [30000, 60000, 120000];
37
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
38
+ try {
39
+ await new Promise((resolve, reject) => {
40
+ const body = JSON.stringify(payload);
41
+ let parsed;
42
+ try { parsed = new URL(url); } catch { return reject(new Error('Invalid callback URL')); }
43
+ const isHttps = parsed.protocol === 'https:';
44
+ const mod = isHttps ? https : http;
45
+ const opts = {
46
+ hostname: parsed.hostname,
47
+ port: parsed.port ? parseInt(parsed.port, 10) : (isHttps ? 443 : 80),
48
+ path: parsed.pathname + parsed.search,
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ 'Content-Length': Buffer.byteLength(body)
53
+ }
54
+ };
55
+ const req = mod.request(opts, res => {
56
+ res.resume();
57
+ if (res.statusCode >= 200 && res.statusCode < 300) resolve();
58
+ else reject(new Error(`Callback returned HTTP ${res.statusCode}`));
59
+ });
60
+ req.on('error', reject);
61
+ req.setTimeout(10000, () => { req.destroy(new Error('Callback timeout')); });
62
+ req.write(body);
63
+ req.end();
64
+ });
65
+ return; // success — stop retrying
66
+ } catch {
67
+ if (attempt < maxRetries) {
68
+ await new Promise(r => setTimeout(r, delays[attempt] || 120000));
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ class WebhookServer {
75
+ /**
76
+ * @param {object} options
77
+ * @param {number} [options.port=3210]
78
+ * @param {string} [options.token=''] Bearer token (empty = no auth)
79
+ * @param {number} [options.rateLimit=60] Max req/min per IP
80
+ * @param {Array} [options.squads=[]] [{name, timeout_ms}] — empty = all allowed
81
+ * @param {Function} [options.onTrigger] async ({squad,input,session_id,metadata,run_id}) -> {response}
82
+ * @param {Function} [options.onQuery] async ({squad,query,max_results}) -> {results}
83
+ * @param {string} [options.version='0.0.0']
84
+ */
85
+ constructor(options = {}) {
86
+ this.port = options.port || 3210;
87
+ this.token = options.token || '';
88
+ this.rateLimit = options.rateLimit || 60;
89
+ this.allowedSquads = options.squads || [];
90
+ this.onTrigger = options.onTrigger || null;
91
+ this.onQuery = options.onQuery || null;
92
+ this.version = options.version || '0.0.0';
93
+
94
+ this._runs = new Map(); // run_id -> {status, response, error, session_id, metadata}
95
+ this._ipCounters = new Map(); // ip -> {count, windowStart}
96
+ this._server = null;
97
+ }
98
+
99
+ _checkRateLimit(ip) {
100
+ const now = Date.now();
101
+ const window = 60_000;
102
+ const entry = this._ipCounters.get(ip) || { count: 0, windowStart: now };
103
+ if (now - entry.windowStart > window) {
104
+ entry.count = 0;
105
+ entry.windowStart = now;
106
+ }
107
+ entry.count++;
108
+ this._ipCounters.set(ip, entry);
109
+ return entry.count <= this.rateLimit;
110
+ }
111
+
112
+ _validateToken(req) {
113
+ if (!this.token) return true;
114
+ const auth = req.headers['authorization'] || '';
115
+ const spaceIdx = auth.indexOf(' ');
116
+ if (spaceIdx === -1) return false;
117
+ const scheme = auth.slice(0, spaceIdx);
118
+ const tok = auth.slice(spaceIdx + 1);
119
+ return scheme === 'Bearer' && tok === this.token;
120
+ }
121
+
122
+ _getSquadNames() {
123
+ return this.allowedSquads.map(s => (typeof s === 'string' ? s : s.name));
124
+ }
125
+
126
+ _isSquadAllowed(squadName) {
127
+ if (this.allowedSquads.length === 0) return true;
128
+ return this._getSquadNames().includes(squadName);
129
+ }
130
+
131
+ _getSquadTimeout(squadName) {
132
+ const entry = this.allowedSquads.find(s => (typeof s === 'string' ? s : s.name) === squadName);
133
+ return (entry && entry.timeout_ms) || 120000;
134
+ }
135
+
136
+ async _handleTrigger(req, res) {
137
+ let body;
138
+ try {
139
+ body = await parseBody(req);
140
+ } catch {
141
+ return sendJSON(res, 400, { ok: false, error: 'Invalid JSON body' });
142
+ }
143
+
144
+ const { squad, input, session_id, callback_url, metadata } = body;
145
+
146
+ if (!squad) return sendJSON(res, 400, { ok: false, error: 'squad is required' });
147
+ if (!input) return sendJSON(res, 400, { ok: false, error: 'input is required' });
148
+
149
+ if (!this._isSquadAllowed(squad)) {
150
+ return sendJSON(res, 404, {
151
+ ok: false,
152
+ error: `Squad "${squad}" not found`,
153
+ available_squads: this._getSquadNames()
154
+ });
155
+ }
156
+
157
+ const run_id = generateRunId();
158
+ this._runs.set(run_id, { status: 'queued', response: null, error: null, session_id, metadata });
159
+
160
+ sendJSON(res, 202, { run_id, status: 'queued' });
161
+
162
+ setImmediate(async () => {
163
+ const run = this._runs.get(run_id);
164
+ run.status = 'running';
165
+
166
+ const timeoutMs = this._getSquadTimeout(squad);
167
+
168
+ let timeoutHandle;
169
+ try {
170
+ const execPromise = this.onTrigger
171
+ ? this.onTrigger({ squad, input, session_id, metadata, run_id })
172
+ : Promise.resolve({ response: `Squad ${squad} queued: ${input}` });
173
+
174
+ const timeoutPromise = new Promise((_, reject) => {
175
+ timeoutHandle = setTimeout(() => reject(new Error('Squad execution timed out')), timeoutMs);
176
+ });
177
+
178
+ const result = await Promise.race([execPromise, timeoutPromise]);
179
+ clearTimeout(timeoutHandle);
180
+ run.status = 'completed';
181
+ run.response = result.response || '';
182
+ } catch (err) {
183
+ clearTimeout(timeoutHandle);
184
+ run.status = 'failed';
185
+ run.error = err.message;
186
+ }
187
+
188
+ if (callback_url) {
189
+ const callbackPayload = {
190
+ run_id,
191
+ session_id,
192
+ response: run.response,
193
+ status: run.status,
194
+ metadata
195
+ };
196
+ postCallback(callback_url, callbackPayload).catch(() => {});
197
+ }
198
+ });
199
+ }
200
+
201
+ _handleStatus(runId, res) {
202
+ const run = this._runs.get(runId);
203
+ if (!run) return sendJSON(res, 404, { ok: false, error: 'Run not found' });
204
+ return sendJSON(res, 200, {
205
+ run_id: runId,
206
+ status: run.status,
207
+ response: run.response || null,
208
+ error: run.error || null
209
+ });
210
+ }
211
+
212
+ async _handleQuery(req, res) {
213
+ let body;
214
+ try {
215
+ body = await parseBody(req);
216
+ } catch {
217
+ return sendJSON(res, 400, { ok: false, error: 'Invalid JSON body' });
218
+ }
219
+
220
+ const { squad, query, max_results } = body;
221
+
222
+ if (!squad) return sendJSON(res, 400, { ok: false, error: 'squad is required' });
223
+ if (!query) return sendJSON(res, 400, { ok: false, error: 'query is required' });
224
+
225
+ if (!this._isSquadAllowed(squad)) {
226
+ return sendJSON(res, 404, {
227
+ ok: false,
228
+ error: `Squad "${squad}" not found`,
229
+ available_squads: this._getSquadNames()
230
+ });
231
+ }
232
+
233
+ const t0 = Date.now();
234
+ const QUERY_TIMEOUT_MS = 10_000;
235
+
236
+ let timeoutHandle;
237
+ try {
238
+ const execPromise = this.onQuery
239
+ ? this.onQuery({ squad, query, max_results: max_results || 10 })
240
+ : Promise.resolve({ results: [] });
241
+
242
+ const timeoutPromise = new Promise((_, reject) => {
243
+ timeoutHandle = setTimeout(() => reject(new Error('Query timed out')), QUERY_TIMEOUT_MS);
244
+ });
245
+
246
+ const result = await Promise.race([execPromise, timeoutPromise]);
247
+ clearTimeout(timeoutHandle);
248
+ const latency_ms = Date.now() - t0;
249
+
250
+ return sendJSON(res, 200, {
251
+ results: result.results || [],
252
+ squad,
253
+ latency_ms
254
+ });
255
+ } catch (err) {
256
+ clearTimeout(timeoutHandle);
257
+ const latency_ms = Date.now() - t0;
258
+ return sendJSON(res, 504, { ok: false, error: err.message, squad, latency_ms });
259
+ }
260
+ }
261
+
262
+ _handleHealth(res) {
263
+ return sendJSON(res, 200, {
264
+ ok: true,
265
+ version: this.version,
266
+ squads: this._getSquadNames()
267
+ });
268
+ }
269
+
270
+ async _handleRequest(req, res) {
271
+ const ip = req.socket.remoteAddress || '0.0.0.0';
272
+ const url = req.url || '/';
273
+ const method = req.method || 'GET';
274
+
275
+ if (!this._checkRateLimit(ip)) {
276
+ return sendJSON(res, 429, { ok: false, error: 'Too many requests' });
277
+ }
278
+
279
+ // Health check — no auth required
280
+ if (method === 'GET' && url === '/health') {
281
+ return this._handleHealth(res);
282
+ }
283
+
284
+ if (!this._validateToken(req)) {
285
+ return sendJSON(res, 401, { ok: false, error: 'Unauthorized' });
286
+ }
287
+
288
+ if (method === 'POST' && url === '/trigger') {
289
+ return this._handleTrigger(req, res);
290
+ }
291
+
292
+ if (method === 'POST' && url === '/query') {
293
+ return this._handleQuery(req, res);
294
+ }
295
+
296
+ const statusMatch = method === 'GET' && url.match(/^\/status\/([^/?]+)/);
297
+ if (statusMatch) {
298
+ return this._handleStatus(statusMatch[1], res);
299
+ }
300
+
301
+ return sendJSON(res, 404, { ok: false, error: 'Not found' });
302
+ }
303
+
304
+ start() {
305
+ return new Promise((resolve, reject) => {
306
+ this._server = http.createServer((req, res) => {
307
+ this._handleRequest(req, res).catch(() => {
308
+ sendJSON(res, 500, { ok: false, error: 'Internal server error' });
309
+ });
310
+ });
311
+ this._server.listen(this.port, () => resolve(this._server));
312
+ this._server.once('error', reject);
313
+ });
314
+ }
315
+
316
+ stop() {
317
+ return new Promise((resolve, reject) => {
318
+ if (!this._server) return resolve();
319
+ // Close idle keep-alive connections so server.close() callback fires promptly
320
+ if (typeof this._server.closeIdleConnections === 'function') {
321
+ this._server.closeIdleConnections();
322
+ }
323
+ this._server.close(err => (err ? reject(err) : resolve()));
324
+ });
325
+ }
326
+ }
327
+
328
+ module.exports = { WebhookServer, postCallback };