@cloudstreamsoftware/claude-tools 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/README.md +152 -37
  2. package/agents/INDEX.md +183 -0
  3. package/agents/architect.md +247 -0
  4. package/agents/build-error-resolver.md +555 -0
  5. package/agents/catalyst-deployer.md +132 -0
  6. package/agents/code-reviewer.md +121 -0
  7. package/agents/compliance-auditor.md +148 -0
  8. package/agents/creator-architect.md +395 -0
  9. package/agents/deluge-reviewer.md +98 -0
  10. package/agents/doc-updater.md +471 -0
  11. package/agents/e2e-runner.md +711 -0
  12. package/agents/planner.md +122 -0
  13. package/agents/refactor-cleaner.md +309 -0
  14. package/agents/security-reviewer.md +582 -0
  15. package/agents/tdd-guide.md +302 -0
  16. package/bin/cloudstream-setup.js +16 -6
  17. package/config/versions.json +63 -0
  18. package/dist/hooks/hooks.json +209 -0
  19. package/dist/index.js +47 -0
  20. package/dist/lib/asset-value.js +609 -0
  21. package/dist/lib/client-manager.js +300 -0
  22. package/dist/lib/command-matcher.js +242 -0
  23. package/dist/lib/cross-session-patterns.js +754 -0
  24. package/dist/lib/intent-classifier.js +1075 -0
  25. package/dist/lib/package-manager.js +374 -0
  26. package/dist/lib/recommendation-engine.js +597 -0
  27. package/dist/lib/session-memory.js +489 -0
  28. package/dist/lib/skill-effectiveness.js +486 -0
  29. package/dist/lib/skill-matcher.js +595 -0
  30. package/dist/lib/tutorial-metrics.js +242 -0
  31. package/dist/lib/tutorial-progress.js +209 -0
  32. package/dist/lib/tutorial-renderer.js +431 -0
  33. package/dist/lib/utils.js +380 -0
  34. package/dist/lib/verify-formatter.js +143 -0
  35. package/dist/lib/workflow-state.js +249 -0
  36. package/hooks/hooks.json +209 -0
  37. package/package.json +5 -1
  38. package/scripts/aggregate-sessions.js +290 -0
  39. package/scripts/branch-name-validator.js +291 -0
  40. package/scripts/build.js +101 -0
  41. package/scripts/commands/client-switch.js +231 -0
  42. package/scripts/deprecate-skill.js +610 -0
  43. package/scripts/diagnose.js +324 -0
  44. package/scripts/doc-freshness.js +168 -0
  45. package/scripts/generate-weekly-digest.js +393 -0
  46. package/scripts/health-check.js +270 -0
  47. package/scripts/hooks/credential-check.js +101 -0
  48. package/scripts/hooks/evaluate-session.js +81 -0
  49. package/scripts/hooks/pre-compact.js +66 -0
  50. package/scripts/hooks/prompt-analyzer.js +276 -0
  51. package/scripts/hooks/prompt-router.js +422 -0
  52. package/scripts/hooks/quality-gate-enforcer.js +371 -0
  53. package/scripts/hooks/session-end.js +156 -0
  54. package/scripts/hooks/session-start.js +195 -0
  55. package/scripts/hooks/skill-injector.js +333 -0
  56. package/scripts/hooks/suggest-compact.js +58 -0
  57. package/scripts/lib/asset-value.js +609 -0
  58. package/scripts/lib/client-manager.js +300 -0
  59. package/scripts/lib/command-matcher.js +242 -0
  60. package/scripts/lib/cross-session-patterns.js +754 -0
  61. package/scripts/lib/intent-classifier.js +1075 -0
  62. package/scripts/lib/package-manager.js +374 -0
  63. package/scripts/lib/recommendation-engine.js +597 -0
  64. package/scripts/lib/session-memory.js +489 -0
  65. package/scripts/lib/skill-effectiveness.js +486 -0
  66. package/scripts/lib/skill-matcher.js +595 -0
  67. package/scripts/lib/tutorial-metrics.js +242 -0
  68. package/scripts/lib/tutorial-progress.js +209 -0
  69. package/scripts/lib/tutorial-renderer.js +431 -0
  70. package/scripts/lib/utils.js +380 -0
  71. package/scripts/lib/verify-formatter.js +143 -0
  72. package/scripts/lib/workflow-state.js +249 -0
  73. package/scripts/onboard.js +363 -0
  74. package/scripts/quarterly-report.js +692 -0
  75. package/scripts/setup-package-manager.js +204 -0
  76. package/scripts/sync-upstream.js +391 -0
  77. package/scripts/test.js +108 -0
  78. package/scripts/tutorial-runner.js +351 -0
  79. package/scripts/validate-all.js +201 -0
  80. package/scripts/verifiers/agents.js +245 -0
  81. package/scripts/verifiers/config.js +186 -0
  82. package/scripts/verifiers/environment.js +123 -0
  83. package/scripts/verifiers/hooks.js +188 -0
  84. package/scripts/verifiers/index.js +38 -0
  85. package/scripts/verifiers/persistence.js +140 -0
  86. package/scripts/verifiers/plugin.js +215 -0
  87. package/scripts/verifiers/skills.js +209 -0
  88. package/scripts/verify-setup.js +164 -0
  89. package/skills/INDEX.md +157 -0
  90. package/skills/backend-patterns/SKILL.md +586 -0
  91. package/skills/backend-patterns/catalyst-patterns.md +128 -0
  92. package/skills/bigquery-patterns/SKILL.md +27 -0
  93. package/skills/bigquery-patterns/performance-optimization.md +518 -0
  94. package/skills/bigquery-patterns/query-patterns.md +372 -0
  95. package/skills/bigquery-patterns/schema-design.md +78 -0
  96. package/skills/cloudstream-project-template/SKILL.md +20 -0
  97. package/skills/cloudstream-project-template/structure.md +65 -0
  98. package/skills/coding-standards/SKILL.md +524 -0
  99. package/skills/coding-standards/deluge-standards.md +83 -0
  100. package/skills/compliance-patterns/SKILL.md +28 -0
  101. package/skills/compliance-patterns/hipaa/audit-requirements.md +251 -0
  102. package/skills/compliance-patterns/hipaa/baa-process.md +298 -0
  103. package/skills/compliance-patterns/hipaa/data-archival-strategy.md +387 -0
  104. package/skills/compliance-patterns/hipaa/phi-handling.md +52 -0
  105. package/skills/compliance-patterns/pci-dss/saq-a-requirements.md +307 -0
  106. package/skills/compliance-patterns/pci-dss/tokenization-patterns.md +382 -0
  107. package/skills/compliance-patterns/pci-dss/zoho-checkout-patterns.md +56 -0
  108. package/skills/compliance-patterns/soc2/access-controls.md +344 -0
  109. package/skills/compliance-patterns/soc2/audit-logging.md +458 -0
  110. package/skills/compliance-patterns/soc2/change-management.md +403 -0
  111. package/skills/compliance-patterns/soc2/deluge-execution-logging.md +407 -0
  112. package/skills/consultancy-workflows/SKILL.md +19 -0
  113. package/skills/consultancy-workflows/client-isolation.md +21 -0
  114. package/skills/consultancy-workflows/documentation-automation.md +454 -0
  115. package/skills/consultancy-workflows/handoff-procedures.md +257 -0
  116. package/skills/consultancy-workflows/knowledge-capture.md +513 -0
  117. package/skills/consultancy-workflows/time-tracking.md +26 -0
  118. package/skills/continuous-learning/SKILL.md +84 -0
  119. package/skills/continuous-learning/config.json +18 -0
  120. package/skills/continuous-learning/evaluate-session.sh +60 -0
  121. package/skills/continuous-learning-v2/SKILL.md +126 -0
  122. package/skills/continuous-learning-v2/config.json +61 -0
  123. package/skills/frontend-patterns/SKILL.md +635 -0
  124. package/skills/frontend-patterns/zoho-widget-patterns.md +103 -0
  125. package/skills/gcp-data-engineering/SKILL.md +36 -0
  126. package/skills/gcp-data-engineering/bigquery/performance-optimization.md +337 -0
  127. package/skills/gcp-data-engineering/dataflow/error-handling.md +496 -0
  128. package/skills/gcp-data-engineering/dataflow/pipeline-patterns.md +444 -0
  129. package/skills/gcp-data-engineering/dbt/model-organization.md +63 -0
  130. package/skills/gcp-data-engineering/dbt/testing-patterns.md +503 -0
  131. package/skills/gcp-data-engineering/medallion-architecture/bronze-layer.md +60 -0
  132. package/skills/gcp-data-engineering/medallion-architecture/gold-layer.md +311 -0
  133. package/skills/gcp-data-engineering/medallion-architecture/layer-transitions.md +517 -0
  134. package/skills/gcp-data-engineering/medallion-architecture/silver-layer.md +305 -0
  135. package/skills/gcp-data-engineering/zoho-to-gcp/data-extraction.md +543 -0
  136. package/skills/gcp-data-engineering/zoho-to-gcp/real-time-vs-batch.md +337 -0
  137. package/skills/security-review/SKILL.md +498 -0
  138. package/skills/security-review/compliance-checklist.md +53 -0
  139. package/skills/strategic-compact/SKILL.md +67 -0
  140. package/skills/tdd-workflow/SKILL.md +413 -0
  141. package/skills/tdd-workflow/zoho-testing.md +124 -0
  142. package/skills/tutorial/SKILL.md +249 -0
  143. package/skills/tutorial/docs/ACCESSIBILITY.md +169 -0
  144. package/skills/tutorial/lessons/00-philosophy-and-workflow.md +198 -0
  145. package/skills/tutorial/lessons/01-basics.md +81 -0
  146. package/skills/tutorial/lessons/02-training.md +86 -0
  147. package/skills/tutorial/lessons/03-commands.md +109 -0
  148. package/skills/tutorial/lessons/04-workflows.md +115 -0
  149. package/skills/tutorial/lessons/05-compliance.md +116 -0
  150. package/skills/tutorial/lessons/06-zoho.md +121 -0
  151. package/skills/tutorial/lessons/07-hooks-system.md +277 -0
  152. package/skills/tutorial/lessons/08-mcp-servers.md +316 -0
  153. package/skills/tutorial/lessons/09-client-management.md +215 -0
  154. package/skills/tutorial/lessons/10-testing-e2e.md +260 -0
  155. package/skills/tutorial/lessons/11-skills-deep-dive.md +272 -0
  156. package/skills/tutorial/lessons/12-rules-system.md +326 -0
  157. package/skills/tutorial/lessons/13-golden-standard-graduation.md +213 -0
  158. package/skills/tutorial/lessons/14-fork-setup-and-sync.md +312 -0
  159. package/skills/tutorial/lessons/15-living-examples-system.md +221 -0
  160. package/skills/tutorial/tracks/accelerated/README.md +134 -0
  161. package/skills/tutorial/tracks/accelerated/assessment/checkpoint-1.md +161 -0
  162. package/skills/tutorial/tracks/accelerated/assessment/checkpoint-2.md +175 -0
  163. package/skills/tutorial/tracks/accelerated/day-1-core-concepts.md +234 -0
  164. package/skills/tutorial/tracks/accelerated/day-2-essential-commands.md +270 -0
  165. package/skills/tutorial/tracks/accelerated/day-3-workflow-mastery.md +305 -0
  166. package/skills/tutorial/tracks/accelerated/day-4-compliance-zoho.md +304 -0
  167. package/skills/tutorial/tracks/accelerated/day-5-hooks-skills.md +344 -0
  168. package/skills/tutorial/tracks/accelerated/day-6-client-testing.md +386 -0
  169. package/skills/tutorial/tracks/accelerated/day-7-graduation.md +369 -0
  170. package/skills/zoho-patterns/CHANGELOG.md +108 -0
  171. package/skills/zoho-patterns/SKILL.md +446 -0
  172. package/skills/zoho-patterns/analytics/dashboard-patterns.md +352 -0
  173. package/skills/zoho-patterns/analytics/zoho-to-bigquery-pipeline.md +427 -0
  174. package/skills/zoho-patterns/catalyst/appsail-deployment.md +349 -0
  175. package/skills/zoho-patterns/catalyst/context-close-patterns.md +354 -0
  176. package/skills/zoho-patterns/catalyst/cron-batch-processing.md +374 -0
  177. package/skills/zoho-patterns/catalyst/function-patterns.md +439 -0
  178. package/skills/zoho-patterns/creator/form-design.md +304 -0
  179. package/skills/zoho-patterns/creator/publish-api-patterns.md +313 -0
  180. package/skills/zoho-patterns/creator/widget-integration.md +306 -0
  181. package/skills/zoho-patterns/creator/workflow-automation.md +253 -0
  182. package/skills/zoho-patterns/deluge/api-patterns.md +468 -0
  183. package/skills/zoho-patterns/deluge/batch-processing.md +403 -0
  184. package/skills/zoho-patterns/deluge/cross-app-integration.md +356 -0
  185. package/skills/zoho-patterns/deluge/error-handling.md +423 -0
  186. package/skills/zoho-patterns/deluge/syntax-reference.md +65 -0
  187. package/skills/zoho-patterns/integration/cors-proxy-architecture.md +426 -0
  188. package/skills/zoho-patterns/integration/crm-books-native-sync.md +277 -0
  189. package/skills/zoho-patterns/integration/oauth-token-management.md +461 -0
  190. package/skills/zoho-patterns/integration/zoho-flow-patterns.md +334 -0
@@ -0,0 +1,374 @@
1
+ # Catalyst Cron Batch Processing
2
+
3
+ ## Critical Constraints
4
+
5
+ > **TIMEOUT:** Cron functions have a 15-minute execution limit. Plan batch operations to complete within this window.
6
+
7
+ > **MANDATORY:** Every Cron function MUST call `context.close()` in ALL exit paths. See context-close-patterns.md for details.
8
+
9
+ ## Cron Function Structure
10
+
11
+ ```javascript
12
+ const catalyst = require("zcatalyst-sdk-node");
13
+
14
+ module.exports = async (cronDetails, context) => {
15
+ const app = catalyst.initialize(context);
16
+ const startTime = Date.now();
17
+ const MAX_RUNTIME_MS = 14 * 60 * 1000; // 14 minutes (1 min buffer)
18
+
19
+ try {
20
+ const results = await processInBatches(app, startTime, MAX_RUNTIME_MS);
21
+ console.log("Batch complete:", JSON.stringify(results));
22
+ context.close();
23
+ } catch (error) {
24
+ console.error("Cron failed:", error.message);
25
+ await notifyAdminOnFailure(app, error);
26
+ context.close();
27
+ }
28
+ };
29
+ ```
30
+
31
+ ## Chunking Patterns for Large Datasets
32
+
33
+ ### Pattern 1: Offset-Based Pagination
34
+
35
+ ```javascript
36
+ async function processInBatches(app, startTime, maxRuntime) {
37
+ const BATCH_SIZE = 100;
38
+ let offset = await getLastOffset(app); // Resume from last run
39
+ let processedCount = 0;
40
+ let errors = [];
41
+
42
+ while (true) {
43
+ // Safety: Check remaining time
44
+ if (Date.now() - startTime > maxRuntime) {
45
+ console.warn("Approaching timeout, saving progress at offset:", offset);
46
+ await saveProgress(app, offset, processedCount, errors);
47
+ break;
48
+ }
49
+
50
+ // Fetch next batch
51
+ const records = await fetchBatch(app, offset, BATCH_SIZE);
52
+
53
+ if (records.length === 0) {
54
+ // All records processed - reset offset for next run
55
+ await saveProgress(app, 0, processedCount, errors);
56
+ break;
57
+ }
58
+
59
+ // Process each record
60
+ for (const record of records) {
61
+ try {
62
+ await processRecord(app, record);
63
+ processedCount++;
64
+ } catch (error) {
65
+ errors.push({
66
+ recordId: record.ROWID,
67
+ error: error.message
68
+ });
69
+ }
70
+ }
71
+
72
+ offset += BATCH_SIZE;
73
+ }
74
+
75
+ return { processedCount, errorCount: errors.length, errors };
76
+ }
77
+ ```
78
+
79
+ ### Pattern 2: Cursor-Based (For APIs with Cursor Pagination)
80
+
81
+ ```javascript
82
+ async function processWithCursor(app, startTime, maxRuntime) {
83
+ let cursor = await getSavedCursor(app); // null on first run
84
+ let processedCount = 0;
85
+
86
+ while (true) {
87
+ if (Date.now() - startTime > maxRuntime) {
88
+ await saveCursor(app, cursor);
89
+ break;
90
+ }
91
+
92
+ const response = await fetchWithCursor(cursor);
93
+ const { data, next_cursor, has_more } = response;
94
+
95
+ for (const item of data) {
96
+ await processItem(app, item);
97
+ processedCount++;
98
+ }
99
+
100
+ if (!has_more) {
101
+ await saveCursor(app, null); // Reset for next full run
102
+ break;
103
+ }
104
+
105
+ cursor = next_cursor;
106
+ }
107
+
108
+ return { processedCount, lastCursor: cursor };
109
+ }
110
+ ```
111
+
112
+ ### Pattern 3: Date-Window Chunking
113
+
114
+ ```javascript
115
+ async function processDateWindow(app) {
116
+ // Process records modified since last successful run
117
+ const lastRunDate = await getLastSuccessfulRun(app);
118
+ const now = new Date();
119
+
120
+ const criteria = `Modified_Date >= '${lastRunDate.toISOString()}' AND Modified_Date < '${now.toISOString()}'`;
121
+
122
+ const records = await fetchAllPages(app, criteria);
123
+
124
+ let processed = 0;
125
+ for (const record of records) {
126
+ await syncToExternalSystem(record);
127
+ processed++;
128
+ }
129
+
130
+ // Only update last-run timestamp on full success
131
+ await setLastSuccessfulRun(app, now);
132
+
133
+ return { processed, window: { from: lastRunDate, to: now } };
134
+ }
135
+ ```
136
+
137
+ ## State Management Between Runs
138
+
139
+ Use Catalyst Data Store (ZCQL) to persist state:
140
+
141
+ ```javascript
142
+ // Save processing state
143
+ async function saveProgress(app, offset, processedCount, errors) {
144
+ const dataStore = app.datastore();
145
+ const table = dataStore.table("BatchState");
146
+
147
+ const existingRows = await app.zcql().executeZCQLQuery(
148
+ "SELECT ROWID FROM BatchState WHERE job_name = 'sync_invoices'"
149
+ );
150
+
151
+ const stateData = {
152
+ job_name: "sync_invoices",
153
+ last_offset: offset,
154
+ last_run: new Date().toISOString(),
155
+ processed_count: processedCount,
156
+ error_count: errors.length,
157
+ errors_json: JSON.stringify(errors.slice(0, 50)), // Cap stored errors
158
+ status: errors.length > 0 ? "partial" : "complete"
159
+ };
160
+
161
+ if (existingRows.length > 0) {
162
+ stateData.ROWID = existingRows[0].BatchState.ROWID;
163
+ await table.updateRow(stateData);
164
+ } else {
165
+ await table.insertRow(stateData);
166
+ }
167
+ }
168
+
169
+ // Retrieve last state
170
+ async function getLastOffset(app) {
171
+ const result = await app.zcql().executeZCQLQuery(
172
+ "SELECT last_offset FROM BatchState WHERE job_name = 'sync_invoices'"
173
+ );
174
+ return result.length > 0 ? parseInt(result[0].BatchState.last_offset) : 0;
175
+ }
176
+ ```
177
+
178
+ ## Error Notification Setup
179
+
180
+ ```javascript
181
+ async function notifyAdminOnFailure(app, error, jobName = "batch_job") {
182
+ // Option 1: Email via Catalyst Mail
183
+ const mailService = app.email();
184
+ await mailService.sendMail({
185
+ from_email: "noreply@your-catalyst-project.com",
186
+ to_email: ["admin@company.com"],
187
+ subject: `[ALERT] Cron Job Failed: ${jobName}`,
188
+ content: `
189
+ <h3>Batch Processing Failure</h3>
190
+ <p><strong>Job:</strong> ${jobName}</p>
191
+ <p><strong>Error:</strong> ${error.message}</p>
192
+ <p><strong>Stack:</strong> <pre>${error.stack}</pre></p>
193
+ <p><strong>Time:</strong> ${new Date().toISOString()}</p>
194
+ `,
195
+ content_type: "html"
196
+ });
197
+
198
+ // Option 2: Zoho Cliq notification
199
+ const axios = require("axios");
200
+ await axios.post(process.env.CLIQ_WEBHOOK_URL, {
201
+ text: `*Cron Job Failed:* ${jobName}\nError: ${error.message}\nTime: ${new Date().toISOString()}`
202
+ });
203
+ }
204
+ ```
205
+
206
+ ## Retry Logic
207
+
208
+ ```javascript
209
+ async function processWithRetry(fn, record, maxRetries = 3) {
210
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
211
+ try {
212
+ return await fn(record);
213
+ } catch (error) {
214
+ if (attempt === maxRetries) {
215
+ throw error; // Final attempt failed
216
+ }
217
+
218
+ // Exponential backoff
219
+ const delay = Math.pow(2, attempt) * 500;
220
+ console.warn(`Attempt ${attempt} failed for record ${record.ROWID}, retrying in ${delay}ms`);
221
+ await new Promise(resolve => setTimeout(resolve, delay));
222
+
223
+ // Don't retry non-transient errors
224
+ if (error.status === 400 || error.status === 404) {
225
+ throw error; // Bad data, retry won't help
226
+ }
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ ## Avoiding Duplicate Processing
233
+
234
+ ### Idempotency Pattern
235
+
236
+ ```javascript
237
+ async function processRecordIdempotent(app, record) {
238
+ const idempotencyKey = `${record.ROWID}_${record.Modified_Date}`;
239
+
240
+ // Check if already processed
241
+ const existing = await app.zcql().executeZCQLQuery(
242
+ `SELECT ROWID FROM ProcessedLog WHERE idempotency_key = '${idempotencyKey}'`
243
+ );
244
+
245
+ if (existing.length > 0) {
246
+ console.log(`Skipping already-processed record: ${idempotencyKey}`);
247
+ return { skipped: true };
248
+ }
249
+
250
+ // Process the record
251
+ const result = await doActualProcessing(record);
252
+
253
+ // Mark as processed
254
+ await app.datastore().table("ProcessedLog").insertRow({
255
+ idempotency_key: idempotencyKey,
256
+ record_id: record.ROWID,
257
+ processed_at: new Date().toISOString(),
258
+ result: JSON.stringify(result)
259
+ });
260
+
261
+ return { skipped: false, result };
262
+ }
263
+ ```
264
+
265
+ ### Locking Pattern (Prevent Concurrent Runs)
266
+
267
+ ```javascript
268
+ async function acquireLock(app, jobName, ttlMinutes = 16) {
269
+ const lockExpiry = new Date(Date.now() + ttlMinutes * 60 * 1000).toISOString();
270
+
271
+ // Check for existing active lock
272
+ const existing = await app.zcql().executeZCQLQuery(
273
+ `SELECT ROWID, lock_expiry FROM CronLocks WHERE job_name = '${jobName}'`
274
+ );
275
+
276
+ if (existing.length > 0) {
277
+ const expiry = new Date(existing[0].CronLocks.lock_expiry);
278
+ if (expiry > new Date()) {
279
+ console.warn("Job already running, skipping this execution");
280
+ return false; // Lock held by another run
281
+ }
282
+ // Expired lock - update it
283
+ await app.datastore().table("CronLocks").updateRow({
284
+ ROWID: existing[0].CronLocks.ROWID,
285
+ lock_expiry: lockExpiry,
286
+ locked_at: new Date().toISOString()
287
+ });
288
+ } else {
289
+ await app.datastore().table("CronLocks").insertRow({
290
+ job_name: jobName,
291
+ lock_expiry: lockExpiry,
292
+ locked_at: new Date().toISOString()
293
+ });
294
+ }
295
+
296
+ return true; // Lock acquired
297
+ }
298
+
299
+ async function releaseLock(app, jobName) {
300
+ const existing = await app.zcql().executeZCQLQuery(
301
+ `SELECT ROWID FROM CronLocks WHERE job_name = '${jobName}'`
302
+ );
303
+ if (existing.length > 0) {
304
+ await app.datastore().table("CronLocks").deleteRow(existing[0].CronLocks.ROWID);
305
+ }
306
+ }
307
+ ```
308
+
309
+ ## Complete Cron Function Template
310
+
311
+ ```javascript
312
+ const catalyst = require("zcatalyst-sdk-node");
313
+
314
+ module.exports = async (cronDetails, context) => {
315
+ const app = catalyst.initialize(context);
316
+ const JOB_NAME = "invoice_sync";
317
+ const startTime = Date.now();
318
+ const MAX_RUNTIME = 14 * 60 * 1000;
319
+
320
+ try {
321
+ // Prevent concurrent execution
322
+ const lockAcquired = await acquireLock(app, JOB_NAME);
323
+ if (!lockAcquired) {
324
+ console.log("Another instance running, exiting");
325
+ context.close();
326
+ return;
327
+ }
328
+
329
+ // Main processing
330
+ const results = await processInBatches(app, startTime, MAX_RUNTIME);
331
+
332
+ // Report results
333
+ console.log(`Completed: ${results.processedCount} processed, ${results.errorCount} errors`);
334
+
335
+ if (results.errorCount > 0) {
336
+ await notifyAdminOnFailure(app, new Error(
337
+ `${results.errorCount} records failed: ${JSON.stringify(results.errors.slice(0, 5))}`
338
+ ), JOB_NAME);
339
+ }
340
+
341
+ await releaseLock(app, JOB_NAME);
342
+ context.close();
343
+
344
+ } catch (error) {
345
+ console.error("Fatal error:", error);
346
+ await notifyAdminOnFailure(app, error, JOB_NAME);
347
+ await releaseLock(app, JOB_NAME);
348
+ context.close();
349
+ }
350
+ };
351
+ ```
352
+
353
+ ## Cron Schedule Configuration
354
+
355
+ Set in `catalyst-config.json`:
356
+
357
+ ```json
358
+ {
359
+ "cron": {
360
+ "function_name": "invoice_sync_cron",
361
+ "cron_expression": "0 */2 * * *",
362
+ "description": "Sync invoices every 2 hours"
363
+ }
364
+ }
365
+ ```
366
+
367
+ | Expression | Schedule |
368
+ |-----------|----------|
369
+ | `0 * * * *` | Every hour |
370
+ | `*/15 * * * *` | Every 15 minutes |
371
+ | `0 */2 * * *` | Every 2 hours |
372
+ | `0 6 * * *` | Daily at 6 AM |
373
+ | `0 0 * * 0` | Weekly on Sunday midnight |
374
+ | `0 0 1 * *` | First of every month |