total-recall-brain 3.41.1 → 3.41.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.
Files changed (105) hide show
  1. package/bin/total-recall.mjs +9 -3
  2. package/frontend/dist/assets/{index-BG9NkVUy.css → index-BIQmqDKP.css} +1 -1
  3. package/frontend/dist/assets/{index-BtuiZqwP.js → index-DFenQQEQ.js} +74 -79
  4. package/frontend/dist/index.html +2 -2
  5. package/frontend/dist/sw.js +8 -5
  6. package/frontend/src/api/auth.ts +0 -3
  7. package/frontend/src/components/plugins/PluginPanel.tsx +7 -3
  8. package/frontend/src/components/plugins/card/CardEngine.tsx +1 -1
  9. package/frontend/src/pages/LoginPage.tsx +1 -41
  10. package/frontend/src/pages/PluginsPage.tsx +14 -7
  11. package/package.json +2 -2
  12. package/scaffold/.agent/skills/plugin/scripts/record.mjs +6 -3
  13. package/scaffold/.agent/skills/total-recall/plugins/auth/README.md +10 -0
  14. package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +197 -31
  15. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +1456 -12
  16. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +4 -4
  17. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +8 -1
  18. package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +210 -27
  19. package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-catalog.mjs +10 -1
  20. package/scaffold/.agent/skills/total-recall/plugins/auth/src/rotation-capability.mjs +15 -0
  21. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +78 -6
  22. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-merge.mjs +131 -0
  23. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +111 -2
  24. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +26 -5
  25. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/DESIGN.md +56 -8
  26. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +1068 -204
  27. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/panel.js +4 -2
  28. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +32 -0
  29. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +436 -274
  30. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +4 -4
  31. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +1 -1
  32. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +16 -2
  33. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +461 -242
  34. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +4 -4
  35. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
  36. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +1 -7
  37. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +17 -2
  38. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +426 -300
  39. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +4 -4
  40. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/DESIGN.md +32 -0
  41. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +350 -283
  42. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4 -4
  43. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +31 -19
  44. package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +10 -0
  45. package/scaffold/.agent/skills/total-recall/plugins/mesh/{src/core/headscale-server.mjs → capability/control-server.mjs} +5 -2
  46. package/scaffold/.agent/skills/total-recall/plugins/mesh/capability/headscale-api.mjs +192 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/mesh/capability/index.mjs +46 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +9 -1
  49. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +23 -22
  50. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +6 -3
  51. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +242 -8
  52. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +14 -9
  53. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/server.mjs +1 -1
  54. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/credentials.mjs +4 -2
  55. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/headscale-client.mjs +34 -181
  56. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-auth.mjs +2 -2
  57. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/presence.mjs +5 -5
  58. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/screen-view.mjs +78 -0
  59. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/DESIGN.md +211 -12
  60. package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/card.js +323 -388
  61. package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +12 -1
  62. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +7 -4
  63. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +6 -6
  64. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +3 -2
  65. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +34 -1
  66. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/bootstrap.mjs +99 -0
  67. package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/README.md +10 -0
  68. package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/architecture.md +5 -0
  69. package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/ide.md +10 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/memory.md +11 -0
  71. package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/secrets.md +7 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/skills.md +7 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +16 -2
  74. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +380 -298
  75. package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -1
  76. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +6 -6
  77. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +1 -1
  78. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +1 -1
  79. package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +17 -3
  80. package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +38 -15
  81. package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +60 -34
  82. package/scaffold/.agent/skills/total-recall/references/auth.md +2 -0
  83. package/src/cli/command.mjs +23 -18
  84. package/src/cli/daemon.mjs +23 -1
  85. package/src/cli/plugin/copies.mjs +8 -5
  86. package/src/cli/plugin/index.mjs +25 -2
  87. package/src/cli/rebuild.mjs +22 -0
  88. package/src/cli/reset-password.mjs +3 -2
  89. package/src/cli/skill.mjs +8 -2
  90. package/src/cli/status.mjs +25 -7
  91. package/src/core/automatic-brain-updates.mjs +9 -3
  92. package/src/core/brain-updates.mjs +63 -5
  93. package/src/core/global-brain-updates.mjs +21 -1
  94. package/src/core/mesh-brain-updates.mjs +5 -2
  95. package/src/core/meta-harness.mjs +16 -41
  96. package/src/core/plugin-runner.mjs +6 -1
  97. package/src/core/plugin-store.mjs +67 -1
  98. package/src/core/plugin-updates.mjs +1 -1
  99. package/src/core/portable-skills-maintenance.mjs +19 -0
  100. package/src/core/required-plugins.mjs +7 -0
  101. package/src/core/skills-registry.mjs +33 -3
  102. package/src/server/auth.mjs +41 -1
  103. package/src/server/memory-app.mjs +2 -1
  104. package/src/server/routes/auth.mjs +25 -1
  105. package/src/server/routes/plugins.mjs +17 -4
@@ -9,6 +9,7 @@
9
9
  import { spawnSync } from 'node:child_process';
10
10
  import fs from 'node:fs';
11
11
  import path from 'node:path';
12
+ import { hostSecretsBrainDir } from './src/host-secrets-store.mjs';
12
13
 
13
14
  function resolveTrPath() {
14
15
  const localTr = path.join(process.cwd(), 'bin', 'total-recall.mjs');
@@ -19,13 +20,13 @@ function resolveTrPath() {
19
20
  }
20
21
 
21
22
  async function resolveSecret(key) {
22
- const { getSecret, defaultBrainDir } = await import('./src/secrets-store.mjs');
23
- const result = await getSecret(process.env.TR_SECRETS_BRAIN || defaultBrainDir(), key, { actor: 'auth-cli', action: 'use' });
23
+ const { getSecret } = await import('./src/secrets-store.mjs');
24
+ const result = await getSecret(hostSecretsBrainDir(), key, { actor: 'auth-cli', action: 'use' });
24
25
  return result.found ? result.value : null;
25
26
  }
26
27
 
27
28
  function resolveBrainDir() {
28
- return process.env.TR_SECRETS_BRAIN || process.env.TR_BRAIN || path.join(process.env.HOME || '', '.agent', 'skills', 'total-recall');
29
+ return hostSecretsBrainDir();
29
30
  }
30
31
 
31
32
  async function listStoredPasskeys() {
@@ -33,7 +34,11 @@ async function listStoredPasskeys() {
33
34
  return listPasskeys(resolveBrainDir());
34
35
  }
35
36
 
36
- async function handleStatus(json) {
37
+ async function handleStatus(subArgs, json) {
38
+ if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
39
+ console.log('Usage: total-recall auth status [--json]');
40
+ return;
41
+ }
37
42
  const passkeys = await listStoredPasskeys();
38
43
  const googleClientId = await resolveSecret('GOOGLE_CLIENT_ID');
39
44
  const googleClientSecret = await resolveSecret('GOOGLE_CLIENT_SECRET');
@@ -87,9 +92,15 @@ async function handleStatus(json) {
87
92
  }
88
93
 
89
94
  async function handlePasskey(subArgs, json) {
90
- const passkeys = await listStoredPasskeys();
91
95
  const action = subArgs[0] || 'list';
92
96
 
97
+ if (action === 'help' || action === '--help' || action === '-h') {
98
+ console.log('Usage: total-recall auth passkey [list|status|delete <id>] [--json]');
99
+ return;
100
+ }
101
+
102
+ const passkeys = await listStoredPasskeys();
103
+
93
104
  if (action === 'list') {
94
105
  if (json) {
95
106
  console.log(JSON.stringify({ count: passkeys.length, passkeys }, null, 2));
@@ -112,25 +123,80 @@ async function handlePasskey(subArgs, json) {
112
123
  return;
113
124
  }
114
125
 
126
+ if (action === 'delete' || action === 'remove' || action === 'rm') {
127
+ const id = subArgs[1];
128
+ if (!id) {
129
+ console.error('Credential ID required: total-recall auth passkey delete <id>');
130
+ process.exitCode = 1;
131
+ return;
132
+ }
133
+ const { deletePasskey } = await import('./src/webauthn-store.mjs');
134
+ const result = deletePasskey(resolveBrainDir(), id);
135
+ if (!result.found) {
136
+ console.error(`Passkey not found: ${id}`);
137
+ process.exitCode = 1;
138
+ return;
139
+ }
140
+ if (json) {
141
+ console.log(JSON.stringify({ success: true, deleted: id }));
142
+ } else {
143
+ console.log(`Passkey removed: ${id}`);
144
+ }
145
+ return;
146
+ }
147
+
115
148
  console.error(`Unknown passkey action: ${action}`);
116
149
  process.exitCode = 1;
117
150
  }
118
151
 
119
152
  async function handleGoogle(subArgs, json) {
153
+ const action = subArgs[0] || 'status';
154
+ if (action === 'help' || action === '--help' || action === '-h') {
155
+ console.log('Usage: total-recall auth google [status|connect|accounts|refresh <id>|disconnect <id>] [--json]');
156
+ return;
157
+ }
120
158
  const { createGoogleOAuth } = await import('./src/google-oauth.mjs');
121
159
  const google = createGoogleOAuth({ brainDir: resolveBrainDir() });
122
- const action = subArgs[0] || 'status';
123
- if (action === 'accounts') { console.log(JSON.stringify({ accounts: google.status() })); return; }
160
+ if (action === 'accounts') {
161
+ const accounts = google.status();
162
+ if (json) {
163
+ console.log(JSON.stringify({ accounts }));
164
+ } else {
165
+ console.log(`Google Connected Accounts: ${accounts.length}`);
166
+ for (const a of accounts) {
167
+ console.log(` • [${a.status}] ${a.email || a.account_id} (Expires: ${a.expiry || 'never'})`);
168
+ }
169
+ }
170
+ return;
171
+ }
124
172
  if (action === 'refresh' || action === 'disconnect') {
125
- if (!subArgs[1]) throw new Error('Google account identifier required');
126
- if (action === 'refresh') { await google.access(subArgs[1]); console.log(JSON.stringify({ accounts: google.status() })); }
127
- else console.log(JSON.stringify(await google.disconnect(subArgs[1])));
173
+ if (!subArgs[1]) {
174
+ console.error(`Google account identifier required: total-recall auth google ${action} <id>`);
175
+ process.exitCode = 1;
176
+ return;
177
+ }
178
+ if (action === 'refresh') {
179
+ await google.access(subArgs[1]);
180
+ const accounts = google.status();
181
+ if (json) console.log(JSON.stringify({ accounts }));
182
+ else console.log(`Account ${subArgs[1]} refreshed.`);
183
+ } else {
184
+ const res = await google.disconnect(subArgs[1]);
185
+ if (json) console.log(JSON.stringify(res));
186
+ else console.log(`Account ${subArgs[1]} disconnected.`);
187
+ }
188
+ return;
189
+ }
190
+ if (!['status', 'connect'].includes(action)) {
191
+ console.error(`Unknown action: ${action}. Use: total-recall auth google [status|connect|accounts|refresh|disconnect]`);
192
+ process.exitCode = 1;
128
193
  return;
129
194
  }
130
- if (!['status', 'connect'].includes(action)) throw new Error('Use google status|connect|accounts|refresh|disconnect');
131
195
  let cfg;
132
196
  try { cfg = google.config(); } catch {
133
- console.log(JSON.stringify({ configured: false, error: 'Configure GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET and GOOGLE_REDIRECT_URI in auth' })); process.exitCode = 2; return;
197
+ console.log(JSON.stringify({ configured: false, error: 'Configure GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET and GOOGLE_REDIRECT_URI in auth' }));
198
+ process.exitCode = 2;
199
+ return;
134
200
  }
135
201
  // Browser initiation sets its own HttpOnly state cookie; never manufacture a state-less consent URL in the CLI.
136
202
  const authUrl = new URL('/auth/google', cfg.redirectUri).href;
@@ -138,7 +204,11 @@ async function handleGoogle(subArgs, json) {
138
204
  console.log(json ? JSON.stringify(out) : `Open sign-in: ${authUrl}`);
139
205
  }
140
206
 
141
- async function handleProbe(json) {
207
+ async function handleProbe(subArgs, json) {
208
+ if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
209
+ console.log('Usage: total-recall auth probe [--json]');
210
+ return;
211
+ }
142
212
  const host = process.env.TR_HOST || 'http://localhost:3000';
143
213
  const endpoints = ['/auth/status', '/auth/providers', '/health'];
144
214
  const results = [];
@@ -182,7 +252,11 @@ async function handleProbe(json) {
182
252
  if (!allOk) process.exitCode = 1;
183
253
  }
184
254
 
185
- async function handleSession(json) {
255
+ async function handleSession(subArgs, json) {
256
+ if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
257
+ console.log('Usage: total-recall auth session [--json]');
258
+ return;
259
+ }
186
260
  const brain = resolveBrainDir();
187
261
  const secretExists = Boolean(await resolveSecret('SESSION_SECRET'));
188
262
 
@@ -208,7 +282,11 @@ async function handleSecret(subArgs, json) {
208
282
  await secretCli(forwarded);
209
283
  }
210
284
 
211
- async function handleAudit(json) {
285
+ async function handleAudit(subArgs, json) {
286
+ if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
287
+ console.log('Usage: total-recall auth audit [--json]');
288
+ return;
289
+ }
212
290
  const brain = resolveBrainDir();
213
291
  const webauthnPath = path.join(brain, 'config', 'webauthn.json');
214
292
  const findings = [];
@@ -284,16 +362,41 @@ async function handleAudit(json) {
284
362
  }
285
363
 
286
364
  async function handleRotate(subArgs, json) {
365
+ if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
366
+ console.log('Usage: total-recall auth rotate [--collaboration] [--json]');
367
+ return;
368
+ }
287
369
  const { createSessionCredentials } = await import('./src/session-credentials.mjs');
288
370
  const { defaultBrainDir } = await import('./src/secrets-store.mjs');
289
371
  const purpose = subArgs.includes('--collaboration') ? 'collaboration' : 'dashboard';
290
372
  const result = createSessionCredentials({ brainDir: process.env.TR_SECRETS_BRAIN || defaultBrainDir() }).rotateSession(purpose);
291
373
  console.log(json ? JSON.stringify(result) : 'Auth signing credential rotated; existing sessions are invalidated.');
292
374
  }
375
+
293
376
  async function handlePassword(args, json) {
294
- if (args[0] !== 'set' || !args.includes('--stdin') || args.slice(1).some(value => !['--stdin', '--force-reset'].includes(value))) throw new Error('Use auth password set --stdin');
377
+ if (!Array.isArray(args) || args.includes('--help') || args.includes('-h') || args[0] === 'help') {
378
+ console.log('Usage: total-recall auth password set --stdin [--force-reset]');
379
+ console.log(' total-recall auth password status [--json]');
380
+ return;
381
+ }
382
+ if (args[0] === 'status') {
383
+ const { createSessionCredentials } = await import('./src/session-credentials.mjs');
384
+ const state = createSessionCredentials({ brainDir: resolveBrainDir() }).dashboardState();
385
+ if (json) {
386
+ console.log(JSON.stringify(state));
387
+ } else {
388
+ console.log(`Dashboard Password: ${state.configured ? 'Configured' : 'Not configured'}`);
389
+ if (state.forcePasswordReset) console.log(' • Password reset is currently enforced');
390
+ }
391
+ return;
392
+ }
393
+ if (args[0] !== 'set' || !args.includes('--stdin') || args.slice(1).some(value => !['--stdin', '--force-reset'].includes(value))) {
394
+ console.error('Use: total-recall auth password set --stdin [--force-reset]');
395
+ process.exitCode = 1;
396
+ return;
397
+ }
295
398
  const chunks = []; let size = 0;
296
- for await (const chunk of process.stdin) { size += chunk.length; if (size > 8192) throw new Error('Password input exceeds limit'); chunks.push(chunk); }
399
+ for await (const chunk of process.stdin) { size += chunk.length; if (size > 8192) { console.error('Password input exceeds limit'); process.exitCode = 1; return; } chunks.push(chunk); }
297
400
  const password = Buffer.concat(chunks).toString('utf8').replace(/\r?\n$/, '');
298
401
  const { createSessionCredentials } = await import('./src/session-credentials.mjs');
299
402
  const result = await createSessionCredentials({ brainDir: resolveBrainDir() }).setDashboardPassword(password, { forcePasswordReset: args.includes('--force-reset') });
@@ -313,10 +416,10 @@ export async function run(argv = []) {
313
416
 
314
417
  switch (command) {
315
418
  case 'status':
316
- await handleStatus(isJson);
419
+ await handleStatus(rest, isJson);
317
420
  break;
318
421
  case 'audit':
319
- await handleAudit(isJson);
422
+ await handleAudit(rest, isJson);
320
423
  break;
321
424
  case 'rotate':
322
425
  await handleRotate(rest, isJson);
@@ -328,32 +431,95 @@ export async function run(argv = []) {
328
431
  await handlePasskey(rest, isJson);
329
432
  break;
330
433
  case 'key': {
434
+ if (rest.includes('--help') || rest.includes('-h') || rest[0] === 'help') {
435
+ console.log('Usage: total-recall auth key list [--json]');
436
+ console.log(' total-recall auth key issue <name> --scopes <csv> [--role <role>] [--kind <kind>] [--expires <date>] [--json]');
437
+ console.log(' total-recall auth key revoke <id> [--json]');
438
+ console.log(' total-recall auth key usage [id] [--json]');
439
+ console.log(' total-recall auth key roles [--json]');
440
+ console.log(' total-recall auth key role-set <name> --scopes <csv> [--json]');
441
+ break;
442
+ }
331
443
  const { createKeyStore } = await import('./src/access-keys.mjs');
332
444
  const store = createKeyStore({ brainDir: resolveBrainDir() });
333
445
  const [action = 'list', id] = rest;
334
446
  const flag = name => { const at = rest.indexOf(name); return at < 0 ? undefined : rest[at + 1]; };
335
447
  let result;
336
- if (action === 'list') result = store.loadKeys().map(({ token_hash, ...key }) => key);
337
- else if (action === 'issue') result = store.issueKey(id, { scopes: flag('--scopes')?.split(','), role: flag('--role'), kind: flag('--kind'), expires_at: flag('--expires') });
338
- else if (action === 'revoke') result = store.revokeKey(id);
339
- else if (action === 'usage') result = store.listKeyUsage(id);
340
- else if (action === 'roles') result = store.loadRoles();
341
- else if (action === 'role-set') result = store.setRole(id, flag('--scopes')?.split(','));
342
- else throw new Error('Use auth key list|issue <name> --scopes <csv> [--role role] [--expires date]|revoke <id>|usage [id]|roles|role-set <name> --scopes <csv>');
343
- console.log(JSON.stringify(result)); break;
448
+ if (action === 'list') {
449
+ const keys = store.loadKeys().map(({ token_hash, ...key }) => key);
450
+ if (isJson) {
451
+ console.log(JSON.stringify(keys));
452
+ } else {
453
+ console.log(`Access Keys: ${keys.length}`);
454
+ for (const k of keys) {
455
+ const status = k.revoked ? '[REVOKED]' : '[ACTIVE]';
456
+ console.log(` • ${status} ${k.name} (${k.token_prefix}…)`);
457
+ console.log(` ID: ${k.id}`);
458
+ console.log(` Scopes: ${(k.scopes || []).join(', ') || 'none'}`);
459
+ if (k.expires_at) console.log(` Expires: ${k.expires_at}`);
460
+ if (k.last_used_at) console.log(` Last used: ${k.last_used_at} (${k.hit_count} hits)`);
461
+ }
462
+ }
463
+ break;
464
+ } else if (action === 'issue') {
465
+ result = store.issueKey(id, { scopes: flag('--scopes')?.split(','), role: flag('--role'), kind: flag('--kind'), expires_at: flag('--expires') });
466
+ } else if (action === 'revoke') {
467
+ result = store.revokeKey(id);
468
+ } else if (action === 'usage') {
469
+ result = store.listKeyUsage(id);
470
+ } else if (action === 'roles') {
471
+ result = store.loadRoles();
472
+ } else if (action === 'role-set') {
473
+ result = store.setRole(id, flag('--scopes')?.split(','));
474
+ } else {
475
+ console.error('Use: total-recall auth key list|issue|revoke|usage|roles|role-set');
476
+ process.exitCode = 1;
477
+ break;
478
+ }
479
+ if (isJson) {
480
+ console.log(JSON.stringify(result));
481
+ } else {
482
+ if (action === 'issue') {
483
+ console.log(`Key issued: ${result.name} (${result.id})`);
484
+ console.log(`Token: ${result.token}`);
485
+ console.log('Store this token now; it will not be displayed again.');
486
+ } else if (action === 'revoke') {
487
+ console.log(`Key revoked: ${id}`);
488
+ } else {
489
+ console.log(JSON.stringify(result, null, 2));
490
+ }
491
+ }
492
+ break;
344
493
  }
345
494
  case 'agent': {
346
495
  // Sign in an agent CLI that authenticates on its own terminal. The pty lives in
347
496
  // scripts/agent-login-pty.py because Node has none and this plugin carries no pty library;
348
497
  // this routes the plugin's own surface to it.
498
+ if (rest.includes('--help') || rest.includes('-h') || rest[0] === 'help') {
499
+ console.log('Usage: total-recall auth agent list [--json]');
500
+ console.log(' total-recall auth agent login <agent> [--code <code>] [--code-file <path>] [--url-file <path>] [--json]');
501
+ break;
502
+ }
349
503
  const { loginAgent, AGENT_LOGIN_PROFILES } = await import('./src/agent-login.mjs');
350
504
  const [action = 'list', name] = rest;
351
505
  const flag = (n) => { const at = rest.indexOf(n); return at < 0 ? undefined : rest[at + 1]; };
352
506
  if (action === 'list') {
353
- console.log(JSON.stringify(Object.keys(AGENT_LOGIN_PROFILES).map((id) => ({ id, ...AGENT_LOGIN_PROFILES[id] }))));
507
+ const list = Object.keys(AGENT_LOGIN_PROFILES).map((id) => ({ id, ...AGENT_LOGIN_PROFILES[id] }));
508
+ if (isJson) {
509
+ console.log(JSON.stringify(list));
510
+ } else {
511
+ console.log('Supported Agent Profiles:');
512
+ for (const item of list) {
513
+ console.log(` • ${item.id}: ${item.name || item.id}`);
514
+ }
515
+ }
516
+ break;
517
+ }
518
+ if (action !== 'login') {
519
+ console.error('Use: total-recall auth agent list | total-recall auth agent login <agent>');
520
+ process.exitCode = 1;
354
521
  break;
355
522
  }
356
- if (action !== 'login') throw new Error('Use auth agent list | auth agent login <agent> [--code <code>] [--code-file <path>] [--url-file <path>]');
357
523
  const result = await loginAgent({
358
524
  agent: name,
359
525
  code: flag('--code'),
@@ -371,10 +537,10 @@ export async function run(argv = []) {
371
537
  await handleGoogle(rest, isJson);
372
538
  break;
373
539
  case 'probe':
374
- await handleProbe(isJson);
540
+ await handleProbe(rest, isJson);
375
541
  break;
376
542
  case 'session':
377
- await handleSession(isJson);
543
+ await handleSession(rest, isJson);
378
544
  break;
379
545
  case 'secret':
380
546
  case 'secrets':