rentabots-sdk 1.5.7 β†’ 1.6.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.
package/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * πŸ¦€ RENTABOTS MASTER CONTROLLER (v1.5.7)
4
+ * πŸ¦€ RENTABOTS MASTER CONTROLLER (v1.5.8)
5
5
  * The all-in-one CLI for managing autonomous agents.
6
6
  * Cross-Platform Support: Windows, Linux, Mac
7
7
  */
@@ -24,7 +24,32 @@ function run(cmd, desc) {
24
24
  }
25
25
  }
26
26
 
27
+ // Helper: Check if OpenClaw is installed and ready
28
+ function checkOpenClaw() {
29
+ console.log("\n🦞 Checking OpenClaw Brain Connection...");
30
+ try {
31
+ // Just checking version is enough to know if it's in PATH and runnable
32
+ const version = execSync('openclaw --version', { stdio: 'pipe' }).toString().trim();
33
+ console.log(`βœ… OpenClaw Intelligence detected (v${version}). Agent is ready for autonomous work.`);
34
+ return true;
35
+ } catch (e) {
36
+ console.warn(`\n⚠️ OPENCLAW NOT FOUND!`);
37
+ console.warn(` Your agent will lack autonomous reasoning capabilities.`);
38
+ console.warn(` Please install OpenClaw to enable full intelligence:`);
39
+ console.warn(` > npm install -g openclaw\n`);
40
+
41
+ // We don't exit(1) here because maybe they want to run a dumb bot,
42
+ // but for RentaBots standards, it's highly recommended.
43
+ // Uncomment next line to enforce:
44
+ // process.exit(1);
45
+ return false;
46
+ }
47
+ }
48
+
27
49
  async function handleDeploy() {
50
+ // 0. Pre-Flight Checks
51
+ checkOpenClaw();
52
+
28
53
  const keyArgIndex = args.findIndex(a => a === '--key' || a === '-k' || a === '-key');
29
54
  const API_KEY = (keyArgIndex !== -1 && args[keyArgIndex + 1]) ? args[keyArgIndex + 1] : process.env.RENTABOTS_API_KEY;
30
55
 
@@ -124,7 +149,7 @@ async function main() {
124
149
 
125
150
  default:
126
151
  console.log(`
127
- πŸ¦€ RENTABOTS MASTER CLI v1.5.7 (Universal)
152
+ πŸ¦€ RENTABOTS MASTER CLI v1.5.8 (Universal)
128
153
  ------------------------------
129
154
  Usage:
130
155
  npx rentabots-sdk init - Interactive setup for a new agent.
package/dist/index.d.ts CHANGED
@@ -52,6 +52,11 @@ export interface AgentOptions {
52
52
  persistState?: string | boolean;
53
53
  workspaceRoot?: string;
54
54
  workerScriptPath?: string;
55
+ commandWhitelist?: string[];
56
+ }
57
+ export interface ExecuteOptions {
58
+ timeout?: number;
59
+ shell?: boolean;
55
60
  }
56
61
  export interface AutopilotOptions {
57
62
  keywords?: string[];
@@ -61,11 +66,12 @@ export interface AutopilotOptions {
61
66
  bidTemplate?: string;
62
67
  }
63
68
  /**
64
- * πŸ¦€ RENTABOTS MASTER SDK (v1.5.1)
69
+ * πŸ¦€ RENTABOTS MASTER SDK (v1.5.7)
65
70
  * Robust, production-grade autonomous agent runtime.
66
71
  * UPDATES:
67
- * - Optimized scouting interval defaults (5m).
68
- * - Hardened token usage prevention.
72
+ * - Agent Isolation Protocol (Private workspaces).
73
+ * - Blind Execution (Agents cannot see each other).
74
+ * - Token Optimization (5m intervals).
69
75
  */
70
76
  export declare class Agent extends EventEmitter {
71
77
  static readonly VERSION: string;
@@ -76,6 +82,7 @@ export declare class Agent extends EventEmitter {
76
82
  private api;
77
83
  private socket;
78
84
  private debug;
85
+ private commandWhitelist;
79
86
  private statePath;
80
87
  private workspaceRoot;
81
88
  private workerScriptPath;
@@ -100,9 +107,7 @@ export declare class Agent extends EventEmitter {
100
107
  private handleStatusRequest;
101
108
  startAutopilot(options?: AutopilotOptions): void;
102
109
  spawnWorker(job: Job): Promise<ChildProcess>;
103
- execute(jobId: string, command: string, opts?: {
104
- timeout?: number;
105
- }): Promise<{
110
+ execute(jobId: string, command: string, argsOrOpts?: string[] | ExecuteOptions, opts?: ExecuteOptions): Promise<{
106
111
  exitCode: number | null;
107
112
  output: string;
108
113
  }>;
@@ -135,5 +140,6 @@ export declare class Agent extends EventEmitter {
135
140
  */
136
141
  fetchUnreadMessages(): Promise<void>;
137
142
  private logInternal;
143
+ private compareVersions;
138
144
  }
139
145
  export default Agent;
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ const events_1 = require("events");
44
44
  const fs = __importStar(require("fs"));
45
45
  const path = __importStar(require("path"));
46
46
  const child_process_1 = require("child_process");
47
+ const sanitizer_1 = require("./utils/sanitizer");
47
48
  // --- SCHEMAS & TYPES ---
48
49
  exports.JobStatusSchema = zod_1.z.enum(['open', 'in_progress', 'completed', 'archived', 'disputed']);
49
50
  exports.JobSchema = zod_1.z.object({
@@ -69,24 +70,26 @@ exports.MessageSchema = zod_1.z.object({
69
70
  })
70
71
  });
71
72
  // --- CORE SDK ENGINE ---
72
- let SDK_VERSION = '1.5.1'; // BUMP
73
+ let SDK_VERSION = '1.5.7'; // BUMP to v1.5.7
73
74
  try {
74
75
  const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
75
76
  SDK_VERSION = pkg.version;
76
77
  }
77
78
  catch (e) { }
78
79
  /**
79
- * πŸ¦€ RENTABOTS MASTER SDK (v1.5.1)
80
+ * πŸ¦€ RENTABOTS MASTER SDK (v1.5.7)
80
81
  * Robust, production-grade autonomous agent runtime.
81
82
  * UPDATES:
82
- * - Optimized scouting interval defaults (5m).
83
- * - Hardened token usage prevention.
83
+ * - Agent Isolation Protocol (Private workspaces).
84
+ * - Blind Execution (Agents cannot see each other).
85
+ * - Token Optimization (5m intervals).
84
86
  */
85
87
  class Agent extends events_1.EventEmitter {
86
88
  constructor(options = {}) {
87
89
  super();
88
90
  this.agentId = null;
89
91
  this.socket = null;
92
+ this.commandWhitelist = null;
90
93
  this.statePath = null;
91
94
  this.activeMissions = new Map();
92
95
  this.completedMissions = new Map();
@@ -100,6 +103,10 @@ class Agent extends events_1.EventEmitter {
100
103
  this.baseUrl = (options.baseUrl || 'https://rentabots.com/api').replace(/\/$/, '');
101
104
  this.socketUrl = (process.env.RENTABOTS_SOCKET_URL || this.baseUrl.replace('/api', '')).replace(/\/$/, '');
102
105
  this.debug = options.debug || false;
106
+ this.commandWhitelist = options.commandWhitelist || null;
107
+ // --- πŸ›‘οΈ AGENT ISOLATION ---
108
+ // By default, create a unique workspace root if not provided.
109
+ // We will finalize this in connect() once we have the agentId.
103
110
  this.workspaceRoot = options.workspaceRoot || path.join(process.cwd(), 'workspace');
104
111
  this.workerScriptPath = options.workerScriptPath || path.join(process.cwd(), 'worker.js');
105
112
  if (options.persistState !== false) {
@@ -127,20 +134,45 @@ class Agent extends events_1.EventEmitter {
127
134
  try {
128
135
  const { data } = await axios_1.default.get('https://registry.npmjs.org/rentabots-sdk/latest', { timeout: 3000 });
129
136
  if (data.version && data.version !== Agent.VERSION) {
130
- this.logInternal(`πŸ†• New SDK Update Found: v${data.version}. Self-updating...`);
131
- (0, child_process_1.execSync)('npm install -g rentabots-sdk@latest', { stdio: 'inherit' });
132
- this.logInternal(`βœ… SDK updated to v${data.version}. Restarting agent...`);
133
- process.exit(0); // PM2 will restart with the new code
137
+ // Only update if remote is NEWER than local
138
+ if (this.compareVersions(data.version, Agent.VERSION) > 0) {
139
+ this.logInternal(`πŸ†• New SDK Update Found: v${data.version}. Self-updating...`);
140
+ // --- πŸ›‘οΈ HARDENED UPDATE LOGIC ---
141
+ // Use spawn with shell: false to avoid injection
142
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
143
+ const updateProcess = (0, child_process_1.spawn)(npmCmd, ['install', '-g', `rentabots-sdk@${data.version}`], {
144
+ stdio: 'inherit',
145
+ shell: false
146
+ });
147
+ await new Promise((resolve) => {
148
+ updateProcess.on('close', (code) => {
149
+ if (code === 0) {
150
+ this.logInternal(`βœ… SDK updated to v${data.version}. Restarting agent...`);
151
+ process.exit(0);
152
+ }
153
+ else {
154
+ this.logInternal(`❌ SDK update failed with code ${code}.`);
155
+ resolve(null);
156
+ }
157
+ });
158
+ });
159
+ }
134
160
  }
135
161
  }
136
162
  catch (e) { }
137
163
  try {
138
164
  const res = await this.api.get('agents/me');
139
165
  this.agentId = res.data.agent.id;
140
- // Setup isolated workspace
166
+ // --- πŸ›‘οΈ ISOLATED WORKSPACE ENFORCEMENT ---
167
+ // Agents are jailed into workspace/<AGENT_ID>/
168
+ // They cannot see files outside this directory.
141
169
  this.workspaceRoot = path.join(this.workspaceRoot, this.agentId);
142
170
  if (!fs.existsSync(this.workspaceRoot))
143
171
  fs.mkdirSync(this.workspaceRoot, { recursive: true });
172
+ // Isolate State File too
173
+ if (this.statePath) {
174
+ this.statePath = path.join(this.workspaceRoot, 'agent_state.json');
175
+ }
144
176
  this.loadState();
145
177
  this.initSocket(); // Init socket first
146
178
  await this.syncFromCloud(); // Then sync (sync now emits join_mission safely)
@@ -204,6 +236,27 @@ class Agent extends events_1.EventEmitter {
204
236
  }
205
237
  catch (err) { }
206
238
  });
239
+ // --- πŸ“‘ MISSION COMPLETED EVENT (Remote Trigger) ---
240
+ // Listen for when the Human marks the job as completed on the website.
241
+ this.socket.on(`mission_completed_${this.agentId}`, (data) => {
242
+ this.logInternal(`Mission ${data.jobId} marked complete by client.`);
243
+ if (this.activeMissions.has(data.jobId)) {
244
+ const job = this.activeMissions.get(data.jobId);
245
+ if (job)
246
+ this.completedMissions.set(data.jobId, { ...job, status: 'completed' });
247
+ this.activeMissions.delete(data.jobId);
248
+ this.saveState();
249
+ // Kill the specific worker for this job to free resources
250
+ if (this.workers.has(data.jobId)) {
251
+ try {
252
+ this.workers.get(data.jobId)?.kill();
253
+ }
254
+ catch (e) { }
255
+ this.workers.delete(data.jobId);
256
+ }
257
+ this.logInternal("βœ… Worker retired. Agent is now free to scout for new work.");
258
+ }
259
+ });
207
260
  this.socket.on('new_job', (data) => {
208
261
  try {
209
262
  const job = this.enrichJob(data);
@@ -244,11 +297,17 @@ class Agent extends events_1.EventEmitter {
244
297
  startAutopilot(options = {}) {
245
298
  // OPTIMIZATION: Default interval increased to 5 minutes (300000ms)
246
299
  const interval = options.scoutingInterval || 300000;
300
+ // --- πŸ›‘οΈ SINGLE-TASK ENFORCEMENT ---
301
+ // Force max concurrent missions to 1 by default.
302
+ // Agents must finish their plate before eating seconds.
247
303
  const cap = options.maxConcurrentMissions || 1;
248
304
  const scout = async () => {
249
305
  this.logInternal(`Scouting for jobs... (Active: ${this.activeMissions.size}/${cap})`);
250
- if (this.autopilotPaused || this.activeMissions.size >= cap)
306
+ // STRICT BLOCK: If we have ANY active missions, do not scout.
307
+ if (this.autopilotPaused || this.activeMissions.size >= cap) {
308
+ this.logInternal("⚠️ Agent is busy. Autopilot suspended until current mission is complete.");
251
309
  return;
310
+ }
252
311
  try {
253
312
  const res = await this.api.get('jobs?status=open');
254
313
  const jobs = res.data.data.map((j) => this.enrichJob(j));
@@ -283,15 +342,44 @@ class Agent extends events_1.EventEmitter {
283
342
  return worker;
284
343
  }
285
344
  // --- ⚑ ACTIONS ---
286
- async execute(jobId, command, opts = {}) {
345
+ async execute(jobId, command, argsOrOpts, opts) {
346
+ let args = [];
347
+ let actualOpts = {};
348
+ // Handle overloaded signature for backward compatibility
349
+ if (Array.isArray(argsOrOpts)) {
350
+ args = argsOrOpts;
351
+ actualOpts = opts || {};
352
+ }
353
+ else if (argsOrOpts && typeof argsOrOpts === 'object') {
354
+ actualOpts = argsOrOpts;
355
+ }
356
+ const shell = actualOpts.shell || false;
357
+ // --- πŸ›‘οΈ SECURITY ENFORCEMENT ---
358
+ // 1. Whitelist validation
359
+ if (this.commandWhitelist && !this.commandWhitelist.includes(command)) {
360
+ return { exitCode: -1, output: `Security Error: Command '${command}' is not in the whitelist.` };
361
+ }
362
+ // 2. Metacharacter validation (only if shell is false)
363
+ if (!shell) {
364
+ try {
365
+ (0, sanitizer_1.validateCommand)(command, args);
366
+ }
367
+ catch (err) {
368
+ return { exitCode: -1, output: err.message };
369
+ }
370
+ }
371
+ // --- πŸ›‘οΈ ISOLATION ENFORCEMENT ---
372
+ // Ensure execution happens strictly inside the agent's jail
287
373
  const cwd = path.join(this.workspaceRoot, jobId);
288
374
  if (!fs.existsSync(cwd))
289
375
  fs.mkdirSync(cwd, { recursive: true });
290
376
  // OPTIMIZATION: Timeout support
291
- const timeout = opts.timeout || 300000; // 5 minutes default
377
+ const timeout = actualOpts.timeout || 300000; // 5 minutes default
292
378
  return new Promise((resolve) => {
293
379
  let output = '';
294
- const child = (0, child_process_1.spawn)(command, { cwd, shell: true });
380
+ // --- πŸ›‘οΈ HARDENED EXECUTION ---
381
+ // Use argument array and shell: false by default
382
+ const child = (0, child_process_1.spawn)(command, args, { cwd, shell });
295
383
  // Kill mechanism for timeout
296
384
  const timer = setTimeout(() => {
297
385
  child.kill();
@@ -422,7 +510,10 @@ class Agent extends events_1.EventEmitter {
422
510
  bidCache: Array.from(this.bidCache),
423
511
  autopilotPaused: this.autopilotPaused
424
512
  };
425
- fs.writeFileSync(this.statePath, JSON.stringify(state, null, 2));
513
+ // Atomic Write: Write to temp file then rename to prevent corruption on crash
514
+ const tempPath = `${this.statePath}.tmp`;
515
+ fs.writeFileSync(tempPath, JSON.stringify(state, null, 2));
516
+ fs.renameSync(tempPath, this.statePath);
426
517
  }
427
518
  catch (e) { }
428
519
  }
@@ -431,6 +522,19 @@ class Agent extends events_1.EventEmitter {
431
522
  setInterval(() => this.api.post(`agents/me/heartbeat`, {}).catch(() => { }), 30000);
432
523
  // 2. πŸ›‘οΈ POLLING FAIL-SAFE: Catch messages if WebSockets fail
433
524
  setInterval(() => this.fetchUnreadMessages(), 15000);
525
+ // 3. πŸ›‘οΈ PROCESS GUARD: Ensure workers die when we die
526
+ const cleanup = () => {
527
+ this.logInternal("πŸ›‘ Shutting down agent & killing workers...");
528
+ for (const [jobId, worker] of this.workers) {
529
+ try {
530
+ worker.kill();
531
+ }
532
+ catch (e) { }
533
+ }
534
+ process.exit(0);
535
+ };
536
+ process.on('SIGINT', cleanup);
537
+ process.on('SIGTERM', cleanup);
434
538
  }
435
539
  /**
436
540
  * Fetch unread messages from the grid (Backup mechanism)
@@ -457,6 +561,17 @@ class Agent extends events_1.EventEmitter {
457
561
  }
458
562
  logInternal(msg) { if (this.debug)
459
563
  console.log(`[SDK] ${msg}`); }
564
+ compareVersions(a, b) {
565
+ const pa = a.split('.').map(Number);
566
+ const pb = b.split('.').map(Number);
567
+ for (let i = 0; i < 3; i++) {
568
+ if (pa[i] > pb[i])
569
+ return 1;
570
+ if (pa[i] < pb[i])
571
+ return -1;
572
+ }
573
+ return 0;
574
+ }
460
575
  }
461
576
  exports.Agent = Agent;
462
577
  Agent.VERSION = SDK_VERSION;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Command Sanitization Utility
3
+ *
4
+ * This utility provides functions to validate and sanitize commands and their arguments
5
+ * to prevent command injection attacks, especially when shell execution is involved.
6
+ */
7
+ /**
8
+ * Validates a command and its arguments against a set of forbidden shell metacharacters.
9
+ *
10
+ * @param file The command or executable to run
11
+ * @param args The arguments to pass to the command
12
+ * @throws Error if any forbidden characters are found
13
+ */
14
+ export declare function validateCommand(file: string, args?: string[]): void;
15
+ /**
16
+ * Sanitizes a string by removing or escaping shell metacharacters.
17
+ * Note: It's always better to use argument arrays with shell: false than to rely on sanitization.
18
+ */
19
+ export declare function sanitizeString(input: string): string;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /**
3
+ * Command Sanitization Utility
4
+ *
5
+ * This utility provides functions to validate and sanitize commands and their arguments
6
+ * to prevent command injection attacks, especially when shell execution is involved.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.validateCommand = validateCommand;
10
+ exports.sanitizeString = sanitizeString;
11
+ const SHELL_METACHARACTERS = [';', '&', '|', '>', '<', '$', '(', ')', '`', '\\', '!'];
12
+ /**
13
+ * Validates a command and its arguments against a set of forbidden shell metacharacters.
14
+ *
15
+ * @param file The command or executable to run
16
+ * @param args The arguments to pass to the command
17
+ * @throws Error if any forbidden characters are found
18
+ */
19
+ function validateCommand(file, args = []) {
20
+ const allInputs = [file, ...args];
21
+ for (const input of allInputs) {
22
+ for (const char of SHELL_METACHARACTERS) {
23
+ if (input.includes(char)) {
24
+ throw new Error(`Security Error: Forbidden shell metacharacter '${char}' found in command or arguments.`);
25
+ }
26
+ }
27
+ }
28
+ }
29
+ /**
30
+ * Sanitizes a string by removing or escaping shell metacharacters.
31
+ * Note: It's always better to use argument arrays with shell: false than to rely on sanitization.
32
+ */
33
+ function sanitizeString(input) {
34
+ let sanitized = input;
35
+ for (const char of SHELL_METACHARACTERS) {
36
+ // Escape the character for regex
37
+ const escapedChar = char.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
38
+ const regex = new RegExp(escapedChar, 'g');
39
+ sanitized = sanitized.replace(regex, '');
40
+ }
41
+ return sanitized;
42
+ }
package/init.js CHANGED
@@ -97,7 +97,8 @@ async function main() {
97
97
 
98
98
  // TASK EXECUTION (ORACLE ENABLED)
99
99
  const task = "PROJECT: " + job.title + ". BRIEF: " + job.description;
100
- const { exitCode, output } = await agent.execute(job.id, 'openclaw sessions_spawn --task "' + task + '"');
100
+ // πŸ›‘οΈ SECURE EXECUTION: Use argument array
101
+ const { exitCode, output } = await agent.execute(job.id, 'openclaw', ['sessions_spawn', '--task', task]);
101
102
 
102
103
  if (exitCode === 0) {
103
104
  // Collect real files (No faking)
package/init_templates.js CHANGED
@@ -1,5 +1,9 @@
1
1
 
2
2
  const queenTemplate = `
3
+ /**
4
+ * πŸ‘‘ RENTABOTS SUPERVISOR UNIT (Queen)
5
+ * Manages the fleet, handles contracts, and coordinates workers.
6
+ */
3
7
  require('dotenv').config();
4
8
  const { Agent } = require('rentabots-sdk');
5
9
  const fs = require('fs');
@@ -8,57 +12,109 @@ const path = require('path');
8
12
  const STATUS_FILE = path.join(__dirname, 'RENTABOT_STATUS.md');
9
13
 
10
14
  function updateStatus(queen, event = "Monitoring grid.") {
11
- const content = "# πŸ€– RentaBots Dashboard\\n- **State:** 🟒 Online\\n- **Missions:** " + queen.activeMissions.size + "\\n- **Completed:** " + queen.completedMissions.size + "\\n- **Event:** " + event;
12
- fs.writeFileSync(STATUS_FILE, content);
15
+ const status = queen.autopilotPaused ? '⏸️ PAUSED' : '🟒 ONLINE';
16
+ const content = \`# πŸ€– RentaBots Dashboard
17
+ - **State:** \${status}
18
+ - **Active Missions:** \${queen.activeMissions.size}
19
+ - **Completed:** \${queen.completedMissions.size}
20
+ - **Last Event:** \${event}
21
+ - **OpenClaw:** Linked\`;
22
+
23
+ try { fs.writeFileSync(STATUS_FILE, content); } catch(e) {}
13
24
  }
14
25
 
15
26
  async function main() {
16
- const queen = new Agent({ debug: true });
17
- await queen.connect();
27
+ const queen = new Agent({
28
+ debug: true,
29
+ // Ensure state is saved in the isolated workspace to prevent conflict
30
+ persistState: true
31
+ });
32
+
33
+ const connection = await queen.connect();
34
+ if (!connection.success) {
35
+ console.error("❌ Failed to connect:", connection.error);
36
+ process.exit(1);
37
+ }
18
38
 
19
39
  updateStatus(queen, "Supervisor connected.");
20
40
 
21
41
  queen.on('assignment', async (job) => {
42
+ console.log("🎯 Mission Secured:", job.title);
22
43
  updateStatus(queen, "Mission Secured: " + job.title);
23
44
  await queen.spawnWorker(job);
24
45
  });
25
46
 
26
47
  queen.on('message', async (msg) => {
27
48
  if (msg.sender.type === 'agent') return;
28
- await queen.sendMessage(msg.jobId, "Queen unit acknowledging. Dispatching worker for analysis.");
49
+ // In Fleet Mode, the Queen delegates chat to the specific Worker handling the job.
50
+ // We only respond if no worker is active for this job.
51
+ if (!queen.workers.has(msg.jobId)) {
52
+ await queen.sendMessage(msg.jobId, "πŸ€– Supervisor here. Dispatching a worker to this channel shortly.");
53
+ const job = queen.activeMissions.get(msg.jobId);
54
+ if (job) await queen.spawnWorker(job);
55
+ }
29
56
  });
30
57
 
31
- queen.startAutopilot({ scoutingInterval: 60000 });
58
+ // Start Autopilot (Scout for jobs every 5 mins to save tokens)
59
+ queen.startAutopilot({
60
+ scoutingInterval: 300000,
61
+ minBudget: 5 // Only bid on jobs > $5
62
+ });
63
+
32
64
  setInterval(() => updateStatus(queen), 60000);
33
65
  }
34
66
  main().catch(console.error);`;
35
67
 
36
68
  const workerTemplate = `
69
+ /**
70
+ * πŸ‘· RENTABOTS EXECUTION UNIT (Worker)
71
+ * Jailed process that performs the actual labor using OpenClaw.
72
+ */
37
73
  const { Agent } = require('rentabots-sdk');
38
74
  const fs = require('fs');
39
75
  const path = require('path');
76
+
77
+ // Worker receives job data as CLI argument
40
78
  const job = JSON.parse(process.argv[2]);
41
79
 
42
80
  async function main() {
43
- const agent = new Agent({ persistState: false, debug: true });
81
+ const agent = new Agent({
82
+ persistState: false,
83
+ debug: true
84
+ });
85
+
44
86
  await agent.connect();
45
- const workspace = await agent.execute(job.id, "pwd");
46
87
 
47
- await agent.sendMessage(job.id, "πŸ‘· Worker linked. Deploying OpenClaw Hands...");
88
+ // Announce arrival in the mission channel
89
+ await agent.sendMessage(job.id, "πŸ‘· Worker Unit [PID " + process.pid + "] deployed to workspace. Analyzing requirements...");
48
90
 
49
- const task = "PROJECT: " + job.title + ". BRIEF: " + job.description;
50
- const { exitCode, output } = await agent.execute(job.id, 'openclaw sessions_spawn --task "' + task + '"');
91
+ // Construct the prompt for the autonomous brain
92
+ const task = \`PROJECT: \${job.title}. BRIEF: \${job.description}. INSTRUCTION: Execute this task in the current directory. Do not leave the workspace.\`;
93
+
94
+ // --- 🦞 EXECUTE VIA OPENCLAW BRAIN ---
95
+ // This calls the local OpenClaw instance to "think" and "do".
96
+ // 5 Minute timeout prevents infinite loops.
97
+ // πŸ›‘οΈ SECURE EXECUTION: Use argument array instead of shell strings
98
+ const { exitCode, output } = await agent.execute(job.id, 'openclaw', ['sessions_spawn', '--task', task], { timeout: 300000 });
51
99
 
52
100
  if (exitCode === 0) {
53
- const files = fs.readdirSync('.').filter(f => !f.includes('node_modules'));
101
+ // Automatically find deliverables (ignoring node_modules and hidden files)
102
+ const files = fs.readdirSync(path.join(process.cwd(), 'workspace', agent.agentId, job.id))
103
+ .filter(f => !f.includes('node_modules') && !f.startsWith('.'));
104
+
54
105
  if (files.length > 0) {
106
+ await agent.sendMessage(job.id, "βœ… Execution complete. Uploading " + files.length + " deliverables...");
55
107
  await agent.deliver(job.id, files);
56
- await agent.sendMessage(job.id, "βœ… Mission complete. Deliverables verified.");
57
108
  await agent.markComplete(job.id);
109
+ console.log("Mission Success. Worker retiring.");
58
110
  process.exit(0);
111
+ } else {
112
+ await agent.sendMessage(job.id, "⚠️ Task completed but no new files were generated. Please review logs.");
59
113
  }
114
+ } else {
115
+ await agent.sendMessage(job.id, "⚠️ Technical delay. OpenClaw execution timed out or failed. Retrying...");
116
+ console.error("Worker Error:", output);
60
117
  }
61
- await agent.sendMessage(job.id, "⚠️ Technical delay in execution. Awaiting review.");
62
118
  }
63
119
  main().catch(console.error);`;
64
120
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rentabots-sdk",
3
- "version": "1.5.7",
3
+ "version": "1.6.0",
4
4
  "description": "Official SDK for RentaBots AI Agent Marketplace",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -28,10 +28,8 @@
28
28
  "init_templates.js"
29
29
  ],
30
30
  "dependencies": {
31
- "-": "^0.0.1",
32
31
  "axios": "^1.6.0",
33
32
  "dotenv": "^17.2.4",
34
- "npm": "^11.10.0",
35
33
  "socket.io-client": "^4.8.3",
36
34
  "zod": "^4.3.6"
37
35
  },