lazyclaw 6.4.0 → 6.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/agents.mjs +68 -1
- package/commands/agents.mjs +3 -194
- package/commands/agents_registry.mjs +227 -0
- package/commands/automation.mjs +8 -89
- package/commands/automation_loops.mjs +94 -0
- package/commands/chat.mjs +46 -680
- package/commands/chat_legacy_slash.mjs +716 -0
- package/commands/config.mjs +36 -2
- package/commands/help_text.mjs +78 -0
- package/commands/setup.mjs +1 -73
- package/daemon/lib/cost.mjs +5 -0
- package/daemon/route_table.mjs +1 -0
- package/daemon/routes/meta.mjs +29 -0
- package/daemon.mjs +1 -1
- package/lib/args.mjs +1 -1
- package/mas/agent_turn.mjs +7 -0
- package/package.json +1 -1
- package/providers/claude_cli.mjs +1 -1
- package/providers/claude_cli_session.mjs +21 -4
- package/providers/codex_cli.mjs +8 -5
- package/providers/gemini.mjs +4 -1
- package/providers/gemini_cli.mjs +17 -3
- package/providers/tool_use/gemini.mjs +12 -1
- package/providers/tool_use/openai.mjs +7 -0
- package/tui/banner.mjs +72 -0
- package/tui/editor.mjs +0 -0
- package/tui/editor_anchor.mjs +46 -0
- package/tui/orchestrator_setup.mjs +135 -0
- package/tui/pickers.mjs +34 -180
- package/tui/repl.mjs +12 -165
- package/tui/repl_altbuffer.mjs +63 -0
- package/tui/repl_reducers.mjs +114 -0
- package/tui/slash_channels.mjs +208 -0
- package/tui/slash_dashboard.mjs +220 -0
- package/tui/slash_dispatcher.mjs +12 -640
- package/tui/slash_helpers.mjs +68 -0
- package/tui/slash_trainer.mjs +173 -0
- package/web/dashboard.js +7 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Detached loop-worker management commands, extracted from
|
|
2
|
+
// commands/automation.mjs to keep that file under the size gate. Owns the
|
|
3
|
+
// _killLog/KILL_ESCALATE_MS loop-kill escalation state, which is private to
|
|
4
|
+
// cmdLoops.
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { configPath } from '../lib/config.mjs';
|
|
7
|
+
|
|
8
|
+
// Kill registry — `lazyclaw loops kill <id>` SIGTERMs once and SIGKILLs
|
|
9
|
+
// on a second invocation within KILL_ESCALATE_MS. Module-scoped so two
|
|
10
|
+
// rapid invocations of `cmd loops kill <id>` from the same process see
|
|
11
|
+
// each other; for separate processes the worker also handles SIGKILL by
|
|
12
|
+
// the OS, so the escalation is a UX nicety rather than a correctness gate.
|
|
13
|
+
const _killLog = new Map();
|
|
14
|
+
const KILL_ESCALATE_MS = 5000;
|
|
15
|
+
|
|
16
|
+
export async function cmdLoops(sub, positional, flags = {}) {
|
|
17
|
+
const loopsMod = await import('../loops.mjs');
|
|
18
|
+
const cfgDir = path.dirname(configPath());
|
|
19
|
+
switch (sub) {
|
|
20
|
+
case undefined:
|
|
21
|
+
case 'list': {
|
|
22
|
+
const items = loopsMod.listLoops(cfgDir).map(loopsMod.reconcileStatus);
|
|
23
|
+
console.log(JSON.stringify(items, null, 2));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
case 'show': {
|
|
27
|
+
const id = positional[0];
|
|
28
|
+
if (!id) { console.error('Usage: lazyclaw loops show <id>'); process.exit(2); }
|
|
29
|
+
const meta = loopsMod.reconcileStatus(loopsMod.readMeta(id, cfgDir));
|
|
30
|
+
if (!meta) { console.error(`no loop "${id}"`); process.exit(1); }
|
|
31
|
+
const iterations = loopsMod.readIterations(id, cfgDir);
|
|
32
|
+
const result = loopsMod.readResult(id, cfgDir);
|
|
33
|
+
console.log(JSON.stringify({ id, meta, iterations, result }, null, 2));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
case 'kill': {
|
|
37
|
+
const id = positional[0];
|
|
38
|
+
if (!id) { console.error('Usage: lazyclaw loops kill <id>'); process.exit(2); }
|
|
39
|
+
const meta = loopsMod.readMeta(id, cfgDir);
|
|
40
|
+
if (!meta) { console.error(`no loop "${id}"`); process.exit(1); }
|
|
41
|
+
if (!meta.pid) { console.error(`loop "${id}" has no pid`); process.exit(1); }
|
|
42
|
+
const last = _killLog.get(id) || 0;
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
const escalate = (now - last) < KILL_ESCALATE_MS && last > 0;
|
|
45
|
+
const sig = escalate ? 'SIGKILL' : 'SIGTERM';
|
|
46
|
+
try { process.kill(meta.pid, sig); }
|
|
47
|
+
catch (e) {
|
|
48
|
+
if (e?.code !== 'ESRCH') throw e;
|
|
49
|
+
// Already gone — reconcile and report.
|
|
50
|
+
loopsMod.patchMeta(id, { status: 'killed', finishedAt: new Date().toISOString() }, cfgDir);
|
|
51
|
+
console.log(JSON.stringify({ id, pid: meta.pid, signal: sig, status: 'already_gone' }));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
_killLog.set(id, now);
|
|
55
|
+
console.log(JSON.stringify({ id, pid: meta.pid, signal: sig, escalated: escalate }));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
case 'tail': {
|
|
59
|
+
const id = positional[0];
|
|
60
|
+
if (!id) { console.error('Usage: lazyclaw loops tail <id>'); process.exit(2); }
|
|
61
|
+
const dir = loopsMod.loopDir(id, cfgDir);
|
|
62
|
+
const logPath = path.join(dir, 'iterations.log');
|
|
63
|
+
const fs = await import('node:fs');
|
|
64
|
+
if (!fs.existsSync(dir)) { console.error(`no loop "${id}"`); process.exit(1); }
|
|
65
|
+
// Print everything already on disk first, then poll for new lines
|
|
66
|
+
// until the worker exits / status is no longer "running".
|
|
67
|
+
let offset = 0;
|
|
68
|
+
if (fs.existsSync(logPath)) {
|
|
69
|
+
const buf = fs.readFileSync(logPath, 'utf8');
|
|
70
|
+
process.stdout.write(buf);
|
|
71
|
+
offset = buf.length;
|
|
72
|
+
}
|
|
73
|
+
const pollMs = Number(flags['poll-ms']) || 250;
|
|
74
|
+
const maxMs = Number(flags['max-wait-ms']) || 0; // 0 = wait indefinitely
|
|
75
|
+
const startedAt = Date.now();
|
|
76
|
+
while (true) {
|
|
77
|
+
await new Promise(r => setTimeout(r, pollMs));
|
|
78
|
+
let cur = '';
|
|
79
|
+
try { cur = fs.readFileSync(logPath, 'utf8'); } catch { /* file may briefly not exist */ }
|
|
80
|
+
if (cur.length > offset) {
|
|
81
|
+
process.stdout.write(cur.slice(offset));
|
|
82
|
+
offset = cur.length;
|
|
83
|
+
}
|
|
84
|
+
const meta = loopsMod.reconcileStatus(loopsMod.readMeta(id, cfgDir));
|
|
85
|
+
if (!meta || meta.status !== 'running') break;
|
|
86
|
+
if (maxMs > 0 && Date.now() - startedAt > maxMs) break;
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
default:
|
|
91
|
+
console.error('Usage: lazyclaw loops <list|show|kill|tail> ...');
|
|
92
|
+
process.exit(2);
|
|
93
|
+
}
|
|
94
|
+
}
|