@web-auto/webauto 0.1.4 → 0.1.7

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 (174) hide show
  1. package/apps/desktop-console/default-settings.json +2 -2
  2. package/apps/desktop-console/dist/main/index.mjs +983 -128
  3. package/apps/desktop-console/dist/main/preload.mjs +7 -0
  4. package/apps/desktop-console/dist/renderer/index.html +622 -50
  5. package/apps/desktop-console/dist/renderer/index.js +2423 -469
  6. package/apps/desktop-console/dist/renderer/run.mts +6 -5
  7. package/apps/desktop-console/entry/ui-cli.mjs +672 -0
  8. package/apps/desktop-console/entry/ui-console.mjs +416 -29
  9. package/apps/webauto/entry/account.mjs +89 -53
  10. package/apps/webauto/entry/browser-status.mjs +7 -10
  11. package/apps/webauto/entry/lib/account-detect.mjs +254 -28
  12. package/apps/webauto/entry/lib/account-store.mjs +219 -30
  13. package/apps/webauto/entry/lib/bus-publish.mjs +63 -0
  14. package/apps/webauto/entry/lib/camo-cli.mjs +93 -0
  15. package/apps/webauto/entry/lib/profilepool.mjs +14 -5
  16. package/apps/webauto/entry/lib/quota-status.mjs +23 -0
  17. package/apps/webauto/entry/lib/schedule-store.mjs +1068 -0
  18. package/apps/webauto/entry/profilepool.mjs +106 -17
  19. package/apps/webauto/entry/schedule.mjs +612 -0
  20. package/apps/webauto/entry/weibo-unified.mjs +134 -0
  21. package/apps/webauto/entry/xhs-install.mjs +256 -31
  22. package/apps/webauto/entry/xhs-status.mjs +5 -2
  23. package/apps/webauto/entry/xhs-unified.mjs +631 -98
  24. package/apps/webauto/resources/container-library/weibo/weibo_detail_page/comment_item/container.json +40 -0
  25. package/apps/webauto/resources/container-library/weibo/weibo_detail_page/reply_expand_button/container.json +38 -0
  26. package/apps/webauto/resources/container-library/weibo/weibo_detail_page/reply_list/container.json +37 -0
  27. package/apps/webauto/resources/container-library/weibo/weibo_search_page/container.json +8 -3
  28. package/apps/webauto/resources/container-library/weibo/weibo_search_page/login_anchor/container.json +30 -0
  29. package/apps/webauto/resources/container-library/weibo/weibo_search_page/search_bar/container.json +47 -0
  30. package/apps/webauto/resources/container-library/weibo/weibo_search_page/search_button/container.json +39 -0
  31. package/bin/camoufox-cli.mjs +61 -0
  32. package/bin/webauto.mjs +301 -54
  33. package/dist/modules/camo-backend/src/index.js +49 -1
  34. package/dist/modules/camo-backend/src/internal/BrowserSession.js +572 -3
  35. package/dist/modules/camo-backend/src/internal/SessionManager.js +13 -1
  36. package/dist/modules/camo-backend/src/internal/storage-paths.js +6 -0
  37. package/dist/modules/collection-manager/bloom-filter.js +91 -0
  38. package/dist/modules/collection-manager/date-utils.js +275 -0
  39. package/dist/modules/collection-manager/index.js +258 -0
  40. package/dist/modules/collection-manager/storage.js +195 -0
  41. package/dist/modules/collection-manager/types.js +47 -0
  42. package/dist/modules/logging/src/index.js +1 -1
  43. package/dist/modules/process-registry/index.js +230 -0
  44. package/dist/modules/rate-limiter/index.js +242 -0
  45. package/dist/modules/workflow/blocks/ExecuteWeiboSearchBlock.js +128 -0
  46. package/dist/modules/workflow/blocks/PersistXhsNoteBlock.js +7 -3
  47. package/dist/modules/workflow/blocks/RenderMarkdown.js +4 -1
  48. package/dist/modules/workflow/blocks/WeiboCollectCommentsBlock.js +282 -0
  49. package/dist/modules/workflow/blocks/WeiboCollectFromLinksBlock.js +283 -0
  50. package/dist/modules/workflow/blocks/WeiboCollectSearchLinksBlock.js +208 -0
  51. package/dist/modules/workflow/blocks/WeiboCollectTimelineListBlock.js +128 -0
  52. package/dist/modules/workflow/blocks/WeiboCollectUserPostsListBlock.js +127 -0
  53. package/dist/modules/workflow/blocks/helpers/downloadPaths.js +21 -0
  54. package/dist/modules/workflow/config/workflowRegistry.js +2 -0
  55. package/dist/modules/workflow/definitions/weibo-search-workflow-v1.js +47 -0
  56. package/dist/modules/workflow/src/runner.js +6 -0
  57. package/dist/modules/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.js +4 -0
  58. package/dist/modules/xiaohongshu/app/src/blocks/Phase3InteractBlock.js +2 -2
  59. package/dist/modules/xiaohongshu/app/src/blocks/helpers/sharding.js +123 -0
  60. package/dist/modules/xiaohongshu/app/src/container-registry/src/index.d.ts +37 -0
  61. package/dist/modules/xiaohongshu/app/src/container-registry/src/index.js +184 -0
  62. package/dist/modules/xiaohongshu/app/src/workflow/blocks/AnchorVerificationBlock.d.ts +31 -0
  63. package/dist/modules/xiaohongshu/app/src/workflow/blocks/AnchorVerificationBlock.js +71 -0
  64. package/dist/modules/xiaohongshu/app/src/workflow/blocks/DetectPageStateBlock.d.ts +48 -0
  65. package/dist/modules/xiaohongshu/app/src/workflow/blocks/DetectPageStateBlock.js +259 -0
  66. package/dist/modules/xiaohongshu/app/src/workflow/blocks/ErrorRecoveryBlock.d.ts +28 -0
  67. package/dist/modules/xiaohongshu/app/src/workflow/blocks/ErrorRecoveryBlock.js +319 -0
  68. package/dist/modules/xiaohongshu/app/src/workflow/blocks/WaitSearchPermitBlock.d.ts +36 -0
  69. package/dist/modules/xiaohongshu/app/src/workflow/blocks/WaitSearchPermitBlock.js +162 -0
  70. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/containerAnchors.d.ts +36 -0
  71. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/containerAnchors.js +301 -0
  72. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/operationLogger.d.ts +29 -0
  73. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/operationLogger.js +195 -0
  74. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/searchPageState.d.ts +25 -0
  75. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/searchPageState.js +164 -0
  76. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/MatchCommentsBlock.d.ts +66 -0
  77. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/MatchCommentsBlock.js +139 -0
  78. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1EnsureServicesBlock.d.ts +16 -0
  79. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1EnsureServicesBlock.js +36 -0
  80. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1MonitorCookieBlock.d.ts +27 -0
  81. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1MonitorCookieBlock.js +213 -0
  82. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.d.ts +18 -0
  83. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.js +121 -0
  84. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2CollectLinksBlock.d.ts +34 -0
  85. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2CollectLinksBlock.js +1249 -0
  86. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2SearchBlock.d.ts +17 -0
  87. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2SearchBlock.js +703 -0
  88. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseDetailBlock.d.ts +15 -0
  89. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseDetailBlock.js +41 -0
  90. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseTabsBlock.d.ts +26 -0
  91. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseTabsBlock.js +44 -0
  92. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CollectCommentsBlock.d.ts +29 -0
  93. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CollectCommentsBlock.js +150 -0
  94. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ExtractDetailBlock.d.ts +38 -0
  95. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ExtractDetailBlock.js +117 -0
  96. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenDetailBlock.d.ts +30 -0
  97. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenDetailBlock.js +102 -0
  98. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenTabsBlock.d.ts +23 -0
  99. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenTabsBlock.js +109 -0
  100. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.d.ts +32 -0
  101. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.js +117 -0
  102. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ProcessSingleNoteBlock.d.ts +35 -0
  103. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ProcessSingleNoteBlock.js +114 -0
  104. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ValidateLinksBlock.d.ts +34 -0
  105. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ValidateLinksBlock.js +90 -0
  106. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase3InteractBlock.d.ts +111 -0
  107. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase3InteractBlock.js +1009 -0
  108. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase4MultiTabHarvestBlock.d.ts +20 -0
  109. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase4MultiTabHarvestBlock.js +233 -0
  110. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/ReplyInteractBlock.d.ts +48 -0
  111. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/ReplyInteractBlock.js +291 -0
  112. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/XhsDiscoverFallbackBlock.d.ts +23 -0
  113. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/XhsDiscoverFallbackBlock.js +240 -0
  114. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatchDsl.d.ts +55 -0
  115. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatchDsl.js +126 -0
  116. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatcher.d.ts +21 -0
  117. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatcher.js +99 -0
  118. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/evidence.d.ts +5 -0
  119. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/evidence.js +27 -0
  120. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/sharding.d.ts +37 -0
  121. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/sharding.js +165 -0
  122. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/xhsComments.d.ts +33 -0
  123. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/xhsComments.js +270 -0
  124. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/index.d.ts +9 -0
  125. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/index.js +9 -0
  126. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/checkpoints.d.ts +50 -0
  127. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/checkpoints.js +222 -0
  128. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/controllerAction.d.ts +10 -0
  129. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/controllerAction.js +43 -0
  130. package/dist/services/shared/serviceProcessLogger.js +1 -1
  131. package/dist/services/unified-api/server.js +105 -11
  132. package/modules/camo-backend/src/index.ts +46 -1
  133. package/modules/camo-backend/src/internal/BrowserSession.ts +619 -3
  134. package/modules/camo-backend/src/internal/SessionManager.ts +12 -1
  135. package/modules/camo-backend/src/internal/storage-paths.ts +5 -0
  136. package/modules/camo-runtime/src/autoscript/action-providers/xhs/comments.mjs +38 -2
  137. package/modules/camo-runtime/src/autoscript/action-providers/xhs/interaction.mjs +47 -2
  138. package/modules/camo-runtime/src/autoscript/action-providers/xhs/search.mjs +94 -11
  139. package/modules/camo-runtime/src/autoscript/action-providers/xhs.mjs +208 -2
  140. package/modules/camo-runtime/src/autoscript/runtime.mjs +7 -1
  141. package/modules/camo-runtime/src/autoscript/xhs-unified-template.mjs +76 -43
  142. package/modules/camo-runtime/src/container/runtime-core/operations/index.mjs +75 -1
  143. package/modules/camo-runtime/src/container/runtime-core/operations/selector-scripts.mjs +71 -4
  144. package/modules/camo-runtime/src/container/runtime-core/operations/tab-pool.mjs +183 -27
  145. package/modules/collection-manager/bloom-filter.ts +112 -0
  146. package/modules/collection-manager/date-utils.ts +316 -0
  147. package/modules/collection-manager/index.ts +309 -0
  148. package/modules/collection-manager/package.json +10 -0
  149. package/modules/collection-manager/storage.ts +174 -0
  150. package/modules/collection-manager/types.ts +156 -0
  151. package/modules/logging/src/index.ts +1 -1
  152. package/modules/process-registry/index.ts +284 -0
  153. package/modules/rate-limiter/index.ts +322 -0
  154. package/modules/state/src/paths.ts +9 -1
  155. package/modules/task-scheduler/index.ts +293 -0
  156. package/modules/workflow/blocks/ExecuteWeiboSearchBlock.ts +167 -0
  157. package/modules/workflow/blocks/PersistXhsNoteBlock.ts +7 -3
  158. package/modules/workflow/blocks/RenderMarkdown.ts +4 -1
  159. package/modules/workflow/blocks/WeiboCollectCommentsBlock.ts +339 -0
  160. package/modules/workflow/blocks/WeiboCollectFromLinksBlock.ts +338 -0
  161. package/modules/workflow/blocks/helpers/downloadPaths.ts +16 -0
  162. package/modules/workflow/config/workflowRegistry.ts +2 -0
  163. package/modules/workflow/definitions/weibo-search-workflow-v1.ts +47 -0
  164. package/modules/workflow/src/runner.ts +6 -0
  165. package/modules/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.ts +1 -1
  166. package/modules/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.ts +4 -0
  167. package/modules/xiaohongshu/app/src/blocks/Phase3InteractBlock.ts +2 -3
  168. package/modules/xiaohongshu/app/src/blocks/helpers/sharding.ts +152 -0
  169. package/package.json +13 -4
  170. package/scripts/postinstall-resources.mjs +62 -0
  171. package/scripts/test/run-coverage.mjs +76 -0
  172. package/scripts/weibo/search.ts +49 -0
  173. package/services/shared/serviceProcessLogger.ts +1 -1
  174. package/services/unified-api/server.ts +98 -12
@@ -275,12 +275,96 @@ class UnifiedApiServer {
275
275
  server.on('request', (req, res) => {
276
276
  void (async () => {
277
277
  const url = new URL(req.url, `http://${req.headers.host}`);
278
+ const normalizeTaskPhase = (value: any) => {
279
+ const text = String(value || '').trim().toLowerCase();
280
+ if (text === 'phase1' || text === 'phase2' || text === 'phase3' || text === 'phase4' || text === 'unified' || text === 'orchestrate') {
281
+ return text;
282
+ }
283
+ return 'unknown';
284
+ };
285
+ const normalizeTaskStatus = (value: any) => {
286
+ const text = String(value || '').trim().toLowerCase();
287
+ if (text === 'starting' || text === 'running' || text === 'paused' || text === 'completed' || text === 'failed' || text === 'aborted') {
288
+ return text;
289
+ }
290
+ return '';
291
+ };
292
+ const ensureTask = (runId: string, seed: any = {}) => {
293
+ const normalizedRunId = String(runId || '').trim();
294
+ if (!normalizedRunId) return null;
295
+ const existing = this.taskRegistry.getTask(normalizedRunId);
296
+ if (existing) return existing;
297
+ const profileId = String(seed?.profileId || 'unknown').trim() || 'unknown';
298
+ const keyword = String(seed?.keyword || '').trim();
299
+ const phase = normalizeTaskPhase(seed?.phase);
300
+ return this.taskRegistry.createTask({
301
+ runId: normalizedRunId,
302
+ profileId,
303
+ keyword,
304
+ phase,
305
+ });
306
+ };
307
+ const applyTaskPatch = (runId: string, payload: any = {}) => {
308
+ const normalizedRunId = String(runId || '').trim();
309
+ if (!normalizedRunId) return;
310
+ ensureTask(normalizedRunId, payload);
311
+ const phase = normalizeTaskPhase(payload?.phase);
312
+ const profileId = String(payload?.profileId || '').trim();
313
+ const keyword = String(payload?.keyword || '').trim();
314
+ const details = payload?.details && typeof payload.details === 'object' ? payload.details : undefined;
315
+ const patch: any = {};
316
+ if (phase !== 'unknown') patch.phase = phase;
317
+ if (profileId) patch.profileId = profileId;
318
+ if (keyword) patch.keyword = keyword;
319
+ if (details) patch.details = details;
320
+ if (Object.keys(patch).length > 0) {
321
+ this.taskRegistry.updateTask(normalizedRunId, patch);
322
+ }
323
+ if (payload?.progress && typeof payload.progress === 'object') {
324
+ this.taskRegistry.updateProgress(normalizedRunId, payload.progress);
325
+ }
326
+ if (payload?.stats && typeof payload.stats === 'object') {
327
+ this.taskRegistry.updateStats(normalizedRunId, payload.stats);
328
+ }
329
+ const status = normalizeTaskStatus(payload?.status);
330
+ if (status) {
331
+ this.taskRegistry.setStatus(normalizedRunId, status as any);
332
+ }
333
+ const errorPayload = payload?.error && typeof payload.error === 'object'
334
+ ? payload.error
335
+ : (payload?.lastError && typeof payload.lastError === 'object' ? payload.lastError : null);
336
+ if (errorPayload) {
337
+ this.taskRegistry.setError(normalizedRunId, {
338
+ message: String(errorPayload.message || 'task_error'),
339
+ code: String(errorPayload.code || 'TASK_ERROR'),
340
+ timestamp: Number(errorPayload.timestamp || Date.now()),
341
+ recoverable: Boolean(errorPayload.recoverable),
342
+ });
343
+ }
344
+ };
278
345
 
279
346
  // Container operations endpoints
280
347
  const containerHandled = await handleContainerOperations(req, res, sessionManager, this.containerExecutor);
281
348
  if (containerHandled) return;
282
349
 
283
350
  // Task state API endpoints
351
+ if (req.method === 'POST' && url.pathname === '/api/v1/tasks') {
352
+ try {
353
+ const payload = await this.readJsonBody(req);
354
+ const runId = String(payload?.runId || payload?.id || '').trim();
355
+ if (!runId) throw new Error('runId is required');
356
+ ensureTask(runId, payload);
357
+ applyTaskPatch(runId, payload);
358
+ const task = this.taskRegistry.getTask(runId);
359
+ res.writeHead(200, { 'Content-Type': 'application/json' });
360
+ res.end(JSON.stringify({ success: true, data: task }));
361
+ } catch (err: any) {
362
+ res.writeHead(400, { 'Content-Type': 'application/json' });
363
+ res.end(JSON.stringify({ success: false, error: err?.message || String(err) }));
364
+ }
365
+ return;
366
+ }
367
+
284
368
  if (req.method === 'GET' && url.pathname === '/api/v1/tasks') {
285
369
  const tasks = this.taskRegistry.getAllTasks();
286
370
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -288,6 +372,17 @@ class UnifiedApiServer {
288
372
  return;
289
373
  }
290
374
 
375
+ if (req.method === 'GET' && url.pathname.includes('/api/v1/tasks/') && url.pathname.includes('/events')) {
376
+ const parts = url.pathname.split('/');
377
+ const tasksIndex = parts.indexOf('tasks');
378
+ const runId = parts[tasksIndex + 1];
379
+ const since = url.searchParams.get('since');
380
+ const events = this.taskRegistry.getEvents(runId, since ? Number(since) : undefined);
381
+ res.writeHead(200, { 'Content-Type': 'application/json' });
382
+ res.end(JSON.stringify({ success: true, data: events }));
383
+ return;
384
+ }
385
+
291
386
  if (req.method === 'GET' && url.pathname.startsWith('/api/v1/tasks/')) {
292
387
  const parts = url.pathname.split('/');
293
388
  const runId = parts[parts.length - 1];
@@ -302,24 +397,13 @@ class UnifiedApiServer {
302
397
  return;
303
398
  }
304
399
 
305
- if (req.method === 'GET' && url.pathname.includes('/api/v1/tasks/') && url.pathname.includes('/events')) {
306
- const parts = url.pathname.split('/');
307
- const tasksIndex = parts.indexOf('tasks');
308
- const runId = parts[tasksIndex + 1];
309
- const since = url.searchParams.get('since');
310
- const events = this.taskRegistry.getEvents(runId, since ? Number(since) : undefined);
311
- res.writeHead(200, { 'Content-Type': 'application/json' });
312
- res.end(JSON.stringify({ success: true, data: events }));
313
- return;
314
- }
315
-
316
400
  if (req.method === 'POST' && url.pathname.includes('/api/v1/tasks/') && url.pathname.includes('/update')) {
317
401
  const parts = url.pathname.split('/');
318
402
  const tasksIndex = parts.indexOf('tasks');
319
403
  const runId = parts[tasksIndex + 1];
320
404
  try {
321
405
  const payload = await this.readJsonBody(req);
322
- this.taskRegistry.updateTask(runId, payload);
406
+ applyTaskPatch(runId, payload);
323
407
  res.writeHead(200, { 'Content-Type': 'application/json' });
324
408
  res.end(JSON.stringify({ success: true }));
325
409
  } catch (err: any) {
@@ -335,6 +419,7 @@ class UnifiedApiServer {
335
419
  const runId = parts[tasksIndex + 1];
336
420
  try {
337
421
  const event = await this.readJsonBody(req);
422
+ ensureTask(runId, event?.data || {});
338
423
  this.taskRegistry.pushEvent(runId, event.type, event.data);
339
424
  res.writeHead(200, { 'Content-Type': 'application/json' });
340
425
  res.end(JSON.stringify({ success: true }));
@@ -351,6 +436,7 @@ class UnifiedApiServer {
351
436
  const runId = parts[tasksIndex + 1];
352
437
  const action = url.searchParams.get('action');
353
438
  try {
439
+ ensureTask(runId, {});
354
440
  if (action === 'pause') {
355
441
  this.taskRegistry.setStatus(runId, 'paused');
356
442
  } else if (action === 'resume') {