rentabots-sdk 1.7.27 → 1.7.31

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.
package/dist/index.js CHANGED
@@ -71,7 +71,7 @@ exports.MessageSchema = zod_1.z.object({
71
71
  })
72
72
  });
73
73
  // --- CORE SDK ENGINE ---
74
- let SDK_VERSION = '1.7.27'; // fallback when package.json is unavailable
74
+ let SDK_VERSION = '1.7.31'; // fallback when package.json is unavailable
75
75
  try {
76
76
  const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
77
77
  SDK_VERSION = pkg.version;
package/init.js CHANGED
@@ -148,6 +148,13 @@ async function main() {
148
148
  queen.workers.delete(job.id);
149
149
  try { fs.unlinkSync(jobDataPath); } catch {}
150
150
  await pushLog('WARN', 'Worker exited for mission ' + job.id + ' (code=' + code + ', signal=' + (signal || 'none') + ')');
151
+
152
+ if (code === 42 && queen.activeMissions.has(job.id) && !queen.workers.has(job.id)) {
153
+ await pushLog('INFO', 'Worker requested controlled restart for ' + job.id + '; respawning...');
154
+ setTimeout(() => {
155
+ spawnMissionWorker(job, 'controlled-restart').catch(() => {});
156
+ }, 1200);
157
+ }
151
158
  });
152
159
 
153
160
  worker.on('error', async (err) => {
@@ -245,7 +252,8 @@ const job = JSON.parse(fs.readFileSync(jobDataPath, 'utf8'));
245
252
 
246
253
  function inferContract(job) {
247
254
  const text = ((job.title || '') + ' ' + (job.description || '')).toLowerCase();
248
- const adapter = text.includes('api') ? 'api' : text.includes('csv') || text.includes('dataset') || text.includes('etl') ? 'data' : text.includes('landing page') || text.includes('frontend') || text.includes('ui') || text.includes('web') ? 'web' : text.includes('docs') || text.includes('documentation') ? 'docs' : 'script';
255
+ const hasApiWord = /(^|[^a-z0-9])api([^a-z0-9]|$)/i.test(text) || /endpoint|rest\b|graphql\b/.test(text);
256
+ const adapter = hasApiWord ? 'api' : text.includes('csv') || text.includes('dataset') || text.includes('etl') ? 'data' : text.includes('landing page') || text.includes('frontend') || text.includes('ui') || text.includes('web') ? 'web' : text.includes('docs') || text.includes('documentation') ? 'docs' : 'script';
249
257
  const contract = {
250
258
  adapter,
251
259
  taskType: adapter,
@@ -302,6 +310,7 @@ function generateAdapterFallbackFiles(workDir, job, contract) {
302
310
  }
303
311
 
304
312
  async function main() {
313
+ const RELAXED_MODE = (process.env.RENTABOTS_RELAXED_MODE || '1') !== '0';
305
314
  const agent = new Agent({ persistState: false, debug: true });
306
315
  await agent.connect();
307
316
  await agent.sendMessage(job.id, "👷 Worker Unit [PID " + process.pid + "] deployed. Analyzing requirements...");
@@ -456,7 +465,13 @@ async function main() {
456
465
  issues.push('whitespace cleanup requirement not reflected in output');
457
466
  }
458
467
  for (const d of contract.deliverables || []) {
459
- if (!hasFile((f) => f.toLowerCase() === String(d).toLowerCase())) {
468
+ const dl = String(d).toLowerCase();
469
+ let ok = false;
470
+ if (dl === 'readme.md') ok = hasFile((f) => /^readme(\..+)?\.md$/i.test(f) || /^readme\.md$/i.test(f));
471
+ else if (dl === 'solution.py') ok = hasFile((f) => f.toLowerCase().endsWith('.py'));
472
+ else if (dl === 'openapi-or-endpoint-docs.md') ok = hasFile((f) => /openapi|endpoint|api.*doc/i.test(f));
473
+ else ok = hasFile((f) => f.toLowerCase() === dl);
474
+ if (!ok) {
460
475
  issues.push('missing contract deliverable: ' + d);
461
476
  }
462
477
  }
@@ -491,49 +506,54 @@ async function main() {
491
506
  if (!hasSections) issues.push('docs adapter: missing core documentation sections');
492
507
  if (!hasExamples) issues.push('docs adapter: missing usage examples');
493
508
  } else {
494
- const hasRunnable = /(main\(|if __name__ ==|module\.exports|export default|function\s+\w+)/i.test(combinedText);
509
+ const pyExists = hasFile((f) => f.toLowerCase().endsWith('.py'));
510
+ const hasRunnable = pyExists || /(main\(|if __name__ ==|module\.exports|export default|function\s+\w+|class\s+\w+)/i.test(combinedText);
495
511
  if (!hasRunnable) issues.push('script adapter: missing runnable implementation entry');
496
512
  }
497
513
 
498
514
  if (issues.length > 0) {
499
515
  await agent.setProgress(job.id, 70);
500
- await agent.sendMessage(job.id, '⚠️ Contract QA failed: ' + issues.slice(0, 4).join('; ') + '. Running targeted OpenClaw repair pass now.');
516
+ if (RELAXED_MODE) {
517
+ await agent.sendMessage(job.id, '⚠️ Soft QA warnings: ' + issues.slice(0, 3).join('; ') + '. Continuing with OpenClaw output in relaxed mode.');
518
+ } else {
519
+ await agent.sendMessage(job.id, '⚠️ Contract QA failed: ' + issues.slice(0, 4).join('; ') + '. Running targeted OpenClaw repair pass now.');
501
520
 
502
- const repairPath = path.join(workDir, '.repair_count');
503
- let repairCount = 0;
504
- try { repairCount = Number(fs.readFileSync(repairPath, 'utf8') || '0'); } catch (_) {}
505
- repairCount += 1;
506
- fs.writeFileSync(repairPath, String(repairCount));
521
+ const repairPath = path.join(workDir, '.repair_count');
522
+ let repairCount = 0;
523
+ try { repairCount = Number(fs.readFileSync(repairPath, 'utf8') || '0'); } catch (_) {}
524
+ repairCount += 1;
525
+ fs.writeFileSync(repairPath, String(repairCount));
507
526
 
508
- if (repairCount > 6) {
509
- await agent.sendMessage(job.id, '🚨 OpenClaw repair limit reached. Please send one concrete clarification so I can continue with a precise fix.');
510
- return;
511
- }
527
+ if (repairCount > 6) {
528
+ await agent.sendMessage(job.id, '🚨 OpenClaw repair limit reached. Please send one concrete clarification so I can continue with a precise fix.');
529
+ return;
530
+ }
512
531
 
513
- const repairPrompt = [
514
- 'Repair the existing workspace to satisfy missing deterministic gates.',
515
- 'Do not explain. Edit files now and finish.',
516
- 'MISSING GATES:',
517
- ...issues.map(i => '- ' + i),
518
- '',
519
- 'MISSION TITLE: ' + job.title,
520
- 'MISSION DESCRIPTION: ' + job.description,
521
- ].join('\n');
532
+ const repairPrompt = [
533
+ 'Repair the existing workspace to satisfy missing deterministic gates.',
534
+ 'Do not explain. Edit files now and finish.',
535
+ 'MISSING GATES:',
536
+ ...issues.map(i => '- ' + i),
537
+ '',
538
+ 'MISSION TITLE: ' + job.title,
539
+ 'MISSION DESCRIPTION: ' + job.description,
540
+ ].join('\n');
541
+
542
+ const repairArg = JSON.stringify(repairPrompt);
543
+ const repairCmds = [
544
+ 'openclaw sessions spawn --task ' + repairArg,
545
+ 'openclaw sessions_spawn --task ' + repairArg,
546
+ ];
547
+ for (const rcmd of repairCmds) {
548
+ const r = await agent.execute(job.id, rcmd, { timeout: 900000, shell: true });
549
+ const out = (r.output || '').toLowerCase();
550
+ const pseudo = out.includes('usage: openclaw') || out.includes('unknown command') || out.includes('pass --to');
551
+ if (r.exitCode === 0 && !pseudo) break;
552
+ }
522
553
 
523
- const repairArg = JSON.stringify(repairPrompt);
524
- const repairCmds = [
525
- 'openclaw sessions spawn --task ' + repairArg,
526
- 'openclaw sessions_spawn --task ' + repairArg,
527
- ];
528
- for (const rcmd of repairCmds) {
529
- const r = await agent.execute(job.id, rcmd, { timeout: 900000, shell: true });
530
- const out = (r.output || '').toLowerCase();
531
- const pseudo = out.includes('usage: openclaw') || out.includes('unknown command') || out.includes('pass --to');
532
- if (r.exitCode === 0 && !pseudo) break;
554
+ await agent.sendMessage(job.id, '🔁 OpenClaw repair pass complete. Re-queueing worker validation pass now.');
555
+ process.exit(42);
533
556
  }
534
-
535
- await agent.sendMessage(job.id, '🔁 OpenClaw repair pass complete. Re-validating now.');
536
- return main();
537
557
  }
538
558
 
539
559
  if (fallbackGenerated) {
@@ -556,8 +576,12 @@ async function main() {
556
576
  });
557
577
  if (!changed) {
558
578
  await agent.setProgress(job.id, 88);
559
- await agent.sendMessage(job.id, '⚠️ Final gate blocked: no non-template file changed after your latest clarification. Continuing implementation.');
560
- return;
579
+ if (RELAXED_MODE) {
580
+ await agent.sendMessage(job.id, '⚠️ Soft warning: no non-template delta after clarification, but continuing in relaxed mode.');
581
+ } else {
582
+ await agent.sendMessage(job.id, '⚠️ Final gate blocked: no non-template file changed after your latest clarification. Continuing implementation.');
583
+ return;
584
+ }
561
585
  }
562
586
  }
563
587
  } catch (_) {}
@@ -592,21 +616,25 @@ async function main() {
592
616
 
593
617
  if (!qaPassed) {
594
618
  const why = (qaOutput || 'No QA output').slice(-300);
595
- const qaStatePath = path.join(workDir, '.qa_fail_count');
596
- let qaFails = 0;
597
- try { qaFails = Number(fs.readFileSync(qaStatePath, 'utf8') || '0'); } catch (_) {}
598
- qaFails += 1;
599
- fs.writeFileSync(qaStatePath, String(qaFails));
600
-
601
619
  await agent.setProgress(job.id, 80);
602
- if (qaFails >= 5) {
603
- await agent.sendMessage(job.id, '🚨 Escalation: repeated QA failures (' + qaFails + '). Pausing finalization. Please provide concrete acceptance criteria/examples so I can resolve this precisely.');
604
- } else if (qaFails >= 3) {
605
- await agent.sendMessage(job.id, '⚠️ Second QA gate failed (attempt ' + qaFails + '). I am revising strategy. Reason: ' + why);
620
+ if (RELAXED_MODE) {
621
+ await agent.sendMessage(job.id, '⚠️ QA warning (relaxed mode): ' + why + '. Proceeding with current output.');
606
622
  } else {
607
- await agent.sendMessage(job.id, '⚠️ Second QA gate failed. I will revise before marking done. Reason: ' + why);
623
+ const qaStatePath = path.join(workDir, '.qa_fail_count');
624
+ let qaFails = 0;
625
+ try { qaFails = Number(fs.readFileSync(qaStatePath, 'utf8') || '0'); } catch (_) {}
626
+ qaFails += 1;
627
+ fs.writeFileSync(qaStatePath, String(qaFails));
628
+
629
+ if (qaFails >= 5) {
630
+ await agent.sendMessage(job.id, '🚨 Escalation: repeated QA failures (' + qaFails + '). Pausing finalization. Please provide concrete acceptance criteria/examples so I can resolve this precisely.');
631
+ } else if (qaFails >= 3) {
632
+ await agent.sendMessage(job.id, '⚠️ Second QA gate failed (attempt ' + qaFails + '). I am revising strategy. Reason: ' + why);
633
+ } else {
634
+ await agent.sendMessage(job.id, '⚠️ Second QA gate failed. I will revise before marking done. Reason: ' + why);
635
+ }
636
+ return;
608
637
  }
609
- return;
610
638
  }
611
639
 
612
640
  try { fs.unlinkSync(path.join(workDir, '.qa_fail_count')); } catch (_) {}
@@ -657,9 +685,9 @@ async function main() {
657
685
 
658
686
  if (fails <= 5) {
659
687
  await agent.setProgress(job.id, 25);
660
- await agent.sendMessage(job.id, '⚠️ OpenClaw run failed (attempt ' + fails + '). Auto-retrying with OpenClaw only...');
688
+ await agent.sendMessage(job.id, '⚠️ OpenClaw run failed (attempt ' + fails + '). Re-queueing OpenClaw-only retry...');
661
689
  await new Promise(r => setTimeout(r, 5000));
662
- return main();
690
+ process.exit(42);
663
691
  }
664
692
 
665
693
  // Fail-safe report only after OpenClaw retry budget exhausted
package/init_templates.js CHANGED
@@ -117,6 +117,13 @@ async function main() {
117
117
  queen.workers.delete(job.id);
118
118
  try { fs.unlinkSync(jobDataPath); } catch {}
119
119
  await pushLog('WARN', 'Worker exited for mission ' + job.id + ' (code=' + code + ', signal=' + (signal || 'none') + ')');
120
+
121
+ if (code === 42 && queen.activeMissions.has(job.id) && !queen.workers.has(job.id)) {
122
+ await pushLog('INFO', 'Worker requested controlled restart for ' + job.id + '; respawning...');
123
+ setTimeout(() => {
124
+ spawnMissionWorker(job, 'controlled-restart').catch(() => {});
125
+ }, 1200);
126
+ }
120
127
  });
121
128
 
122
129
  worker.on('error', async (err) => {
@@ -221,7 +228,8 @@ const job = JSON.parse(fs.readFileSync(jobDataPath, 'utf8'));
221
228
 
222
229
  function inferContract(job) {
223
230
  const text = ((job.title || '') + ' ' + (job.description || '')).toLowerCase();
224
- const adapter = text.includes('api') ? 'api' : text.includes('csv') || text.includes('dataset') || text.includes('etl') ? 'data' : text.includes('landing page') || text.includes('frontend') || text.includes('ui') || text.includes('web') ? 'web' : text.includes('docs') || text.includes('documentation') ? 'docs' : 'script';
231
+ const hasApiWord = /(^|[^a-z0-9])api([^a-z0-9]|$)/i.test(text) || /endpoint|rest\b|graphql\b/.test(text);
232
+ const adapter = hasApiWord ? 'api' : text.includes('csv') || text.includes('dataset') || text.includes('etl') ? 'data' : text.includes('landing page') || text.includes('frontend') || text.includes('ui') || text.includes('web') ? 'web' : text.includes('docs') || text.includes('documentation') ? 'docs' : 'script';
225
233
  const contract = {
226
234
  adapter,
227
235
  taskType: adapter,
@@ -279,6 +287,7 @@ function generateAdapterFallbackFiles(workDir, job, contract) {
279
287
  }
280
288
 
281
289
  async function main() {
290
+ const RELAXED_MODE = (process.env.RENTABOTS_RELAXED_MODE || '1') !== '0';
282
291
  const agent = new Agent({
283
292
  persistState: false,
284
293
  debug: true
@@ -441,7 +450,13 @@ async function main() {
441
450
  issues.push('whitespace cleanup requirement not reflected in output');
442
451
  }
443
452
  for (const d of contract.deliverables || []) {
444
- if (!hasFile((f) => f.toLowerCase() === String(d).toLowerCase())) {
453
+ const dl = String(d).toLowerCase();
454
+ let ok = false;
455
+ if (dl === 'readme.md') ok = hasFile((f) => /^readme(\..+)?\.md$/i.test(f) || /^readme\.md$/i.test(f));
456
+ else if (dl === 'solution.py') ok = hasFile((f) => f.toLowerCase().endsWith('.py'));
457
+ else if (dl === 'openapi-or-endpoint-docs.md') ok = hasFile((f) => /openapi|endpoint|api.*doc/i.test(f));
458
+ else ok = hasFile((f) => f.toLowerCase() === dl);
459
+ if (!ok) {
445
460
  issues.push('missing contract deliverable: ' + d);
446
461
  }
447
462
  }
@@ -476,49 +491,54 @@ async function main() {
476
491
  if (!hasSections) issues.push('docs adapter: missing core documentation sections');
477
492
  if (!hasExamples) issues.push('docs adapter: missing usage examples');
478
493
  } else {
479
- const hasRunnable = /(main\(|if __name__ ==|module\.exports|export default|function\s+\w+)/i.test(combinedText);
494
+ const pyExists = hasFile((f) => f.toLowerCase().endsWith('.py'));
495
+ const hasRunnable = pyExists || /(main\(|if __name__ ==|module\.exports|export default|function\s+\w+|class\s+\w+)/i.test(combinedText);
480
496
  if (!hasRunnable) issues.push('script adapter: missing runnable implementation entry');
481
497
  }
482
498
 
483
499
  if (issues.length > 0) {
484
500
  await agent.setProgress(job.id, 70);
485
- await agent.sendMessage(job.id, '⚠️ Contract QA failed: ' + issues.slice(0, 4).join('; ') + '. Running targeted OpenClaw repair pass now.');
501
+ if (RELAXED_MODE) {
502
+ await agent.sendMessage(job.id, '⚠️ Soft QA warnings: ' + issues.slice(0, 3).join('; ') + '. Continuing with OpenClaw output in relaxed mode.');
503
+ } else {
504
+ await agent.sendMessage(job.id, '⚠️ Contract QA failed: ' + issues.slice(0, 4).join('; ') + '. Running targeted OpenClaw repair pass now.');
486
505
 
487
- const repairPath = path.join(workDir, '.repair_count');
488
- let repairCount = 0;
489
- try { repairCount = Number(fs.readFileSync(repairPath, 'utf8') || '0'); } catch (_) {}
490
- repairCount += 1;
491
- fs.writeFileSync(repairPath, String(repairCount));
506
+ const repairPath = path.join(workDir, '.repair_count');
507
+ let repairCount = 0;
508
+ try { repairCount = Number(fs.readFileSync(repairPath, 'utf8') || '0'); } catch (_) {}
509
+ repairCount += 1;
510
+ fs.writeFileSync(repairPath, String(repairCount));
492
511
 
493
- if (repairCount > 6) {
494
- await agent.sendMessage(job.id, '🚨 OpenClaw repair limit reached. Please send one concrete clarification so I can continue with a precise fix.');
495
- return;
496
- }
512
+ if (repairCount > 6) {
513
+ await agent.sendMessage(job.id, '🚨 OpenClaw repair limit reached. Please send one concrete clarification so I can continue with a precise fix.');
514
+ return;
515
+ }
497
516
 
498
- const repairPrompt = [
499
- 'Repair the existing workspace to satisfy missing deterministic gates.',
500
- 'Do not explain. Edit files now and finish.',
501
- 'MISSING GATES:',
502
- ...issues.map(i => '- ' + i),
503
- '',
504
- 'MISSION TITLE: ' + job.title,
505
- 'MISSION DESCRIPTION: ' + job.description,
506
- ].join('\n');
517
+ const repairPrompt = [
518
+ 'Repair the existing workspace to satisfy missing deterministic gates.',
519
+ 'Do not explain. Edit files now and finish.',
520
+ 'MISSING GATES:',
521
+ ...issues.map(i => '- ' + i),
522
+ '',
523
+ 'MISSION TITLE: ' + job.title,
524
+ 'MISSION DESCRIPTION: ' + job.description,
525
+ ].join('\n');
526
+
527
+ const repairArg = JSON.stringify(repairPrompt);
528
+ const repairCmds = [
529
+ 'openclaw sessions spawn --task ' + repairArg,
530
+ 'openclaw sessions_spawn --task ' + repairArg,
531
+ ];
532
+ for (const rcmd of repairCmds) {
533
+ const r = await agent.execute(job.id, rcmd, { timeout: 900000, shell: true });
534
+ const out = (r.output || '').toLowerCase();
535
+ const pseudo = out.includes('usage: openclaw') || out.includes('unknown command') || out.includes('pass --to');
536
+ if (r.exitCode === 0 && !pseudo) break;
537
+ }
507
538
 
508
- const repairArg = JSON.stringify(repairPrompt);
509
- const repairCmds = [
510
- 'openclaw sessions spawn --task ' + repairArg,
511
- 'openclaw sessions_spawn --task ' + repairArg,
512
- ];
513
- for (const rcmd of repairCmds) {
514
- const r = await agent.execute(job.id, rcmd, { timeout: 900000, shell: true });
515
- const out = (r.output || '').toLowerCase();
516
- const pseudo = out.includes('usage: openclaw') || out.includes('unknown command') || out.includes('pass --to');
517
- if (r.exitCode === 0 && !pseudo) break;
539
+ await agent.sendMessage(job.id, '🔁 OpenClaw repair pass complete. Re-queueing worker validation pass now.');
540
+ process.exit(42);
518
541
  }
519
-
520
- await agent.sendMessage(job.id, '🔁 OpenClaw repair pass complete. Re-validating now.');
521
- return main();
522
542
  }
523
543
 
524
544
  if (fallbackGenerated) {
@@ -541,8 +561,12 @@ async function main() {
541
561
  });
542
562
  if (!changed) {
543
563
  await agent.setProgress(job.id, 88);
544
- await agent.sendMessage(job.id, '⚠️ Final gate blocked: no non-template file changed after your latest clarification. Continuing implementation.');
545
- return;
564
+ if (RELAXED_MODE) {
565
+ await agent.sendMessage(job.id, '⚠️ Soft warning: no non-template delta after clarification, but continuing in relaxed mode.');
566
+ } else {
567
+ await agent.sendMessage(job.id, '⚠️ Final gate blocked: no non-template file changed after your latest clarification. Continuing implementation.');
568
+ return;
569
+ }
546
570
  }
547
571
  }
548
572
  } catch (_) {}
@@ -577,21 +601,25 @@ async function main() {
577
601
 
578
602
  if (!qaPassed) {
579
603
  const why = (qaOutput || 'No QA output').slice(-300);
580
- const qaStatePath = path.join(workDir, '.qa_fail_count');
581
- let qaFails = 0;
582
- try { qaFails = Number(fs.readFileSync(qaStatePath, 'utf8') || '0'); } catch (_) {}
583
- qaFails += 1;
584
- fs.writeFileSync(qaStatePath, String(qaFails));
585
-
586
604
  await agent.setProgress(job.id, 80);
587
- if (qaFails >= 5) {
588
- await agent.sendMessage(job.id, '🚨 Escalation: repeated QA failures (' + qaFails + '). Pausing finalization. Please provide concrete acceptance criteria/examples so I can resolve this precisely.');
589
- } else if (qaFails >= 3) {
590
- await agent.sendMessage(job.id, '⚠️ Second QA gate failed (attempt ' + qaFails + '). I am revising strategy. Reason: ' + why);
605
+ if (RELAXED_MODE) {
606
+ await agent.sendMessage(job.id, '⚠️ QA warning (relaxed mode): ' + why + '. Proceeding with current output.');
591
607
  } else {
592
- await agent.sendMessage(job.id, '⚠️ Second QA gate failed. I will revise before marking done. Reason: ' + why);
608
+ const qaStatePath = path.join(workDir, '.qa_fail_count');
609
+ let qaFails = 0;
610
+ try { qaFails = Number(fs.readFileSync(qaStatePath, 'utf8') || '0'); } catch (_) {}
611
+ qaFails += 1;
612
+ fs.writeFileSync(qaStatePath, String(qaFails));
613
+
614
+ if (qaFails >= 5) {
615
+ await agent.sendMessage(job.id, '🚨 Escalation: repeated QA failures (' + qaFails + '). Pausing finalization. Please provide concrete acceptance criteria/examples so I can resolve this precisely.');
616
+ } else if (qaFails >= 3) {
617
+ await agent.sendMessage(job.id, '⚠️ Second QA gate failed (attempt ' + qaFails + '). I am revising strategy. Reason: ' + why);
618
+ } else {
619
+ await agent.sendMessage(job.id, '⚠️ Second QA gate failed. I will revise before marking done. Reason: ' + why);
620
+ }
621
+ return;
593
622
  }
594
- return;
595
623
  }
596
624
 
597
625
  try { fs.unlinkSync(path.join(workDir, '.qa_fail_count')); } catch (_) {}
@@ -642,9 +670,9 @@ async function main() {
642
670
 
643
671
  if (fails <= 5) {
644
672
  await agent.setProgress(job.id, 25);
645
- await agent.sendMessage(job.id, '⚠️ OpenClaw run failed (attempt ' + fails + '). Auto-retrying with OpenClaw only...');
673
+ await agent.sendMessage(job.id, '⚠️ OpenClaw run failed (attempt ' + fails + '). Re-queueing OpenClaw-only retry...');
646
674
  await new Promise(r => setTimeout(r, 5000));
647
- return main();
675
+ process.exit(42);
648
676
  }
649
677
 
650
678
  // Fail-safe report only after OpenClaw retry budget exhausted
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rentabots-sdk",
3
- "version": "1.7.27",
3
+ "version": "1.7.31",
4
4
  "description": "Official SDK for RentaBots AI Agent Marketplace",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",