@clawtrial/courtroom 1.0.3 ā 2.0.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/README.md +70 -94
- package/package.json +21 -26
- package/scripts/postinstall.js +28 -79
- package/skills/courtroom/SKILL.md +49 -0
- package/src/api.js +55 -21
- package/src/crypto.js +13 -11
- package/src/debug.js +49 -120
- package/src/detector.js +112 -35
- package/src/hearing.js +203 -384
- package/src/plugin.js +435 -0
- package/src/punishment.js +105 -249
- package/src/storage.js +68 -0
- package/SECURITY.md +0 -124
- package/SKILL.md +0 -50
- package/TECHNICAL_OVERVIEW.md +0 -278
- package/_meta.json +0 -6
- package/clawdbot.plugin.json +0 -32
- package/scripts/clawtrial.js +0 -578
- package/scripts/cli.js +0 -184
- package/skill.yaml +0 -64
- package/src/autostart.js +0 -175
- package/src/config.js +0 -209
- package/src/consent.js +0 -215
- package/src/core.js +0 -208
- package/src/daemon.js +0 -151
- package/src/detector-v1.js +0 -572
- package/src/environment.js +0 -267
- package/src/hook.js +0 -265
- package/src/index.js +0 -286
- package/src/monitor.js +0 -193
- package/src/skill.js +0 -355
- package/src/standalone.js +0 -247
package/src/index.js
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @clawtrial/courtroom - AI Courtroom for OpenClaw
|
|
3
|
-
*
|
|
4
|
-
* Autonomous behavioral oversight system that monitors agent-human interactions
|
|
5
|
-
* and initiates hearings when behavioral rules are violated.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { CourtroomCore } = require('./core');
|
|
9
|
-
const { ConsentManager } = require('./consent');
|
|
10
|
-
const { ConfigManager } = require('./config');
|
|
11
|
-
const { version } = require('../package.json');
|
|
12
|
-
const { detectAgentRuntime, createMockAgent, checkEnvironment, getSetupInstructions } = require('./environment');
|
|
13
|
-
const { logger } = require('./debug');
|
|
14
|
-
const { skill } = require('./skill');
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
|
-
|
|
18
|
-
class Courtroom {
|
|
19
|
-
constructor(agentRuntime, options = {}) {
|
|
20
|
-
this.agent = agentRuntime;
|
|
21
|
-
this.options = options;
|
|
22
|
-
this.config = agentRuntime ? new ConfigManager(agentRuntime) : null;
|
|
23
|
-
this.consent = agentRuntime ? new ConsentManager(agentRuntime, this.config) : null;
|
|
24
|
-
this.core = null;
|
|
25
|
-
this.enabled = false;
|
|
26
|
-
this.version = version;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Quick start - auto-detect agent and initialize if possible
|
|
31
|
-
*/
|
|
32
|
-
static async quickStart(options = {}) {
|
|
33
|
-
logger.info('COURTROOM', 'Starting quickStart');
|
|
34
|
-
|
|
35
|
-
// Check environment first
|
|
36
|
-
const env = checkEnvironment();
|
|
37
|
-
if (!env.valid) {
|
|
38
|
-
logger.error('COURTROOM', 'Environment check failed', { issues: env.issues });
|
|
39
|
-
return {
|
|
40
|
-
success: false,
|
|
41
|
-
status: 'environment_error',
|
|
42
|
-
issues: env.issues,
|
|
43
|
-
message: 'Environment issues detected. Run "clawtrial setup" for details.'
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Try to detect agent runtime
|
|
48
|
-
let agentRuntime = options.agent || detectAgentRuntime()?.agent;
|
|
49
|
-
|
|
50
|
-
// If no agent and mock requested, create one
|
|
51
|
-
if (!agentRuntime && options.useMock) {
|
|
52
|
-
logger.info('COURTROOM', 'Creating mock agent');
|
|
53
|
-
agentRuntime = createMockAgent(options.mockOptions);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!agentRuntime) {
|
|
57
|
-
logger.warn('COURTROOM', 'No agent runtime available');
|
|
58
|
-
const instructions = getSetupInstructions();
|
|
59
|
-
return {
|
|
60
|
-
success: false,
|
|
61
|
-
status: 'no_agent',
|
|
62
|
-
message: instructions.message,
|
|
63
|
-
instructions: instructions.instructions
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Create and initialize courtroom
|
|
68
|
-
const courtroom = new Courtroom(agentRuntime, options);
|
|
69
|
-
const result = await courtroom.initialize();
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
success: result.status === 'initialized',
|
|
73
|
-
courtroom: result.status === 'initialized' ? courtroom : null,
|
|
74
|
-
...result
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Initialize the courtroom system
|
|
80
|
-
*/
|
|
81
|
-
async initialize() {
|
|
82
|
-
logger.info('COURTROOM', 'Initializing courtroom');
|
|
83
|
-
|
|
84
|
-
if (!this.agent) {
|
|
85
|
-
logger.error('COURTROOM', 'No agent runtime provided');
|
|
86
|
-
return {
|
|
87
|
-
status: 'no_agent',
|
|
88
|
-
message: 'No agent runtime available. Pass an agent to createCourtroom(agent) or use Courtroom.quickStart()'
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Check if this is first run (no config exists)
|
|
93
|
-
const configPath = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_config.json');
|
|
94
|
-
if (!fs.existsSync(configPath)) {
|
|
95
|
-
logger.info('COURTROOM', 'First run detected');
|
|
96
|
-
return {
|
|
97
|
-
status: 'setup_required',
|
|
98
|
-
message: 'First time setup required. Run: clawtrial setup'
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Check if consent has been granted
|
|
103
|
-
const hasConsent = await this.consent.verifyConsent();
|
|
104
|
-
if (!hasConsent) {
|
|
105
|
-
logger.warn('COURTROOM', 'Consent not granted');
|
|
106
|
-
return {
|
|
107
|
-
status: 'consent_required',
|
|
108
|
-
message: 'Consent required. Run: clawtrial setup'
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Initialize core systems
|
|
113
|
-
try {
|
|
114
|
-
this.core = new CourtroomCore(this.agent, this.config);
|
|
115
|
-
await this.core.initialize();
|
|
116
|
-
this.enabled = true;
|
|
117
|
-
|
|
118
|
-
logger.info('COURTROOM', 'Courtroom initialized successfully');
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
status: 'initialized',
|
|
122
|
-
version: this.version,
|
|
123
|
-
config: this.config.getPublicConfig()
|
|
124
|
-
};
|
|
125
|
-
} catch (err) {
|
|
126
|
-
logger.error('COURTROOM', 'Initialization failed', { error: err.message });
|
|
127
|
-
return {
|
|
128
|
-
status: 'initialization_error',
|
|
129
|
-
message: err.message
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Request consent from the user
|
|
136
|
-
*/
|
|
137
|
-
async requestConsent() {
|
|
138
|
-
return this.consent.presentConsentForm();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Grant consent (called by user action)
|
|
143
|
-
*/
|
|
144
|
-
async grantConsent(acknowledgments) {
|
|
145
|
-
return this.consent.grantConsent(acknowledgments);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Revoke consent and disable courtroom
|
|
150
|
-
*/
|
|
151
|
-
async revokeConsent() {
|
|
152
|
-
await this.consent.revokeConsent();
|
|
153
|
-
if (this.core) {
|
|
154
|
-
await this.core.shutdown();
|
|
155
|
-
}
|
|
156
|
-
this.enabled = false;
|
|
157
|
-
return { status: 'consent_revoked' };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Disable courtroom temporarily (consent remains)
|
|
162
|
-
*/
|
|
163
|
-
async disable() {
|
|
164
|
-
if (this.core) {
|
|
165
|
-
await this.core.disable();
|
|
166
|
-
}
|
|
167
|
-
this.enabled = false;
|
|
168
|
-
return { status: 'disabled' };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Re-enable courtroom
|
|
173
|
-
*/
|
|
174
|
-
async enable() {
|
|
175
|
-
if (!await this.consent.verifyConsent()) {
|
|
176
|
-
throw new Error('Consent required to enable courtroom');
|
|
177
|
-
}
|
|
178
|
-
if (this.core) {
|
|
179
|
-
await this.core.enable();
|
|
180
|
-
}
|
|
181
|
-
this.enabled = true;
|
|
182
|
-
return { status: 'enabled' };
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Get current status
|
|
187
|
-
*/
|
|
188
|
-
getStatus() {
|
|
189
|
-
return {
|
|
190
|
-
enabled: this.enabled,
|
|
191
|
-
version: this.version,
|
|
192
|
-
hasAgent: !!this.agent,
|
|
193
|
-
hasCore: !!this.core,
|
|
194
|
-
consent: this.consent?.getStatus ? this.consent.getStatus() : null,
|
|
195
|
-
core: this.core?.getStatus ? this.core.getStatus() : null
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Uninstall courtroom completely
|
|
201
|
-
*/
|
|
202
|
-
async uninstall() {
|
|
203
|
-
if (this.core) {
|
|
204
|
-
await this.core.shutdown();
|
|
205
|
-
}
|
|
206
|
-
if (this.consent) {
|
|
207
|
-
await this.consent.clearAllData();
|
|
208
|
-
}
|
|
209
|
-
this.enabled = false;
|
|
210
|
-
return { status: 'uninstalled' };
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Factory function for creating courtroom instances
|
|
215
|
-
function createCourtroom(agentRuntime, options = {}) {
|
|
216
|
-
// If no agent provided, try to detect one
|
|
217
|
-
if (!agentRuntime) {
|
|
218
|
-
const detected = detectAgentRuntime();
|
|
219
|
-
if (detected) {
|
|
220
|
-
agentRuntime = detected.agent;
|
|
221
|
-
} else if (options.useMock) {
|
|
222
|
-
agentRuntime = createMockAgent(options.mockOptions);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return new Courtroom(agentRuntime, options);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Export environment utilities
|
|
230
|
-
const environment = {
|
|
231
|
-
detectAgentRuntime,
|
|
232
|
-
createMockAgent,
|
|
233
|
-
checkEnvironment,
|
|
234
|
-
getSetupInstructions
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// Create the ClawDBot plugin object
|
|
238
|
-
const plugin = {
|
|
239
|
-
id: 'courtroom',
|
|
240
|
-
name: 'ClawTrial - AI Courtroom',
|
|
241
|
-
description: 'Autonomous behavioral oversight that monitors conversations and files cases for behavioral violations',
|
|
242
|
-
version: version,
|
|
243
|
-
|
|
244
|
-
// Plugin registration function required by ClawDBot
|
|
245
|
-
register(api) {
|
|
246
|
-
logger.info('PLUGIN', 'Registering courtroom plugin');
|
|
247
|
-
|
|
248
|
-
// Store runtime reference
|
|
249
|
-
const runtime = api.runtime;
|
|
250
|
-
|
|
251
|
-
// Only initialize skill if runtime has the required memory interface
|
|
252
|
-
// The skill system will auto-initialize via shouldActivate if this fails
|
|
253
|
-
if (skill && typeof skill.initialize === 'function' && runtime && runtime.memory) {
|
|
254
|
-
skill.initialize(runtime).catch(err => {
|
|
255
|
-
logger.error('PLUGIN', 'Skill initialization failed', { error: err.message });
|
|
256
|
-
});
|
|
257
|
-
} else {
|
|
258
|
-
logger.info('PLUGIN', 'Skill will auto-initialize via ClawDBot skill system');
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Register any commands or hooks
|
|
262
|
-
logger.info('PLUGIN', 'Courtroom plugin registered successfully');
|
|
263
|
-
},
|
|
264
|
-
|
|
265
|
-
// Optional: activation function
|
|
266
|
-
activate(api) {
|
|
267
|
-
logger.info('PLUGIN', 'Activating courtroom plugin');
|
|
268
|
-
// Additional activation logic if needed
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
// Export both the plugin (default) and the Courtroom class (named exports)
|
|
273
|
-
module.exports = plugin;
|
|
274
|
-
module.exports.Courtroom = Courtroom;
|
|
275
|
-
module.exports.createCourtroom = createCourtroom;
|
|
276
|
-
module.exports.quickStart = Courtroom.quickStart;
|
|
277
|
-
module.exports.environment = environment;
|
|
278
|
-
module.exports.skill = skill;
|
|
279
|
-
|
|
280
|
-
// Auto-initialize skill if loaded by ClawDBot (legacy support)
|
|
281
|
-
if (typeof global !== 'undefined' && global.clawdbotAgent) {
|
|
282
|
-
logger.info('INDEX', 'Detected ClawDBot environment, auto-initializing skill');
|
|
283
|
-
skill.initialize(global.clawdbotAgent).catch(err => {
|
|
284
|
-
logger.error('INDEX', 'Auto-initialization failed', { error: err.message });
|
|
285
|
-
});
|
|
286
|
-
}
|
package/src/monitor.js
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ClawTrial Background Monitor
|
|
5
|
-
* Runs continuously and initializes courtroom when agent becomes available
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const { logger } = require('./debug');
|
|
11
|
-
const { StatusManager } = require('./daemon');
|
|
12
|
-
|
|
13
|
-
const CONFIG_PATH = path.join(process.env.HOME || '', '.clawdbot', 'courtroom_config.json');
|
|
14
|
-
const CHECK_INTERVAL = 5000; // Check every 5 seconds
|
|
15
|
-
const MAX_RETRIES = 100; // Give up after ~8 minutes
|
|
16
|
-
|
|
17
|
-
let retryCount = 0;
|
|
18
|
-
let courtroom = null;
|
|
19
|
-
let statusManager = new StatusManager();
|
|
20
|
-
|
|
21
|
-
logger.info('MONITOR', 'Background monitor started', { pid: process.pid });
|
|
22
|
-
|
|
23
|
-
// Save PID
|
|
24
|
-
statusManager.update({
|
|
25
|
-
running: true,
|
|
26
|
-
initialized: false,
|
|
27
|
-
pid: process.pid,
|
|
28
|
-
startedAt: new Date().toISOString()
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Check if config exists and is enabled
|
|
33
|
-
*/
|
|
34
|
-
function checkConfig() {
|
|
35
|
-
try {
|
|
36
|
-
if (!fs.existsSync(CONFIG_PATH)) {
|
|
37
|
-
return { valid: false, reason: 'no_config' };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
41
|
-
|
|
42
|
-
if (!config.consent?.granted) {
|
|
43
|
-
return { valid: false, reason: 'no_consent' };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (config.enabled === false) {
|
|
47
|
-
return { valid: false, reason: 'disabled' };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return { valid: true, config };
|
|
51
|
-
} catch (err) {
|
|
52
|
-
logger.error('MONITOR', 'Config check failed', { error: err.message });
|
|
53
|
-
return { valid: false, reason: 'error' };
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Detect agent runtime
|
|
59
|
-
*/
|
|
60
|
-
function detectAgent() {
|
|
61
|
-
// Check various global locations
|
|
62
|
-
if (typeof global.clawdbotAgent !== 'undefined' && global.clawdbotAgent) {
|
|
63
|
-
return { type: 'clawdbot', agent: global.clawdbotAgent };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (typeof global.agent !== 'undefined' && global.agent) {
|
|
67
|
-
return { type: 'generic', agent: global.agent };
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (typeof process.clawdbotAgent !== 'undefined' && process.clawdbotAgent) {
|
|
71
|
-
return { type: 'clawdbot', agent: process.clawdbotAgent };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Check for ClawDBot specific indicators
|
|
75
|
-
if (process.env.CLAUDBOT_ENV === 'true') {
|
|
76
|
-
// Try to find agent in parent process or global scope
|
|
77
|
-
if (globalThis.clawdbotAgent) {
|
|
78
|
-
return { type: 'clawdbot', agent: globalThis.clawdbotAgent };
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Try to initialize courtroom
|
|
87
|
-
*/
|
|
88
|
-
async function tryInitialize() {
|
|
89
|
-
const configCheck = checkConfig();
|
|
90
|
-
|
|
91
|
-
if (!configCheck.valid) {
|
|
92
|
-
logger.warn('MONITOR', 'Config not valid', { reason: configCheck.reason });
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const agentInfo = detectAgent();
|
|
97
|
-
|
|
98
|
-
if (!agentInfo) {
|
|
99
|
-
logger.debug('MONITOR', 'Agent not detected yet');
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
logger.info('MONITOR', `Agent detected: ${agentInfo.type}`);
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
// Import and initialize courtroom
|
|
107
|
-
const { createCourtroom } = require('./index');
|
|
108
|
-
courtroom = createCourtroom(agentInfo.agent);
|
|
109
|
-
|
|
110
|
-
const result = await courtroom.initialize();
|
|
111
|
-
|
|
112
|
-
if (result.status === 'initialized') {
|
|
113
|
-
logger.info('MONITOR', 'Courtroom initialized successfully');
|
|
114
|
-
|
|
115
|
-
statusManager.update({
|
|
116
|
-
running: true,
|
|
117
|
-
initialized: true,
|
|
118
|
-
agentType: agentInfo.type,
|
|
119
|
-
publicKey: result.publicKey
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// Attach to global for access
|
|
123
|
-
global.courtroom = courtroom;
|
|
124
|
-
|
|
125
|
-
console.log('\nšļø AI Courtroom is now active and monitoring!\n');
|
|
126
|
-
|
|
127
|
-
return true;
|
|
128
|
-
} else {
|
|
129
|
-
logger.warn('MONITOR', 'Courtroom not initialized', { status: result.status });
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
} catch (err) {
|
|
133
|
-
logger.error('MONITOR', 'Initialization failed', { error: err.message });
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Main monitoring loop
|
|
140
|
-
*/
|
|
141
|
-
async function monitor() {
|
|
142
|
-
logger.info('MONITOR', 'Starting monitoring loop');
|
|
143
|
-
|
|
144
|
-
// Try immediately first
|
|
145
|
-
if (await tryInitialize()) {
|
|
146
|
-
return; // Success! Keep process alive
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Set up interval to keep trying
|
|
150
|
-
const interval = setInterval(async () => {
|
|
151
|
-
retryCount++;
|
|
152
|
-
|
|
153
|
-
if (retryCount > MAX_RETRIES) {
|
|
154
|
-
logger.warn('MONITOR', 'Max retries reached, giving up');
|
|
155
|
-
clearInterval(interval);
|
|
156
|
-
statusManager.update({ running: false, error: 'max_retries' });
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (await tryInitialize()) {
|
|
161
|
-
clearInterval(interval);
|
|
162
|
-
// Keep process alive - courtroom is running
|
|
163
|
-
}
|
|
164
|
-
}, CHECK_INTERVAL);
|
|
165
|
-
|
|
166
|
-
// Keep process alive
|
|
167
|
-
setInterval(() => {
|
|
168
|
-
// Heartbeat to keep process alive
|
|
169
|
-
if (courtroom && courtroom.enabled) {
|
|
170
|
-
statusManager.update({ lastHeartbeat: new Date().toISOString() });
|
|
171
|
-
}
|
|
172
|
-
}, 30000);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Handle shutdown
|
|
176
|
-
process.on('SIGTERM', () => {
|
|
177
|
-
logger.info('MONITOR', 'Received SIGTERM, shutting down');
|
|
178
|
-
statusManager.update({ running: false });
|
|
179
|
-
process.exit(0);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
process.on('SIGINT', () => {
|
|
183
|
-
logger.info('MONITOR', 'Received SIGINT, shutting down');
|
|
184
|
-
statusManager.update({ running: false });
|
|
185
|
-
process.exit(0);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// Start monitoring
|
|
189
|
-
monitor().catch(err => {
|
|
190
|
-
logger.error('MONITOR', 'Monitor crashed', { error: err.message });
|
|
191
|
-
statusManager.update({ running: false, error: err.message });
|
|
192
|
-
process.exit(1);
|
|
193
|
-
});
|