@wabot-dev/framework 0.9.27 → 2.0.0-beta.0

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 (94) hide show
  1. package/README.md +27 -0
  2. package/bin/skills.mjs +151 -0
  3. package/bin/wabot-skills.mjs +120 -0
  4. package/dist/build/build.js +1031 -8
  5. package/dist/src/addon/chat-bot/in-memory/InMemoryChatMemory.js +1 -3
  6. package/dist/src/addon/chat-bot/xai/XAIChatAdapter.js +180 -0
  7. package/dist/src/addon/chat-controller/cmd/cmdChannelSocketPath.js +1 -5
  8. package/dist/src/addon/chat-controller/hubspot/@hubspot.js +28 -0
  9. package/dist/src/addon/chat-controller/hubspot/HubSpotChannel.js +81 -0
  10. package/dist/src/addon/chat-controller/hubspot/HubSpotChannelConfig.js +20 -0
  11. package/dist/src/addon/chat-controller/hubspot/HubSpotReceiver.js +42 -0
  12. package/dist/src/addon/chat-controller/hubspot/HubSpotSender.js +118 -0
  13. package/dist/src/addon/chat-controller/hubspot/HubSpotWebhookController.js +122 -0
  14. package/dist/src/addon/chat-controller/hubspot/downloadHubSpotAttachments.js +45 -0
  15. package/dist/src/addon/chat-controller/hubspot/hubspotChannelName.js +3 -0
  16. package/dist/src/addon/chat-controller/hubspot/verifyHubSpotSignatureV3.js +28 -0
  17. package/dist/src/addon/chat-controller/{telegram/markdownToTelegramHtml.js → markdown/markdownToChatHtml.js} +5 -8
  18. package/dist/src/addon/chat-controller/slack/@slack.js +22 -0
  19. package/dist/src/addon/chat-controller/slack/SlackChannel.js +187 -0
  20. package/dist/src/addon/chat-controller/slack/SlackChannelConfig.js +12 -0
  21. package/dist/src/addon/chat-controller/slack/markdownToSlackMrkdwn.js +38 -0
  22. package/dist/src/addon/chat-controller/slack/slackChannelName.js +3 -0
  23. package/dist/src/addon/chat-controller/telegram/TelegramChannel.js +2 -2
  24. package/dist/src/addon/ui/preact/PreactRenderer.js +86 -0
  25. package/dist/src/addon/ui/preact/outlet.js +22 -0
  26. package/dist/src/addon/ui/preact/preactClientRuntime.js +67 -0
  27. package/dist/src/core/repository/CrudRepository.js +7 -7
  28. package/dist/src/feature/async/computeDedupKey.js +1 -1
  29. package/dist/src/feature/chat-controller/runChatControllers.js +4 -1
  30. package/dist/src/feature/pg/@pgExtension.js +2 -4
  31. package/dist/src/feature/project-runner/ProjectRunner.js +62 -10
  32. package/dist/src/feature/project-runner/scanner.js +1 -1
  33. package/dist/src/feature/repository/@memExtension.js +1 -2
  34. package/dist/src/feature/rest-controller/runRestControllers.js +11 -6
  35. package/dist/src/feature/ui-controller/actions.js +35 -0
  36. package/dist/src/feature/ui-controller/bundler/UiBundler.js +191 -0
  37. package/dist/src/feature/ui-controller/bundler/devMiddleware.js +41 -0
  38. package/dist/src/feature/ui-controller/bundler/index.js +4 -0
  39. package/dist/src/feature/ui-controller/bundler/manifest.js +34 -0
  40. package/dist/src/feature/ui-controller/bundler/navRuntime.js +236 -0
  41. package/dist/src/feature/ui-controller/bundler/pageAssets.js +30 -0
  42. package/dist/src/feature/ui-controller/document/escape.js +17 -0
  43. package/dist/src/feature/ui-controller/document/helpers.js +13 -0
  44. package/dist/src/feature/ui-controller/document/renderDocument.js +43 -0
  45. package/dist/src/feature/ui-controller/island/IslandRegistry.js +68 -0
  46. package/dist/src/feature/ui-controller/island/island.js +40 -0
  47. package/dist/src/feature/ui-controller/island/serialize.js +35 -0
  48. package/dist/src/feature/ui-controller/metadata/@action.js +18 -0
  49. package/dist/src/feature/ui-controller/metadata/@uiController.js +19 -0
  50. package/dist/src/feature/ui-controller/metadata/@uiMiddleware.js +20 -0
  51. package/dist/src/feature/ui-controller/metadata/@view.js +18 -0
  52. package/dist/src/feature/ui-controller/metadata/UiControllerMetadataStore.js +107 -0
  53. package/dist/src/feature/ui-controller/renderer/UiRendererRegistry.js +42 -0
  54. package/dist/src/feature/ui-controller/runUiControllers.js +285 -0
  55. package/dist/src/index.d.ts +640 -3
  56. package/dist/src/index.js +32 -3
  57. package/dist/src/testing/LlmJudge.js +93 -0
  58. package/dist/src/testing/MockChatAdapter.js +68 -0
  59. package/dist/src/testing/TestChatMemory.js +73 -0
  60. package/dist/src/testing/asyncHarness.js +66 -0
  61. package/dist/src/testing/auth.js +114 -0
  62. package/dist/src/testing/chatBotHarness.js +88 -0
  63. package/dist/src/testing/chatControllerHarness.js +94 -0
  64. package/dist/src/testing/conformance/chatAdapterConformanceCases.js +656 -0
  65. package/dist/src/testing/fixtures.js +53 -0
  66. package/dist/src/testing/helpers.js +42 -0
  67. package/dist/src/testing/index.d.ts +818 -0
  68. package/dist/src/testing/index.js +14 -0
  69. package/dist/src/testing/repositories.js +34 -0
  70. package/dist/src/testing/restHarness.js +127 -0
  71. package/dist/src/testing/testImageBase64.js +5 -0
  72. package/dist/src/testing/uiHarness.js +102 -0
  73. package/dist/src/testing/validation.js +66 -0
  74. package/dist/src/ui/client.js +6 -0
  75. package/dist/src/ui/index.d.ts +427 -0
  76. package/dist/src/ui/index.js +29 -0
  77. package/dist/src/ui/jsx-dev-runtime.d.ts +1 -0
  78. package/dist/src/ui/jsx-dev-runtime.js +1 -0
  79. package/dist/src/ui/jsx-runtime.d.ts +1 -0
  80. package/dist/src/ui/jsx-runtime.js +1 -0
  81. package/package.json +48 -11
  82. package/skills/wabot-async/SKILL.md +143 -0
  83. package/skills/wabot-auth/SKILL.md +153 -0
  84. package/skills/wabot-chat/SKILL.md +140 -0
  85. package/skills/wabot-di-config/SKILL.md +117 -0
  86. package/skills/wabot-framework/SKILL.md +81 -0
  87. package/skills/wabot-framework/references/quickstart.md +85 -0
  88. package/skills/wabot-mindset/SKILL.md +159 -0
  89. package/skills/wabot-ops/SKILL.md +151 -0
  90. package/skills/wabot-persistence/SKILL.md +159 -0
  91. package/skills/wabot-rest-socket/SKILL.md +167 -0
  92. package/skills/wabot-testing/SKILL.md +214 -0
  93. package/skills/wabot-ui/SKILL.md +201 -0
  94. package/skills/wabot-validation/SKILL.md +108 -0
package/README.md CHANGED
@@ -28,6 +28,7 @@
28
28
  - 🤖 **Soporte para bots con IA** - Inteligencia artificial lista para usar
29
29
  - 🧠 **Múltiples proveedores de IA** - OpenAI, Google Gemini, Anthropic Claude
30
30
  - 💬 **Múltiples canales de chat** - Telegram, WhatsApp, Socket y más
31
+ - 🧪 **Sistema de testing integrado** - Prueba tus chatbots sin API keys ni base de datos
31
32
  - 📖 **Documentación clara y en español** - Aprende sin barreras de idioma
32
33
 
33
34
  ---
@@ -103,6 +104,32 @@ Los addons opcionales (`pg`, SDKs de IA) se mantienen como `peerDependencies` y
103
104
 
104
105
  ---
105
106
 
107
+ ## 🧪 Testing
108
+
109
+ El framework incluye un sistema de testing en `@wabot-dev/framework/testing`, agnóstico al runner (node:test, vitest, bun test). Permite probar chatbots de forma determinista —sin API keys ni base de datos— y también evaluar el comportamiento real con un juez LLM.
110
+
111
+ ```ts
112
+ import { createChatBotHarness, LlmJudge } from '@wabot-dev/framework/testing'
113
+
114
+ // Determinista: el LLM se simula, tus tools se ejecutan de verdad
115
+ const harness = createChatBotHarness({ mindset: EliaMindset })
116
+ harness.adapter.callTool('saveEvent', { title: 'Demo', ... }).reply('¡Agendado!')
117
+
118
+ const turn = await harness.send('agenda una demo mañana')
119
+ // turn.replies, turn.toolCalls (con resultados reales), harness.history()
120
+
121
+ // Evals: juzga conversaciones reales con un LLM
122
+ const judge = new LlmJudge({ adapter, models: [{ model: 'claude-haiku-4-5' }] })
123
+ await judge.assert({
124
+ transcript: harness.history(),
125
+ criteria: 'Responde en español y confirma la fecha del evento',
126
+ })
127
+ ```
128
+
129
+ También incluye harnesses para controllers de chat (`createChatControllerHarness`), endpoints REST con guards JWT/API-Key reales (`createRestHarness`), commands y crons (`createAsyncHarness`), repositorios en memoria (`useMemoryRepositories`), aserciones de validación y una suite de conformance para adapters LLM propios (`chatAdapterConformanceCases`).
130
+
131
+ ---
132
+
106
133
  ## 💬 Plataformas Soportadas
107
134
 
108
135
  Wabot se integra nativamente con las principales plataformas de mensajería:
package/bin/skills.mjs ADDED
@@ -0,0 +1,151 @@
1
+ // Canonical skills installer for @wabot-dev/framework.
2
+ //
3
+ // The packaged agent skills live at `<package>/skills/<name>/SKILL.md` and are
4
+ // versioned together with the framework API they document. This module is the
5
+ // single source of truth for enumerating and installing them; it is consumed by
6
+ // the `wabot-skills` bin and by @wabot-dev/create during project bootstrap.
7
+ //
8
+ // Dependency-free (node builtins only) so `npx wabot-skills` works with just the
9
+ // framework installed.
10
+
11
+ import fs from 'fs/promises'
12
+ import fss from 'fs'
13
+ import os from 'os'
14
+ import path from 'path'
15
+ import { fileURLToPath } from 'url'
16
+
17
+ export const SKILLS_DIR = fileURLToPath(new URL('../skills', import.meta.url))
18
+
19
+ const AGENT_DIRS = {
20
+ claude: ['.claude', 'skills'],
21
+ codex: ['.codex', 'skills'],
22
+ agents: ['.agents', 'skills'],
23
+ }
24
+
25
+ export function listSupportedAgents() {
26
+ return Object.keys(AGENT_DIRS)
27
+ }
28
+
29
+ export function agentSkillsSegments(agent) {
30
+ const segments = AGENT_DIRS[agent]
31
+ if (!segments) {
32
+ throw new Error(
33
+ `Unsupported agent "${agent}". Supported: ${listSupportedAgents().join(', ')}`,
34
+ )
35
+ }
36
+ return segments
37
+ }
38
+
39
+ export function listSkillNames() {
40
+ return fss
41
+ .readdirSync(SKILLS_DIR, { withFileTypes: true })
42
+ .filter(
43
+ (entry) =>
44
+ entry.isDirectory() &&
45
+ fss.existsSync(path.join(SKILLS_DIR, entry.name, 'SKILL.md')),
46
+ )
47
+ .map((entry) => entry.name)
48
+ .sort()
49
+ }
50
+
51
+ export function listSkills() {
52
+ return listSkillNames().map((name) => ({
53
+ name,
54
+ sourceDir: path.join(SKILLS_DIR, name),
55
+ }))
56
+ }
57
+
58
+ export function getSkill(name) {
59
+ const sourceDir = path.join(SKILLS_DIR, name)
60
+ if (!fss.existsSync(path.join(sourceDir, 'SKILL.md'))) {
61
+ return null
62
+ }
63
+ return { name, sourceDir }
64
+ }
65
+
66
+ function resolveSkills(skillNames) {
67
+ if (!skillNames || skillNames.length === 0) {
68
+ return listSkills()
69
+ }
70
+ return skillNames.map((name) => {
71
+ const skill = getSkill(name)
72
+ if (!skill) {
73
+ throw new Error(
74
+ `Unknown skill "${name}". Available skills: ${listSkillNames().join(', ')}`,
75
+ )
76
+ }
77
+ return skill
78
+ })
79
+ }
80
+
81
+ // Copy skills into `<projectDir>/.<agent>/skills`. By default existing skills are
82
+ // left untouched (bootstrap semantics). Pass `{ overwrite: true }` to refresh them
83
+ // to the currently installed framework version (resync semantics).
84
+ export async function installSkillsInProject(projectDir, agents, skillNames, options = {}) {
85
+ if (!projectDir) {
86
+ throw new Error('installSkillsInProject: projectDir is required')
87
+ }
88
+
89
+ const resolvedAgents = (agents && agents.length > 0 ? agents : ['claude']).map((agent) => {
90
+ agentSkillsSegments(agent)
91
+ return agent
92
+ })
93
+
94
+ const skills = resolveSkills(skillNames)
95
+ const overwrite = options.overwrite === true
96
+ const installations = []
97
+
98
+ for (const agent of resolvedAgents) {
99
+ const baseDir = path.join(projectDir, ...agentSkillsSegments(agent))
100
+ await fs.mkdir(baseDir, { recursive: true })
101
+
102
+ for (const skill of skills) {
103
+ const installDir = path.join(baseDir, skill.name)
104
+ try {
105
+ await fs.cp(skill.sourceDir, installDir, {
106
+ recursive: true,
107
+ force: overwrite,
108
+ errorOnExist: !overwrite,
109
+ })
110
+ installations.push({
111
+ agent,
112
+ skill: skill.name,
113
+ installDir,
114
+ status: overwrite ? 'synced' : 'installed',
115
+ })
116
+ } catch (error) {
117
+ if (!overwrite && (error?.code === 'ERR_FS_CP_EEXIST' || error?.code === 'EEXIST')) {
118
+ installations.push({ agent, skill: skill.name, installDir, status: 'skipped' })
119
+ continue
120
+ }
121
+ throw error
122
+ }
123
+ }
124
+ }
125
+
126
+ return { agents: resolvedAgents, skills: skills.map((skill) => skill.name), installations }
127
+ }
128
+
129
+ // Copy a single skill into agent homes under `homeRoot` (defaults to $HOME).
130
+ export async function installSkillGlobally(skillName, agents, homeRoot = os.homedir()) {
131
+ const skill = getSkill(skillName)
132
+ if (!skill) {
133
+ throw new Error(
134
+ `Unknown skill "${skillName}". Available skills: ${listSkillNames().join(', ')}`,
135
+ )
136
+ }
137
+ const resolvedAgents = (agents && agents.length > 0 ? agents : ['claude']).map((agent) => {
138
+ agentSkillsSegments(agent)
139
+ return agent
140
+ })
141
+
142
+ const installations = []
143
+ for (const agent of resolvedAgents) {
144
+ const baseDir = path.join(homeRoot, ...agentSkillsSegments(agent))
145
+ await fs.mkdir(baseDir, { recursive: true })
146
+ const installDir = path.join(baseDir, skill.name)
147
+ await fs.cp(skill.sourceDir, installDir, { recursive: true, force: true, errorOnExist: false })
148
+ installations.push({ agent, skill: skill.name, installDir })
149
+ }
150
+ return { skill: skill.name, homeRoot, installations }
151
+ }
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+
3
+ // wabot-skills — install / resync the packaged Wabot agent skills that ship with
4
+ // the installed @wabot-dev/framework version.
5
+ //
6
+ // wabot-skills list
7
+ // wabot-skills sync [--agents claude,codex,agents] [--project <dir>]
8
+ // wabot-skills add <skill> [--agents claude,codex,agents] [--home <dir>]
9
+ //
10
+ // `sync` refreshes the project's skills to match the currently installed framework
11
+ // version (overwrites in place). Run it after upgrading @wabot-dev/framework.
12
+
13
+ import fss from 'fs'
14
+ import path from 'path'
15
+
16
+ import {
17
+ installSkillGlobally,
18
+ installSkillsInProject,
19
+ listSkillNames,
20
+ listSupportedAgents,
21
+ } from './skills.mjs'
22
+
23
+ function parseFlags(argv) {
24
+ const flags = {}
25
+ const positional = []
26
+ for (let i = 0; i < argv.length; i++) {
27
+ const arg = argv[i]
28
+ if (arg.startsWith('--')) {
29
+ const key = arg.slice(2)
30
+ const next = argv[i + 1]
31
+ if (next === undefined || next.startsWith('--')) {
32
+ flags[key] = true
33
+ } else {
34
+ flags[key] = next
35
+ i++
36
+ }
37
+ } else {
38
+ positional.push(arg)
39
+ }
40
+ }
41
+ return { flags, positional }
42
+ }
43
+
44
+ function parseAgents(value) {
45
+ if (!value || value === true) return listSupportedAgents()
46
+ return String(value)
47
+ .split(',')
48
+ .map((a) => a.trim())
49
+ .filter(Boolean)
50
+ }
51
+
52
+ function usage() {
53
+ console.log(`wabot-skills — manage packaged Wabot agent skills
54
+
55
+ Usage:
56
+ wabot-skills list
57
+ wabot-skills sync [--agents claude,codex,agents] [--project <dir>]
58
+ wabot-skills add <skill> [--agents claude,codex,agents] [--home <dir>]
59
+
60
+ sync refreshes existing project skills to the installed framework version.`)
61
+ }
62
+
63
+ async function main() {
64
+ const [command, ...rest] = process.argv.slice(2)
65
+ const { flags, positional } = parseFlags(rest)
66
+
67
+ if (!command || command === 'help' || flags.help) {
68
+ usage()
69
+ return
70
+ }
71
+
72
+ if (command === 'list') {
73
+ for (const name of listSkillNames()) console.log(name)
74
+ return
75
+ }
76
+
77
+ if (command === 'sync') {
78
+ const projectDir = path.resolve(flags.project || process.cwd())
79
+ // Default to the agent dirs that already exist in the project; fall back to claude.
80
+ let agents = flags.agents
81
+ ? parseAgents(flags.agents)
82
+ : listSupportedAgents().filter((agent) =>
83
+ fss.existsSync(path.join(projectDir, `.${agent}`, 'skills')),
84
+ )
85
+ if (agents.length === 0) agents = ['claude']
86
+
87
+ const result = await installSkillsInProject(projectDir, agents, undefined, { overwrite: true })
88
+ const byAgent = new Map()
89
+ for (const item of result.installations) {
90
+ byAgent.set(item.agent, (byAgent.get(item.agent) ?? 0) + 1)
91
+ }
92
+ for (const [agent, count] of byAgent) {
93
+ console.log(`✓ synced ${count} skills into .${agent}/skills`)
94
+ }
95
+ return
96
+ }
97
+
98
+ if (command === 'add') {
99
+ const skillName = positional[0]
100
+ if (!skillName) {
101
+ console.error('Error: `wabot-skills add` requires a skill name')
102
+ process.exit(1)
103
+ }
104
+ const agents = parseAgents(flags.agents)
105
+ const result = await installSkillGlobally(skillName, agents, flags.home || undefined)
106
+ for (const item of result.installations) {
107
+ console.log(`✓ installed ${result.skill} -> ${item.installDir}`)
108
+ }
109
+ return
110
+ }
111
+
112
+ console.error(`Error: unknown command "${command}"`)
113
+ usage()
114
+ process.exit(1)
115
+ }
116
+
117
+ main().catch((error) => {
118
+ console.error('Error:', error.message)
119
+ process.exit(1)
120
+ })