@johpaz/hive-sdk 0.0.12 → 0.0.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.
Files changed (199) hide show
  1. package/.github/CODEOWNERS +9 -0
  2. package/.github/workflows/publish.yml +89 -0
  3. package/.github/workflows/version-bump.yml +102 -0
  4. package/CHANGELOG.md +38 -0
  5. package/README.md +158 -0
  6. package/bun.lock +543 -0
  7. package/bunfig.toml +7 -0
  8. package/docs/API-AGENTS.md +316 -0
  9. package/docs/API-CONTEXT-COMPILER.md +252 -0
  10. package/docs/API-DAG-SCHEDULER.md +273 -0
  11. package/docs/API-TOOLS-SKILLS-CHANNELS.md +293 -0
  12. package/docs/API-WORKERS-EVENTS.md +152 -0
  13. package/docs/INDEX.md +141 -0
  14. package/docs/README.md +68 -0
  15. package/package.json +54 -105
  16. package/packages/cli/package.json +17 -0
  17. package/packages/cli/src/commands/init.ts +56 -0
  18. package/packages/cli/src/commands/run.ts +45 -0
  19. package/packages/cli/src/commands/test.ts +42 -0
  20. package/packages/cli/src/commands/trace.ts +55 -0
  21. package/packages/cli/src/index.ts +43 -0
  22. package/packages/core/package.json +58 -0
  23. package/packages/core/src/ace/Curator.ts +158 -0
  24. package/packages/core/src/ace/Reflector.ts +200 -0
  25. package/packages/core/src/ace/Tracer.ts +100 -0
  26. package/packages/core/src/ace/index.ts +4 -0
  27. package/packages/core/src/agent/AgentRunner.ts +699 -0
  28. package/packages/core/src/agent/Compaction.ts +221 -0
  29. package/packages/core/src/agent/ContextCompiler.ts +567 -0
  30. package/packages/core/src/agent/ContextGuard.ts +91 -0
  31. package/packages/core/src/agent/ConversationStore.ts +244 -0
  32. package/packages/core/src/agent/Hooks.ts +166 -0
  33. package/packages/core/src/agent/NativeTools.ts +31 -0
  34. package/packages/core/src/agent/PromptBuilder.ts +169 -0
  35. package/packages/core/src/agent/Service.ts +267 -0
  36. package/packages/core/src/agent/StuckLoop.ts +133 -0
  37. package/packages/core/src/agent/index.ts +12 -0
  38. package/packages/core/src/agent/providers/LLMClient.ts +149 -0
  39. package/packages/core/src/agent/providers/anthropic.ts +212 -0
  40. package/packages/core/src/agent/providers/gemini.ts +215 -0
  41. package/packages/core/src/agent/providers/index.ts +199 -0
  42. package/packages/core/src/agent/providers/interface.ts +195 -0
  43. package/packages/core/src/agent/providers/ollama.ts +175 -0
  44. package/packages/core/src/agent/providers/openai-compat.ts +231 -0
  45. package/packages/core/src/agent/providers.ts +1 -0
  46. package/packages/core/src/agent/selectors/PlaybookSelector.ts +147 -0
  47. package/packages/core/src/agent/selectors/SkillSelector.ts +478 -0
  48. package/packages/core/src/agent/selectors/ToolSelector.ts +577 -0
  49. package/packages/core/src/agent/selectors/index.ts +6 -0
  50. package/packages/core/src/api/createAgent.test.ts +48 -0
  51. package/packages/core/src/api/createAgent.ts +122 -0
  52. package/packages/core/src/api/index.ts +2 -0
  53. package/packages/core/src/canvas/CanvasManager.ts +390 -0
  54. package/packages/core/src/canvas/a2ui-tools.ts +255 -0
  55. package/packages/core/src/canvas/canvas-tools.ts +448 -0
  56. package/packages/core/src/canvas/emitter.ts +149 -0
  57. package/packages/core/src/canvas/index.ts +6 -0
  58. package/packages/core/src/config/index.ts +2 -0
  59. package/packages/core/src/config/loader.ts +554 -0
  60. package/packages/core/src/ethics/EthicsGuard.test.ts +54 -0
  61. package/packages/core/src/ethics/EthicsGuard.ts +66 -0
  62. package/packages/core/src/ethics/index.ts +2 -0
  63. package/packages/core/src/gateway/channel-notify.test.ts +14 -0
  64. package/packages/core/src/gateway/channel-notify.ts +12 -0
  65. package/packages/core/src/gateway/index.ts +1 -0
  66. package/packages/core/src/index.ts +37 -0
  67. package/packages/core/src/mcp/MCPClient.ts +439 -0
  68. package/packages/core/src/mcp/MCPToolAdapter.ts +176 -0
  69. package/packages/core/src/mcp/config.ts +13 -0
  70. package/packages/core/src/mcp/hot-reload.ts +147 -0
  71. package/packages/core/src/mcp/index.ts +11 -0
  72. package/packages/core/src/mcp/logger.ts +42 -0
  73. package/packages/core/src/mcp/singleton.ts +21 -0
  74. package/packages/core/src/mcp/transports/index.ts +67 -0
  75. package/packages/core/src/mcp/transports/sse.ts +241 -0
  76. package/packages/core/src/mcp/transports/websocket.ts +159 -0
  77. package/packages/core/src/memory/Scratchpad.test.ts +47 -0
  78. package/packages/core/src/memory/Scratchpad.ts +37 -0
  79. package/packages/core/src/memory/Storage.ts +6 -0
  80. package/packages/core/src/memory/index.ts +2 -0
  81. package/packages/core/src/multimodal/VisionService.ts +293 -0
  82. package/packages/core/src/multimodal/index.ts +2 -0
  83. package/packages/core/src/multimodal/types.ts +28 -0
  84. package/packages/core/src/security/Pairing.ts +250 -0
  85. package/packages/core/src/security/RateLimit.ts +270 -0
  86. package/packages/core/src/security/index.ts +4 -0
  87. package/packages/core/src/skills/SkillLoader.ts +388 -0
  88. package/packages/core/src/skills/bundled-data.generated.ts +3332 -0
  89. package/packages/core/src/skills/defineSkill.ts +18 -0
  90. package/packages/core/src/skills/index.ts +4 -0
  91. package/packages/core/src/state/index.ts +2 -0
  92. package/packages/core/src/state/store.ts +312 -0
  93. package/packages/core/src/storage/SQLiteStorage.ts +407 -0
  94. package/packages/core/src/storage/crypto.ts +101 -0
  95. package/packages/core/src/storage/index.ts +10 -0
  96. package/packages/core/src/storage/onboarding.ts +1603 -0
  97. package/packages/core/src/storage/schema.ts +689 -0
  98. package/packages/core/src/storage/seed.ts +740 -0
  99. package/packages/core/src/storage/usage.ts +374 -0
  100. package/packages/core/src/swarm/AgentBus.ts +460 -0
  101. package/packages/core/src/swarm/AgentExecutor.ts +53 -0
  102. package/packages/core/src/swarm/Coordinator.ts +251 -0
  103. package/packages/core/src/swarm/EventBridge.ts +122 -0
  104. package/packages/core/src/swarm/EventBus.ts +169 -0
  105. package/packages/core/src/swarm/TaskGraph.ts +192 -0
  106. package/packages/core/src/swarm/TaskNode.ts +97 -0
  107. package/packages/core/src/swarm/TaskResult.ts +22 -0
  108. package/packages/core/src/swarm/WorkerPool.ts +236 -0
  109. package/packages/core/src/swarm/errors.ts +37 -0
  110. package/packages/core/src/swarm/index.ts +30 -0
  111. package/packages/core/src/swarm/presets/HiveLearnPreset.ts +99 -0
  112. package/packages/core/src/swarm/presets/ResearchPreset.ts +97 -0
  113. package/packages/core/src/swarm/presets/index.ts +4 -0
  114. package/packages/core/src/swarm/strategies/ParallelStrategy.ts +21 -0
  115. package/packages/core/src/swarm/strategies/PriorityStrategy.ts +46 -0
  116. package/packages/core/src/swarm/strategies/index.ts +3 -0
  117. package/packages/core/src/swarm/types.ts +164 -0
  118. package/packages/core/src/tools/ToolExecutor.ts +58 -0
  119. package/packages/core/src/tools/ToolRegistry.test.ts +98 -0
  120. package/packages/core/src/tools/ToolRegistry.ts +61 -0
  121. package/packages/core/src/tools/agents/get-available-models.ts +118 -0
  122. package/packages/core/src/tools/agents/index.ts +715 -0
  123. package/packages/core/src/tools/bridge-events.ts +26 -0
  124. package/packages/core/src/tools/canvas/index.ts +375 -0
  125. package/packages/core/src/tools/cli/index.ts +142 -0
  126. package/packages/core/src/tools/codebridge/index.ts +342 -0
  127. package/packages/core/src/tools/core/index.ts +476 -0
  128. package/packages/core/src/tools/cron/index.ts +626 -0
  129. package/packages/core/src/tools/filesystem/fs-delete.ts +78 -0
  130. package/packages/core/src/tools/filesystem/fs-edit.ts +106 -0
  131. package/packages/core/src/tools/filesystem/fs-exists.ts +63 -0
  132. package/packages/core/src/tools/filesystem/fs-glob.ts +108 -0
  133. package/packages/core/src/tools/filesystem/fs-list.ts +129 -0
  134. package/packages/core/src/tools/filesystem/fs-read.ts +72 -0
  135. package/packages/core/src/tools/filesystem/fs-write.ts +67 -0
  136. package/packages/core/src/tools/filesystem/index.ts +34 -0
  137. package/packages/core/src/tools/filesystem/workspace-guard.ts +62 -0
  138. package/packages/core/src/tools/index.ts +231 -0
  139. package/packages/core/src/tools/meeting/index.ts +363 -0
  140. package/packages/core/src/tools/office/index.ts +47 -0
  141. package/packages/core/src/tools/office/office-escribir-docx.ts +192 -0
  142. package/packages/core/src/tools/office/office-escribir-pdf.ts +172 -0
  143. package/packages/core/src/tools/office/office-escribir-pptx.ts +174 -0
  144. package/packages/core/src/tools/office/office-escribir-xlsx.ts +116 -0
  145. package/packages/core/src/tools/office/office-leer-docx.ts +93 -0
  146. package/packages/core/src/tools/office/office-leer-pdf.ts +114 -0
  147. package/packages/core/src/tools/office/office-leer-pptx.ts +136 -0
  148. package/packages/core/src/tools/office/office-leer-xlsx.ts +124 -0
  149. package/packages/core/src/tools/projects/index.ts +37 -0
  150. package/packages/core/src/tools/projects/project-create.ts +94 -0
  151. package/packages/core/src/tools/projects/project-done.ts +66 -0
  152. package/packages/core/src/tools/projects/project-fail.ts +66 -0
  153. package/packages/core/src/tools/projects/project-list.ts +96 -0
  154. package/packages/core/src/tools/projects/project-update.ts +72 -0
  155. package/packages/core/src/tools/projects/task-create.ts +68 -0
  156. package/packages/core/src/tools/projects/task-evaluate.ts +93 -0
  157. package/packages/core/src/tools/projects/task-update.ts +93 -0
  158. package/packages/core/src/tools/types.ts +39 -0
  159. package/packages/core/src/tools/voice/index.ts +104 -0
  160. package/packages/core/src/tools/web/browser-click.ts +78 -0
  161. package/packages/core/src/tools/web/browser-extract.ts +139 -0
  162. package/packages/core/src/tools/web/browser-navigate.ts +106 -0
  163. package/packages/core/src/tools/web/browser-screenshot.ts +87 -0
  164. package/packages/core/src/tools/web/browser-script.ts +88 -0
  165. package/packages/core/src/tools/web/browser-service.ts +554 -0
  166. package/packages/core/src/tools/web/browser-type.ts +101 -0
  167. package/packages/core/src/tools/web/browser-wait.ts +136 -0
  168. package/packages/core/src/tools/web/index.ts +41 -0
  169. package/packages/core/src/tools/web/web-fetch.ts +78 -0
  170. package/packages/core/src/tools/web/web-search.ts +123 -0
  171. package/packages/core/src/utils/benchmark.ts +80 -0
  172. package/packages/core/src/utils/crypto.ts +73 -0
  173. package/packages/core/src/utils/date.ts +42 -0
  174. package/packages/core/src/utils/index.ts +10 -0
  175. package/packages/core/src/utils/logger.ts +389 -0
  176. package/packages/core/src/utils/retry.ts +70 -0
  177. package/packages/core/src/utils/toon.ts +253 -0
  178. package/packages/core/src/voice/index.ts +656 -0
  179. package/test/setup-db.ts +216 -0
  180. package/tsconfig.json +39 -0
  181. package/src/agents.ts +0 -1
  182. package/src/canvas.ts +0 -1
  183. package/src/channels.ts +0 -1
  184. package/src/config.ts +0 -1
  185. package/src/events.ts +0 -1
  186. package/src/gateway.ts +0 -1
  187. package/src/index.ts +0 -304
  188. package/src/mcp.ts +0 -1
  189. package/src/multimodal.ts +0 -1
  190. package/src/scheduler.ts +0 -1
  191. package/src/security.ts +0 -1
  192. package/src/skills.ts +0 -1
  193. package/src/state.ts +0 -1
  194. package/src/storage.ts +0 -1
  195. package/src/tools.ts +0 -1
  196. package/src/tts.ts +0 -1
  197. package/src/types.ts +0 -82
  198. package/src/utils.ts +0 -1
  199. package/src/voice.ts +0 -1
@@ -0,0 +1,192 @@
1
+ /**
2
+ * office_escribir_docx - Generar un archivo Word (.docx)
3
+ *
4
+ * @category office
5
+ * @seedId office_escribir_docx
6
+ * @spanish crear word, generar docx, escribir documento word, exportar a docx
7
+ */
8
+
9
+ import type { Tool } from "../types.ts";
10
+ import { logger } from "../../utils/logger.ts";
11
+ import * as path from "node:path";
12
+ import * as fs from "node:fs";
13
+
14
+ const log = logger.child("office-escribir-docx");
15
+
16
+ interface ParrafoInput {
17
+ texto: string;
18
+ tipo?: "titulo1" | "titulo2" | "titulo3" | "parrafo" | "lista";
19
+ negrita?: boolean;
20
+ cursiva?: boolean;
21
+ }
22
+
23
+ interface TablaInput {
24
+ filas: Array<{ celdas: string[] }>;
25
+ encabezado?: boolean;
26
+ }
27
+
28
+ export const officeEscribirDocxTool: Tool = {
29
+ name: "office_escribir_docx",
30
+ description:
31
+ "Generar un archivo Word (.docx) con párrafos, títulos y tablas. Spanish: crear word, generar docx, escribir documento word, exportar a docx",
32
+ parameters: {
33
+ type: "object",
34
+ properties: {
35
+ ruta: {
36
+ type: "string",
37
+ description: "Ruta donde guardar el archivo .docx generado",
38
+ },
39
+ titulo: {
40
+ type: "string",
41
+ description: "Título del documento (opcional)",
42
+ },
43
+ parrafos: {
44
+ type: "array",
45
+ description:
46
+ "Array de párrafos a incluir. Cada párrafo tiene: texto (string), tipo ('titulo1'|'titulo2'|'titulo3'|'parrafo'|'lista'), negrita (bool), cursiva (bool)",
47
+ items: {
48
+ type: "object",
49
+ properties: {
50
+ texto: { type: "string" },
51
+ tipo: { type: "string" },
52
+ negrita: { type: "boolean" },
53
+ cursiva: { type: "boolean" },
54
+ },
55
+ },
56
+ },
57
+ tablas: {
58
+ type: "array",
59
+ description:
60
+ "Array de tablas a incluir. Cada tabla tiene: filas (array de {celdas: string[]}), encabezado (bool, primera fila como encabezado)",
61
+ items: {
62
+ type: "object",
63
+ properties: {
64
+ filas: { type: "array", items: { type: "object" } },
65
+ encabezado: { type: "boolean" },
66
+ },
67
+ },
68
+ },
69
+ },
70
+ required: ["ruta"],
71
+ },
72
+ execute: async (params: Record<string, unknown>) => {
73
+ const ruta = params.ruta as string;
74
+ const titulo = params.titulo as string | undefined;
75
+ const parrafosInput = (params.parrafos as ParrafoInput[]) ?? [];
76
+ const tablasInput = (params.tablas as TablaInput[]) ?? [];
77
+
78
+ log.debug(`Generando DOCX: ${ruta}`);
79
+
80
+ try {
81
+ const {
82
+ Document,
83
+ Packer,
84
+ Paragraph,
85
+ TextRun,
86
+ HeadingLevel,
87
+ Table,
88
+ TableRow,
89
+ TableCell,
90
+ WidthType,
91
+ BorderStyle,
92
+ } = await import("docx");
93
+
94
+ const seccionChildren: any[] = [];
95
+
96
+ // Título del documento
97
+ if (titulo) {
98
+ seccionChildren.push(
99
+ new Paragraph({
100
+ text: titulo,
101
+ heading: HeadingLevel.TITLE,
102
+ })
103
+ );
104
+ }
105
+
106
+ // Párrafos
107
+ for (const p of parrafosInput) {
108
+ const run = new TextRun({
109
+ text: p.texto,
110
+ bold: p.negrita ?? false,
111
+ italics: p.cursiva ?? false,
112
+ });
113
+
114
+ let heading: any = undefined;
115
+ if (p.tipo === "titulo1") heading = HeadingLevel.HEADING_1;
116
+ else if (p.tipo === "titulo2") heading = HeadingLevel.HEADING_2;
117
+ else if (p.tipo === "titulo3") heading = HeadingLevel.HEADING_3;
118
+
119
+ const opciones: any = { children: [run] };
120
+ if (heading) opciones.heading = heading;
121
+ if (p.tipo === "lista") opciones.bullet = { level: 0 };
122
+
123
+ seccionChildren.push(new Paragraph(opciones));
124
+ }
125
+
126
+ // Tablas
127
+ for (const tablaInput of tablasInput) {
128
+ const filas = tablaInput.filas.map((fila, filaIdx) => {
129
+ const celdas = fila.celdas.map((textoCelda) => {
130
+ const esEncabezado = tablaInput.encabezado && filaIdx === 0;
131
+ return new TableCell({
132
+ children: [
133
+ new Paragraph({
134
+ children: [
135
+ new TextRun({
136
+ text: textoCelda,
137
+ bold: esEncabezado,
138
+ }),
139
+ ],
140
+ }),
141
+ ],
142
+ });
143
+ });
144
+ return new TableRow({ children: celdas });
145
+ });
146
+
147
+ seccionChildren.push(
148
+ new Table({
149
+ rows: filas,
150
+ width: { size: 100, type: WidthType.PERCENTAGE },
151
+ })
152
+ );
153
+
154
+ // Línea en blanco después de la tabla
155
+ seccionChildren.push(new Paragraph({ text: "" }));
156
+ }
157
+
158
+ const doc = new Document({
159
+ sections: [
160
+ {
161
+ children: seccionChildren,
162
+ },
163
+ ],
164
+ });
165
+
166
+ const rutaAbsoluta = path.resolve(ruta);
167
+ const dirDestino = path.dirname(rutaAbsoluta);
168
+ if (!fs.existsSync(dirDestino)) {
169
+ fs.mkdirSync(dirDestino, { recursive: true });
170
+ }
171
+
172
+ const buffer = await Packer.toBuffer(doc);
173
+ fs.writeFileSync(rutaAbsoluta, buffer);
174
+
175
+ log.info(`DOCX generado: ${rutaAbsoluta} (${buffer.length} bytes)`);
176
+
177
+ return {
178
+ ok: true,
179
+ ruta: rutaAbsoluta,
180
+ bytesEscritos: buffer.length,
181
+ parrafos: parrafosInput.length,
182
+ tablas: tablasInput.length,
183
+ };
184
+ } catch (error) {
185
+ log.error(`Error generando DOCX: ${(error as Error).message}`);
186
+ return {
187
+ ok: false,
188
+ error: `No se pudo generar el archivo DOCX: ${(error as Error).message}`,
189
+ };
190
+ }
191
+ },
192
+ };
@@ -0,0 +1,172 @@
1
+ /**
2
+ * office_escribir_pdf - Generar un archivo PDF desde texto
3
+ *
4
+ * @category office
5
+ * @seedId office_escribir_pdf
6
+ * @spanish crear pdf, generar pdf, escribir pdf, exportar a pdf
7
+ */
8
+
9
+ import type { Tool } from "../types.ts";
10
+ import { logger } from "../../utils/logger.ts";
11
+ import * as path from "node:path";
12
+ import * as fs from "node:fs";
13
+
14
+ const log = logger.child("office-escribir-pdf");
15
+
16
+ export const officeEscribirPdfTool: Tool = {
17
+ name: "office_escribir_pdf",
18
+ description:
19
+ "Generar un archivo PDF desde texto con configuración de márgenes y tamaño de página. Spanish: crear pdf, generar pdf, escribir pdf, exportar a pdf",
20
+ parameters: {
21
+ type: "object",
22
+ properties: {
23
+ ruta: {
24
+ type: "string",
25
+ description: "Ruta donde guardar el archivo PDF generado",
26
+ },
27
+ contenido: {
28
+ type: "string",
29
+ description: "Texto o contenido a escribir en el PDF",
30
+ },
31
+ titulo: {
32
+ type: "string",
33
+ description: "Título del documento (opcional)",
34
+ },
35
+ tamaño_pagina: {
36
+ type: "string",
37
+ description: "Tamaño de página: 'A4' o 'Letter' (default: 'A4')",
38
+ enum: ["A4", "Letter"],
39
+ },
40
+ margen: {
41
+ type: "number",
42
+ description: "Margen en puntos (default: 50)",
43
+ },
44
+ tamaño_fuente: {
45
+ type: "number",
46
+ description: "Tamaño de fuente en puntos (default: 12)",
47
+ },
48
+ },
49
+ required: ["ruta", "contenido"],
50
+ },
51
+ execute: async (params: Record<string, unknown>) => {
52
+ const ruta = params.ruta as string;
53
+ const contenido = params.contenido as string;
54
+ const titulo = params.titulo as string | undefined;
55
+ const tamañoPagina = (params.tamaño_pagina as string) ?? "A4";
56
+ const margen = (params.margen as number) ?? 50;
57
+ const tamañoFuente = (params.tamaño_fuente as number) ?? 12;
58
+
59
+ log.debug(`Generando PDF: ${ruta}`);
60
+
61
+ try {
62
+ const { PDFDocument, StandardFonts, rgb, PageSizes } = await import("pdf-lib");
63
+
64
+ const pdfDoc = await PDFDocument.create();
65
+
66
+ if (titulo) {
67
+ pdfDoc.setTitle(titulo);
68
+ }
69
+ pdfDoc.setCreator("Hive Agent");
70
+ pdfDoc.setCreationDate(new Date());
71
+
72
+ const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
73
+ const pageSize = tamañoPagina === "Letter" ? PageSizes.Letter : PageSizes.A4;
74
+
75
+ const anchoPagina = pageSize[0];
76
+ const altoPagina = pageSize[1];
77
+ const anchoUtil = anchoPagina - margen * 2;
78
+ const lineHeight = tamañoFuente * 1.4;
79
+
80
+ // Si hay título, agregarlo primero
81
+ const lineasTodas: string[] = [];
82
+ if (titulo) {
83
+ lineasTodas.push(titulo);
84
+ lineasTodas.push(""); // línea en blanco
85
+ }
86
+
87
+ // Dividir el contenido en líneas respetando saltos de línea
88
+ const lineasContenido = contenido.split("\n");
89
+
90
+ // Para cada línea, dividir en sublíneas si es demasiado larga
91
+ for (const linea of lineasContenido) {
92
+ if (linea.trim() === "") {
93
+ lineasTodas.push("");
94
+ continue;
95
+ }
96
+
97
+ const palabras = linea.split(" ");
98
+ let lineaActual = "";
99
+
100
+ for (const palabra of palabras) {
101
+ const prueba = lineaActual ? `${lineaActual} ${palabra}` : palabra;
102
+ const ancho = font.widthOfTextAtSize(prueba, tamañoFuente);
103
+
104
+ if (ancho > anchoUtil && lineaActual) {
105
+ lineasTodas.push(lineaActual);
106
+ lineaActual = palabra;
107
+ } else {
108
+ lineaActual = prueba;
109
+ }
110
+ }
111
+
112
+ if (lineaActual) {
113
+ lineasTodas.push(lineaActual);
114
+ }
115
+ }
116
+
117
+ // Distribuir líneas en páginas
118
+ const lineasPorPagina = Math.floor((altoPagina - margen * 2) / lineHeight);
119
+ let paginaActual = pdfDoc.addPage(pageSize);
120
+ let yActual = altoPagina - margen;
121
+ let lineasEnPagina = 0;
122
+
123
+ for (let i = 0; i < lineasTodas.length; i++) {
124
+ if (lineasEnPagina >= lineasPorPagina) {
125
+ paginaActual = pdfDoc.addPage(pageSize);
126
+ yActual = altoPagina - margen;
127
+ lineasEnPagina = 0;
128
+ }
129
+
130
+ const esEncabezado = titulo && i === 0;
131
+ const fuenteActual = esEncabezado ? tamañoFuente + 4 : tamañoFuente;
132
+
133
+ if (lineasTodas[i] !== "") {
134
+ paginaActual.drawText(lineasTodas[i], {
135
+ x: margen,
136
+ y: yActual,
137
+ size: fuenteActual,
138
+ font,
139
+ color: rgb(0, 0, 0),
140
+ });
141
+ }
142
+
143
+ yActual -= lineHeight;
144
+ lineasEnPagina++;
145
+ }
146
+
147
+ const rutaAbsoluta = path.resolve(ruta);
148
+ const dirDestino = path.dirname(rutaAbsoluta);
149
+ if (!fs.existsSync(dirDestino)) {
150
+ fs.mkdirSync(dirDestino, { recursive: true });
151
+ }
152
+
153
+ const pdfBytes = await pdfDoc.save();
154
+ fs.writeFileSync(rutaAbsoluta, pdfBytes);
155
+
156
+ log.info(`PDF generado: ${rutaAbsoluta} (${pdfDoc.getPageCount()} páginas)`);
157
+
158
+ return {
159
+ ok: true,
160
+ ruta: rutaAbsoluta,
161
+ paginas: pdfDoc.getPageCount(),
162
+ bytesEscritos: pdfBytes.length,
163
+ };
164
+ } catch (error) {
165
+ log.error(`Error generando PDF: ${(error as Error).message}`);
166
+ return {
167
+ ok: false,
168
+ error: `No se pudo generar el PDF: ${(error as Error).message}`,
169
+ };
170
+ }
171
+ },
172
+ };
@@ -0,0 +1,174 @@
1
+ /**
2
+ * office_escribir_pptx - Generar un archivo PowerPoint (.pptx)
3
+ *
4
+ * @category office
5
+ * @seedId office_escribir_pptx
6
+ * @spanish crear powerpoint, generar pptx, escribir presentacion, exportar a pptx
7
+ */
8
+
9
+ import type { Tool } from "../types.ts";
10
+ import { logger } from "../../utils/logger.ts";
11
+ import * as path from "node:path";
12
+ import * as fs from "node:fs";
13
+
14
+ const log = logger.child("office-escribir-pptx");
15
+
16
+ interface DiapositivaInput {
17
+ titulo?: string;
18
+ contenido?: string;
19
+ puntos?: string[];
20
+ notas?: string;
21
+ }
22
+
23
+ export const officeEscribirPptxTool: Tool = {
24
+ name: "office_escribir_pptx",
25
+ description:
26
+ "Generar un archivo PowerPoint (.pptx) desde un array de diapositivas con título y contenido. Spanish: crear powerpoint, generar pptx, escribir presentacion, exportar a pptx",
27
+ parameters: {
28
+ type: "object",
29
+ properties: {
30
+ ruta: {
31
+ type: "string",
32
+ description: "Ruta donde guardar el archivo .pptx generado",
33
+ },
34
+ titulo_presentacion: {
35
+ type: "string",
36
+ description: "Título de la presentación (aparece en la primera diapositiva)",
37
+ },
38
+ diapositivas: {
39
+ type: "array",
40
+ description:
41
+ "Array de diapositivas. Cada una tiene: titulo (string), contenido (string, texto libre), puntos (string[], lista de viñetas), notas (string, notas del presentador)",
42
+ items: {
43
+ type: "object",
44
+ properties: {
45
+ titulo: { type: "string" },
46
+ contenido: { type: "string" },
47
+ puntos: { type: "array", items: { type: "string" } },
48
+ notas: { type: "string" },
49
+ },
50
+ },
51
+ },
52
+ },
53
+ required: ["ruta", "diapositivas"],
54
+ },
55
+ execute: async (params: Record<string, unknown>) => {
56
+ const ruta = params.ruta as string;
57
+ const tituloPresentacion = params.titulo_presentacion as string | undefined;
58
+ const diapositivasInput = params.diapositivas as DiapositivaInput[];
59
+
60
+ log.debug(`Generando PPTX: ${ruta}`);
61
+
62
+ try {
63
+ const pptxgen = (await import("pptxgenjs")).default;
64
+ const pres = new pptxgen();
65
+
66
+ // Configuración básica
67
+ pres.layout = "LAYOUT_16x9";
68
+
69
+ // Diapositiva de título (si se proporciona título de presentación)
70
+ if (tituloPresentacion) {
71
+ const slidePortada = pres.addSlide();
72
+ slidePortada.addText(tituloPresentacion, {
73
+ x: "10%",
74
+ y: "35%",
75
+ w: "80%",
76
+ h: "30%",
77
+ fontSize: 36,
78
+ bold: true,
79
+ align: "center",
80
+ color: "363636",
81
+ });
82
+ }
83
+
84
+ // Diapositivas de contenido
85
+ for (const diapoInput of diapositivasInput) {
86
+ const slide = pres.addSlide();
87
+
88
+ // Título de la diapositiva
89
+ if (diapoInput.titulo) {
90
+ slide.addText(diapoInput.titulo, {
91
+ x: "5%",
92
+ y: "5%",
93
+ w: "90%",
94
+ h: "15%",
95
+ fontSize: 24,
96
+ bold: true,
97
+ color: "363636",
98
+ valign: "middle",
99
+ });
100
+ }
101
+
102
+ const yContenido = diapoInput.titulo ? "22%" : "10%";
103
+ const hContenido = diapoInput.titulo ? "70%" : "82%";
104
+
105
+ // Puntos de viñeta (tienen prioridad sobre contenido libre)
106
+ if (diapoInput.puntos && diapoInput.puntos.length > 0) {
107
+ const textoPuntos = diapoInput.puntos.map((punto) => ({
108
+ text: punto,
109
+ options: {
110
+ bullet: true,
111
+ fontSize: 18,
112
+ color: "595959",
113
+ breakLine: true,
114
+ } as any,
115
+ }));
116
+
117
+ slide.addText(textoPuntos, {
118
+ x: "5%",
119
+ y: yContenido,
120
+ w: "90%",
121
+ h: hContenido,
122
+ valign: "top",
123
+ });
124
+ } else if (diapoInput.contenido) {
125
+ // Texto libre
126
+ slide.addText(diapoInput.contenido, {
127
+ x: "5%",
128
+ y: yContenido,
129
+ w: "90%",
130
+ h: hContenido,
131
+ fontSize: 16,
132
+ color: "595959",
133
+ valign: "top",
134
+ wrap: true,
135
+ });
136
+ }
137
+
138
+ // Notas del presentador
139
+ if (diapoInput.notas) {
140
+ slide.addNotes(diapoInput.notas);
141
+ }
142
+ }
143
+
144
+ const rutaAbsoluta = path.resolve(ruta);
145
+ const dirDestino = path.dirname(rutaAbsoluta);
146
+ if (!fs.existsSync(dirDestino)) {
147
+ fs.mkdirSync(dirDestino, { recursive: true });
148
+ }
149
+
150
+ // Escribir el archivo
151
+ await pres.writeFile({ fileName: rutaAbsoluta });
152
+
153
+ const stats = fs.statSync(rutaAbsoluta);
154
+
155
+ log.info(
156
+ `PPTX generado: ${rutaAbsoluta} (${diapositivasInput.length} diapositivas)`
157
+ );
158
+
159
+ return {
160
+ ok: true,
161
+ ruta: rutaAbsoluta,
162
+ totalDiapositivas:
163
+ diapositivasInput.length + (tituloPresentacion ? 1 : 0),
164
+ bytesEscritos: stats.size,
165
+ };
166
+ } catch (error) {
167
+ log.error(`Error generando PPTX: ${(error as Error).message}`);
168
+ return {
169
+ ok: false,
170
+ error: `No se pudo generar el archivo PowerPoint: ${(error as Error).message}`,
171
+ };
172
+ }
173
+ },
174
+ };
@@ -0,0 +1,116 @@
1
+ /**
2
+ * office_escribir_xlsx - Generar un archivo Excel (.xlsx)
3
+ *
4
+ * @category office
5
+ * @seedId office_escribir_xlsx
6
+ * @spanish crear excel, generar xlsx, escribir excel, exportar a xlsx
7
+ */
8
+
9
+ import type { Tool } from "../types.ts";
10
+ import { logger } from "../../utils/logger.ts";
11
+ import * as path from "node:path";
12
+ import * as fs from "node:fs";
13
+
14
+ const log = logger.child("office-escribir-xlsx");
15
+
16
+ interface HojaInput {
17
+ nombre: string;
18
+ datos: Record<string, any>[] | any[][];
19
+ encabezados?: string[];
20
+ }
21
+
22
+ export const officeEscribirXlsxTool: Tool = {
23
+ name: "office_escribir_xlsx",
24
+ description:
25
+ "Generar un archivo Excel (.xlsx) desde un objeto JSON con hojas, filas y columnas. Spanish: crear excel, generar xlsx, escribir excel, exportar a xlsx",
26
+ parameters: {
27
+ type: "object",
28
+ properties: {
29
+ ruta: {
30
+ type: "string",
31
+ description: "Ruta donde guardar el archivo .xlsx generado",
32
+ },
33
+ hojas: {
34
+ type: "array",
35
+ description:
36
+ "Array de hojas a crear. Cada hoja tiene: nombre (string), datos (array de objetos o array de arrays), encabezados (string[], opcional para forzar orden de columnas)",
37
+ items: {
38
+ type: "object",
39
+ properties: {
40
+ nombre: { type: "string" },
41
+ datos: { type: "array" },
42
+ encabezados: { type: "array", items: { type: "string" } },
43
+ },
44
+ },
45
+ },
46
+ },
47
+ required: ["ruta", "hojas"],
48
+ },
49
+ execute: async (params: Record<string, unknown>) => {
50
+ const ruta = params.ruta as string;
51
+ const hojasInput = params.hojas as HojaInput[];
52
+
53
+ log.debug(`Generando XLSX: ${ruta}`);
54
+
55
+ try {
56
+ const XLSX = await import("xlsx");
57
+
58
+ const workbook = XLSX.utils.book_new();
59
+
60
+ for (const hojaInput of hojasInput) {
61
+ let worksheet: any;
62
+
63
+ if (
64
+ hojasInput.length > 0 &&
65
+ Array.isArray(hojaInput.datos) &&
66
+ hojaInput.datos.length > 0 &&
67
+ Array.isArray(hojaInput.datos[0])
68
+ ) {
69
+ // Si los datos son array de arrays
70
+ worksheet = XLSX.utils.aoa_to_sheet(hojaInput.datos as any[][]);
71
+ } else {
72
+ // Si los datos son array de objetos
73
+ worksheet = XLSX.utils.json_to_sheet(
74
+ hojaInput.datos as Record<string, any>[],
75
+ hojaInput.encabezados ? { header: hojaInput.encabezados } : undefined
76
+ );
77
+ }
78
+
79
+ XLSX.utils.book_append_sheet(workbook, worksheet, hojaInput.nombre);
80
+ }
81
+
82
+ const rutaAbsoluta = path.resolve(ruta);
83
+ const dirDestino = path.dirname(rutaAbsoluta);
84
+ if (!fs.existsSync(dirDestino)) {
85
+ fs.mkdirSync(dirDestino, { recursive: true });
86
+ }
87
+
88
+ const buffer = XLSX.write(workbook, { type: "buffer", bookType: "xlsx" });
89
+ fs.writeFileSync(rutaAbsoluta, buffer);
90
+
91
+ const totalFilas = hojasInput.reduce(
92
+ (acc, h) => acc + h.datos.length,
93
+ 0
94
+ );
95
+
96
+ log.info(
97
+ `XLSX generado: ${rutaAbsoluta} (${hojasInput.length} hojas, ${totalFilas} filas)`
98
+ );
99
+
100
+ return {
101
+ ok: true,
102
+ ruta: rutaAbsoluta,
103
+ hojas: hojasInput.map((h) => h.nombre),
104
+ totalHojas: hojasInput.length,
105
+ totalFilas,
106
+ bytesEscritos: buffer.length,
107
+ };
108
+ } catch (error) {
109
+ log.error(`Error generando XLSX: ${(error as Error).message}`);
110
+ return {
111
+ ok: false,
112
+ error: `No se pudo generar el archivo Excel: ${(error as Error).message}`,
113
+ };
114
+ }
115
+ },
116
+ };