whale-code 6.5.8 → 6.5.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 (99) hide show
  1. package/dist/cli/services/agent-loop.js +26 -2
  2. package/dist/cli/services/agent-loop.js.map +1 -1
  3. package/dist/cli/services/hooks.js +2 -1
  4. package/dist/cli/services/hooks.js.map +1 -1
  5. package/dist/cli/services/telemetry-spans.js +1 -0
  6. package/dist/cli/services/telemetry-spans.js.map +1 -1
  7. package/dist/cli/services/telemetry.d.ts +23 -0
  8. package/dist/cli/services/telemetry.js +45 -1
  9. package/dist/cli/services/telemetry.js.map +1 -1
  10. package/dist/server/handlers/__test-utils__/test-db.d.ts +17 -3
  11. package/dist/server/handlers/__test-utils__/test-db.js +113 -14
  12. package/dist/server/handlers/__test-utils__/test-db.js.map +1 -1
  13. package/dist/server/handlers/affiliates.d.ts +9 -0
  14. package/dist/server/handlers/affiliates.js +197 -0
  15. package/dist/server/handlers/affiliates.js.map +1 -0
  16. package/dist/server/handlers/api-docs.d.ts +4 -2
  17. package/dist/server/handlers/api-docs.js +204 -1681
  18. package/dist/server/handlers/api-docs.js.map +1 -1
  19. package/dist/server/handlers/campaigns.d.ts +9 -0
  20. package/dist/server/handlers/campaigns.js +237 -0
  21. package/dist/server/handlers/campaigns.js.map +1 -0
  22. package/dist/server/handlers/catalog-schemas.js +9 -9
  23. package/dist/server/handlers/catalog-schemas.js.map +1 -1
  24. package/dist/server/handlers/catalog.js +1 -1
  25. package/dist/server/handlers/catalog.js.map +1 -1
  26. package/dist/server/handlers/comms-documents.js +28 -2
  27. package/dist/server/handlers/comms-documents.js.map +1 -1
  28. package/dist/server/handlers/comms-pdf-generation.js +25 -3
  29. package/dist/server/handlers/comms-pdf-generation.js.map +1 -1
  30. package/dist/server/handlers/comms-pdf-helpers.js +4 -4
  31. package/dist/server/handlers/comms-pdf-helpers.js.map +1 -1
  32. package/dist/server/handlers/comms.d.ts +100 -0
  33. package/dist/server/handlers/comms.js +146 -12
  34. package/dist/server/handlers/comms.js.map +1 -1
  35. package/dist/server/handlers/coupons.d.ts +9 -0
  36. package/dist/server/handlers/coupons.js +220 -0
  37. package/dist/server/handlers/coupons.js.map +1 -0
  38. package/dist/server/handlers/embeddings.js +1 -1
  39. package/dist/server/handlers/embeddings.js.map +1 -1
  40. package/dist/server/handlers/enrichment.js +2 -622
  41. package/dist/server/handlers/enrichment.js.map +1 -1
  42. package/dist/server/handlers/fulfillment.d.ts +9 -0
  43. package/dist/server/handlers/fulfillment.js +209 -0
  44. package/dist/server/handlers/fulfillment.js.map +1 -0
  45. package/dist/server/handlers/google-ads.d.ts +24 -0
  46. package/dist/server/handlers/google-ads.js +2199 -0
  47. package/dist/server/handlers/google-ads.js.map +1 -0
  48. package/dist/server/handlers/invoices.d.ts +9 -0
  49. package/dist/server/handlers/invoices.js +252 -0
  50. package/dist/server/handlers/invoices.js.map +1 -0
  51. package/dist/server/handlers/loyalty.d.ts +9 -0
  52. package/dist/server/handlers/loyalty.js +197 -0
  53. package/dist/server/handlers/loyalty.js.map +1 -0
  54. package/dist/server/handlers/meta-ads-graph-api.js +18 -3
  55. package/dist/server/handlers/meta-ads-graph-api.js.map +1 -1
  56. package/dist/server/handlers/phone.d.ts +9 -0
  57. package/dist/server/handlers/phone.js +197 -0
  58. package/dist/server/handlers/phone.js.map +1 -0
  59. package/dist/server/handlers/pipeline.d.ts +9 -0
  60. package/dist/server/handlers/pipeline.js +277 -0
  61. package/dist/server/handlers/pipeline.js.map +1 -0
  62. package/dist/server/handlers/qr-codes.d.ts +9 -0
  63. package/dist/server/handlers/qr-codes.js +198 -0
  64. package/dist/server/handlers/qr-codes.js.map +1 -0
  65. package/dist/server/handlers/reviews.d.ts +9 -0
  66. package/dist/server/handlers/reviews.js +171 -0
  67. package/dist/server/handlers/reviews.js.map +1 -0
  68. package/dist/server/handlers/segments.d.ts +9 -0
  69. package/dist/server/handlers/segments.js +229 -0
  70. package/dist/server/handlers/segments.js.map +1 -0
  71. package/dist/server/handlers/social.d.ts +9 -0
  72. package/dist/server/handlers/social.js +81 -0
  73. package/dist/server/handlers/social.js.map +1 -0
  74. package/dist/server/handlers/tax.d.ts +9 -0
  75. package/dist/server/handlers/tax.js +182 -0
  76. package/dist/server/handlers/tax.js.map +1 -0
  77. package/dist/server/handlers/wallet.d.ts +9 -0
  78. package/dist/server/handlers/wallet.js +203 -0
  79. package/dist/server/handlers/wallet.js.map +1 -0
  80. package/dist/server/handlers/webhooks-mgmt.d.ts +9 -0
  81. package/dist/server/handlers/webhooks-mgmt.js +181 -0
  82. package/dist/server/handlers/webhooks-mgmt.js.map +1 -0
  83. package/dist/server/handlers/wholesale.d.ts +9 -0
  84. package/dist/server/handlers/wholesale.js +219 -0
  85. package/dist/server/handlers/wholesale.js.map +1 -0
  86. package/dist/server/index.js +20 -9
  87. package/dist/server/index.js.map +1 -1
  88. package/dist/server/lib/clickhouse-buffer.js +1 -0
  89. package/dist/server/lib/clickhouse-buffer.js.map +1 -1
  90. package/dist/server/lib/coa-renderer.d.ts +1 -1
  91. package/dist/server/lib/coa-renderer.js +32 -10
  92. package/dist/server/lib/coa-renderer.js.map +1 -1
  93. package/dist/server/server-worker.d.ts +1 -0
  94. package/dist/server/server-worker.js +464 -3
  95. package/dist/server/server-worker.js.map +1 -1
  96. package/dist/server/tool-router.js +118 -4
  97. package/dist/server/tool-router.js.map +1 -1
  98. package/package.json +26 -3
  99. package/vendor/ink/package.json +0 -2
@@ -6,6 +6,8 @@ import { createLogger } from "./lib/logger.js";
6
6
  import { sanitizeError } from "../shared/agent-core.js";
7
7
  import { getServiceClient } from "./lib/supabase-client.js";
8
8
  import { processWorkflowSteps, processWaitingSteps, processScheduleTriggers, enforceWorkflowTimeouts, processEventTriggers, cleanupOrphanedSteps, processDlqRetries } from "./handlers/workflows.js";
9
+ import { handleGoogleAds } from "./handlers/google-ads.js";
10
+ import { handleMetaAds } from "./handlers/meta-ads.js";
9
11
  const log = createLogger("server-worker");
10
12
 
11
13
  // ============================================================================
@@ -22,6 +24,442 @@ export async function enforceNodeOfflineStatus(sb) {
22
24
  return data?.length || 0;
23
25
  }
24
26
 
27
+ // ============================================================================
28
+ // EVENT BUS PROCESSOR — google.campaign_create, meta.campaign_create
29
+ // ============================================================================
30
+
31
+ const EVENT_BUS_BATCH_SIZE = 10;
32
+ const PROCESSING_OWNER = `worker-${process.pid}`;
33
+ export async function processEventBusEvents(sb) {
34
+ // 1. Claim pending events with atomic update
35
+ const now = new Date().toISOString();
36
+ const {
37
+ data: events,
38
+ error: claimErr
39
+ } = await sb.from("event_bus").update({
40
+ status: "processing",
41
+ processing_started_at: now,
42
+ processing_owner: PROCESSING_OWNER
43
+ }).eq("status", "pending").is("processing_owner", null).lte("next_attempt_at", now).in("event_type", ["google.campaign_create", "meta.campaign_create"]).order("next_attempt_at", {
44
+ ascending: true
45
+ }).limit(EVENT_BUS_BATCH_SIZE).select("*");
46
+ if (claimErr) {
47
+ log.warn({
48
+ err: claimErr.message
49
+ }, "event_bus claim failed");
50
+ return 0;
51
+ }
52
+ if (!events?.length) return 0;
53
+ let processed = 0;
54
+ for (const event of events) {
55
+ try {
56
+ await processOneEvent(sb, event);
57
+ processed++;
58
+ } catch (err) {
59
+ log.error({
60
+ err: sanitizeError(err),
61
+ eventId: event.id,
62
+ eventType: event.event_type
63
+ }, "event_bus processing failed unexpectedly");
64
+ }
65
+ }
66
+ return processed;
67
+ }
68
+ async function processOneEvent(sb, event) {
69
+ const {
70
+ id,
71
+ event_type,
72
+ store_id,
73
+ payload,
74
+ attempts,
75
+ max_attempts
76
+ } = event;
77
+ try {
78
+ let result;
79
+ if (event_type === "google.campaign_create") {
80
+ // dispatch_google_campaign passes channel_config = campaign.channel_config.google (already extracted)
81
+ const channelConfig = payload.channel_config;
82
+ if (!channelConfig || Object.keys(channelConfig).length === 0) throw new Error("payload.channel_config missing");
83
+
84
+ // Check if campaign already created (retry-safe — avoids duplicates)
85
+ let googleCampaignId;
86
+ if (payload.campaign_id) {
87
+ const {
88
+ data: existing
89
+ } = await sb.from("google_campaigns").select("id, status").eq("campaign_id", payload.campaign_id).eq("store_id", store_id).limit(1).maybeSingle();
90
+ if (existing) {
91
+ googleCampaignId = existing.id;
92
+ log.info({
93
+ eventId: id,
94
+ googleCampaignId
95
+ }, "google campaign already exists, skipping create_full");
96
+ result = {
97
+ success: true,
98
+ data: {
99
+ campaign: existing
100
+ }
101
+ };
102
+ }
103
+ }
104
+ if (!googleCampaignId) {
105
+ // Create the full campaign (campaign + ad group + ad + keywords)
106
+ result = await handleGoogleAds(sb, {
107
+ action: "create_full",
108
+ ...channelConfig
109
+ }, store_id);
110
+ if (!result.success) throw new Error(result.error || "create_full failed");
111
+ const campaignData = result.data;
112
+ const campaignObj = campaignData?.campaign;
113
+ googleCampaignId = campaignObj?.id;
114
+ if (!googleCampaignId) throw new Error("create_full succeeded but returned no campaign ID");
115
+ }
116
+
117
+ // Publish to Google Ads API
118
+ if (googleCampaignId) {
119
+ const pubResult = await handleGoogleAds(sb, {
120
+ action: "publish",
121
+ type: "campaign",
122
+ campaign_id: googleCampaignId
123
+ }, store_id);
124
+ if (!pubResult.success) throw new Error(pubResult.error || "publish failed");
125
+ }
126
+
127
+ // Update campaign channel_config status to synced
128
+ if (payload.campaign_id) {
129
+ const {
130
+ data: camp
131
+ } = await sb.from("campaigns").select("channel_config").eq("id", payload.campaign_id).single();
132
+ if (camp) {
133
+ const config = camp.channel_config || {};
134
+ const google = config.google || {};
135
+ google.google_status = "synced";
136
+ config.google = google;
137
+ await sb.from("campaigns").update({
138
+ channel_config: config
139
+ }).eq("id", payload.campaign_id);
140
+ }
141
+ }
142
+ } else if (event_type === "meta.campaign_create") {
143
+ // dispatch_meta_campaign passes channel_config = campaign.channel_config.meta (already extracted)
144
+ const channelConfig = payload.channel_config;
145
+ if (!channelConfig || Object.keys(channelConfig).length === 0) throw new Error("payload.channel_config missing");
146
+
147
+ // Check if meta campaign already created (retry-safe — avoids duplicates)
148
+ let metaCampaignId;
149
+ if (payload.campaign_id) {
150
+ const {
151
+ data: existing
152
+ } = await sb.from("meta_campaigns").select("id, status").eq("campaign_id", payload.campaign_id).eq("store_id", store_id).limit(1).maybeSingle();
153
+ if (existing) {
154
+ metaCampaignId = existing.id;
155
+ log.info({
156
+ eventId: id,
157
+ metaCampaignId
158
+ }, "meta campaign already exists, skipping create_full");
159
+ result = {
160
+ success: true,
161
+ data: {
162
+ campaign: existing
163
+ }
164
+ };
165
+ }
166
+ }
167
+ if (!metaCampaignId) {
168
+ result = await handleMetaAds(sb, {
169
+ action: "create_full",
170
+ ...channelConfig
171
+ }, store_id);
172
+ if (!result.success) throw new Error(result.error || "meta create_full failed");
173
+ const campaignData = result.data;
174
+ metaCampaignId = campaignData?.campaign?.id;
175
+ if (!metaCampaignId) throw new Error("meta create_full succeeded but returned no campaign ID");
176
+ }
177
+ if (metaCampaignId) {
178
+ const pubResult = await handleMetaAds(sb, {
179
+ action: "publish",
180
+ type: "campaign",
181
+ campaign_id: metaCampaignId
182
+ }, store_id);
183
+ if (!pubResult.success) throw new Error(pubResult.error || "meta publish failed");
184
+ }
185
+
186
+ // Update campaign channel_config status
187
+ if (payload.campaign_id) {
188
+ const {
189
+ data: camp
190
+ } = await sb.from("campaigns").select("channel_config").eq("id", payload.campaign_id).single();
191
+ if (camp) {
192
+ const config = camp.channel_config || {};
193
+ const meta = config.meta || {};
194
+ meta.meta_status = "synced";
195
+ config.meta = meta;
196
+ await sb.from("campaigns").update({
197
+ channel_config: config
198
+ }).eq("id", payload.campaign_id);
199
+ }
200
+ }
201
+ } else {
202
+ throw new Error(`unsupported event_type: ${event_type}`);
203
+ }
204
+
205
+ // Mark as processed
206
+ await sb.from("event_bus").update({
207
+ status: "processed",
208
+ processed_at: new Date().toISOString(),
209
+ result: result.data ?? {},
210
+ error_message: null
211
+ }).eq("id", id);
212
+ log.info({
213
+ eventId: id,
214
+ eventType: event_type,
215
+ storeId: store_id
216
+ }, "event_bus event processed");
217
+ } catch (err) {
218
+ const errMsg = err instanceof Error ? err.message : String(err);
219
+ const nextAttempt = attempts + 1;
220
+ if (nextAttempt >= max_attempts) {
221
+ // Exhausted retries — mark as failed
222
+ await sb.from("event_bus").update({
223
+ status: "failed",
224
+ attempts: nextAttempt,
225
+ error_message: errMsg,
226
+ processed_at: new Date().toISOString()
227
+ }).eq("id", id);
228
+
229
+ // Mark campaign channel status as failed
230
+ if (payload.campaign_id) {
231
+ const statusField = event_type === "google.campaign_create" ? "google_status" : "meta_status";
232
+ const channelKey = event_type === "google.campaign_create" ? "google" : "meta";
233
+ const {
234
+ data: camp
235
+ } = await sb.from("campaigns").select("channel_config").eq("id", payload.campaign_id).single();
236
+ if (camp) {
237
+ const config = camp.channel_config || {};
238
+ const channel = config[channelKey] || {};
239
+ channel[statusField] = "failed";
240
+ config[channelKey] = channel;
241
+ await sb.from("campaigns").update({
242
+ channel_config: config
243
+ }).eq("id", payload.campaign_id);
244
+ }
245
+ }
246
+ log.error({
247
+ eventId: id,
248
+ eventType: event_type,
249
+ attempts: nextAttempt,
250
+ err: errMsg
251
+ }, "event_bus event failed permanently");
252
+ } else {
253
+ // Exponential backoff: 30s, 60s, 120s, 240s, ...
254
+ const backoffMs = 30_000 * Math.pow(2, nextAttempt - 1);
255
+ const nextAttemptAt = new Date(Date.now() + backoffMs).toISOString();
256
+ await sb.from("event_bus").update({
257
+ status: "pending",
258
+ attempts: nextAttempt,
259
+ error_message: errMsg,
260
+ next_attempt_at: nextAttemptAt,
261
+ processing_started_at: null,
262
+ processing_owner: null
263
+ }).eq("id", id);
264
+ log.warn({
265
+ eventId: id,
266
+ eventType: event_type,
267
+ attempts: nextAttempt,
268
+ nextAttemptAt,
269
+ err: errMsg
270
+ }, "event_bus event retrying");
271
+ }
272
+ }
273
+ }
274
+
275
+ // ============================================================================
276
+ // HEALTH CHECK WRITER — write service health to health_check_log every 5 min
277
+ // ============================================================================
278
+
279
+ let lastHealthCheckAt = 0;
280
+ const HEALTH_CHECK_INTERVAL_MS = 5 * 60_000;
281
+ const HEALTH_ENDPOINTS = [{
282
+ name: "gateway",
283
+ url: "https://whale-gateway.fly.dev/health"
284
+ }, {
285
+ name: "database",
286
+ url: `https://uaednwpxursknmwdeejn.supabase.co/auth/v1/health`
287
+ }, {
288
+ name: "cdn",
289
+ url: "https://pub-fdd30d88678d4a73b0b9bed2f75b848d.r2.dev/WhaleToolsUpdater.dmg",
290
+ method: "HEAD"
291
+ }];
292
+ async function writeHealthChecks(sb) {
293
+ if (Date.now() - lastHealthCheckAt < HEALTH_CHECK_INTERVAL_MS) return 0;
294
+ lastHealthCheckAt = Date.now();
295
+ const checks = {};
296
+ let overallStatus = "healthy";
297
+ for (const ep of HEALTH_ENDPOINTS) {
298
+ const start = Date.now();
299
+ try {
300
+ const controller = new AbortController();
301
+ const timer = setTimeout(() => controller.abort(), 8000);
302
+ const res = await fetch(ep.url, {
303
+ method: ep.method ?? "GET",
304
+ signal: controller.signal,
305
+ headers: {
306
+ "User-Agent": "WhaleAgent-HealthCheck/1.0"
307
+ }
308
+ });
309
+ clearTimeout(timer);
310
+ const ms = Date.now() - start;
311
+ const ok = res.ok;
312
+ checks[ep.name] = {
313
+ status: ok ? "healthy" : "unhealthy",
314
+ ms
315
+ };
316
+ if (!ok) overallStatus = "unhealthy";
317
+ } catch (err) {
318
+ checks[ep.name] = {
319
+ status: "unhealthy",
320
+ ms: null,
321
+ error: err instanceof Error ? err.message : String(err)
322
+ };
323
+ overallStatus = "unhealthy";
324
+ }
325
+ }
326
+ const avgMs = Object.values(checks).filter(c => c.ms !== null).reduce((sum, c, _, arr) => sum + (c.ms ?? 0) / arr.length, 0);
327
+ const {
328
+ error
329
+ } = await sb.from("health_check_log").insert({
330
+ service: "platform",
331
+ status: overallStatus,
332
+ response_time_ms: Math.round(avgMs),
333
+ checks
334
+ });
335
+ if (error) {
336
+ log.warn({
337
+ err: error.message
338
+ }, "health_check_log insert failed");
339
+ return 0;
340
+ }
341
+ return 1;
342
+ }
343
+
344
+ // ============================================================================
345
+ // GOOGLE ADS STATUS POLLING — sync active campaigns every 5 minutes
346
+ // ============================================================================
347
+
348
+ let lastGooglePollAt = 0;
349
+ const GOOGLE_POLL_INTERVAL_MS = 5 * 60_000; // 5 minutes
350
+
351
+ async function pollGoogleAdsStatus(sb) {
352
+ // Only run every 5 minutes
353
+ if (Date.now() - lastGooglePollAt < GOOGLE_POLL_INTERVAL_MS) return 0;
354
+ lastGooglePollAt = Date.now();
355
+
356
+ // Find stores with active Google campaigns (status creating/ENABLED/PAUSED)
357
+ const {
358
+ data: activeStores
359
+ } = await sb.from("google_campaigns").select("store_id").in("status", ["PUBLISHED", "SYNCED", "ENABLED", "PAUSED"]).not("google_campaign_id", "is", null);
360
+ if (!activeStores?.length) return 0;
361
+
362
+ // Deduplicate store IDs
363
+ const storeIds = [...new Set(activeStores.map(r => r.store_id))];
364
+ let synced = 0;
365
+ for (const storeId of storeIds) {
366
+ try {
367
+ const result = await handleGoogleAds(sb, {
368
+ action: "sync"
369
+ }, storeId);
370
+ if (result.success) {
371
+ const data = result.data;
372
+ synced += data?.synced || 0;
373
+ }
374
+ } catch (e) {
375
+ log.warn({
376
+ storeId,
377
+ err: e instanceof Error ? e.message : String(e)
378
+ }, "Google Ads sync poll failed");
379
+ }
380
+ }
381
+ if (synced > 0) {
382
+ log.info({
383
+ synced,
384
+ stores: storeIds.length
385
+ }, "Google Ads status poll completed");
386
+ }
387
+ return synced;
388
+ }
389
+
390
+ // ============================================================================
391
+ // META ADS STATUS POLLING — sync active campaigns + check token expiry
392
+ // ============================================================================
393
+
394
+ let lastMetaPollAt = 0;
395
+ const META_POLL_INTERVAL_MS = 5 * 60_000; // 5 minutes
396
+ const META_TOKEN_WARNING_DAYS = 7;
397
+ async function pollMetaAdsStatus(sb) {
398
+ if (Date.now() - lastMetaPollAt < META_POLL_INTERVAL_MS) return 0;
399
+ lastMetaPollAt = Date.now();
400
+ let actions = 0;
401
+
402
+ // 1. Check for expiring/expired tokens across all active Meta integrations
403
+ const {
404
+ data: integrations
405
+ } = await sb.from("meta_integrations").select("store_id, token_expires_at, status").in("status", ["active", "expiring_soon"]);
406
+ for (const int of integrations || []) {
407
+ if (!int.token_expires_at) continue;
408
+ const daysLeft = (new Date(int.token_expires_at).getTime() - Date.now()) / (1000 * 60 * 60 * 24);
409
+ if (daysLeft <= 0 && int.status !== "token_expired") {
410
+ await sb.from("meta_integrations").update({
411
+ status: "token_expired",
412
+ updated_at: new Date().toISOString()
413
+ }).eq("store_id", int.store_id);
414
+ log.warn({
415
+ storeId: int.store_id,
416
+ daysLeft: 0
417
+ }, "Meta token expired — marked for reconnect");
418
+ actions++;
419
+ } else if (daysLeft <= META_TOKEN_WARNING_DAYS && daysLeft > 0 && int.status === "active") {
420
+ await sb.from("meta_integrations").update({
421
+ status: "expiring_soon",
422
+ updated_at: new Date().toISOString()
423
+ }).eq("store_id", int.store_id);
424
+ log.info({
425
+ storeId: int.store_id,
426
+ daysLeft: Math.round(daysLeft)
427
+ }, "Meta token expiring soon");
428
+ actions++;
429
+ }
430
+ }
431
+
432
+ // 2. Sync campaign status for stores with active Meta campaigns
433
+ const {
434
+ data: activeStores
435
+ } = await sb.from("meta_campaigns").select("store_id").in("status", ["PUBLISHED", "ACTIVE", "PAUSED"]).not("meta_campaign_id", "is", null);
436
+ if (!activeStores?.length) return actions;
437
+ const storeIds = [...new Set(activeStores.map(r => r.store_id))];
438
+ for (const storeId of storeIds) {
439
+ try {
440
+ const result = await handleMetaAds(sb, {
441
+ action: "sync"
442
+ }, storeId);
443
+ if (result.success) {
444
+ const data = result.data;
445
+ actions += data?.synced || 0;
446
+ }
447
+ } catch (e) {
448
+ log.warn({
449
+ storeId,
450
+ err: e instanceof Error ? e.message : String(e)
451
+ }, "Meta Ads sync poll failed");
452
+ }
453
+ }
454
+ if (actions > 0) {
455
+ log.info({
456
+ actions,
457
+ stores: storeIds.length
458
+ }, "Meta Ads status poll completed");
459
+ }
460
+ return actions;
461
+ }
462
+
25
463
  // ============================================================================
26
464
  // WORKER LOOP (15-second interval, safety net for missed pg NOTIFY)
27
465
  // ============================================================================
@@ -40,7 +478,7 @@ async function workflowWorkerLoop() {
40
478
  const [stepResult, waitingResolved] = await Promise.all([processWorkflowSteps(sb, 10), processWaitingSteps(sb), Promise.resolve(sb.rpc("expire_pending_waitpoints")).then(() => {}).catch(e => log.warn({
41
479
  err: e.message
42
480
  }, "expire_pending_waitpoints failed"))]);
43
- const [scheduled, timedOut, eventsProcessed, orphansCleaned, dlqRetried, nodesOfflined] = await Promise.all([processScheduleTriggers(sb).catch(e => {
481
+ const [scheduled, timedOut, eventsProcessed, orphansCleaned, dlqRetried, nodesOfflined, eventBusProcessed,, metaPolled, healthChecked] = await Promise.all([processScheduleTriggers(sb).catch(e => {
44
482
  log.warn({
45
483
  err: e.message
46
484
  }, "processScheduleTriggers failed");
@@ -70,8 +508,28 @@ async function workflowWorkerLoop() {
70
508
  err: e.message
71
509
  }, "enforceNodeOfflineStatus failed");
72
510
  return 0;
511
+ }), processEventBusEvents(sb).catch(e => {
512
+ log.warn({
513
+ err: e.message
514
+ }, "processEventBusEvents failed");
515
+ return 0;
516
+ }), pollGoogleAdsStatus(sb).catch(e => {
517
+ log.warn({
518
+ err: e.message
519
+ }, "pollGoogleAdsStatus failed");
520
+ return 0;
521
+ }), pollMetaAdsStatus(sb).catch(e => {
522
+ log.warn({
523
+ err: e.message
524
+ }, "pollMetaAdsStatus failed");
525
+ return 0;
526
+ }), writeHealthChecks(sb).catch(e => {
527
+ log.warn({
528
+ err: e.message
529
+ }, "writeHealthChecks failed");
530
+ return 0;
73
531
  })]);
74
- if (stepResult.processed > 0 || waitingResolved > 0 || scheduled > 0 || timedOut > 0 || eventsProcessed > 0 || stepResult.reclaimed > 0 || orphansCleaned > 0 || dlqRetried > 0 || nodesOfflined > 0) {
532
+ if (stepResult.processed > 0 || waitingResolved > 0 || scheduled > 0 || timedOut > 0 || eventsProcessed > 0 || stepResult.reclaimed > 0 || orphansCleaned > 0 || dlqRetried > 0 || nodesOfflined > 0 || eventBusProcessed > 0 || metaPolled > 0 || healthChecked > 0) {
75
533
  log.info({
76
534
  processed: stepResult.processed,
77
535
  errors: stepResult.errors,
@@ -82,7 +540,10 @@ async function workflowWorkerLoop() {
82
540
  events: eventsProcessed,
83
541
  orphans: orphansCleaned,
84
542
  dlqRetries: dlqRetried,
85
- nodesOfflined
543
+ nodesOfflined,
544
+ eventBus: eventBusProcessed,
545
+ metaPoll: metaPolled,
546
+ healthCheck: healthChecked
86
547
  }, "worker tick");
87
548
  }
88
549
  if (consecutiveErrors > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"server-worker.js","names":["createLogger","sanitizeError","getServiceClient","processWorkflowSteps","processWaitingSteps","processScheduleTriggers","enforceWorkflowTimeouts","processEventTriggers","cleanupOrphanedSteps","processDlqRetries","log","enforceNodeOfflineStatus","sb","threshold","Date","now","toISOString","data","from","update","status","eq","lt","select","length","BASE_WORKER_INTERVAL_MS","MAX_WORKER_INTERVAL_MS","workerRunning","consecutiveErrors","currentWorkerInterval","workerInterval","workflowWorkerLoop","stepResult","waitingResolved","Promise","all","resolve","rpc","then","catch","e","warn","err","message","scheduled","timedOut","eventsProcessed","orphansCleaned","dlqRetried","nodesOfflined","processed","reclaimed","info","errors","waiting","events","orphans","dlqRetries","clearInterval","setInterval","intervalMs","error","newInterval","Math","min","pow","startWorkerLoop","stopWorkerLoop"],"sources":["../../src/server/server-worker.ts"],"sourcesContent":["// server/server-worker.ts — Persistent workflow worker loop\n// Safety net for missed pg NOTIFY — processes steps, triggers, timeouts, events, etc.\n// Extracted from index.ts for modularity.\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createLogger } from \"./lib/logger.js\";\nimport { sanitizeError } from \"../shared/agent-core.js\";\nimport { getServiceClient } from \"./lib/supabase-client.js\";\nimport {\n processWorkflowSteps, processWaitingSteps,\n processScheduleTriggers, enforceWorkflowTimeouts,\n processEventTriggers, cleanupOrphanedSteps, processDlqRetries,\n} from \"./handlers/workflows.js\";\n\nconst log = createLogger(\"server-worker\");\n\n// ============================================================================\n// NODE OFFLINE DETECTION\n// ============================================================================\n\nexport async function enforceNodeOfflineStatus(sb: SupabaseClient): Promise<number> {\n const threshold = new Date(Date.now() - 3 * 60_000).toISOString();\n const { data } = await sb.from(\"nodes\").update({ status: \"offline\" }).eq(\"status\", \"online\").lt(\"last_heartbeat\", threshold).select(\"id\");\n return data?.length || 0;\n}\n\n// ============================================================================\n// WORKER LOOP (15-second interval, safety net for missed pg NOTIFY)\n// ============================================================================\n\nconst BASE_WORKER_INTERVAL_MS = 15_000;\nconst MAX_WORKER_INTERVAL_MS = 60_000;\nlet workerRunning = false;\nlet consecutiveErrors = 0;\nlet currentWorkerInterval = BASE_WORKER_INTERVAL_MS;\nlet workerInterval: ReturnType<typeof setInterval>;\n\nasync function workflowWorkerLoop(): Promise<void> {\n if (workerRunning) return;\n workerRunning = true;\n try {\n const sb = getServiceClient();\n const [stepResult, waitingResolved] = await Promise.all([\n processWorkflowSteps(sb, 10),\n processWaitingSteps(sb),\n Promise.resolve(sb.rpc(\"expire_pending_waitpoints\")).then(() => {}).catch(e => log.warn({ err: e.message }, \"expire_pending_waitpoints failed\")),\n ]);\n\n const [scheduled, timedOut, eventsProcessed, orphansCleaned, dlqRetried, nodesOfflined] = await Promise.all([\n processScheduleTriggers(sb).catch(e => { log.warn({ err: e.message }, \"processScheduleTriggers failed\"); return 0; }),\n enforceWorkflowTimeouts(sb).catch(e => { log.warn({ err: e.message }, \"enforceWorkflowTimeouts failed\"); return 0; }),\n processEventTriggers(sb).catch(e => { log.warn({ err: e.message }, \"processEventTriggers failed\"); return 0; }),\n cleanupOrphanedSteps(sb).catch(e => { log.warn({ err: e.message }, \"cleanupOrphanedSteps failed\"); return 0; }),\n processDlqRetries(sb).catch(e => { log.warn({ err: e.message }, \"processDlqRetries failed\"); return 0; }),\n enforceNodeOfflineStatus(sb).catch(e => { log.warn({ err: e.message }, \"enforceNodeOfflineStatus failed\"); return 0; }),\n ]);\n\n if (stepResult.processed > 0 || waitingResolved > 0 || scheduled > 0 || timedOut > 0 || eventsProcessed > 0 || (stepResult as any).reclaimed > 0 || orphansCleaned > 0 || dlqRetried > 0 || nodesOfflined > 0) {\n log.info({ processed: stepResult.processed, errors: stepResult.errors, reclaimed: (stepResult as any).reclaimed || 0, waiting: waitingResolved, scheduled, timedOut, events: eventsProcessed, orphans: orphansCleaned, dlqRetries: dlqRetried, nodesOfflined }, \"worker tick\");\n }\n\n if (consecutiveErrors > 0) {\n consecutiveErrors = 0;\n if (currentWorkerInterval !== BASE_WORKER_INTERVAL_MS) {\n currentWorkerInterval = BASE_WORKER_INTERVAL_MS;\n clearInterval(workerInterval);\n workerInterval = setInterval(workflowWorkerLoop, currentWorkerInterval);\n log.info({ intervalMs: currentWorkerInterval }, \"worker interval reset after recovery\");\n }\n }\n } catch (err) {\n consecutiveErrors++;\n log.error({ err: sanitizeError(err), consecutiveErrors }, \"worker error\");\n if (consecutiveErrors >= 3) {\n const newInterval = Math.min(BASE_WORKER_INTERVAL_MS * Math.pow(2, consecutiveErrors - 2), MAX_WORKER_INTERVAL_MS);\n if (newInterval !== currentWorkerInterval) {\n currentWorkerInterval = newInterval;\n clearInterval(workerInterval);\n workerInterval = setInterval(workflowWorkerLoop, currentWorkerInterval);\n log.warn({ intervalMs: currentWorkerInterval, consecutiveErrors }, \"worker interval increased due to repeated errors\");\n }\n }\n } finally {\n workerRunning = false;\n }\n}\n\nexport function startWorkerLoop(): void {\n workerInterval = setInterval(workflowWorkerLoop, BASE_WORKER_INTERVAL_MS);\n}\n\nexport function stopWorkerLoop(): void {\n clearInterval(workerInterval);\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"server-worker.js","names":["createLogger","sanitizeError","getServiceClient","processWorkflowSteps","processWaitingSteps","processScheduleTriggers","enforceWorkflowTimeouts","processEventTriggers","cleanupOrphanedSteps","processDlqRetries","handleGoogleAds","handleMetaAds","log","enforceNodeOfflineStatus","sb","threshold","Date","now","toISOString","data","from","update","status","eq","lt","select","length","EVENT_BUS_BATCH_SIZE","PROCESSING_OWNER","process","pid","processEventBusEvents","events","error","claimErr","processing_started_at","processing_owner","is","lte","in","order","ascending","limit","warn","err","message","processed","event","processOneEvent","eventId","id","eventType","event_type","store_id","payload","attempts","max_attempts","result","channelConfig","channel_config","Object","keys","Error","googleCampaignId","campaign_id","existing","maybeSingle","info","success","campaign","action","campaignData","campaignObj","pubResult","type","camp","single","config","google","google_status","metaCampaignId","meta","meta_status","processed_at","error_message","storeId","errMsg","String","nextAttempt","statusField","channelKey","channel","backoffMs","Math","pow","nextAttemptAt","next_attempt_at","lastHealthCheckAt","HEALTH_CHECK_INTERVAL_MS","HEALTH_ENDPOINTS","name","url","method","writeHealthChecks","checks","overallStatus","ep","start","controller","AbortController","timer","setTimeout","abort","res","fetch","signal","headers","clearTimeout","ms","ok","avgMs","values","filter","c","reduce","sum","_","arr","insert","service","response_time_ms","round","lastGooglePollAt","GOOGLE_POLL_INTERVAL_MS","pollGoogleAdsStatus","activeStores","not","storeIds","Set","map","r","synced","e","stores","lastMetaPollAt","META_POLL_INTERVAL_MS","META_TOKEN_WARNING_DAYS","pollMetaAdsStatus","actions","integrations","int","token_expires_at","daysLeft","getTime","updated_at","BASE_WORKER_INTERVAL_MS","MAX_WORKER_INTERVAL_MS","workerRunning","consecutiveErrors","currentWorkerInterval","workerInterval","workflowWorkerLoop","stepResult","waitingResolved","Promise","all","resolve","rpc","then","catch","scheduled","timedOut","eventsProcessed","orphansCleaned","dlqRetried","nodesOfflined","eventBusProcessed","metaPolled","healthChecked","reclaimed","errors","waiting","orphans","dlqRetries","eventBus","metaPoll","healthCheck","clearInterval","setInterval","intervalMs","newInterval","min","startWorkerLoop","stopWorkerLoop"],"sources":["../../src/server/server-worker.ts"],"sourcesContent":["// server/server-worker.ts — Persistent workflow worker loop\n// Safety net for missed pg NOTIFY — processes steps, triggers, timeouts, events, etc.\n// Extracted from index.ts for modularity.\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createLogger } from \"./lib/logger.js\";\nimport { sanitizeError } from \"../shared/agent-core.js\";\nimport { getServiceClient } from \"./lib/supabase-client.js\";\nimport {\n processWorkflowSteps, processWaitingSteps,\n processScheduleTriggers, enforceWorkflowTimeouts,\n processEventTriggers, cleanupOrphanedSteps, processDlqRetries,\n} from \"./handlers/workflows.js\";\nimport { handleGoogleAds } from \"./handlers/google-ads.js\";\nimport { handleMetaAds } from \"./handlers/meta-ads.js\";\n\nconst log = createLogger(\"server-worker\");\n\n// ============================================================================\n// NODE OFFLINE DETECTION\n// ============================================================================\n\nexport async function enforceNodeOfflineStatus(sb: SupabaseClient): Promise<number> {\n const threshold = new Date(Date.now() - 3 * 60_000).toISOString();\n const { data } = await sb.from(\"nodes\").update({ status: \"offline\" }).eq(\"status\", \"online\").lt(\"last_heartbeat\", threshold).select(\"id\");\n return data?.length || 0;\n}\n\n// ============================================================================\n// EVENT BUS PROCESSOR — google.campaign_create, meta.campaign_create\n// ============================================================================\n\nconst EVENT_BUS_BATCH_SIZE = 10;\nconst PROCESSING_OWNER = `worker-${process.pid}`;\n\ninterface EventBusRow {\n id: string;\n event_type: string;\n store_id: string;\n payload: Record<string, unknown>;\n status: string;\n attempts: number;\n max_attempts: number;\n next_attempt_at: string | null;\n processing_started_at: string | null;\n processing_owner: string | null;\n processed_at: string | null;\n error_message: string | null;\n result: Record<string, unknown> | null;\n}\n\nexport async function processEventBusEvents(sb: SupabaseClient): Promise<number> {\n // 1. Claim pending events with atomic update\n const now = new Date().toISOString();\n const { data: events, error: claimErr } = await sb\n .from(\"event_bus\")\n .update({\n status: \"processing\",\n processing_started_at: now,\n processing_owner: PROCESSING_OWNER,\n })\n .eq(\"status\", \"pending\")\n .is(\"processing_owner\", null)\n .lte(\"next_attempt_at\", now)\n .in(\"event_type\", [\"google.campaign_create\", \"meta.campaign_create\"])\n .order(\"next_attempt_at\", { ascending: true })\n .limit(EVENT_BUS_BATCH_SIZE)\n .select(\"*\");\n\n if (claimErr) {\n log.warn({ err: claimErr.message }, \"event_bus claim failed\");\n return 0;\n }\n if (!events?.length) return 0;\n\n let processed = 0;\n\n for (const event of events as EventBusRow[]) {\n try {\n await processOneEvent(sb, event);\n processed++;\n } catch (err) {\n log.error({ err: sanitizeError(err), eventId: event.id, eventType: event.event_type }, \"event_bus processing failed unexpectedly\");\n }\n }\n\n return processed;\n}\n\nasync function processOneEvent(sb: SupabaseClient, event: EventBusRow): Promise<void> {\n const { id, event_type, store_id, payload, attempts, max_attempts } = event;\n\n try {\n let result!: { success: boolean; data?: unknown; error?: string };\n\n if (event_type === \"google.campaign_create\") {\n // dispatch_google_campaign passes channel_config = campaign.channel_config.google (already extracted)\n const channelConfig = payload.channel_config as Record<string, unknown> | undefined;\n if (!channelConfig || Object.keys(channelConfig).length === 0) throw new Error(\"payload.channel_config missing\");\n\n // Check if campaign already created (retry-safe — avoids duplicates)\n let googleCampaignId: string | undefined;\n if (payload.campaign_id) {\n const { data: existing } = await sb.from(\"google_campaigns\")\n .select(\"id, status\")\n .eq(\"campaign_id\", payload.campaign_id)\n .eq(\"store_id\", store_id)\n .limit(1)\n .maybeSingle();\n if (existing) {\n googleCampaignId = existing.id;\n log.info({ eventId: id, googleCampaignId }, \"google campaign already exists, skipping create_full\");\n result = { success: true, data: { campaign: existing } };\n }\n }\n\n if (!googleCampaignId) {\n // Create the full campaign (campaign + ad group + ad + keywords)\n result = await handleGoogleAds(sb, {\n action: \"create_full\",\n ...channelConfig,\n }, store_id);\n\n if (!result.success) throw new Error(result.error || \"create_full failed\");\n\n const campaignData = result.data as Record<string, unknown>;\n const campaignObj = campaignData?.campaign as Record<string, unknown> | undefined;\n googleCampaignId = campaignObj?.id as string | undefined;\n if (!googleCampaignId) throw new Error(\"create_full succeeded but returned no campaign ID\");\n }\n\n // Publish to Google Ads API\n if (googleCampaignId) {\n const pubResult = await handleGoogleAds(sb, {\n action: \"publish\",\n type: \"campaign\",\n campaign_id: googleCampaignId,\n }, store_id);\n\n if (!pubResult.success) throw new Error(pubResult.error || \"publish failed\");\n }\n\n // Update campaign channel_config status to synced\n if (payload.campaign_id) {\n const { data: camp } = await sb.from(\"campaigns\").select(\"channel_config\").eq(\"id\", payload.campaign_id).single();\n if (camp) {\n const config = (camp.channel_config || {}) as Record<string, unknown>;\n const google = (config.google || {}) as Record<string, unknown>;\n google.google_status = \"synced\";\n config.google = google;\n await sb.from(\"campaigns\").update({ channel_config: config }).eq(\"id\", payload.campaign_id);\n }\n }\n\n } else if (event_type === \"meta.campaign_create\") {\n // dispatch_meta_campaign passes channel_config = campaign.channel_config.meta (already extracted)\n const channelConfig = payload.channel_config as Record<string, unknown> | undefined;\n if (!channelConfig || Object.keys(channelConfig).length === 0) throw new Error(\"payload.channel_config missing\");\n\n // Check if meta campaign already created (retry-safe — avoids duplicates)\n let metaCampaignId: string | undefined;\n if (payload.campaign_id) {\n const { data: existing } = await sb.from(\"meta_campaigns\")\n .select(\"id, status\")\n .eq(\"campaign_id\", payload.campaign_id)\n .eq(\"store_id\", store_id)\n .limit(1)\n .maybeSingle();\n if (existing) {\n metaCampaignId = existing.id;\n log.info({ eventId: id, metaCampaignId }, \"meta campaign already exists, skipping create_full\");\n result = { success: true, data: { campaign: existing } };\n }\n }\n\n if (!metaCampaignId) {\n result = await handleMetaAds(sb, {\n action: \"create_full\",\n ...channelConfig,\n }, store_id);\n\n if (!result.success) throw new Error(result.error || \"meta create_full failed\");\n\n const campaignData = result.data as Record<string, unknown>;\n metaCampaignId = (campaignData?.campaign as Record<string, unknown>)?.id as string | undefined;\n if (!metaCampaignId) throw new Error(\"meta create_full succeeded but returned no campaign ID\");\n }\n\n if (metaCampaignId) {\n const pubResult = await handleMetaAds(sb, {\n action: \"publish\",\n type: \"campaign\",\n campaign_id: metaCampaignId,\n }, store_id);\n\n if (!pubResult.success) throw new Error(pubResult.error || \"meta publish failed\");\n }\n\n // Update campaign channel_config status\n if (payload.campaign_id) {\n const { data: camp } = await sb.from(\"campaigns\").select(\"channel_config\").eq(\"id\", payload.campaign_id).single();\n if (camp) {\n const config = (camp.channel_config || {}) as Record<string, unknown>;\n const meta = (config.meta || {}) as Record<string, unknown>;\n meta.meta_status = \"synced\";\n config.meta = meta;\n await sb.from(\"campaigns\").update({ channel_config: config }).eq(\"id\", payload.campaign_id);\n }\n }\n\n } else {\n throw new Error(`unsupported event_type: ${event_type}`);\n }\n\n // Mark as processed\n await sb.from(\"event_bus\").update({\n status: \"processed\",\n processed_at: new Date().toISOString(),\n result: result.data as Record<string, unknown> ?? {},\n error_message: null,\n }).eq(\"id\", id);\n\n log.info({ eventId: id, eventType: event_type, storeId: store_id }, \"event_bus event processed\");\n\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n const nextAttempt = attempts + 1;\n\n if (nextAttempt >= max_attempts) {\n // Exhausted retries — mark as failed\n await sb.from(\"event_bus\").update({\n status: \"failed\",\n attempts: nextAttempt,\n error_message: errMsg,\n processed_at: new Date().toISOString(),\n }).eq(\"id\", id);\n\n // Mark campaign channel status as failed\n if (payload.campaign_id) {\n const statusField = event_type === \"google.campaign_create\" ? \"google_status\" : \"meta_status\";\n const channelKey = event_type === \"google.campaign_create\" ? \"google\" : \"meta\";\n const { data: camp } = await sb.from(\"campaigns\").select(\"channel_config\").eq(\"id\", payload.campaign_id).single();\n if (camp) {\n const config = (camp.channel_config || {}) as Record<string, unknown>;\n const channel = (config[channelKey] || {}) as Record<string, unknown>;\n channel[statusField] = \"failed\";\n config[channelKey] = channel;\n await sb.from(\"campaigns\").update({ channel_config: config }).eq(\"id\", payload.campaign_id);\n }\n }\n\n log.error({ eventId: id, eventType: event_type, attempts: nextAttempt, err: errMsg }, \"event_bus event failed permanently\");\n } else {\n // Exponential backoff: 30s, 60s, 120s, 240s, ...\n const backoffMs = 30_000 * Math.pow(2, nextAttempt - 1);\n const nextAttemptAt = new Date(Date.now() + backoffMs).toISOString();\n\n await sb.from(\"event_bus\").update({\n status: \"pending\",\n attempts: nextAttempt,\n error_message: errMsg,\n next_attempt_at: nextAttemptAt,\n processing_started_at: null,\n processing_owner: null,\n }).eq(\"id\", id);\n\n log.warn({ eventId: id, eventType: event_type, attempts: nextAttempt, nextAttemptAt, err: errMsg }, \"event_bus event retrying\");\n }\n }\n}\n\n// ============================================================================\n// HEALTH CHECK WRITER — write service health to health_check_log every 5 min\n// ============================================================================\n\nlet lastHealthCheckAt = 0;\nconst HEALTH_CHECK_INTERVAL_MS = 5 * 60_000;\n\nconst HEALTH_ENDPOINTS = [\n { name: \"gateway\", url: \"https://whale-gateway.fly.dev/health\" },\n { name: \"database\", url: `https://uaednwpxursknmwdeejn.supabase.co/auth/v1/health` },\n { name: \"cdn\", url: \"https://pub-fdd30d88678d4a73b0b9bed2f75b848d.r2.dev/WhaleToolsUpdater.dmg\", method: \"HEAD\" as const },\n];\n\nasync function writeHealthChecks(sb: SupabaseClient): Promise<number> {\n if (Date.now() - lastHealthCheckAt < HEALTH_CHECK_INTERVAL_MS) return 0;\n lastHealthCheckAt = Date.now();\n\n const checks: Record<string, { status: string; ms: number | null; error?: string }> = {};\n let overallStatus: \"healthy\" | \"unhealthy\" = \"healthy\";\n\n for (const ep of HEALTH_ENDPOINTS) {\n const start = Date.now();\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 8000);\n const res = await fetch(ep.url, {\n method: ep.method ?? \"GET\",\n signal: controller.signal,\n headers: { \"User-Agent\": \"WhaleAgent-HealthCheck/1.0\" },\n });\n clearTimeout(timer);\n const ms = Date.now() - start;\n const ok = res.ok;\n checks[ep.name] = { status: ok ? \"healthy\" : \"unhealthy\", ms };\n if (!ok) overallStatus = \"unhealthy\";\n } catch (err) {\n checks[ep.name] = {\n status: \"unhealthy\",\n ms: null,\n error: err instanceof Error ? err.message : String(err),\n };\n overallStatus = \"unhealthy\";\n }\n }\n\n const avgMs = Object.values(checks)\n .filter((c) => c.ms !== null)\n .reduce((sum, c, _, arr) => sum + (c.ms ?? 0) / arr.length, 0);\n\n const { error } = await sb.from(\"health_check_log\").insert({\n service: \"platform\",\n status: overallStatus,\n response_time_ms: Math.round(avgMs),\n checks,\n });\n\n if (error) {\n log.warn({ err: error.message }, \"health_check_log insert failed\");\n return 0;\n }\n\n return 1;\n}\n\n// ============================================================================\n// GOOGLE ADS STATUS POLLING — sync active campaigns every 5 minutes\n// ============================================================================\n\nlet lastGooglePollAt = 0;\nconst GOOGLE_POLL_INTERVAL_MS = 5 * 60_000; // 5 minutes\n\nasync function pollGoogleAdsStatus(sb: SupabaseClient): Promise<number> {\n // Only run every 5 minutes\n if (Date.now() - lastGooglePollAt < GOOGLE_POLL_INTERVAL_MS) return 0;\n lastGooglePollAt = Date.now();\n\n // Find stores with active Google campaigns (status creating/ENABLED/PAUSED)\n const { data: activeStores } = await sb\n .from(\"google_campaigns\")\n .select(\"store_id\")\n .in(\"status\", [\"PUBLISHED\", \"SYNCED\", \"ENABLED\", \"PAUSED\"])\n .not(\"google_campaign_id\", \"is\", null);\n\n if (!activeStores?.length) return 0;\n\n // Deduplicate store IDs\n const storeIds = [...new Set(activeStores.map(r => r.store_id))];\n let synced = 0;\n\n for (const storeId of storeIds) {\n try {\n const result = await handleGoogleAds(sb, { action: \"sync\" }, storeId);\n if (result.success) {\n const data = result.data as Record<string, unknown>;\n synced += (data?.synced as number) || 0;\n }\n } catch (e) {\n log.warn({ storeId, err: e instanceof Error ? e.message : String(e) }, \"Google Ads sync poll failed\");\n }\n }\n\n if (synced > 0) {\n log.info({ synced, stores: storeIds.length }, \"Google Ads status poll completed\");\n }\n return synced;\n}\n\n// ============================================================================\n// META ADS STATUS POLLING — sync active campaigns + check token expiry\n// ============================================================================\n\nlet lastMetaPollAt = 0;\nconst META_POLL_INTERVAL_MS = 5 * 60_000; // 5 minutes\nconst META_TOKEN_WARNING_DAYS = 7;\n\nasync function pollMetaAdsStatus(sb: SupabaseClient): Promise<number> {\n if (Date.now() - lastMetaPollAt < META_POLL_INTERVAL_MS) return 0;\n lastMetaPollAt = Date.now();\n\n let actions = 0;\n\n // 1. Check for expiring/expired tokens across all active Meta integrations\n const { data: integrations } = await sb\n .from(\"meta_integrations\")\n .select(\"store_id, token_expires_at, status\")\n .in(\"status\", [\"active\", \"expiring_soon\"]);\n\n for (const int of integrations || []) {\n if (!int.token_expires_at) continue;\n const daysLeft = (new Date(int.token_expires_at).getTime() - Date.now()) / (1000 * 60 * 60 * 24);\n\n if (daysLeft <= 0 && int.status !== \"token_expired\") {\n await sb.from(\"meta_integrations\")\n .update({ status: \"token_expired\", updated_at: new Date().toISOString() })\n .eq(\"store_id\", int.store_id);\n log.warn({ storeId: int.store_id, daysLeft: 0 }, \"Meta token expired — marked for reconnect\");\n actions++;\n } else if (daysLeft <= META_TOKEN_WARNING_DAYS && daysLeft > 0 && int.status === \"active\") {\n await sb.from(\"meta_integrations\")\n .update({ status: \"expiring_soon\", updated_at: new Date().toISOString() })\n .eq(\"store_id\", int.store_id);\n log.info({ storeId: int.store_id, daysLeft: Math.round(daysLeft) }, \"Meta token expiring soon\");\n actions++;\n }\n }\n\n // 2. Sync campaign status for stores with active Meta campaigns\n const { data: activeStores } = await sb\n .from(\"meta_campaigns\")\n .select(\"store_id\")\n .in(\"status\", [\"PUBLISHED\", \"ACTIVE\", \"PAUSED\"])\n .not(\"meta_campaign_id\", \"is\", null);\n\n if (!activeStores?.length) return actions;\n\n const storeIds = [...new Set(activeStores.map(r => r.store_id))];\n\n for (const storeId of storeIds) {\n try {\n const result = await handleMetaAds(sb, { action: \"sync\" }, storeId);\n if (result.success) {\n const data = result.data as Record<string, unknown>;\n actions += (data?.synced as number) || 0;\n }\n } catch (e) {\n log.warn({ storeId, err: e instanceof Error ? e.message : String(e) }, \"Meta Ads sync poll failed\");\n }\n }\n\n if (actions > 0) {\n log.info({ actions, stores: storeIds.length }, \"Meta Ads status poll completed\");\n }\n return actions;\n}\n\n// ============================================================================\n// WORKER LOOP (15-second interval, safety net for missed pg NOTIFY)\n// ============================================================================\n\nconst BASE_WORKER_INTERVAL_MS = 15_000;\nconst MAX_WORKER_INTERVAL_MS = 60_000;\nlet workerRunning = false;\nlet consecutiveErrors = 0;\nlet currentWorkerInterval = BASE_WORKER_INTERVAL_MS;\nlet workerInterval: ReturnType<typeof setInterval>;\n\nasync function workflowWorkerLoop(): Promise<void> {\n if (workerRunning) return;\n workerRunning = true;\n try {\n const sb = getServiceClient();\n const [stepResult, waitingResolved] = await Promise.all([\n processWorkflowSteps(sb, 10),\n processWaitingSteps(sb),\n Promise.resolve(sb.rpc(\"expire_pending_waitpoints\")).then(() => {}).catch(e => log.warn({ err: e.message }, \"expire_pending_waitpoints failed\")),\n ]);\n\n const [scheduled, timedOut, eventsProcessed, orphansCleaned, dlqRetried, nodesOfflined, eventBusProcessed, , metaPolled, healthChecked] = await Promise.all([\n processScheduleTriggers(sb).catch(e => { log.warn({ err: e.message }, \"processScheduleTriggers failed\"); return 0; }),\n enforceWorkflowTimeouts(sb).catch(e => { log.warn({ err: e.message }, \"enforceWorkflowTimeouts failed\"); return 0; }),\n processEventTriggers(sb).catch(e => { log.warn({ err: e.message }, \"processEventTriggers failed\"); return 0; }),\n cleanupOrphanedSteps(sb).catch(e => { log.warn({ err: e.message }, \"cleanupOrphanedSteps failed\"); return 0; }),\n processDlqRetries(sb).catch(e => { log.warn({ err: e.message }, \"processDlqRetries failed\"); return 0; }),\n enforceNodeOfflineStatus(sb).catch(e => { log.warn({ err: e.message }, \"enforceNodeOfflineStatus failed\"); return 0; }),\n processEventBusEvents(sb).catch(e => { log.warn({ err: e.message }, \"processEventBusEvents failed\"); return 0; }),\n pollGoogleAdsStatus(sb).catch(e => { log.warn({ err: e.message }, \"pollGoogleAdsStatus failed\"); return 0; }),\n pollMetaAdsStatus(sb).catch(e => { log.warn({ err: e.message }, \"pollMetaAdsStatus failed\"); return 0; }),\n writeHealthChecks(sb).catch(e => { log.warn({ err: e.message }, \"writeHealthChecks failed\"); return 0; }),\n ]);\n\n if (stepResult.processed > 0 || waitingResolved > 0 || scheduled > 0 || timedOut > 0 || eventsProcessed > 0 || (stepResult as any).reclaimed > 0 || orphansCleaned > 0 || dlqRetried > 0 || nodesOfflined > 0 || eventBusProcessed > 0 || metaPolled > 0 || healthChecked > 0) {\n log.info({ processed: stepResult.processed, errors: stepResult.errors, reclaimed: (stepResult as any).reclaimed || 0, waiting: waitingResolved, scheduled, timedOut, events: eventsProcessed, orphans: orphansCleaned, dlqRetries: dlqRetried, nodesOfflined, eventBus: eventBusProcessed, metaPoll: metaPolled, healthCheck: healthChecked }, \"worker tick\");\n }\n\n if (consecutiveErrors > 0) {\n consecutiveErrors = 0;\n if (currentWorkerInterval !== BASE_WORKER_INTERVAL_MS) {\n currentWorkerInterval = BASE_WORKER_INTERVAL_MS;\n clearInterval(workerInterval);\n workerInterval = setInterval(workflowWorkerLoop, currentWorkerInterval);\n log.info({ intervalMs: currentWorkerInterval }, \"worker interval reset after recovery\");\n }\n }\n } catch (err) {\n consecutiveErrors++;\n log.error({ err: sanitizeError(err), consecutiveErrors }, \"worker error\");\n if (consecutiveErrors >= 3) {\n const newInterval = Math.min(BASE_WORKER_INTERVAL_MS * Math.pow(2, consecutiveErrors - 2), MAX_WORKER_INTERVAL_MS);\n if (newInterval !== currentWorkerInterval) {\n currentWorkerInterval = newInterval;\n clearInterval(workerInterval);\n workerInterval = setInterval(workflowWorkerLoop, currentWorkerInterval);\n log.warn({ intervalMs: currentWorkerInterval, consecutiveErrors }, \"worker interval increased due to repeated errors\");\n }\n }\n } finally {\n workerRunning = false;\n }\n}\n\nexport function startWorkerLoop(): void {\n workerInterval = setInterval(workflowWorkerLoop, BASE_WORKER_INTERVAL_MS);\n}\n\nexport function stopWorkerLoop(): void {\n clearInterval(workerInterval);\n}\n"],"mappings":"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","ignoreList":[]}