openclaw-aegis 1.5.1 → 1.5.3
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/cli/index.js +77 -19
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.js +77 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -116,6 +116,7 @@ var aegisConfigSchema = import_zod.z.object({
|
|
|
116
116
|
l1BackoffMultiplier: import_zod.z.number().min(1).default(3),
|
|
117
117
|
l2MaxAttempts: import_zod.z.number().int().min(1).default(2),
|
|
118
118
|
l2CooldownMs: import_zod.z.number().int().min(1e3).default(6e4),
|
|
119
|
+
l3Enabled: import_zod.z.boolean().default(false),
|
|
119
120
|
l3MaxAttempts: import_zod.z.number().int().min(1).default(2),
|
|
120
121
|
l3CooldownMs: import_zod.z.number().int().min(1e3).default(3e4),
|
|
121
122
|
l3SafeModeArgs: import_zod.z.array(import_zod.z.string()).default(["--no-plugins", "--default-routes"]),
|
|
@@ -3912,6 +3913,13 @@ var RecoveryOrchestrator = class extends import_node_events3.EventEmitter {
|
|
|
3912
3913
|
getCircuitBreaker() {
|
|
3913
3914
|
return this.circuitBreaker;
|
|
3914
3915
|
}
|
|
3916
|
+
/**
|
|
3917
|
+
* Public entry point for on-demand L3 deep repair (e.g. from /repair bot command).
|
|
3918
|
+
* Runs L3 patterns regardless of l3Enabled config — caller is responsible for confirmation.
|
|
3919
|
+
*/
|
|
3920
|
+
async triggerL3() {
|
|
3921
|
+
return this.attemptL3();
|
|
3922
|
+
}
|
|
3915
3923
|
async recover(_healthScore) {
|
|
3916
3924
|
if (this.recovering) return [];
|
|
3917
3925
|
this.recovering = true;
|
|
@@ -3931,24 +3939,36 @@ var RecoveryOrchestrator = class extends import_node_events3.EventEmitter {
|
|
|
3931
3939
|
actions.push(...retryL1.actions);
|
|
3932
3940
|
if (retryL1.success) return actions;
|
|
3933
3941
|
}
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
if (
|
|
3942
|
+
let l3Success = false;
|
|
3943
|
+
if (this.config.recovery.l3Enabled) {
|
|
3944
|
+
const l3Result = await this.attemptL3();
|
|
3945
|
+
actions.push(...l3Result.actions);
|
|
3946
|
+
l3Success = l3Result.success;
|
|
3947
|
+
if (l3Success) {
|
|
3948
|
+
const retryL1 = await this.attemptL1();
|
|
3949
|
+
actions.push(...retryL1.actions);
|
|
3950
|
+
if (retryL1.success) return actions;
|
|
3951
|
+
}
|
|
3952
|
+
} else {
|
|
3953
|
+
this.emitEvent({ type: "L3_DISABLED" });
|
|
3940
3954
|
}
|
|
3941
|
-
if (!l2Result.success && l2Result.noMatch && !
|
|
3955
|
+
if (!l2Result.success && l2Result.noMatch && !l3Success) {
|
|
3956
|
+
const l3Note = this.config.recovery.l3Enabled ? "" : " L3 deep repair is DISABLED \u2014 enable it with l3Enabled = true in [recovery] config if you want Aegis to attempt network repair, dependency rebuild, safe mode, and disk cleanup.";
|
|
3942
3957
|
this.emitEvent({
|
|
3943
3958
|
type: "FAST_PATH_L4",
|
|
3944
|
-
reason:
|
|
3959
|
+
reason: `L1+L2 exhausted \u2014 no auto-repair available.${l3Note}`
|
|
3945
3960
|
});
|
|
3946
3961
|
}
|
|
3947
3962
|
this.circuitBreaker.recordFailedCycle();
|
|
3948
3963
|
if (this.circuitBreaker.isTripped()) {
|
|
3949
3964
|
this.emitEvent({ type: "CIRCUIT_BREAKER_TRIPPED" });
|
|
3950
3965
|
}
|
|
3951
|
-
|
|
3966
|
+
let reason;
|
|
3967
|
+
if (!this.config.recovery.l3Enabled) {
|
|
3968
|
+
reason = !l2Result.success && l2Result.noMatch ? "No matching failure pattern \u2014 cannot auto-repair. L3 deep repair is DISABLED (enable with l3Enabled = true to allow network repair, dependency rebuild, safe mode boot, and disk cleanup)" : "Recovery exhausted \u2014 L1+L2 failed. L3 deep repair is DISABLED (enable with l3Enabled = true)";
|
|
3969
|
+
} else {
|
|
3970
|
+
reason = !l2Result.success && l2Result.noMatch && !l3Success ? "No matching failure pattern \u2014 cannot auto-repair" : "Recovery exhausted \u2014 L1+L2+L3 failed";
|
|
3971
|
+
}
|
|
3952
3972
|
this.emitEvent({ type: "L4_ALERT", reason, actions });
|
|
3953
3973
|
return actions;
|
|
3954
3974
|
} finally {
|
|
@@ -4298,16 +4318,19 @@ var BotCommandHandler = class {
|
|
|
4298
4318
|
this.register("alerts", "Alert channel status", this.cmdAlerts.bind(this));
|
|
4299
4319
|
this.register("version", "Version, uptime, platform", this.cmdVersion.bind(this));
|
|
4300
4320
|
this.register("help", "List available commands", this.cmdHelp.bind(this));
|
|
4321
|
+
this.register("repair", "Attempt L3 deep repair (requires confirmation)", this.cmdRepair.bind(this));
|
|
4301
4322
|
}
|
|
4302
4323
|
commands = /* @__PURE__ */ new Map();
|
|
4303
4324
|
register(name, description, handler) {
|
|
4304
4325
|
this.commands.set(name, { description, handler });
|
|
4305
4326
|
}
|
|
4306
4327
|
async handle(input) {
|
|
4307
|
-
const
|
|
4328
|
+
const parts = input.trim().replace(/^\//, "").split(/\s+/);
|
|
4329
|
+
const cmd = (parts[0] ?? "").toLowerCase();
|
|
4330
|
+
const args = parts.slice(1).join(" ");
|
|
4308
4331
|
const entry = this.commands.get(cmd);
|
|
4309
4332
|
if (!entry) return null;
|
|
4310
|
-
return entry.handler();
|
|
4333
|
+
return entry.handler(args);
|
|
4311
4334
|
}
|
|
4312
4335
|
getCommandList() {
|
|
4313
4336
|
return Array.from(this.commands.entries()).map(([name, { description }]) => ({
|
|
@@ -4316,7 +4339,7 @@ var BotCommandHandler = class {
|
|
|
4316
4339
|
}));
|
|
4317
4340
|
}
|
|
4318
4341
|
// --- Commands ---
|
|
4319
|
-
async cmdHealth() {
|
|
4342
|
+
async cmdHealth(_args) {
|
|
4320
4343
|
const score = this.deps.monitor.getLastScore() ?? await this.deps.monitor.runAllProbes();
|
|
4321
4344
|
const passed = score.probeResults.filter((p) => p.healthy).length;
|
|
4322
4345
|
const failed = score.probeResults.filter((p) => !p.healthy);
|
|
@@ -4332,7 +4355,7 @@ Probes: ${passed}/${score.probeResults.length} passed`;
|
|
|
4332
4355
|
}
|
|
4333
4356
|
return { text };
|
|
4334
4357
|
}
|
|
4335
|
-
async cmdStatus() {
|
|
4358
|
+
async cmdStatus(_args) {
|
|
4336
4359
|
const score = this.deps.monitor.getLastScore() ?? await this.deps.monitor.runAllProbes();
|
|
4337
4360
|
const icon = score.band === "healthy" ? "\u2705" : score.band === "degraded" ? "\u26A0\uFE0F" : "\u{1F6A8}";
|
|
4338
4361
|
let text = `${icon} Health: ${score.band.toUpperCase()} (score: ${score.total})
|
|
@@ -4345,7 +4368,7 @@ Probes: ${passed}/${score.probeResults.length} passed`;
|
|
|
4345
4368
|
}
|
|
4346
4369
|
return { text };
|
|
4347
4370
|
}
|
|
4348
|
-
cmdIncidents() {
|
|
4371
|
+
cmdIncidents(_args) {
|
|
4349
4372
|
if (!this.deps.incidents) {
|
|
4350
4373
|
return { text: "No incident logger available." };
|
|
4351
4374
|
}
|
|
@@ -4375,7 +4398,7 @@ Avg MTTR: ${(stats.averageMttrMs / 1e3).toFixed(1)}s`;
|
|
|
4375
4398
|
}
|
|
4376
4399
|
return { text };
|
|
4377
4400
|
}
|
|
4378
|
-
cmdRecovery() {
|
|
4401
|
+
cmdRecovery(_args) {
|
|
4379
4402
|
if (!this.deps.recovery) {
|
|
4380
4403
|
return { text: "Recovery: idle (orchestrator not active)" };
|
|
4381
4404
|
}
|
|
@@ -4398,7 +4421,7 @@ Dead man's switch: ${state}`;
|
|
|
4398
4421
|
}
|
|
4399
4422
|
return { text };
|
|
4400
4423
|
}
|
|
4401
|
-
cmdBackups() {
|
|
4424
|
+
cmdBackups(_args) {
|
|
4402
4425
|
if (!this.deps.backup) {
|
|
4403
4426
|
return { text: "Backup manager not available." };
|
|
4404
4427
|
}
|
|
@@ -4417,7 +4440,7 @@ Checksum: ${latest.checksum.slice(0, 12)}...`;
|
|
|
4417
4440
|
}
|
|
4418
4441
|
return { text };
|
|
4419
4442
|
}
|
|
4420
|
-
cmdAlerts() {
|
|
4443
|
+
cmdAlerts(_args) {
|
|
4421
4444
|
const channels = this.deps.config.alerts.channels;
|
|
4422
4445
|
if (channels.length === 0) {
|
|
4423
4446
|
return { text: "No alert channels configured. Run 'aegis init' to add one." };
|
|
@@ -4430,7 +4453,7 @@ Checksum: ${latest.checksum.slice(0, 12)}...`;
|
|
|
4430
4453
|
}
|
|
4431
4454
|
return { text };
|
|
4432
4455
|
}
|
|
4433
|
-
cmdVersion() {
|
|
4456
|
+
cmdVersion(_args) {
|
|
4434
4457
|
const uptimeMs = Date.now() - this.deps.startedAt;
|
|
4435
4458
|
const hours = Math.floor(uptimeMs / 36e5);
|
|
4436
4459
|
const minutes = Math.floor(uptimeMs % 36e5 / 6e4);
|
|
@@ -4445,7 +4468,7 @@ Uptime: ${hours}h ${minutes}m`;
|
|
|
4445
4468
|
Host: ${os8.hostname()}`;
|
|
4446
4469
|
return { text };
|
|
4447
4470
|
}
|
|
4448
|
-
cmdHelp() {
|
|
4471
|
+
cmdHelp(_args) {
|
|
4449
4472
|
let text = "Aegis Bot Commands:\n";
|
|
4450
4473
|
for (const [name, { description }] of this.commands) {
|
|
4451
4474
|
text += `
|
|
@@ -4453,6 +4476,41 @@ Host: ${os8.hostname()}`;
|
|
|
4453
4476
|
}
|
|
4454
4477
|
return { text };
|
|
4455
4478
|
}
|
|
4479
|
+
async cmdRepair(args) {
|
|
4480
|
+
if (!this.deps.recovery) {
|
|
4481
|
+
return { text: "Recovery orchestrator not available." };
|
|
4482
|
+
}
|
|
4483
|
+
if (args.trim().toLowerCase() !== "confirm") {
|
|
4484
|
+
let text2 = "\u26A0\uFE0F L3 DEEP REPAIR \u2014 DESTRUCTIVE OPERATION\n";
|
|
4485
|
+
text2 += "\nThis will attempt the following repairs on your server:\n";
|
|
4486
|
+
text2 += "\n 1. Network repair \u2014 flush DNS cache, reset TUN interface, check default routes";
|
|
4487
|
+
text2 += "\n 2. Process resurrection \u2014 reinstall gateway binary via npm install -g openclaw";
|
|
4488
|
+
text2 += "\n 3. Dependency rebuild \u2014 delete and rebuild node_modules with npm install --production";
|
|
4489
|
+
text2 += "\n 4. Safe mode boot \u2014 start gateway with minimal config (no plugins, default routes)";
|
|
4490
|
+
text2 += "\n 5. Disk cleanup \u2014 truncate oversized logs, delete rotated logs, clear temp files";
|
|
4491
|
+
text2 += "\n\n\u{1F6A8} These actions CAN affect other services on this server.";
|
|
4492
|
+
text2 += "\nOnly proceed if normal recovery (L1 restart + L2 targeted repair) has failed.\n";
|
|
4493
|
+
text2 += "\nTo proceed, send: /repair confirm";
|
|
4494
|
+
return { text: text2 };
|
|
4495
|
+
}
|
|
4496
|
+
let text = "\u{1F527} Running L3 deep repair...\n";
|
|
4497
|
+
try {
|
|
4498
|
+
const result = await this.deps.recovery.triggerL3();
|
|
4499
|
+
if (result.actions.length === 0) {
|
|
4500
|
+
text += "\nNo matching failure patterns detected \u2014 nothing to repair.";
|
|
4501
|
+
return { text };
|
|
4502
|
+
}
|
|
4503
|
+
for (const action of result.actions) {
|
|
4504
|
+
const mark = action.result === "success" ? "\u2705" : "\u274C";
|
|
4505
|
+
text += `
|
|
4506
|
+
${mark} ${action.action} (${action.level}, ${action.durationMs}ms)`;
|
|
4507
|
+
}
|
|
4508
|
+
text += result.success ? "\n\n\u2705 L3 repair succeeded. Gateway should recover shortly." : "\n\n\u274C L3 repair did not resolve the issue. Manual intervention required.";
|
|
4509
|
+
} catch {
|
|
4510
|
+
text += "\n\u274C L3 repair failed with an unexpected error. Check logs for details.";
|
|
4511
|
+
}
|
|
4512
|
+
return { text };
|
|
4513
|
+
}
|
|
4456
4514
|
};
|
|
4457
4515
|
|
|
4458
4516
|
// src/bot/telegram.ts
|