@predicatelabs/sdk 0.99.9

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 (302) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +252 -0
  3. package/dist/actions.d.ts +185 -0
  4. package/dist/actions.d.ts.map +1 -0
  5. package/dist/actions.js +1120 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/agent-runtime.d.ts +352 -0
  8. package/dist/agent-runtime.d.ts.map +1 -0
  9. package/dist/agent-runtime.js +1170 -0
  10. package/dist/agent-runtime.js.map +1 -0
  11. package/dist/agent.d.ts +164 -0
  12. package/dist/agent.d.ts.map +1 -0
  13. package/dist/agent.js +408 -0
  14. package/dist/agent.js.map +1 -0
  15. package/dist/asserts/expect.d.ts +159 -0
  16. package/dist/asserts/expect.d.ts.map +1 -0
  17. package/dist/asserts/expect.js +547 -0
  18. package/dist/asserts/expect.js.map +1 -0
  19. package/dist/asserts/index.d.ts +58 -0
  20. package/dist/asserts/index.d.ts.map +1 -0
  21. package/dist/asserts/index.js +70 -0
  22. package/dist/asserts/index.js.map +1 -0
  23. package/dist/asserts/query.d.ts +199 -0
  24. package/dist/asserts/query.d.ts.map +1 -0
  25. package/dist/asserts/query.js +288 -0
  26. package/dist/asserts/query.js.map +1 -0
  27. package/dist/backends/actions.d.ts +119 -0
  28. package/dist/backends/actions.d.ts.map +1 -0
  29. package/dist/backends/actions.js +291 -0
  30. package/dist/backends/actions.js.map +1 -0
  31. package/dist/backends/browser-use-adapter.d.ts +131 -0
  32. package/dist/backends/browser-use-adapter.d.ts.map +1 -0
  33. package/dist/backends/browser-use-adapter.js +219 -0
  34. package/dist/backends/browser-use-adapter.js.map +1 -0
  35. package/dist/backends/cdp-backend.d.ts +66 -0
  36. package/dist/backends/cdp-backend.d.ts.map +1 -0
  37. package/dist/backends/cdp-backend.js +273 -0
  38. package/dist/backends/cdp-backend.js.map +1 -0
  39. package/dist/backends/index.d.ts +80 -0
  40. package/dist/backends/index.d.ts.map +1 -0
  41. package/dist/backends/index.js +101 -0
  42. package/dist/backends/index.js.map +1 -0
  43. package/dist/backends/protocol.d.ts +156 -0
  44. package/dist/backends/protocol.d.ts.map +1 -0
  45. package/dist/backends/protocol.js +16 -0
  46. package/dist/backends/protocol.js.map +1 -0
  47. package/dist/backends/sentience-context.d.ts +143 -0
  48. package/dist/backends/sentience-context.d.ts.map +1 -0
  49. package/dist/backends/sentience-context.js +359 -0
  50. package/dist/backends/sentience-context.js.map +1 -0
  51. package/dist/backends/snapshot.d.ts +188 -0
  52. package/dist/backends/snapshot.d.ts.map +1 -0
  53. package/dist/backends/snapshot.js +360 -0
  54. package/dist/backends/snapshot.js.map +1 -0
  55. package/dist/browser.d.ts +154 -0
  56. package/dist/browser.d.ts.map +1 -0
  57. package/dist/browser.js +920 -0
  58. package/dist/browser.js.map +1 -0
  59. package/dist/canonicalization.d.ts +126 -0
  60. package/dist/canonicalization.d.ts.map +1 -0
  61. package/dist/canonicalization.js +161 -0
  62. package/dist/canonicalization.js.map +1 -0
  63. package/dist/captcha/strategies.d.ts +12 -0
  64. package/dist/captcha/strategies.d.ts.map +1 -0
  65. package/dist/captcha/strategies.js +43 -0
  66. package/dist/captcha/strategies.js.map +1 -0
  67. package/dist/captcha/types.d.ts +45 -0
  68. package/dist/captcha/types.d.ts.map +1 -0
  69. package/dist/captcha/types.js +12 -0
  70. package/dist/captcha/types.js.map +1 -0
  71. package/dist/cli.d.ts +5 -0
  72. package/dist/cli.d.ts.map +1 -0
  73. package/dist/cli.js +422 -0
  74. package/dist/cli.js.map +1 -0
  75. package/dist/conversational-agent.d.ts +123 -0
  76. package/dist/conversational-agent.d.ts.map +1 -0
  77. package/dist/conversational-agent.js +341 -0
  78. package/dist/conversational-agent.js.map +1 -0
  79. package/dist/cursor-policy.d.ts +41 -0
  80. package/dist/cursor-policy.d.ts.map +1 -0
  81. package/dist/cursor-policy.js +81 -0
  82. package/dist/cursor-policy.js.map +1 -0
  83. package/dist/debugger.d.ts +28 -0
  84. package/dist/debugger.d.ts.map +1 -0
  85. package/dist/debugger.js +107 -0
  86. package/dist/debugger.js.map +1 -0
  87. package/dist/expect.d.ts +16 -0
  88. package/dist/expect.d.ts.map +1 -0
  89. package/dist/expect.js +67 -0
  90. package/dist/expect.js.map +1 -0
  91. package/dist/failure-artifacts.d.ts +95 -0
  92. package/dist/failure-artifacts.d.ts.map +1 -0
  93. package/dist/failure-artifacts.js +805 -0
  94. package/dist/failure-artifacts.js.map +1 -0
  95. package/dist/generator.d.ts +16 -0
  96. package/dist/generator.d.ts.map +1 -0
  97. package/dist/generator.js +205 -0
  98. package/dist/generator.js.map +1 -0
  99. package/dist/index.d.ts +37 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +160 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/inspector.d.ts +13 -0
  104. package/dist/inspector.d.ts.map +1 -0
  105. package/dist/inspector.js +153 -0
  106. package/dist/inspector.js.map +1 -0
  107. package/dist/llm-provider.d.ts +144 -0
  108. package/dist/llm-provider.d.ts.map +1 -0
  109. package/dist/llm-provider.js +460 -0
  110. package/dist/llm-provider.js.map +1 -0
  111. package/dist/ordinal.d.ts +90 -0
  112. package/dist/ordinal.d.ts.map +1 -0
  113. package/dist/ordinal.js +249 -0
  114. package/dist/ordinal.js.map +1 -0
  115. package/dist/overlay.d.ts +63 -0
  116. package/dist/overlay.d.ts.map +1 -0
  117. package/dist/overlay.js +102 -0
  118. package/dist/overlay.js.map +1 -0
  119. package/dist/protocols/browser-protocol.d.ts +79 -0
  120. package/dist/protocols/browser-protocol.d.ts.map +1 -0
  121. package/dist/protocols/browser-protocol.js +9 -0
  122. package/dist/protocols/browser-protocol.js.map +1 -0
  123. package/dist/query.d.ts +66 -0
  124. package/dist/query.d.ts.map +1 -0
  125. package/dist/query.js +482 -0
  126. package/dist/query.js.map +1 -0
  127. package/dist/read.d.ts +47 -0
  128. package/dist/read.d.ts.map +1 -0
  129. package/dist/read.js +128 -0
  130. package/dist/read.js.map +1 -0
  131. package/dist/recorder.d.ts +44 -0
  132. package/dist/recorder.d.ts.map +1 -0
  133. package/dist/recorder.js +262 -0
  134. package/dist/recorder.js.map +1 -0
  135. package/dist/runtime-agent.d.ts +72 -0
  136. package/dist/runtime-agent.d.ts.map +1 -0
  137. package/dist/runtime-agent.js +357 -0
  138. package/dist/runtime-agent.js.map +1 -0
  139. package/dist/screenshot.d.ts +17 -0
  140. package/dist/screenshot.d.ts.map +1 -0
  141. package/dist/screenshot.js +40 -0
  142. package/dist/screenshot.js.map +1 -0
  143. package/dist/snapshot-diff.d.ts +23 -0
  144. package/dist/snapshot-diff.d.ts.map +1 -0
  145. package/dist/snapshot-diff.js +119 -0
  146. package/dist/snapshot-diff.js.map +1 -0
  147. package/dist/snapshot.d.ts +47 -0
  148. package/dist/snapshot.d.ts.map +1 -0
  149. package/dist/snapshot.js +358 -0
  150. package/dist/snapshot.js.map +1 -0
  151. package/dist/textSearch.d.ts +64 -0
  152. package/dist/textSearch.d.ts.map +1 -0
  153. package/dist/textSearch.js +113 -0
  154. package/dist/textSearch.js.map +1 -0
  155. package/dist/tools/context.d.ts +18 -0
  156. package/dist/tools/context.d.ts.map +1 -0
  157. package/dist/tools/context.js +40 -0
  158. package/dist/tools/context.js.map +1 -0
  159. package/dist/tools/defaults.d.ts +5 -0
  160. package/dist/tools/defaults.d.ts.map +1 -0
  161. package/dist/tools/defaults.js +368 -0
  162. package/dist/tools/defaults.js.map +1 -0
  163. package/dist/tools/filesystem.d.ts +12 -0
  164. package/dist/tools/filesystem.d.ts.map +1 -0
  165. package/dist/tools/filesystem.js +137 -0
  166. package/dist/tools/filesystem.js.map +1 -0
  167. package/dist/tools/index.d.ts +5 -0
  168. package/dist/tools/index.d.ts.map +1 -0
  169. package/dist/tools/index.js +15 -0
  170. package/dist/tools/index.js.map +1 -0
  171. package/dist/tools/registry.d.ts +38 -0
  172. package/dist/tools/registry.d.ts.map +1 -0
  173. package/dist/tools/registry.js +100 -0
  174. package/dist/tools/registry.js.map +1 -0
  175. package/dist/tracing/cloud-sink.d.ts +189 -0
  176. package/dist/tracing/cloud-sink.d.ts.map +1 -0
  177. package/dist/tracing/cloud-sink.js +1067 -0
  178. package/dist/tracing/cloud-sink.js.map +1 -0
  179. package/dist/tracing/index-schema.d.ts +231 -0
  180. package/dist/tracing/index-schema.d.ts.map +1 -0
  181. package/dist/tracing/index-schema.js +235 -0
  182. package/dist/tracing/index-schema.js.map +1 -0
  183. package/dist/tracing/index.d.ts +12 -0
  184. package/dist/tracing/index.d.ts.map +1 -0
  185. package/dist/tracing/index.js +28 -0
  186. package/dist/tracing/index.js.map +1 -0
  187. package/dist/tracing/indexer.d.ts +20 -0
  188. package/dist/tracing/indexer.d.ts.map +1 -0
  189. package/dist/tracing/indexer.js +347 -0
  190. package/dist/tracing/indexer.js.map +1 -0
  191. package/dist/tracing/jsonl-sink.d.ts +51 -0
  192. package/dist/tracing/jsonl-sink.d.ts.map +1 -0
  193. package/dist/tracing/jsonl-sink.js +329 -0
  194. package/dist/tracing/jsonl-sink.js.map +1 -0
  195. package/dist/tracing/sink.d.ts +25 -0
  196. package/dist/tracing/sink.d.ts.map +1 -0
  197. package/dist/tracing/sink.js +15 -0
  198. package/dist/tracing/sink.js.map +1 -0
  199. package/dist/tracing/tracer-factory.d.ts +102 -0
  200. package/dist/tracing/tracer-factory.d.ts.map +1 -0
  201. package/dist/tracing/tracer-factory.js +375 -0
  202. package/dist/tracing/tracer-factory.js.map +1 -0
  203. package/dist/tracing/tracer.d.ts +140 -0
  204. package/dist/tracing/tracer.d.ts.map +1 -0
  205. package/dist/tracing/tracer.js +336 -0
  206. package/dist/tracing/tracer.js.map +1 -0
  207. package/dist/tracing/types.d.ts +203 -0
  208. package/dist/tracing/types.d.ts.map +1 -0
  209. package/dist/tracing/types.js +8 -0
  210. package/dist/tracing/types.js.map +1 -0
  211. package/dist/types.d.ts +422 -0
  212. package/dist/types.d.ts.map +1 -0
  213. package/dist/types.js +6 -0
  214. package/dist/types.js.map +1 -0
  215. package/dist/utils/action-executor.d.ts +25 -0
  216. package/dist/utils/action-executor.d.ts.map +1 -0
  217. package/dist/utils/action-executor.js +121 -0
  218. package/dist/utils/action-executor.js.map +1 -0
  219. package/dist/utils/browser-evaluator.d.ts +76 -0
  220. package/dist/utils/browser-evaluator.d.ts.map +1 -0
  221. package/dist/utils/browser-evaluator.js +130 -0
  222. package/dist/utils/browser-evaluator.js.map +1 -0
  223. package/dist/utils/browser.d.ts +30 -0
  224. package/dist/utils/browser.d.ts.map +1 -0
  225. package/dist/utils/browser.js +75 -0
  226. package/dist/utils/browser.js.map +1 -0
  227. package/dist/utils/element-filter.d.ts +76 -0
  228. package/dist/utils/element-filter.d.ts.map +1 -0
  229. package/dist/utils/element-filter.js +195 -0
  230. package/dist/utils/element-filter.js.map +1 -0
  231. package/dist/utils/grid-utils.d.ts +37 -0
  232. package/dist/utils/grid-utils.d.ts.map +1 -0
  233. package/dist/utils/grid-utils.js +283 -0
  234. package/dist/utils/grid-utils.js.map +1 -0
  235. package/dist/utils/llm-interaction-handler.d.ts +41 -0
  236. package/dist/utils/llm-interaction-handler.d.ts.map +1 -0
  237. package/dist/utils/llm-interaction-handler.js +171 -0
  238. package/dist/utils/llm-interaction-handler.js.map +1 -0
  239. package/dist/utils/llm-response-builder.d.ts +56 -0
  240. package/dist/utils/llm-response-builder.d.ts.map +1 -0
  241. package/dist/utils/llm-response-builder.js +130 -0
  242. package/dist/utils/llm-response-builder.js.map +1 -0
  243. package/dist/utils/selector-utils.d.ts +12 -0
  244. package/dist/utils/selector-utils.d.ts.map +1 -0
  245. package/dist/utils/selector-utils.js +32 -0
  246. package/dist/utils/selector-utils.js.map +1 -0
  247. package/dist/utils/snapshot-event-builder.d.ts +28 -0
  248. package/dist/utils/snapshot-event-builder.d.ts.map +1 -0
  249. package/dist/utils/snapshot-event-builder.js +88 -0
  250. package/dist/utils/snapshot-event-builder.js.map +1 -0
  251. package/dist/utils/snapshot-processor.d.ts +27 -0
  252. package/dist/utils/snapshot-processor.d.ts.map +1 -0
  253. package/dist/utils/snapshot-processor.js +47 -0
  254. package/dist/utils/snapshot-processor.js.map +1 -0
  255. package/dist/utils/trace-event-builder.d.ts +122 -0
  256. package/dist/utils/trace-event-builder.d.ts.map +1 -0
  257. package/dist/utils/trace-event-builder.js +365 -0
  258. package/dist/utils/trace-event-builder.js.map +1 -0
  259. package/dist/utils/trace-file-manager.d.ts +70 -0
  260. package/dist/utils/trace-file-manager.d.ts.map +1 -0
  261. package/dist/utils/trace-file-manager.js +194 -0
  262. package/dist/utils/trace-file-manager.js.map +1 -0
  263. package/dist/utils/zod.d.ts +5 -0
  264. package/dist/utils/zod.d.ts.map +1 -0
  265. package/dist/utils/zod.js +80 -0
  266. package/dist/utils/zod.js.map +1 -0
  267. package/dist/utils.d.ts +8 -0
  268. package/dist/utils.d.ts.map +1 -0
  269. package/dist/utils.js +13 -0
  270. package/dist/utils.js.map +1 -0
  271. package/dist/verification.d.ts +194 -0
  272. package/dist/verification.d.ts.map +1 -0
  273. package/dist/verification.js +530 -0
  274. package/dist/verification.js.map +1 -0
  275. package/dist/vision-executor.d.ts +18 -0
  276. package/dist/vision-executor.d.ts.map +1 -0
  277. package/dist/vision-executor.js +60 -0
  278. package/dist/vision-executor.js.map +1 -0
  279. package/dist/visual-agent.d.ts +120 -0
  280. package/dist/visual-agent.d.ts.map +1 -0
  281. package/dist/visual-agent.js +796 -0
  282. package/dist/visual-agent.js.map +1 -0
  283. package/dist/wait.d.ts +35 -0
  284. package/dist/wait.d.ts.map +1 -0
  285. package/dist/wait.js +76 -0
  286. package/dist/wait.js.map +1 -0
  287. package/package.json +94 -0
  288. package/spec/README.md +72 -0
  289. package/spec/SNAPSHOT_V1.md +208 -0
  290. package/spec/sdk-types.md +259 -0
  291. package/spec/snapshot.schema.json +148 -0
  292. package/src/extension/background.js +104 -0
  293. package/src/extension/content.js +162 -0
  294. package/src/extension/injected_api.js +1399 -0
  295. package/src/extension/manifest.json +36 -0
  296. package/src/extension/pkg/README.md +1340 -0
  297. package/src/extension/pkg/package.json +15 -0
  298. package/src/extension/pkg/sentience_core.d.ts +51 -0
  299. package/src/extension/pkg/sentience_core.js +371 -0
  300. package/src/extension/pkg/sentience_core_bg.wasm +0 -0
  301. package/src/extension/pkg/sentience_core_bg.wasm.d.ts +10 -0
  302. package/src/extension/release.json +116 -0
@@ -0,0 +1,805 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.FailureArtifactBuffer = void 0;
40
+ const child_process_1 = require("child_process");
41
+ const fs_1 = __importDefault(require("fs"));
42
+ const http = __importStar(require("http"));
43
+ const https = __importStar(require("https"));
44
+ const os_1 = __importDefault(require("os"));
45
+ const path_1 = __importDefault(require("path"));
46
+ const url_1 = require("url");
47
+ const zlib = __importStar(require("zlib"));
48
+ const SENTIENCE_API_URL = 'https://api.sentienceapi.com';
49
+ async function writeJsonAtomic(filePath, data) {
50
+ const tmpPath = `${filePath}.tmp`;
51
+ await fs_1.default.promises.writeFile(tmpPath, JSON.stringify(data, null, 2));
52
+ await fs_1.default.promises.rename(tmpPath, filePath);
53
+ }
54
+ /**
55
+ * Check if ffmpeg is available on the system PATH.
56
+ */
57
+ function isFfmpegAvailable() {
58
+ try {
59
+ const result = (0, child_process_1.spawnSync)('ffmpeg', ['-version'], {
60
+ timeout: 5000,
61
+ stdio: 'pipe',
62
+ });
63
+ return result.status === 0;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ /**
70
+ * Get ffmpeg version as a tuple [major, minor] or null if unable to determine.
71
+ * Used to determine which flags to use (e.g., -vsync vs -fps_mode).
72
+ */
73
+ function getFfmpegVersion() {
74
+ try {
75
+ const result = (0, child_process_1.spawnSync)('ffmpeg', ['-version'], {
76
+ timeout: 5000,
77
+ stdio: 'pipe',
78
+ });
79
+ if (result.status !== 0) {
80
+ return null;
81
+ }
82
+ const output = result.stdout?.toString('utf-8') || '';
83
+ // Parse version from output like "ffmpeg version 7.0.1 ..." or "ffmpeg version n7.0.1 ..."
84
+ const match = output.match(/ffmpeg version [n]?(\d+)\.(\d+)/i);
85
+ if (match) {
86
+ return [parseInt(match[1], 10), parseInt(match[2], 10)];
87
+ }
88
+ return null;
89
+ }
90
+ catch {
91
+ return null;
92
+ }
93
+ }
94
+ /**
95
+ * Generate an MP4 video clip from a directory of frames using ffmpeg.
96
+ */
97
+ function generateClipFromFrames(framesDir, outputPath, fps = 8) {
98
+ // Find all frame files and sort them
99
+ const files = fs_1.default
100
+ .readdirSync(framesDir)
101
+ .filter(f => f.startsWith('frame_') && (f.endsWith('.png') || f.endsWith('.jpeg') || f.endsWith('.jpg')))
102
+ .sort();
103
+ if (files.length === 0) {
104
+ console.warn('No frame files found for clip generation');
105
+ return false;
106
+ }
107
+ // Create a temporary file list for ffmpeg concat demuxer
108
+ // Use relative path (just filename) since we run ffmpeg with cwd=framesDir
109
+ const listFile = 'frames_list.txt';
110
+ const listFilePath = path_1.default.join(framesDir, listFile);
111
+ const frameDuration = 1.0 / fps;
112
+ try {
113
+ // Write the frames list file
114
+ const listContent = files.map(f => `file '${f}'\nduration ${frameDuration}`).join('\n') +
115
+ `\nfile '${files[files.length - 1]}'`; // ffmpeg concat quirk
116
+ fs_1.default.writeFileSync(listFilePath, listContent);
117
+ // Determine which vsync/fps_mode flag to use based on ffmpeg version
118
+ // -vsync is deprecated in ffmpeg 7.0+, use -fps_mode instead (available since 5.1)
119
+ const version = getFfmpegVersion();
120
+ let syncArgs;
121
+ if (version && (version[0] > 5 || (version[0] === 5 && version[1] >= 1))) {
122
+ // ffmpeg 5.1+: use -fps_mode
123
+ syncArgs = ['-fps_mode', 'vfr'];
124
+ }
125
+ else {
126
+ // ffmpeg < 5.1: use legacy -vsync
127
+ syncArgs = ['-vsync', 'vfr'];
128
+ }
129
+ // Run ffmpeg to generate the clip
130
+ const result = (0, child_process_1.spawnSync)('ffmpeg', [
131
+ '-y',
132
+ '-f',
133
+ 'concat',
134
+ '-safe',
135
+ '0',
136
+ '-i',
137
+ listFile,
138
+ ...syncArgs,
139
+ '-pix_fmt',
140
+ 'yuv420p',
141
+ '-c:v',
142
+ 'libx264',
143
+ '-crf',
144
+ '23',
145
+ outputPath,
146
+ ], {
147
+ timeout: 60000, // 1 minute timeout
148
+ cwd: framesDir,
149
+ stdio: 'pipe',
150
+ });
151
+ if (result.status !== 0) {
152
+ const stderr = result.stderr?.toString('utf-8').slice(0, 500) ?? '';
153
+ console.warn(`ffmpeg failed with return code ${result.status}: ${stderr}`);
154
+ return false;
155
+ }
156
+ return fs_1.default.existsSync(outputPath);
157
+ }
158
+ catch (err) {
159
+ console.warn(`Error generating clip: ${err}`);
160
+ return false;
161
+ }
162
+ finally {
163
+ // Clean up the list file
164
+ try {
165
+ fs_1.default.unlinkSync(listFilePath);
166
+ }
167
+ catch {
168
+ // ignore
169
+ }
170
+ }
171
+ }
172
+ function redactSnapshotDefaults(payload) {
173
+ if (!payload || typeof payload !== 'object') {
174
+ return payload;
175
+ }
176
+ const elements = Array.isArray(payload.elements) ? payload.elements : null;
177
+ if (!elements) {
178
+ return payload;
179
+ }
180
+ const redactedElements = elements.map((el) => {
181
+ if (!el || typeof el !== 'object')
182
+ return el;
183
+ const inputType = String(el.input_type || '').toLowerCase();
184
+ if (['password', 'email', 'tel'].includes(inputType) && 'value' in el) {
185
+ return { ...el, value: null, value_redacted: true };
186
+ }
187
+ return el;
188
+ });
189
+ return { ...payload, elements: redactedElements };
190
+ }
191
+ class FailureArtifactBuffer {
192
+ constructor(runId, options = {}, timeNow = () => Date.now()) {
193
+ this.frames = [];
194
+ this.steps = [];
195
+ this.persisted = false;
196
+ this.runId = runId;
197
+ this.options = {
198
+ bufferSeconds: options.bufferSeconds ?? 15,
199
+ captureOnAction: options.captureOnAction ?? true,
200
+ fps: options.fps ?? 0,
201
+ persistMode: options.persistMode ?? 'onFail',
202
+ outputDir: options.outputDir ?? '.sentience/artifacts',
203
+ onBeforePersist: options.onBeforePersist ?? null,
204
+ redactSnapshotValues: options.redactSnapshotValues ?? true,
205
+ clip: {
206
+ mode: options.clip?.mode ?? 'auto',
207
+ fps: options.clip?.fps ?? 8,
208
+ seconds: options.clip?.seconds,
209
+ },
210
+ };
211
+ this.timeNow = timeNow;
212
+ this.tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'sentience-artifacts-'));
213
+ this.framesDir = path_1.default.join(this.tempDir, 'frames');
214
+ fs_1.default.mkdirSync(this.framesDir, { recursive: true });
215
+ }
216
+ getOptions() {
217
+ return this.options;
218
+ }
219
+ recordStep(action, stepId, stepIndex, url) {
220
+ this.steps.push({
221
+ ts: this.timeNow(),
222
+ action,
223
+ step_id: stepId,
224
+ step_index: stepIndex,
225
+ url,
226
+ });
227
+ }
228
+ async addFrame(image, fmt = 'jpeg') {
229
+ const ts = this.timeNow();
230
+ const fileName = `frame_${ts}.${fmt}`;
231
+ const filePath = path_1.default.join(this.framesDir, fileName);
232
+ await fs_1.default.promises.writeFile(filePath, image);
233
+ this.frames.push({ ts, fileName, filePath });
234
+ this.prune();
235
+ }
236
+ frameCount() {
237
+ return this.frames.length;
238
+ }
239
+ prune() {
240
+ const cutoff = this.timeNow() - this.options.bufferSeconds * 1000;
241
+ const keep = [];
242
+ for (const frame of this.frames) {
243
+ if (frame.ts >= cutoff) {
244
+ keep.push(frame);
245
+ }
246
+ else {
247
+ try {
248
+ fs_1.default.unlinkSync(frame.filePath);
249
+ }
250
+ catch {
251
+ // ignore
252
+ }
253
+ }
254
+ }
255
+ this.frames = keep;
256
+ }
257
+ async persist(reason, status, snapshot, diagnostics, metadata) {
258
+ if (this.persisted) {
259
+ return null;
260
+ }
261
+ const outDir = this.options.outputDir;
262
+ await fs_1.default.promises.mkdir(outDir, { recursive: true });
263
+ const ts = this.timeNow();
264
+ const runDir = path_1.default.join(outDir, `${this.runId}-${ts}`);
265
+ const framesOut = path_1.default.join(runDir, 'frames');
266
+ await fs_1.default.promises.mkdir(framesOut, { recursive: true });
267
+ for (const frame of this.frames) {
268
+ await fs_1.default.promises.copyFile(frame.filePath, path_1.default.join(framesOut, frame.fileName));
269
+ }
270
+ await writeJsonAtomic(path_1.default.join(runDir, 'steps.json'), this.steps);
271
+ let snapshotPayload = snapshot;
272
+ if (snapshotPayload && this.options.redactSnapshotValues) {
273
+ snapshotPayload = redactSnapshotDefaults(snapshotPayload);
274
+ }
275
+ let diagnosticsPayload = diagnostics;
276
+ let framePaths = this.frames.map(frame => frame.filePath);
277
+ let dropFrames = false;
278
+ if (this.options.onBeforePersist) {
279
+ try {
280
+ const result = this.options.onBeforePersist({
281
+ runId: this.runId,
282
+ reason,
283
+ status,
284
+ snapshot: snapshotPayload,
285
+ diagnostics: diagnosticsPayload,
286
+ framePaths,
287
+ metadata: metadata ?? {},
288
+ });
289
+ if (result.snapshot !== undefined) {
290
+ snapshotPayload = result.snapshot;
291
+ }
292
+ if (result.diagnostics !== undefined) {
293
+ diagnosticsPayload = result.diagnostics;
294
+ }
295
+ if (result.framePaths) {
296
+ framePaths = result.framePaths;
297
+ }
298
+ dropFrames = Boolean(result.dropFrames);
299
+ }
300
+ catch {
301
+ dropFrames = true;
302
+ }
303
+ }
304
+ if (!dropFrames) {
305
+ for (const framePath of framePaths) {
306
+ if (!fs_1.default.existsSync(framePath)) {
307
+ continue;
308
+ }
309
+ const fileName = path_1.default.basename(framePath);
310
+ await fs_1.default.promises.copyFile(framePath, path_1.default.join(framesOut, fileName));
311
+ }
312
+ }
313
+ let snapshotWritten = false;
314
+ if (snapshotPayload) {
315
+ await writeJsonAtomic(path_1.default.join(runDir, 'snapshot.json'), snapshotPayload);
316
+ snapshotWritten = true;
317
+ }
318
+ let diagnosticsWritten = false;
319
+ if (diagnosticsPayload) {
320
+ await writeJsonAtomic(path_1.default.join(runDir, 'diagnostics.json'), diagnosticsPayload);
321
+ diagnosticsWritten = true;
322
+ }
323
+ // Generate video clip from frames (optional, requires ffmpeg)
324
+ let clipGenerated = false;
325
+ const clipOptions = this.options.clip;
326
+ if (!dropFrames && framePaths.length > 0 && clipOptions.mode !== 'off') {
327
+ let shouldGenerate = false;
328
+ if (clipOptions.mode === 'auto') {
329
+ // Only generate if ffmpeg is available
330
+ shouldGenerate = isFfmpegAvailable();
331
+ if (!shouldGenerate) {
332
+ // Silent in auto mode - just skip
333
+ }
334
+ }
335
+ else if (clipOptions.mode === 'on') {
336
+ // Always attempt to generate
337
+ shouldGenerate = true;
338
+ if (!isFfmpegAvailable()) {
339
+ console.warn("ffmpeg not found on PATH but clip.mode='on'. " +
340
+ 'Install ffmpeg to generate video clips.');
341
+ shouldGenerate = false;
342
+ }
343
+ }
344
+ if (shouldGenerate) {
345
+ const clipPath = path_1.default.join(runDir, 'failure.mp4');
346
+ clipGenerated = generateClipFromFrames(framesOut, clipPath, clipOptions.fps ?? 8);
347
+ if (clipGenerated) {
348
+ console.log(`Generated failure clip: ${clipPath}`);
349
+ }
350
+ else {
351
+ console.warn('Failed to generate video clip');
352
+ }
353
+ }
354
+ }
355
+ const manifest = {
356
+ run_id: this.runId,
357
+ created_at_ms: ts,
358
+ status,
359
+ reason,
360
+ buffer_seconds: this.options.bufferSeconds,
361
+ frame_count: dropFrames ? 0 : framePaths.length,
362
+ frames: dropFrames ? [] : framePaths.map(p => ({ file: path_1.default.basename(p), ts: null })),
363
+ snapshot: snapshotWritten ? 'snapshot.json' : null,
364
+ diagnostics: diagnosticsWritten ? 'diagnostics.json' : null,
365
+ clip: clipGenerated ? 'failure.mp4' : null,
366
+ clip_fps: clipGenerated ? (clipOptions.fps ?? 8) : null,
367
+ metadata: metadata ?? {},
368
+ frames_redacted: !dropFrames && Boolean(this.options.onBeforePersist),
369
+ frames_dropped: dropFrames,
370
+ };
371
+ await writeJsonAtomic(path_1.default.join(runDir, 'manifest.json'), manifest);
372
+ this.persisted = true;
373
+ return runDir;
374
+ }
375
+ async cleanup() {
376
+ await fs_1.default.promises.rm(this.tempDir, { recursive: true, force: true });
377
+ }
378
+ /**
379
+ * Upload persisted artifacts to cloud storage.
380
+ *
381
+ * This method uploads all artifacts from a persisted directory to cloud storage
382
+ * using presigned URLs from the gateway. It follows the same pattern as trace
383
+ * screenshot uploads.
384
+ *
385
+ * @param apiKey - Sentience API key for authentication
386
+ * @param apiUrl - Sentience API base URL (default: https://api.sentienceapi.com)
387
+ * @param persistedDir - Path to persisted artifacts directory. If undefined, uses the
388
+ * most recent persist() output directory.
389
+ * @param logger - Optional logger for progress/error messages
390
+ * @returns artifact_index_key on success, null on failure
391
+ *
392
+ * @example
393
+ * const buf = new FailureArtifactBuffer('run-123', options);
394
+ * await buf.addFrame(screenshotBytes);
395
+ * const runDir = await buf.persist('assertion failed', 'failure');
396
+ * const artifactKey = await buf.uploadToCloud('sk-...');
397
+ * // artifactKey can be passed to /v1/traces/complete
398
+ */
399
+ async uploadToCloud(apiKey, apiUrl, persistedDir, logger) {
400
+ const baseUrl = apiUrl || SENTIENCE_API_URL;
401
+ // Determine which directory to upload
402
+ let targetDir = persistedDir;
403
+ if (!targetDir) {
404
+ // Find most recent persisted directory
405
+ const outputDir = this.options.outputDir;
406
+ if (!fs_1.default.existsSync(outputDir)) {
407
+ logger?.warn('No artifacts directory found');
408
+ return null;
409
+ }
410
+ // Look for directories matching runId pattern
411
+ const entries = fs_1.default.readdirSync(outputDir, { withFileTypes: true });
412
+ const matchingDirs = entries
413
+ .filter(e => e.isDirectory() && e.name.startsWith(this.runId))
414
+ .map(e => ({
415
+ name: e.name,
416
+ path: path_1.default.join(outputDir, e.name),
417
+ mtime: fs_1.default.statSync(path_1.default.join(outputDir, e.name)).mtimeMs,
418
+ }))
419
+ .sort((a, b) => b.mtime - a.mtime);
420
+ if (matchingDirs.length === 0) {
421
+ logger?.warn(`No persisted artifacts found for runId=${this.runId}`);
422
+ return null;
423
+ }
424
+ targetDir = matchingDirs[0].path;
425
+ }
426
+ if (!fs_1.default.existsSync(targetDir)) {
427
+ logger?.warn(`Artifacts directory not found: ${targetDir}`);
428
+ return null;
429
+ }
430
+ // Read manifest to understand what files need uploading
431
+ const manifestPath = path_1.default.join(targetDir, 'manifest.json');
432
+ if (!fs_1.default.existsSync(manifestPath)) {
433
+ logger?.warn('manifest.json not found in artifacts directory');
434
+ return null;
435
+ }
436
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf-8'));
437
+ // Build list of artifacts to upload
438
+ const artifacts = this.collectArtifactsForUpload(targetDir, manifest);
439
+ if (artifacts.length === 0) {
440
+ logger?.warn('No artifacts to upload');
441
+ return null;
442
+ }
443
+ logger?.info(`Uploading ${artifacts.length} artifact(s) to cloud`);
444
+ // Request presigned URLs from gateway
445
+ const uploadUrls = await this.requestArtifactUrls(apiKey, baseUrl, artifacts, logger);
446
+ if (!uploadUrls) {
447
+ return null;
448
+ }
449
+ // Upload artifacts in parallel
450
+ const artifactIndexKey = await this.uploadArtifacts(artifacts, uploadUrls, logger);
451
+ if (artifactIndexKey) {
452
+ // Report completion to gateway
453
+ await this.completeArtifacts(apiKey, baseUrl, artifactIndexKey, artifacts, logger);
454
+ }
455
+ return artifactIndexKey;
456
+ }
457
+ collectArtifactsForUpload(persistedDir, manifest) {
458
+ const artifacts = [];
459
+ // Core JSON artifacts
460
+ const jsonFiles = ['manifest.json', 'steps.json'];
461
+ if (manifest.snapshot) {
462
+ jsonFiles.push('snapshot.json');
463
+ }
464
+ if (manifest.diagnostics) {
465
+ jsonFiles.push('diagnostics.json');
466
+ }
467
+ for (const filename of jsonFiles) {
468
+ const filePath = path_1.default.join(persistedDir, filename);
469
+ if (fs_1.default.existsSync(filePath)) {
470
+ artifacts.push({
471
+ name: filename,
472
+ sizeBytes: fs_1.default.statSync(filePath).size,
473
+ contentType: 'application/json',
474
+ filePath,
475
+ });
476
+ }
477
+ }
478
+ // Video clip
479
+ if (manifest.clip) {
480
+ const clipPath = path_1.default.join(persistedDir, 'failure.mp4');
481
+ if (fs_1.default.existsSync(clipPath)) {
482
+ artifacts.push({
483
+ name: 'failure.mp4',
484
+ sizeBytes: fs_1.default.statSync(clipPath).size,
485
+ contentType: 'video/mp4',
486
+ filePath: clipPath,
487
+ });
488
+ }
489
+ }
490
+ // Frames
491
+ const framesDir = path_1.default.join(persistedDir, 'frames');
492
+ if (fs_1.default.existsSync(framesDir)) {
493
+ const frameFiles = fs_1.default.readdirSync(framesDir).sort();
494
+ for (const frameFile of frameFiles) {
495
+ const ext = path_1.default.extname(frameFile).toLowerCase();
496
+ if (['.jpeg', '.jpg', '.png'].includes(ext)) {
497
+ const framePath = path_1.default.join(framesDir, frameFile);
498
+ const contentType = ext === '.png' ? 'image/png' : 'image/jpeg';
499
+ artifacts.push({
500
+ name: `frames/${frameFile}`,
501
+ sizeBytes: fs_1.default.statSync(framePath).size,
502
+ contentType,
503
+ filePath: framePath,
504
+ });
505
+ }
506
+ }
507
+ }
508
+ return artifacts;
509
+ }
510
+ async requestArtifactUrls(apiKey, apiUrl, artifacts, logger) {
511
+ try {
512
+ // Prepare request payload (exclude local path)
513
+ const artifactsPayload = artifacts.map(a => ({
514
+ name: a.name,
515
+ size_bytes: a.sizeBytes,
516
+ content_type: a.contentType,
517
+ }));
518
+ const body = JSON.stringify({
519
+ run_id: this.runId,
520
+ artifacts: artifactsPayload,
521
+ });
522
+ return new Promise(resolve => {
523
+ const url = new url_1.URL(`${apiUrl}/v1/traces/artifacts/init`);
524
+ const protocol = url.protocol === 'https:' ? https : http;
525
+ const options = {
526
+ hostname: url.hostname,
527
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
528
+ path: url.pathname + url.search,
529
+ method: 'POST',
530
+ headers: {
531
+ 'Content-Type': 'application/json',
532
+ 'Content-Length': Buffer.byteLength(body),
533
+ Authorization: `Bearer ${apiKey}`,
534
+ },
535
+ timeout: 30000,
536
+ };
537
+ const req = protocol.request(options, res => {
538
+ let data = '';
539
+ res.on('data', chunk => {
540
+ data += chunk;
541
+ });
542
+ res.on('end', () => {
543
+ if (res.statusCode === 200) {
544
+ try {
545
+ resolve(JSON.parse(data));
546
+ }
547
+ catch {
548
+ logger?.warn('Failed to parse artifact upload URLs response');
549
+ resolve(null);
550
+ }
551
+ }
552
+ else {
553
+ logger?.warn(`Failed to get artifact upload URLs: HTTP ${res.statusCode}`);
554
+ resolve(null);
555
+ }
556
+ });
557
+ });
558
+ req.on('error', error => {
559
+ logger?.error(`Error requesting artifact upload URLs: ${error.message}`);
560
+ resolve(null);
561
+ });
562
+ req.on('timeout', () => {
563
+ req.destroy();
564
+ logger?.warn('Artifact URLs request timeout');
565
+ resolve(null);
566
+ });
567
+ req.write(body);
568
+ req.end();
569
+ });
570
+ }
571
+ catch (error) {
572
+ logger?.error(`Error requesting artifact upload URLs: ${error.message}`);
573
+ return null;
574
+ }
575
+ }
576
+ async uploadArtifacts(artifacts, uploadUrls, logger) {
577
+ const urlMap = new Map();
578
+ for (const item of uploadUrls.upload_urls) {
579
+ urlMap.set(item.name, item);
580
+ }
581
+ const indexUpload = uploadUrls.artifact_index_upload;
582
+ const storageKeys = new Map();
583
+ const uploadPromises = [];
584
+ for (const artifact of artifacts) {
585
+ const urlInfo = urlMap.get(artifact.name);
586
+ if (!urlInfo) {
587
+ continue;
588
+ }
589
+ const uploadPromise = this.uploadSingleArtifact(artifact, urlInfo, logger).then(success => ({
590
+ name: artifact.name,
591
+ success,
592
+ }));
593
+ uploadPromises.push(uploadPromise);
594
+ }
595
+ // Wait for all uploads
596
+ const results = await Promise.all(uploadPromises);
597
+ let uploadedCount = 0;
598
+ const failedNames = [];
599
+ for (const result of results) {
600
+ if (result.success) {
601
+ uploadedCount++;
602
+ const urlInfo = urlMap.get(result.name);
603
+ if (urlInfo?.storage_key) {
604
+ storageKeys.set(result.name, urlInfo.storage_key);
605
+ }
606
+ }
607
+ else {
608
+ failedNames.push(result.name);
609
+ }
610
+ }
611
+ if (uploadedCount === artifacts.length) {
612
+ logger?.info(`All ${uploadedCount} artifacts uploaded successfully`);
613
+ }
614
+ else {
615
+ logger?.warn(`Uploaded ${uploadedCount}/${artifacts.length} artifacts. Failed: ${failedNames.join(', ')}`);
616
+ }
617
+ // Upload artifact index file
618
+ if (indexUpload && uploadedCount > 0) {
619
+ return this.uploadArtifactIndex(artifacts, storageKeys, indexUpload, logger);
620
+ }
621
+ return null;
622
+ }
623
+ async uploadSingleArtifact(artifact, urlInfo, logger) {
624
+ try {
625
+ const data = fs_1.default.readFileSync(artifact.filePath);
626
+ return new Promise(resolve => {
627
+ const url = new url_1.URL(urlInfo.upload_url);
628
+ const protocol = url.protocol === 'https:' ? https : http;
629
+ const options = {
630
+ hostname: url.hostname,
631
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
632
+ path: url.pathname + url.search,
633
+ method: 'PUT',
634
+ headers: {
635
+ 'Content-Type': artifact.contentType,
636
+ 'Content-Length': data.length,
637
+ },
638
+ timeout: 60000,
639
+ };
640
+ const req = protocol.request(options, res => {
641
+ res.on('data', () => { });
642
+ res.on('end', () => {
643
+ if (res.statusCode === 200) {
644
+ resolve(true);
645
+ }
646
+ else {
647
+ logger?.warn(`Artifact ${artifact.name} upload failed: HTTP ${res.statusCode}`);
648
+ resolve(false);
649
+ }
650
+ });
651
+ });
652
+ req.on('error', error => {
653
+ logger?.warn(`Artifact ${artifact.name} upload error: ${error.message}`);
654
+ resolve(false);
655
+ });
656
+ req.on('timeout', () => {
657
+ req.destroy();
658
+ logger?.warn(`Artifact ${artifact.name} upload timeout`);
659
+ resolve(false);
660
+ });
661
+ req.write(data);
662
+ req.end();
663
+ });
664
+ }
665
+ catch (error) {
666
+ logger?.warn(`Artifact ${artifact.name} upload error: ${error.message}`);
667
+ return false;
668
+ }
669
+ }
670
+ async uploadArtifactIndex(artifacts, storageKeys, indexUpload, logger) {
671
+ try {
672
+ // Build index content
673
+ const indexData = {
674
+ run_id: this.runId,
675
+ created_at_ms: Date.now(),
676
+ artifacts: artifacts
677
+ .filter(a => storageKeys.has(a.name))
678
+ .map(a => ({
679
+ name: a.name,
680
+ storage_key: storageKeys.get(a.name) || '',
681
+ content_type: a.contentType,
682
+ })),
683
+ };
684
+ // Compress and upload
685
+ const indexJson = Buffer.from(JSON.stringify(indexData, null, 2), 'utf-8');
686
+ const compressed = zlib.gzipSync(indexJson);
687
+ return new Promise(resolve => {
688
+ const url = new url_1.URL(indexUpload.upload_url);
689
+ const protocol = url.protocol === 'https:' ? https : http;
690
+ const options = {
691
+ hostname: url.hostname,
692
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
693
+ path: url.pathname + url.search,
694
+ method: 'PUT',
695
+ headers: {
696
+ 'Content-Type': 'application/json',
697
+ 'Content-Encoding': 'gzip',
698
+ 'Content-Length': compressed.length,
699
+ },
700
+ timeout: 30000,
701
+ };
702
+ const req = protocol.request(options, res => {
703
+ res.on('data', () => { });
704
+ res.on('end', () => {
705
+ if (res.statusCode === 200) {
706
+ logger?.info('Artifact index uploaded successfully');
707
+ resolve(indexUpload.storage_key || '');
708
+ }
709
+ else {
710
+ logger?.warn(`Artifact index upload failed: HTTP ${res.statusCode}`);
711
+ resolve(null);
712
+ }
713
+ });
714
+ });
715
+ req.on('error', error => {
716
+ logger?.warn(`Error uploading artifact index: ${error.message}`);
717
+ resolve(null);
718
+ });
719
+ req.on('timeout', () => {
720
+ req.destroy();
721
+ logger?.warn('Artifact index upload timeout');
722
+ resolve(null);
723
+ });
724
+ req.write(compressed);
725
+ req.end();
726
+ });
727
+ }
728
+ catch (error) {
729
+ logger?.warn(`Error uploading artifact index: ${error.message}`);
730
+ return null;
731
+ }
732
+ }
733
+ async completeArtifacts(apiKey, apiUrl, artifactIndexKey, artifacts, logger) {
734
+ try {
735
+ // Calculate stats
736
+ const totalSize = artifacts.reduce((sum, a) => sum + a.sizeBytes, 0);
737
+ const framesArtifacts = artifacts.filter(a => a.name.startsWith('frames/'));
738
+ const framesTotal = framesArtifacts.reduce((sum, a) => sum + a.sizeBytes, 0);
739
+ // Get individual file sizes
740
+ const manifestSize = artifacts.find(a => a.name === 'manifest.json')?.sizeBytes || 0;
741
+ const snapshotSize = artifacts.find(a => a.name === 'snapshot.json')?.sizeBytes || 0;
742
+ const diagnosticsSize = artifacts.find(a => a.name === 'diagnostics.json')?.sizeBytes || 0;
743
+ const stepsSize = artifacts.find(a => a.name === 'steps.json')?.sizeBytes || 0;
744
+ const clipSize = artifacts.find(a => a.name === 'failure.mp4')?.sizeBytes || 0;
745
+ const body = JSON.stringify({
746
+ run_id: this.runId,
747
+ artifact_index_key: artifactIndexKey,
748
+ stats: {
749
+ manifest_size_bytes: manifestSize,
750
+ snapshot_size_bytes: snapshotSize,
751
+ diagnostics_size_bytes: diagnosticsSize,
752
+ steps_size_bytes: stepsSize,
753
+ clip_size_bytes: clipSize,
754
+ frames_total_size_bytes: framesTotal,
755
+ frames_count: framesArtifacts.length,
756
+ total_artifact_size_bytes: totalSize,
757
+ },
758
+ });
759
+ return new Promise(resolve => {
760
+ const url = new url_1.URL(`${apiUrl}/v1/traces/artifacts/complete`);
761
+ const protocol = url.protocol === 'https:' ? https : http;
762
+ const options = {
763
+ hostname: url.hostname,
764
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
765
+ path: url.pathname + url.search,
766
+ method: 'POST',
767
+ headers: {
768
+ 'Content-Type': 'application/json',
769
+ 'Content-Length': Buffer.byteLength(body),
770
+ Authorization: `Bearer ${apiKey}`,
771
+ },
772
+ timeout: 10000,
773
+ };
774
+ const req = protocol.request(options, res => {
775
+ res.on('data', () => { });
776
+ res.on('end', () => {
777
+ if (res.statusCode === 200) {
778
+ logger?.info('Artifact completion reported to gateway');
779
+ }
780
+ else {
781
+ logger?.warn(`Failed to report artifact completion: HTTP ${res.statusCode}`);
782
+ }
783
+ resolve();
784
+ });
785
+ });
786
+ req.on('error', error => {
787
+ logger?.warn(`Error reporting artifact completion: ${error.message}`);
788
+ resolve();
789
+ });
790
+ req.on('timeout', () => {
791
+ req.destroy();
792
+ logger?.warn('Artifact completion request timeout');
793
+ resolve();
794
+ });
795
+ req.write(body);
796
+ req.end();
797
+ });
798
+ }
799
+ catch (error) {
800
+ logger?.warn(`Error reporting artifact completion: ${error.message}`);
801
+ }
802
+ }
803
+ }
804
+ exports.FailureArtifactBuffer = FailureArtifactBuffer;
805
+ //# sourceMappingURL=failure-artifacts.js.map