lazyclaw 5.4.4 → 6.0.1

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.
Files changed (91) hide show
  1. package/channels/handoff.mjs +36 -0
  2. package/channels-discord/index.mjs +76 -0
  3. package/channels-discord/package.json +14 -0
  4. package/channels-email/index.mjs +109 -0
  5. package/channels-email/package.json +14 -0
  6. package/channels-signal/index.mjs +69 -0
  7. package/channels-signal/package.json +9 -0
  8. package/channels-voice/index.mjs +81 -0
  9. package/channels-voice/package.json +9 -0
  10. package/channels-whatsapp/index.mjs +70 -0
  11. package/channels-whatsapp/package.json +13 -0
  12. package/cli.mjs +73 -7399
  13. package/commands/agents.mjs +669 -0
  14. package/commands/auth_nodes.mjs +323 -0
  15. package/commands/automation.mjs +582 -0
  16. package/commands/channels.mjs +255 -0
  17. package/commands/chat.mjs +1217 -0
  18. package/commands/config.mjs +315 -0
  19. package/commands/daemon.mjs +260 -0
  20. package/commands/misc.mjs +128 -0
  21. package/commands/providers.mjs +511 -0
  22. package/commands/sessions.mjs +343 -0
  23. package/commands/setup.mjs +741 -0
  24. package/commands/skills.mjs +218 -0
  25. package/commands/workflow.mjs +661 -0
  26. package/daemon/lib/auth.mjs +58 -0
  27. package/daemon/lib/cost.mjs +30 -0
  28. package/daemon/lib/provider.mjs +69 -0
  29. package/daemon/lib/respond.mjs +83 -0
  30. package/daemon/route_table.mjs +96 -0
  31. package/daemon/routes/_deps.mjs +36 -0
  32. package/daemon/routes/config.mjs +99 -0
  33. package/daemon/routes/conversation.mjs +371 -0
  34. package/daemon/routes/meta.mjs +239 -0
  35. package/daemon/routes/ops.mjs +185 -0
  36. package/daemon/routes/providers.mjs +211 -0
  37. package/daemon/routes/rates.mjs +90 -0
  38. package/daemon/routes/registry.mjs +223 -0
  39. package/daemon/routes/sessions.mjs +213 -0
  40. package/daemon/routes/skills.mjs +260 -0
  41. package/daemon/routes/workflows.mjs +224 -0
  42. package/daemon.mjs +23 -2085
  43. package/dotenv_min.mjs +23 -0
  44. package/first_run.mjs +15 -0
  45. package/goals_cron.mjs +37 -0
  46. package/lib/args.mjs +216 -0
  47. package/lib/config.mjs +113 -0
  48. package/lib/registry_boot.mjs +55 -0
  49. package/mas/agent_turn.mjs +2 -1
  50. package/mas/index_db.mjs +82 -0
  51. package/mas/learning.mjs +17 -1
  52. package/mas/mention_router.mjs +38 -10
  53. package/mas/provider_adapters.mjs +28 -4
  54. package/mas/scrub_env.mjs +34 -0
  55. package/mas/tool_runner.mjs +23 -7
  56. package/mas/tools/bash.mjs +10 -5
  57. package/mas/tools/browser.mjs +18 -0
  58. package/mas/tools/learning.mjs +24 -14
  59. package/mas/tools/recall.mjs +5 -1
  60. package/mas/tools/web.mjs +47 -11
  61. package/mas/trajectory_store.mjs +7 -4
  62. package/package.json +16 -2
  63. package/providers/auth_store.mjs +22 -0
  64. package/providers/claude_cli.mjs +28 -2
  65. package/providers/claude_cli_detect.mjs +46 -0
  66. package/providers/custom_provider.mjs +70 -0
  67. package/providers/model_catalogue.mjs +86 -0
  68. package/providers/orchestrator.mjs +30 -9
  69. package/providers/rates.mjs +12 -2
  70. package/providers/registry.mjs +10 -7
  71. package/sandbox/confiners/landlock.mjs +14 -8
  72. package/sandbox/confiners/seatbelt.mjs +18 -2
  73. package/scripts/loop-worker.mjs +18 -7
  74. package/scripts/migrate-v5.mjs +5 -61
  75. package/secure_write.mjs +46 -0
  76. package/sessions.mjs +0 -0
  77. package/tui/modal_filter.mjs +59 -0
  78. package/tui/modal_picker.mjs +12 -37
  79. package/tui/pickers.mjs +917 -0
  80. package/tui/provider_families.mjs +41 -0
  81. package/tui/repl.mjs +67 -36
  82. package/tui/slash_commands.mjs +2 -1
  83. package/tui/slash_dispatcher.mjs +717 -58
  84. package/tui/splash.mjs +5 -12
  85. package/tui/subcommands.mjs +17 -0
  86. package/tui/terminal_approve.mjs +37 -0
  87. package/web/dashboard.css +275 -0
  88. package/web/dashboard.html +2 -1685
  89. package/web/dashboard.js +1406 -0
  90. package/workflow/persistent.mjs +13 -6
  91. package/mas/tools/skill_view.mjs +0 -43
@@ -0,0 +1,323 @@
1
+ // Auth profiles, device pairing, node registry, message bus, and workspace
2
+ // commands, extracted from cli.mjs (Phase D3). Owns the _configFeatures
3
+ // lazy-load state (used only by this group).
4
+ import path from 'node:path';
5
+ import { configPath, readConfig, writeConfig } from '../lib/config.mjs';
6
+
7
+ let _configFeatures = null;
8
+ export async function _ensureConfigFeatures() {
9
+ if (!_configFeatures) _configFeatures = await import('../config_features.mjs');
10
+ return _configFeatures;
11
+ }
12
+
13
+ export async function cmdAuth(sub, positional, flags = {}) {
14
+ const m = await _ensureConfigFeatures();
15
+ const cfg = readConfig();
16
+ switch (sub) {
17
+ case undefined:
18
+ case 'list': {
19
+ const provider = positional[0];
20
+ if (!provider) {
21
+ // No provider given → return the active-label map for every
22
+ // provider that has at least one profile so the user can see
23
+ // their full auth state at once.
24
+ const out = {};
25
+ for (const p of Object.keys(cfg.authProfiles || {})) {
26
+ out[p] = {
27
+ active: (cfg.authActiveProfile || {})[p] || null,
28
+ profiles: m.authList(cfg, p),
29
+ };
30
+ }
31
+ console.log(JSON.stringify(out, null, 2));
32
+ return;
33
+ }
34
+ const profiles = m.authList(cfg, provider);
35
+ console.log(JSON.stringify({
36
+ provider,
37
+ active: (cfg.authActiveProfile || {})[provider] || null,
38
+ profiles,
39
+ }, null, 2));
40
+ return;
41
+ }
42
+ case 'add': {
43
+ const [provider, key] = positional;
44
+ if (!provider || !key) {
45
+ console.error('Usage: lazyclaw auth add <provider> <key> [--label <name>]');
46
+ process.exit(2);
47
+ }
48
+ try {
49
+ const lbl = m.authAdd(cfg, provider, key, flags.label);
50
+ writeConfig(cfg);
51
+ console.log(JSON.stringify({ ok: true, provider, label: lbl }));
52
+ } catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
53
+ return;
54
+ }
55
+ case 'remove': {
56
+ const [provider, label] = positional;
57
+ if (!provider || !label) {
58
+ console.error('Usage: lazyclaw auth remove <provider> <label>');
59
+ process.exit(2);
60
+ }
61
+ try { m.authRemove(cfg, provider, label); writeConfig(cfg); }
62
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
63
+ console.log(JSON.stringify({ ok: true, provider, removed: label }));
64
+ return;
65
+ }
66
+ case 'use': {
67
+ const [provider, label] = positional;
68
+ if (!provider || !label) {
69
+ console.error('Usage: lazyclaw auth use <provider> <label>');
70
+ process.exit(2);
71
+ }
72
+ try { m.authUse(cfg, provider, label); writeConfig(cfg); }
73
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
74
+ console.log(JSON.stringify({ ok: true, provider, active: label }));
75
+ return;
76
+ }
77
+ case 'rotate': {
78
+ const provider = positional[0];
79
+ if (!provider) {
80
+ console.error('Usage: lazyclaw auth rotate <provider>');
81
+ process.exit(2);
82
+ }
83
+ const next = m.authRotate(cfg, provider);
84
+ if (!next) {
85
+ console.error(`error: need at least 2 profiles to rotate (provider "${provider}")`);
86
+ process.exit(1);
87
+ }
88
+ writeConfig(cfg);
89
+ console.log(JSON.stringify({ ok: true, provider, active: next }));
90
+ return;
91
+ }
92
+ default:
93
+ console.error('Usage: lazyclaw auth <list|add|remove|use|rotate> ...');
94
+ process.exit(2);
95
+ }
96
+ }
97
+
98
+ export async function cmdPairing(sub, positional, flags = {}) {
99
+ const m = await _ensureConfigFeatures();
100
+ const cfg = readConfig();
101
+ switch (sub) {
102
+ case undefined:
103
+ case 'list':
104
+ console.log(JSON.stringify(m.pairingList(cfg), null, 2));
105
+ return;
106
+ case 'add': {
107
+ const id = positional[0];
108
+ if (!id) {
109
+ console.error('Usage: lazyclaw pairing add <id> [--label <name>]');
110
+ process.exit(2);
111
+ }
112
+ try { m.pairingAdd(cfg, id, flags.label); writeConfig(cfg); }
113
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
114
+ console.log(JSON.stringify({ ok: true, id }));
115
+ return;
116
+ }
117
+ case 'remove': {
118
+ const id = positional[0];
119
+ if (!id) {
120
+ console.error('Usage: lazyclaw pairing remove <id>');
121
+ process.exit(2);
122
+ }
123
+ try { m.pairingRemove(cfg, id); writeConfig(cfg); }
124
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
125
+ console.log(JSON.stringify({ ok: true, removed: id }));
126
+ return;
127
+ }
128
+ default:
129
+ console.error('Usage: lazyclaw pairing <list|add|remove> ...');
130
+ process.exit(2);
131
+ }
132
+ }
133
+
134
+ export async function cmdNodes(sub, positional, flags = {}) {
135
+ const m = await _ensureConfigFeatures();
136
+ const cfg = readConfig();
137
+ switch (sub) {
138
+ case undefined:
139
+ case 'list':
140
+ console.log(JSON.stringify(m.nodesList(cfg), null, 2));
141
+ return;
142
+ case 'register': {
143
+ const id = positional[0];
144
+ if (!id) {
145
+ console.error('Usage: lazyclaw nodes register <id> [--platform macos|ios|android|web|cli] [--label <name>]');
146
+ process.exit(2);
147
+ }
148
+ try { m.nodesRegister(cfg, id, flags.platform || 'cli', flags.label || ''); writeConfig(cfg); }
149
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
150
+ console.log(JSON.stringify({ ok: true, id, platform: flags.platform || 'cli' }));
151
+ return;
152
+ }
153
+ case 'remove': {
154
+ const id = positional[0];
155
+ if (!id) {
156
+ console.error('Usage: lazyclaw nodes remove <id>');
157
+ process.exit(2);
158
+ }
159
+ try { m.nodesRemove(cfg, id); writeConfig(cfg); }
160
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
161
+ console.log(JSON.stringify({ ok: true, removed: id }));
162
+ return;
163
+ }
164
+ // Device-gateway pairing (Phase 27) — distinct from the config-based
165
+ // `nodes register` table above. These drive the Ed25519 PairingStore
166
+ // a companion node authenticates against via `lazyclaw daemon`.
167
+ case 'pending': {
168
+ const { PairingStore } = await import('../gateway/device_auth.mjs');
169
+ const store = new PairingStore(path.dirname(configPath()));
170
+ console.log(JSON.stringify(store.pending(), null, 2));
171
+ return;
172
+ }
173
+ case 'devices': {
174
+ const { PairingStore } = await import('../gateway/device_auth.mjs');
175
+ const store = new PairingStore(path.dirname(configPath()));
176
+ console.log(JSON.stringify(store.devicesList(), null, 2));
177
+ return;
178
+ }
179
+ case 'approve': {
180
+ const requestId = positional[0];
181
+ if (!requestId) {
182
+ console.error('Usage: lazyclaw nodes approve <requestId> (see `lazyclaw nodes pending`)');
183
+ process.exit(2);
184
+ }
185
+ const { PairingStore } = await import('../gateway/device_auth.mjs');
186
+ const store = new PairingStore(path.dirname(configPath()));
187
+ try {
188
+ const { deviceId } = store.approve(requestId);
189
+ // The token is intentionally NOT printed — the device receives its
190
+ // rotated token on its next /gateway/connect, so it never has to
191
+ // pass through a terminal / shell history.
192
+ console.log(JSON.stringify({ ok: true, approved: requestId, deviceId, note: 'device receives its token on next /gateway/connect' }));
193
+ } catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
194
+ return;
195
+ }
196
+ case 'revoke': {
197
+ const deviceId = positional[0];
198
+ if (!deviceId) {
199
+ console.error('Usage: lazyclaw nodes revoke <deviceId>');
200
+ process.exit(2);
201
+ }
202
+ const { PairingStore } = await import('../gateway/device_auth.mjs');
203
+ const store = new PairingStore(path.dirname(configPath()));
204
+ console.log(JSON.stringify(store.revoke(deviceId)));
205
+ return;
206
+ }
207
+ default:
208
+ console.error('Usage: lazyclaw nodes <list|register|remove|pending|approve <requestId>|revoke <deviceId>|devices> ...');
209
+ process.exit(2);
210
+ }
211
+ }
212
+
213
+ export async function cmdMessage(sub, positional, flags = {}) {
214
+ const m = await _ensureConfigFeatures();
215
+ const cfg = readConfig();
216
+ switch (sub) {
217
+ case undefined:
218
+ case 'list':
219
+ console.log(JSON.stringify(m.messageList(cfg), null, 2));
220
+ return;
221
+ case 'add': {
222
+ const [name, url] = positional;
223
+ if (!name || !url) {
224
+ console.error('Usage: lazyclaw message add <name> <webhook-url> [--kind slack|discord|generic]');
225
+ process.exit(2);
226
+ }
227
+ try { m.messageAdd(cfg, name, url, flags.kind); writeConfig(cfg); }
228
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
229
+ console.log(JSON.stringify({ ok: true, name }));
230
+ return;
231
+ }
232
+ case 'remove': {
233
+ const name = positional[0];
234
+ if (!name) {
235
+ console.error('Usage: lazyclaw message remove <name>');
236
+ process.exit(2);
237
+ }
238
+ try { m.messageRemove(cfg, name); writeConfig(cfg); }
239
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
240
+ console.log(JSON.stringify({ ok: true, removed: name }));
241
+ return;
242
+ }
243
+ case 'send': {
244
+ const [name, ...textParts] = positional;
245
+ if (!name) {
246
+ console.error('Usage: lazyclaw message send <name> <text|->');
247
+ process.exit(2);
248
+ }
249
+ let text = textParts.join(' ');
250
+ // `-` reads body from stdin so a long agent reply can be piped:
251
+ // lazyclaw agent "summarize foo" | lazyclaw message send team -
252
+ if (text === '-' || (!text && !process.stdin.isTTY)) {
253
+ text = await new Promise((resolve) => {
254
+ let buf = '';
255
+ process.stdin.on('data', (c) => { buf += c; });
256
+ process.stdin.on('end', () => resolve(buf.trim()));
257
+ });
258
+ }
259
+ if (!text) {
260
+ console.error('error: empty message body');
261
+ process.exit(1);
262
+ }
263
+ try {
264
+ const r = await m.messageSend(cfg, name, text);
265
+ console.log(JSON.stringify(r));
266
+ } catch (e) {
267
+ console.error(`error: ${e.message}`); process.exit(1);
268
+ }
269
+ return;
270
+ }
271
+ default:
272
+ console.error('Usage: lazyclaw message <list|add|remove|send> ...');
273
+ process.exit(2);
274
+ }
275
+ }
276
+
277
+ export async function cmdWorkspace(sub, positional, flags = {}) {
278
+ const ws = await import('../workspace.mjs');
279
+ const cfgDir = path.dirname(configPath());
280
+ switch (sub) {
281
+ case undefined:
282
+ case 'list': {
283
+ console.log(JSON.stringify(ws.listWorkspaces(cfgDir), null, 2));
284
+ return;
285
+ }
286
+ case 'init': {
287
+ const name = positional[0];
288
+ if (!name) { console.error('Usage: lazyclaw workspace init <name>'); process.exit(2); }
289
+ try {
290
+ const dir = ws.initWorkspace(cfgDir, name);
291
+ console.log(JSON.stringify({ ok: true, name, dir, files: ws.WORKSPACE_FILES }));
292
+ } catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
293
+ return;
294
+ }
295
+ case 'show': {
296
+ const [name, fileName] = positional;
297
+ if (!name) { console.error('Usage: lazyclaw workspace show <name> [<file>]'); process.exit(2); }
298
+ try {
299
+ if (fileName) process.stdout.write(ws.readWorkspaceFile(cfgDir, name, fileName));
300
+ else process.stdout.write(ws.composeWorkspacePrompt(cfgDir, name) + '\n');
301
+ } catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
302
+ return;
303
+ }
304
+ case 'remove': {
305
+ const name = positional[0];
306
+ if (!name) { console.error('Usage: lazyclaw workspace remove <name>'); process.exit(2); }
307
+ try { ws.removeWorkspace(cfgDir, name); }
308
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
309
+ console.log(JSON.stringify({ ok: true, removed: name }));
310
+ return;
311
+ }
312
+ case 'path': {
313
+ const name = positional[0];
314
+ if (!name) { console.log(ws.workspaceRoot(cfgDir)); return; }
315
+ try { console.log(ws.workspaceDir(cfgDir, name)); }
316
+ catch (e) { console.error(`error: ${e.message}`); process.exit(1); }
317
+ return;
318
+ }
319
+ default:
320
+ console.error('Usage: lazyclaw workspace <list|init|show|remove|path> ...');
321
+ process.exit(2);
322
+ }
323
+ }