whale-code 6.4.0 → 6.5.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 (187) hide show
  1. package/bin/swagmanager-mcp.js +7 -0
  2. package/dist/cli/app.js +30 -2
  3. package/dist/cli/chat/ChatApp.d.ts +4 -4
  4. package/dist/cli/chat/ChatApp.js +114 -44
  5. package/dist/cli/chat/ChatInput.d.ts +13 -6
  6. package/dist/cli/chat/ChatInput.js +433 -89
  7. package/dist/cli/chat/MemoryManager.d.ts +15 -0
  8. package/dist/cli/chat/MemoryManager.js +61 -0
  9. package/dist/cli/chat/MessageList.d.ts +8 -0
  10. package/dist/cli/chat/MessageList.js +1 -1
  11. package/dist/cli/chat/NodeManager.d.ts +30 -0
  12. package/dist/cli/chat/NodeManager.js +89 -0
  13. package/dist/cli/chat/NodeSelector.d.ts +19 -0
  14. package/dist/cli/chat/NodeSelector.js +37 -0
  15. package/dist/cli/chat/PlanApproval.d.ts +17 -0
  16. package/dist/cli/chat/PlanApproval.js +82 -0
  17. package/dist/cli/chat/SessionManager.d.ts +16 -0
  18. package/dist/cli/chat/SessionManager.js +43 -0
  19. package/dist/cli/chat/SlashMenu.d.ts +38 -0
  20. package/dist/cli/chat/SlashMenu.js +208 -0
  21. package/dist/cli/chat/StatusBar.d.ts +16 -0
  22. package/dist/cli/chat/StatusBar.js +22 -0
  23. package/dist/cli/chat/ThemeSelector.d.ts +14 -0
  24. package/dist/cli/chat/ThemeSelector.js +29 -0
  25. package/dist/cli/chat/ToolIndicator.d.ts +8 -0
  26. package/dist/cli/chat/ToolIndicator.js +33 -9
  27. package/dist/cli/chat/hooks/useAgentLoop.d.ts +2 -1
  28. package/dist/cli/chat/hooks/useAgentLoop.js +22 -17
  29. package/dist/cli/chat/hooks/useSlashCommands.d.ts +19 -0
  30. package/dist/cli/chat/hooks/useSlashCommands.js +254 -15
  31. package/dist/cli/commands/config-cmd.js +4 -25
  32. package/dist/cli/commands/db.d.ts +13 -0
  33. package/dist/cli/commands/db.js +243 -0
  34. package/dist/cli/commands/doctor.js +6 -9
  35. package/dist/cli/commands/mcp.js +1 -20
  36. package/dist/cli/services/agent-events.d.ts +22 -1
  37. package/dist/cli/services/agent-events.js +9 -0
  38. package/dist/cli/services/agent-loop.js +66 -2
  39. package/dist/cli/services/agent-worker-base.js +21 -6
  40. package/dist/cli/services/api-retry.d.ts +25 -0
  41. package/dist/cli/services/api-retry.js +91 -0
  42. package/dist/cli/services/auth-service.d.ts +1 -1
  43. package/dist/cli/services/auth-service.js +40 -19
  44. package/dist/cli/services/background-processes.js +26 -2
  45. package/dist/cli/services/config-store.d.ts +13 -1
  46. package/dist/cli/services/config-store.js +116 -13
  47. package/dist/cli/services/format-server-response.js +12 -6
  48. package/dist/cli/services/ink-resize-fix.d.ts +18 -0
  49. package/dist/cli/services/ink-resize-fix.js +66 -0
  50. package/dist/cli/services/interactive-tools.d.ts +14 -0
  51. package/dist/cli/services/interactive-tools.js +47 -2
  52. package/dist/cli/services/keybinding-manager.js +1 -1
  53. package/dist/cli/services/local-tools.js +35 -2
  54. package/dist/cli/services/server-tools.js +175 -3
  55. package/dist/cli/services/subagent.js +15 -3
  56. package/dist/cli/services/system-prompt.js +5 -3
  57. package/dist/cli/services/task-decomposer.d.ts +35 -0
  58. package/dist/cli/services/task-decomposer.js +199 -0
  59. package/dist/cli/services/team-lead.d.ts +18 -0
  60. package/dist/cli/services/team-lead.js +80 -0
  61. package/dist/cli/services/teammate.js +5 -5
  62. package/dist/cli/services/telemetry.d.ts +8 -2
  63. package/dist/cli/services/telemetry.js +116 -92
  64. package/dist/cli/services/tools/agent-tools.d.ts +1 -0
  65. package/dist/cli/services/tools/agent-tools.js +50 -4
  66. package/dist/cli/services/tools/file-ops.d.ts +2 -0
  67. package/dist/cli/services/tools/file-ops.js +71 -19
  68. package/dist/cli/services/tools/shell-exec.js +22 -12
  69. package/dist/cli/shared/Theme.d.ts +1 -2
  70. package/dist/cli/shared/Theme.js +1 -1
  71. package/dist/cli/shared/WhaleBanner.d.ts +4 -1
  72. package/dist/cli/shared/WhaleBanner.js +12 -8
  73. package/dist/cli/shared/markdown.d.ts +5 -4
  74. package/dist/cli/shared/markdown.js +376 -334
  75. package/dist/cli/shared/theme-manager.d.ts +27 -0
  76. package/dist/cli/shared/theme-manager.js +178 -0
  77. package/dist/cli/shared/theme-presets.d.ts +16 -0
  78. package/dist/cli/shared/theme-presets.js +265 -0
  79. package/dist/index.js +0 -51
  80. package/dist/node/adapters/imessage.d.ts +10 -0
  81. package/dist/node/adapters/imessage.js +45 -6
  82. package/dist/node/cli.js +459 -8
  83. package/dist/node/config.d.ts +17 -0
  84. package/dist/node/gateway-client.d.ts +55 -0
  85. package/dist/node/gateway-client.js +201 -0
  86. package/dist/node/portal/clipboard.d.ts +28 -0
  87. package/dist/node/portal/clipboard.js +183 -0
  88. package/dist/node/portal/discovery.d.ts +29 -0
  89. package/dist/node/portal/discovery.js +61 -0
  90. package/dist/node/portal/forward.d.ts +30 -0
  91. package/dist/node/portal/forward.js +90 -0
  92. package/dist/node/portal/index.d.ts +47 -0
  93. package/dist/node/portal/index.js +250 -0
  94. package/dist/node/portal/multiplexer.d.ts +48 -0
  95. package/dist/node/portal/multiplexer.js +207 -0
  96. package/dist/node/portal/permissions.d.ts +36 -0
  97. package/dist/node/portal/permissions.js +131 -0
  98. package/dist/node/portal/protocol.d.ts +140 -0
  99. package/dist/node/portal/protocol.js +193 -0
  100. package/dist/node/portal/screen.d.ts +18 -0
  101. package/dist/node/portal/screen.js +93 -0
  102. package/dist/node/portal/session.d.ts +68 -0
  103. package/dist/node/portal/session.js +127 -0
  104. package/dist/node/portal/shell.d.ts +26 -0
  105. package/dist/node/portal/shell.js +142 -0
  106. package/dist/node/portal/stream.d.ts +43 -0
  107. package/dist/node/portal/stream.js +90 -0
  108. package/dist/node/portal/transfer.d.ts +33 -0
  109. package/dist/node/portal/transfer.js +231 -0
  110. package/dist/node/portal/ui.d.ts +16 -0
  111. package/dist/node/portal/ui.js +148 -0
  112. package/dist/node/remote-desktop/compile-helper.d.ts +13 -0
  113. package/dist/node/remote-desktop/compile-helper.js +73 -0
  114. package/dist/node/remote-desktop/index.d.ts +67 -0
  115. package/dist/node/remote-desktop/index.js +220 -0
  116. package/dist/node/remote-desktop/protocol.d.ts +96 -0
  117. package/dist/node/remote-desktop/protocol.js +67 -0
  118. package/dist/node/runtime.d.ts +8 -1
  119. package/dist/node/runtime.js +117 -9
  120. package/dist/server/handlers/__test-utils__/test-db.d.ts +25 -0
  121. package/dist/server/handlers/__test-utils__/test-db.js +128 -0
  122. package/dist/server/handlers/api-keys.js +26 -2
  123. package/dist/server/handlers/browser.d.ts +0 -4
  124. package/dist/server/handlers/browser.js +0 -46
  125. package/dist/server/handlers/catalog.js +37 -14
  126. package/dist/server/handlers/clickhouse.d.ts +10 -0
  127. package/dist/server/handlers/clickhouse.js +215 -0
  128. package/dist/server/handlers/comms.d.ts +308 -4
  129. package/dist/server/handlers/comms.js +444 -11
  130. package/dist/server/handlers/creations.js +1 -1
  131. package/dist/server/handlers/crm.d.ts +54 -8
  132. package/dist/server/handlers/crm.js +353 -68
  133. package/dist/server/handlers/embeddings.js +3 -3
  134. package/dist/server/handlers/enrichment.js +39 -55
  135. package/dist/server/handlers/inventory.js +1 -1
  136. package/dist/server/handlers/kali.d.ts +9 -1
  137. package/dist/server/handlers/kali.js +50 -1
  138. package/dist/server/handlers/media.d.ts +8 -0
  139. package/dist/server/handlers/media.js +902 -0
  140. package/dist/server/handlers/meta-ads.js +6 -3
  141. package/dist/server/handlers/nodes.d.ts +2 -0
  142. package/dist/server/handlers/nodes.js +331 -40
  143. package/dist/server/handlers/operations.d.ts +4 -6
  144. package/dist/server/handlers/operations.js +99 -38
  145. package/dist/server/handlers/platform.js +224 -107
  146. package/dist/server/handlers/remove-bg.d.ts +6 -0
  147. package/dist/server/handlers/remove-bg.js +96 -0
  148. package/dist/server/handlers/storefront.d.ts +6 -0
  149. package/dist/server/handlers/storefront.js +477 -0
  150. package/dist/server/handlers/supply-chain.js +21 -3
  151. package/dist/server/handlers/workflow-steps.js +87 -31
  152. package/dist/server/handlers/workflows.js +4 -1
  153. package/dist/server/index.js +334 -88
  154. package/dist/server/lib/clickhouse-buffer.d.ts +48 -0
  155. package/dist/server/lib/clickhouse-buffer.js +175 -0
  156. package/dist/server/lib/clickhouse-client.d.ts +112 -0
  157. package/dist/server/lib/clickhouse-client.js +141 -0
  158. package/dist/server/lib/coa-renderer.d.ts +91 -0
  159. package/dist/server/lib/coa-renderer.js +411 -0
  160. package/dist/server/lib/compaction-service.js +45 -1
  161. package/dist/server/lib/pdf-renderer.d.ts +143 -0
  162. package/dist/server/lib/pdf-renderer.js +867 -0
  163. package/dist/server/lib/react-pdf-layout.d.ts +40 -0
  164. package/dist/server/lib/react-pdf-layout.js +437 -0
  165. package/dist/server/lib/server-agent-loop.d.ts +2 -0
  166. package/dist/server/lib/server-agent-loop.js +61 -15
  167. package/dist/server/lib/server-subagent.d.ts +3 -0
  168. package/dist/server/lib/server-subagent.js +7 -4
  169. package/dist/server/lib/supabase-client.js +51 -3
  170. package/dist/server/lib/template-resolver.js +14 -4
  171. package/dist/server/lib/utils.js +15 -0
  172. package/dist/server/local-agent-gateway.d.ts +44 -0
  173. package/dist/server/local-agent-gateway.js +389 -49
  174. package/dist/server/providers/anthropic.js +12 -2
  175. package/dist/server/providers/gemini.js +17 -2
  176. package/dist/server/proxy-handlers.js +151 -0
  177. package/dist/server/tool-router.d.ts +2 -2
  178. package/dist/server/tool-router.js +25 -35
  179. package/dist/shared/agent-core.d.ts +5 -2
  180. package/dist/shared/agent-core.js +30 -4
  181. package/dist/shared/api-client.js +54 -3
  182. package/dist/shared/sse-parser.d.ts +1 -1
  183. package/dist/shared/sse-parser.js +5 -2
  184. package/dist/shared/tool-dispatch.js +1 -1
  185. package/package.json +16 -10
  186. package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +0 -11
  187. package/dist/server/handlers/__test-utils__/mock-supabase.js +0 -393
@@ -0,0 +1,40 @@
1
+ import { type LayoutElement, type PageConfig, type StyleConfig } from "./pdf-renderer.js";
2
+ export declare function renderLayoutToPdf(layout: LayoutElement[] | Record<string, unknown> | undefined | null, pageConfig: PageConfig | undefined | null, styles: StyleConfig | undefined | null, data: Record<string, unknown>): Promise<Buffer>;
3
+ interface LabelPageConfig {
4
+ cols: number;
5
+ rows: number;
6
+ pageWidth: number;
7
+ pageHeight: number;
8
+ labelWidth: number;
9
+ labelHeight: number;
10
+ margins: {
11
+ top: number;
12
+ left: number;
13
+ };
14
+ gutters?: {
15
+ horizontal: number;
16
+ vertical: number;
17
+ };
18
+ }
19
+ interface LabelElement {
20
+ type: string;
21
+ field?: string;
22
+ x: number;
23
+ y: number;
24
+ width: number;
25
+ height: number;
26
+ style?: Record<string, unknown>;
27
+ text?: string;
28
+ format?: string;
29
+ color?: string;
30
+ backgroundColor?: string;
31
+ }
32
+ export declare function renderLabelToPdf(layout: {
33
+ version: number;
34
+ elements: LabelElement[];
35
+ }, pageConfig: LabelPageConfig | any, items: Record<string, unknown>[]): Promise<Buffer>;
36
+ export declare function renderHtmlToPdf(html: string, options?: {
37
+ format?: string;
38
+ landscape?: boolean;
39
+ }): Promise<Buffer>;
40
+ export {};
@@ -0,0 +1,437 @@
1
+ // server/lib/react-pdf-layout.ts — Generic React-PDF layout engine
2
+ // Replaces Playwright HTML→PDF with pure Node.js React-PDF rendering.
3
+ import React from "react";
4
+ import { Document, Page, Text, View, Image, renderToBuffer, } from "@react-pdf/renderer";
5
+ import { resolveBinding, resolveText, generateQRSvg, } from "./pdf-renderer.js";
6
+ const e = React.createElement;
7
+ // ============================================================================
8
+ // Unit Helpers
9
+ // ============================================================================
10
+ /** Convert margin string (e.g. "15mm", "1cm", "72pt", number) to points */
11
+ function toPoints(val, fallback = 42.5) {
12
+ if (val === undefined || val === null)
13
+ return fallback;
14
+ if (typeof val === "number")
15
+ return val;
16
+ const num = parseFloat(val);
17
+ if (isNaN(num))
18
+ return fallback;
19
+ if (val.endsWith("mm"))
20
+ return num * 2.835;
21
+ if (val.endsWith("cm"))
22
+ return num * 28.35;
23
+ if (val.endsWith("in"))
24
+ return num * 72;
25
+ if (val.endsWith("pt"))
26
+ return num;
27
+ if (val.endsWith("px"))
28
+ return num * 0.75;
29
+ return num; // default to points
30
+ }
31
+ /** Map page size string to React-PDF page size */
32
+ function mapPageSize(size) {
33
+ if (!size)
34
+ return "A4";
35
+ const s = size.toUpperCase();
36
+ if (s === "LETTER")
37
+ return "LETTER";
38
+ if (s === "LEGAL")
39
+ return "LEGAL";
40
+ if (s === "TABLOID")
41
+ return "TABLOID";
42
+ return s; // A4, A3, etc. — React-PDF supports these directly
43
+ }
44
+ // ============================================================================
45
+ // Element Renderer — ports renderElement() from pdf-renderer.ts
46
+ // ============================================================================
47
+ function renderElement(el, data, key) {
48
+ switch (el.type) {
49
+ case "text": {
50
+ const text = resolveText(el.content || el.text || "", data);
51
+ return e(View, { key, style: { marginBottom: 2 } }, e(Text, {
52
+ style: {
53
+ fontSize: el.fontSize || undefined,
54
+ color: el.color || undefined,
55
+ fontFamily: el.bold ? "Helvetica-Bold" : undefined,
56
+ textAlign: el.align || undefined,
57
+ },
58
+ }, text));
59
+ }
60
+ case "header": {
61
+ const text = resolveText(el.content || el.text || "", data);
62
+ return e(View, {
63
+ key,
64
+ style: {
65
+ backgroundColor: el.backgroundColor || undefined,
66
+ padding: typeof el.padding === "number" ? el.padding : el.padding ? toPoints(el.padding, 0) : undefined,
67
+ marginBottom: 4,
68
+ },
69
+ }, e(Text, {
70
+ style: {
71
+ fontFamily: "Helvetica-Bold",
72
+ fontSize: el.fontSize || undefined,
73
+ color: el.color || undefined,
74
+ textAlign: el.align || undefined,
75
+ },
76
+ }, text));
77
+ }
78
+ case "image": {
79
+ let src = el.src || el.url || "";
80
+ if (el.bind) {
81
+ const resolved = resolveBinding(el.bind, data);
82
+ if (typeof resolved === "string")
83
+ src = resolved;
84
+ }
85
+ if (!src)
86
+ return e(View, { key });
87
+ const style = {};
88
+ if (el.width)
89
+ style.width = typeof el.width === "number" ? el.width : toPoints(el.width, 100);
90
+ if (el.height)
91
+ style.height = typeof el.height === "number" ? el.height : toPoints(el.height, 100);
92
+ return e(Image, { key, src, style });
93
+ }
94
+ case "qrcode": {
95
+ let qrData = el.data || el.content || "";
96
+ if (el.bind) {
97
+ const resolved = resolveBinding(el.bind, data);
98
+ if (typeof resolved === "string")
99
+ qrData = resolved;
100
+ }
101
+ qrData = resolveText(qrData, data);
102
+ const qrSize = el.size || (typeof el.width === "number" ? el.width : 100);
103
+ const svg = generateQRSvg(qrData, qrSize);
104
+ const dataUri = `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`;
105
+ return e(Image, { key, src: dataUri, style: { width: qrSize, height: qrSize } });
106
+ }
107
+ case "spacer": {
108
+ const h = el.height || 10;
109
+ return e(View, { key, style: { height: typeof h === "number" ? h : toPoints(h, 10) } });
110
+ }
111
+ case "line": {
112
+ const color = el.color || "#000";
113
+ const thickness = el.thickness || 1;
114
+ return e(View, {
115
+ key,
116
+ style: {
117
+ borderTopWidth: thickness,
118
+ borderTopColor: color,
119
+ borderTopStyle: "solid",
120
+ marginVertical: 8,
121
+ },
122
+ });
123
+ }
124
+ case "columns": {
125
+ const cols = el.columns || el.children || [];
126
+ const children = cols.map((child, i) => {
127
+ if (Array.isArray(child)) {
128
+ return e(View, { key: `col-${i}`, style: { flex: 1 } }, ...child.map((c, j) => renderElement(c, data, `col-${i}-${j}`)));
129
+ }
130
+ return e(View, { key: `col-${i}`, style: { flex: 1 } }, renderElement(child, data, `col-${i}-inner`));
131
+ });
132
+ return e(View, { key, style: { flexDirection: "row", gap: 8 } }, ...children);
133
+ }
134
+ case "page_break": {
135
+ return e(View, { key, break: true });
136
+ }
137
+ case "grid": {
138
+ const items = (Array.isArray(el.items) ? el.items : Array.isArray(el.content) ? el.content : []);
139
+ const rows = items.map((item, i) => {
140
+ const label = item.label || item.value || "";
141
+ let value = "";
142
+ if (item.bind) {
143
+ const resolved = resolveBinding(item.bind, data);
144
+ value = resolved !== undefined && resolved !== null ? String(resolved) : "";
145
+ }
146
+ value = resolveText(value || "", data);
147
+ return e(View, { key: `row-${i}`, style: { flexDirection: "row", borderBottomWidth: 0.5, borderBottomColor: "#ddd" } }, e(View, { style: { width: "40%", padding: 4 } }, e(Text, { style: { fontFamily: "Helvetica-Bold", fontSize: 9 } }, label)), e(View, { style: { width: "60%", padding: 4 } }, e(Text, { style: { fontSize: 9 } }, value)));
148
+ });
149
+ return e(View, { key, style: { width: "100%" } }, ...rows);
150
+ }
151
+ case "table": {
152
+ const headers = el.headers || [];
153
+ const rowFields = el.row_fields;
154
+ let bodyRows = el.rows || [];
155
+ if (el.bind) {
156
+ const resolved = resolveBinding(el.bind, data);
157
+ if (Array.isArray(resolved))
158
+ bodyRows = resolved;
159
+ }
160
+ const colCount = headers.length || (rowFields?.length || 0);
161
+ const colWidth = colCount > 0 ? `${(100 / colCount).toFixed(1)}%` : "100%";
162
+ const cellStyle = { padding: 5, borderWidth: 0.5, borderColor: "#ddd" };
163
+ const headerCellStyle = { ...cellStyle, backgroundColor: "#f5f5f5" };
164
+ const headerRow = headers.length
165
+ ? e(View, { key: "thead", style: { flexDirection: "row" } }, ...headers.map((h, i) => e(View, { key: `th-${i}`, style: { ...headerCellStyle, width: colWidth } }, e(Text, { style: { fontSize: 8, fontFamily: "Helvetica-Bold" } }, h))))
166
+ : null;
167
+ const dataRows = bodyRows.map((row, ri) => {
168
+ let cells;
169
+ if (Array.isArray(row)) {
170
+ cells = row.map((c) => String(c ?? ""));
171
+ }
172
+ else if (rowFields && rowFields.length) {
173
+ cells = rowFields.map(field => String(row[field] ?? ""));
174
+ }
175
+ else {
176
+ cells = headers.map((h) => {
177
+ const key2 = h.toLowerCase().replace(/[^a-z0-9]/g, "");
178
+ return String(row[h] ?? row[key2] ?? row[h.replace(/\s+/g, "")] ?? "");
179
+ });
180
+ }
181
+ return e(View, { key: `tr-${ri}`, style: { flexDirection: "row" } }, ...cells.map((cell, ci) => e(View, { key: `td-${ri}-${ci}`, style: { ...cellStyle, width: colWidth } }, e(Text, { style: { fontSize: 8 } }, cell))));
182
+ });
183
+ const tableChildren = [];
184
+ if (headerRow)
185
+ tableChildren.push(headerRow);
186
+ tableChildren.push(...dataRows);
187
+ return e(View, { key, style: { width: "100%" } }, ...tableChildren);
188
+ }
189
+ case "box": {
190
+ const inner = (el.children || []).map((child, i) => renderElement(child, data, `box-${i}`));
191
+ const text = el.content || el.text ? resolveText(el.content || el.text || "", data) : "";
192
+ const boxChildren = [];
193
+ if (text)
194
+ boxChildren.push(e(Text, { key: "box-text", style: { textAlign: "center" } }, text));
195
+ boxChildren.push(...inner);
196
+ return e(View, {
197
+ key,
198
+ style: {
199
+ textAlign: "center",
200
+ border: el.border ? undefined : undefined,
201
+ borderWidth: el.border ? 1 : undefined,
202
+ borderColor: el.border ? (el.border.includes("#") ? el.border.split(" ").find(s => s.startsWith("#")) : "#000") : undefined,
203
+ backgroundColor: el.background || el.backgroundColor || undefined,
204
+ padding: typeof el.padding === "number" ? el.padding : el.padding ? toPoints(el.padding, 0) : undefined,
205
+ },
206
+ }, ...boxChildren);
207
+ }
208
+ default: {
209
+ if (el.children && Array.isArray(el.children)) {
210
+ return e(View, { key }, ...el.children.map((child, i) => renderElement(child, data, `child-${i}`)));
211
+ }
212
+ return e(View, { key });
213
+ }
214
+ }
215
+ }
216
+ // ============================================================================
217
+ // Layout Normalization — same logic as renderLayoutToHtml lines 896-924
218
+ // ============================================================================
219
+ function normalizeLayout(layout) {
220
+ if (!layout)
221
+ return [];
222
+ if (Array.isArray(layout))
223
+ return layout;
224
+ const obj = layout;
225
+ const elements = [];
226
+ if (Array.isArray(obj.pages)) {
227
+ for (const page of obj.pages) {
228
+ if (page.header)
229
+ elements.push(page.header);
230
+ if (Array.isArray(page.body))
231
+ elements.push(...page.body);
232
+ else if (page.body)
233
+ elements.push(page.body);
234
+ if (Array.isArray(page.sections))
235
+ elements.push(...page.sections);
236
+ if (Array.isArray(page.elements))
237
+ elements.push(...page.elements);
238
+ if (page.footer)
239
+ elements.push(page.footer);
240
+ }
241
+ }
242
+ else if (Array.isArray(obj.sections)) {
243
+ elements.push(...obj.sections);
244
+ }
245
+ else if (Array.isArray(obj.children)) {
246
+ elements.push(...obj.children);
247
+ }
248
+ else {
249
+ if (obj.header)
250
+ elements.push(obj.header);
251
+ if (obj.body) {
252
+ if (Array.isArray(obj.body))
253
+ elements.push(...obj.body);
254
+ else
255
+ elements.push(obj.body);
256
+ }
257
+ if (obj.footer)
258
+ elements.push(obj.footer);
259
+ }
260
+ return elements;
261
+ }
262
+ // ============================================================================
263
+ // renderLayoutToPdf — Generic element-based layout → PDF buffer
264
+ // ============================================================================
265
+ export async function renderLayoutToPdf(layout, pageConfig, styles, data) {
266
+ const elements = normalizeLayout(layout);
267
+ const pageSize = mapPageSize(pageConfig?.size);
268
+ const margins = pageConfig?.margins || {};
269
+ const orientation = pageConfig?.orientation === "landscape" ? "landscape" : "portrait";
270
+ const fontFamily = styles?.fontFamily?.split(",")[0]?.trim() || "Helvetica";
271
+ const fontSize = styles?.fontSize || 12;
272
+ const color = styles?.color || "#000";
273
+ const pageStyle = {
274
+ paddingTop: toPoints(margins.top, 42.5),
275
+ paddingRight: toPoints(margins.right, 42.5),
276
+ paddingBottom: toPoints(margins.bottom, 42.5),
277
+ paddingLeft: toPoints(margins.left, 42.5),
278
+ fontFamily,
279
+ fontSize,
280
+ color,
281
+ lineHeight: 1.4,
282
+ };
283
+ const rendered = elements.map((el, i) => renderElement(el, data, `el-${i}`));
284
+ const doc = e(Document, {}, e(Page, { size: pageSize, orientation, style: pageStyle }, ...rendered));
285
+ const buffer = await renderToBuffer(doc);
286
+ return Buffer.from(buffer);
287
+ }
288
+ export async function renderLabelToPdf(layout, pageConfig, items) {
289
+ const IN = 72; // 1 inch = 72 points
290
+ const cols = pageConfig.cols || 2;
291
+ const rows = pageConfig.rows || 5;
292
+ const pageW = (pageConfig.pageWidth || 8.5) * IN;
293
+ const pageH = (pageConfig.pageHeight || 11) * IN;
294
+ const labelW = (pageConfig.labelWidth || 4) * IN;
295
+ const labelH = (pageConfig.labelHeight || 2) * IN;
296
+ const mTop = (pageConfig.margins?.top || 0.5) * IN;
297
+ const mLeft = (pageConfig.margins?.left || 0.15625) * IN;
298
+ const gH = (pageConfig.gutters?.horizontal || 0.125) * IN;
299
+ const gV = (pageConfig.gutters?.vertical || 0) * IN;
300
+ const labelsPerPage = cols * rows;
301
+ const pageCount = Math.ceil(items.length / labelsPerPage);
302
+ const pages = [];
303
+ for (let p = 0; p < pageCount; p++) {
304
+ const labelViews = [];
305
+ for (let slot = 0; slot < labelsPerPage; slot++) {
306
+ const itemIdx = p * labelsPerPage + slot;
307
+ if (itemIdx >= items.length)
308
+ break;
309
+ const item = items[itemIdx];
310
+ const row = Math.floor(slot / cols);
311
+ const col = slot % cols;
312
+ const x = mLeft + col * (labelW + gH);
313
+ const y = mTop + row * (labelH + gV);
314
+ const labelElements = layout.elements.map((le, ei) => {
315
+ const elX = le.x * labelW;
316
+ const elY = le.y * labelH;
317
+ const elW = le.width * labelW;
318
+ const elH = le.height * labelH;
319
+ const value = le.field ? String(item[le.field] ?? "") : (le.text || "");
320
+ switch (le.type) {
321
+ case "text":
322
+ return e(View, {
323
+ key: `le-${ei}`,
324
+ style: { position: "absolute", left: elX, top: elY, width: elW, height: elH },
325
+ }, e(Text, {
326
+ style: {
327
+ fontSize: le.style?.fontSize || 8,
328
+ color: le.color || "#000",
329
+ fontFamily: (le.style?.bold || le.style?.fontWeight === "bold") ? "Helvetica-Bold" : "Helvetica",
330
+ },
331
+ }, value));
332
+ case "badge":
333
+ return e(View, {
334
+ key: `le-${ei}`,
335
+ style: {
336
+ position: "absolute", left: elX, top: elY, width: elW, height: elH,
337
+ backgroundColor: le.backgroundColor || "#4CAF50",
338
+ borderRadius: 4,
339
+ justifyContent: "center",
340
+ alignItems: "center",
341
+ },
342
+ }, e(Text, { style: { fontSize: 7, color: le.color || "#fff", fontFamily: "Helvetica-Bold" } }, value));
343
+ case "image": {
344
+ const src = le.field ? String(item[le.field] ?? "") : "";
345
+ if (!src)
346
+ return e(View, { key: `le-${ei}` });
347
+ return e(Image, {
348
+ key: `le-${ei}`,
349
+ src,
350
+ style: { position: "absolute", left: elX, top: elY, width: elW, height: elH },
351
+ });
352
+ }
353
+ case "qr_code": {
354
+ const svg = generateQRSvg(value, Math.min(elW, elH));
355
+ const dataUri = `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`;
356
+ return e(Image, {
357
+ key: `le-${ei}`,
358
+ src: dataUri,
359
+ style: { position: "absolute", left: elX, top: elY, width: Math.min(elW, elH), height: Math.min(elW, elH) },
360
+ });
361
+ }
362
+ case "date": {
363
+ const dateVal = le.field ? item[le.field] : null;
364
+ const formatted = dateVal ? new Date(String(dateVal)).toLocaleDateString() : value;
365
+ return e(View, {
366
+ key: `le-${ei}`,
367
+ style: { position: "absolute", left: elX, top: elY, width: elW, height: elH },
368
+ }, e(Text, { style: { fontSize: le.style?.fontSize || 8, color: le.color || "#000" } }, formatted));
369
+ }
370
+ default:
371
+ return e(View, { key: `le-${ei}` });
372
+ }
373
+ });
374
+ labelViews.push(e(View, {
375
+ key: `label-${slot}`,
376
+ style: {
377
+ position: "absolute",
378
+ left: x,
379
+ top: y,
380
+ width: labelW,
381
+ height: labelH,
382
+ },
383
+ }, ...labelElements));
384
+ }
385
+ pages.push(e(Page, {
386
+ key: `page-${p}`,
387
+ size: [pageW, pageH],
388
+ style: { position: "relative" },
389
+ }, ...labelViews));
390
+ }
391
+ const doc = e(Document, {}, ...pages);
392
+ const buffer = await renderToBuffer(doc);
393
+ return Buffer.from(buffer);
394
+ }
395
+ // ============================================================================
396
+ // renderHtmlToPdf — Direct HTML string → PDF (replaces Playwright)
397
+ // ============================================================================
398
+ export async function renderHtmlToPdf(html, options) {
399
+ // Sanitize: strip dangerous tags (same P0 security as the old Playwright path)
400
+ const sanitizedHtml = html
401
+ .replace(/<script[\s\S]*?<\/script>/gi, "")
402
+ .replace(/<script[^>]*\/?>/gi, "")
403
+ .replace(/<iframe[\s\S]*?<\/iframe>/gi, "")
404
+ .replace(/<iframe[^>]*\/?>/gi, "")
405
+ .replace(/<object[\s\S]*?<\/object>/gi, "")
406
+ .replace(/<object[^>]*\/?>/gi, "")
407
+ .replace(/<embed[^>]*\/?>/gi, "")
408
+ .replace(/<link[^>]*\/?>/gi, "");
409
+ // Use react-pdf-html to render HTML inside React-PDF
410
+ let Html;
411
+ try {
412
+ const mod = await import("react-pdf-html");
413
+ Html = mod.default || mod.Html || mod;
414
+ }
415
+ catch {
416
+ // Fallback: extract text content and render as plain text
417
+ const textContent = sanitizedHtml.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
418
+ const pageSize = mapPageSize(options?.format);
419
+ const orientation = options?.landscape ? "landscape" : "portrait";
420
+ const doc = e(Document, {}, e(Page, {
421
+ size: pageSize,
422
+ orientation,
423
+ style: { padding: 28, fontFamily: "Helvetica", fontSize: 12 },
424
+ }, e(Text, {}, textContent)));
425
+ const buffer = await renderToBuffer(doc);
426
+ return Buffer.from(buffer);
427
+ }
428
+ const pageSize = mapPageSize(options?.format);
429
+ const orientation = options?.landscape ? "landscape" : "portrait";
430
+ const doc = e(Document, {}, e(Page, {
431
+ size: pageSize,
432
+ orientation,
433
+ style: { padding: 28, fontFamily: "Helvetica", fontSize: 12, lineHeight: 1.4 },
434
+ }, e(Html, { style: { fontSize: 12 } }, sanitizedHtml)));
435
+ const buffer = await renderToBuffer(doc);
436
+ return Buffer.from(buffer);
437
+ }
@@ -102,6 +102,8 @@ export interface ServerAgentLoopResult {
102
102
  consecutiveFailedTurns: number;
103
103
  };
104
104
  turns: TurnMetrics[];
105
+ /** Final stop reason from the last API response (end_turn, tool_use, max_tokens) */
106
+ stopReason: string;
105
107
  }
106
108
  export type { SubagentProgressCallback, SubagentProgressEvent };
107
109
  export declare function runServerAgentLoop(opts: ServerAgentLoopOptions): Promise<ServerAgentLoopResult>;