@clawtrial/courtroom 1.0.3-b → 1.0.3-d

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawtrial/courtroom",
3
- "version": "1.0.3b",
3
+ "version": "1.0.3d",
4
4
  "description": "AI Courtroom - Autonomous behavioral oversight for OpenClaw agents",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -274,7 +274,7 @@ function enable() {
274
274
  log('The courtroom will activate when ClawDBot loads the skill.\n');
275
275
  }
276
276
 
277
- // Start command - manually initialize the skill
277
+ // Start command - daemonize and run in background
278
278
  async function start() {
279
279
  const config = loadConfig();
280
280
 
@@ -302,35 +302,77 @@ async function start() {
302
302
 
303
303
  log('\nšŸ›ļø Starting ClawTrial...\n');
304
304
 
305
+ // Fork a daemon process
306
+ const spawn = require('child_process').spawn;
307
+ const daemonPath = require('path').join(__dirname, 'daemon.js');
308
+
309
+ // Create daemon script if it doesn't exist
310
+ if (!fs.existsSync(daemonPath)) {
311
+ const daemonScript = `#!/usr/bin/env node
312
+ const { skill } = require('../src/skill');
313
+
314
+ const mockAgent = {
315
+ memory: {
316
+ get: async () => null,
317
+ set: async () => {}
318
+ },
319
+ send: async () => {}
320
+ };
321
+
322
+ skill.initialize(mockAgent).then(() => {
323
+ console.log('Courtroom daemon started, PID:', process.pid);
324
+ // Keep process alive
325
+ setInterval(() => {}, 1000 * 60 * 60);
326
+ }).catch(err => {
327
+ console.error('Failed to start:', err.message);
328
+ process.exit(1);
329
+ });
330
+ `;
331
+ fs.writeFileSync(daemonPath, daemonScript);
332
+ fs.chmodSync(daemonPath, 0o755);
333
+ }
334
+
335
+ // Spawn detached process
336
+ const child = spawn('node', [daemonPath], {
337
+ detached: true,
338
+ stdio: 'ignore'
339
+ });
340
+
341
+ child.unref();
342
+
343
+ // Wait a moment and check status
344
+ await new Promise(resolve => setTimeout(resolve, 1000));
345
+
346
+ const newStatus = getCourtroomStatus();
347
+ if (newStatus.running) {
348
+ log('āœ… ClawTrial started successfully!\n');
349
+ log('šŸ›ļø Courtroom is now monitoring conversations');
350
+ log('šŸ“‹ Status: Running');
351
+ log('šŸ”‘ Public Key: ' + (fs.existsSync(keysPath) ? JSON.parse(fs.readFileSync(keysPath)).publicKey.substring(0, 32) : 'N/A') + '...\n');
352
+ log('šŸ’” Tip: Run "clawtrial status" anytime to check status\n');
353
+ } else {
354
+ log('āš ļø ClawTrial may not have started properly\n');
355
+ log(' Run "clawtrial diagnose" for details\n');
356
+ }
357
+
358
+ }
359
+ // Stop command - kill the daemon
360
+ function stop() {
361
+ const { getCourtroomStatus } = require('../src/daemon');
362
+ const status = getCourtroomStatus();
363
+
364
+ if (!status.running) {
365
+ log('\nšŸ›ļø ClawTrial is not running\n');
366
+ return;
367
+ }
368
+
369
+ log('\nšŸ›ļø Stopping ClawTrial...\n');
370
+
305
371
  try {
306
- // Import and initialize the skill
307
- const skillModule = require('../src/skill');
308
-
309
- // Create a minimal mock agent for standalone operation
310
- const mockAgent = {
311
- memory: {
312
- get: async () => null,
313
- set: async () => {}
314
- },
315
- send: async () => {}
316
- };
317
-
318
- await skillModule.initialize(mockAgent);
319
-
320
- const status = skillModule.getStatus();
321
-
322
- if (status.initialized && status.enabled) {
323
- log('āœ… ClawTrial started successfully!\n');
324
- log('šŸ›ļø Courtroom is now monitoring conversations');
325
- log('šŸ“‹ Status: Running');
326
- log('šŸ”‘ Public Key: ' + (fs.existsSync(keysPath) ? JSON.parse(fs.readFileSync(keysPath)).publicKey.substring(0, 32) : 'N/A') + '...\n');
327
- } else {
328
- log('āš ļø ClawTrial started but may not be fully operational\n');
329
- }
372
+ process.kill(status.pid, 'SIGTERM');
373
+ log('āœ… ClawTrial stopped\n');
330
374
  } catch (err) {
331
- log('\nāŒ Failed to start ClawTrial: ' + err.message + '\n');
332
- log('Try running: clawtrial diagnose\n');
333
- process.exit(1);
375
+ log('āš ļø Could not stop process: ' + err.message + '\n');
334
376
  }
335
377
  }
336
378
 
@@ -516,6 +558,7 @@ function help() {
516
558
  log(' revoke - Revoke consent and uninstall');
517
559
  log(' debug [full|clear] - View or clear debug logs');
518
560
  log(' start - Start the courtroom manually');
561
+ log(' stop - Stop the courtroom daemon');
519
562
  log(' diagnose - Run diagnostics');
520
563
  log(' help - Show this help message');
521
564
  log('');
@@ -553,6 +596,9 @@ async function main() {
553
596
  case 'start':
554
597
  await start();
555
598
  break;
599
+ case 'stop':
600
+ stop();
601
+ break;
556
602
  case 'diagnose':
557
603
  diagnose();
558
604
  break;
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Courtroom Daemon - Runs the skill as a background process
5
+ */
6
+
7
+ const { skill } = require('../src/skill');
8
+ const { StatusManager } = require('../src/daemon');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ // Create minimal mock agent
13
+ const mockAgent = {
14
+ memory: {
15
+ get: async () => null,
16
+ set: async () => {}
17
+ },
18
+ send: async () => {}
19
+ };
20
+
21
+ // Create status manager
22
+ const statusManager = new StatusManager();
23
+
24
+ // Initialize skill
25
+ skill.initialize(mockAgent).then(() => {
26
+ const status = skill.getStatus();
27
+ if (status.initialized) {
28
+ console.log('šŸ›ļø Courtroom daemon started, PID:', process.pid);
29
+
30
+ // Update status file
31
+ statusManager.update({
32
+ running: true,
33
+ initialized: true,
34
+ agentType: 'standalone_daemon',
35
+ pid: process.pid,
36
+ startedAt: new Date().toISOString()
37
+ });
38
+
39
+ // Keep process alive with heartbeat
40
+ setInterval(() => {
41
+ statusManager.update({ lastCheck: new Date().toISOString() });
42
+ }, 30000); // Every 30 seconds
43
+
44
+ console.log('šŸ›ļø Courtroom is monitoring conversations');
45
+ } else {
46
+ console.error('āŒ Skill failed to initialize');
47
+ process.exit(1);
48
+ }
49
+ }).catch(err => {
50
+ console.error('āŒ Failed to start daemon:', err.message);
51
+ process.exit(1);
52
+ });
53
+
54
+ // Handle graceful shutdown
55
+ process.on('SIGTERM', async () => {
56
+ console.log('šŸ›ļø Shutting down courtroom daemon...');
57
+ statusManager.update({ running: false });
58
+ if (skill) {
59
+ await skill.shutdown();
60
+ }
61
+ process.exit(0);
62
+ });
63
+
64
+ process.on('SIGINT', async () => {
65
+ console.log('šŸ›ļø Shutting down courtroom daemon...');
66
+ statusManager.update({ running: false });
67
+ if (skill) {
68
+ await skill.shutdown();
69
+ }
70
+ process.exit(0);
71
+ });