total-recall-brain 3.40.1 → 3.40.2

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 (186) hide show
  1. package/README.md +152 -41
  2. package/bin/total-recall.mjs +16 -76
  3. package/docs/reference/cli-reference.md +10 -10
  4. package/extension/background.js +6 -2
  5. package/extension/config.js +8 -0
  6. package/extension/lib/brain-client.js +29 -4
  7. package/extension/options/options.html +1 -0
  8. package/extension/sidepanel/sidepanel.html +1 -0
  9. package/frontend/dist/assets/{index-CvQRdT2j.css → index-AGUes-VG.css} +1 -1
  10. package/frontend/dist/assets/index-Bt5iVPZj.js +5618 -0
  11. package/frontend/dist/index.html +2 -2
  12. package/frontend/src/api/keys.ts +9 -2
  13. package/frontend/src/api/plugins.ts +4 -0
  14. package/frontend/src/components/plugins/card/CardEngine.tsx +2 -1
  15. package/frontend/src/components/plugins/card/card.css +1 -1
  16. package/frontend/src/pages/IntegrationsPage.tsx +315 -8
  17. package/frontend/src/pages/PluginsPage.tsx +532 -87
  18. package/package.json +6 -5
  19. package/scaffold/.agent/skills/meta-harness/references/optimized/original.md +1 -1
  20. package/scaffold/.agent/skills/meta-harness/references/optimized/section-09.md +1 -1
  21. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +10 -10
  22. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -6
  23. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +0 -1
  24. package/scaffold/.agent/skills/total-recall/plugins/auth/src/browser-session.mjs +309 -4
  25. package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +44 -34
  26. package/scaffold/.agent/skills/total-recall/plugins/auth/src/google-oauth.mjs +6 -4
  27. package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-catalog.mjs +1 -1
  28. package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-rotation-recipes.mjs +118 -4
  29. package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/auth.mjs +2 -2
  30. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +3 -3
  31. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +1 -1
  32. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-rotate.mjs +24 -6
  33. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +164 -123
  34. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +65 -0
  35. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +76 -0
  36. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +19 -0
  37. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +290 -0
  38. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +446 -0
  39. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +152 -0
  40. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +71 -0
  41. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +36 -0
  42. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +35 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +175 -0
  44. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +18 -0
  45. package/scaffold/.agent/skills/total-recall/plugins/code-quality/LICENSE +21 -0
  46. package/scaffold/.agent/skills/total-recall/plugins/code-quality/README.md +84 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +79 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/check.mjs +6 -0
  49. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/report.mjs +6 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +27 -0
  51. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/snapshot.mjs +163 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +757 -0
  53. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/config.schema.json +86 -0
  54. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/detect.mjs +89 -0
  55. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +145 -0
  56. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +210 -0
  57. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/state.mjs +20 -0
  58. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +2869 -0
  59. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +28 -0
  60. package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +186 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +18 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +272 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +129 -0
  64. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/LICENSE +21 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/README.md +73 -0
  66. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/cli.mjs +375 -0
  67. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/echo-tool.mjs +23 -0
  68. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/group.mjs +262 -0
  69. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/project-id.mjs +74 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +2869 -0
  71. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +31 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +221 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/schemas/cli_command.schema.json +107 -0
  74. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/skills/composable-cli/SKILL.md +63 -0
  75. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +313 -0
  76. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compile.mjs +19 -0
  77. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compose.mjs +144 -0
  78. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/contract.mjs +110 -0
  79. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/library.mjs +23 -0
  80. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/registry.mjs +192 -0
  81. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/resolve.mjs +221 -0
  82. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/risk.mjs +126 -0
  83. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/ssss-documents.mjs +29 -0
  84. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +18 -0
  85. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +273 -0
  86. package/scaffold/.agent/skills/total-recall/plugins/embeddings/LICENSE +21 -0
  87. package/scaffold/.agent/skills/total-recall/plugins/embeddings/README.md +20 -0
  88. package/scaffold/.agent/skills/total-recall/plugins/embeddings/cli.mjs +310 -0
  89. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +2826 -0
  90. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +11 -0
  91. package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +62 -0
  92. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +297 -0
  93. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/LICENSE +21 -0
  94. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +43 -0
  95. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/cli.mjs +258 -0
  96. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4005 -0
  97. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +20 -0
  98. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +125 -0
  99. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +18 -0
  100. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +278 -0
  101. package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +9 -0
  102. package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +6 -0
  103. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +4721 -0
  104. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -21
  105. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +14 -1
  106. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +10 -0
  107. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/headscale-client.mjs +5 -1
  108. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +4 -13
  109. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-access-verify.mjs +22 -0
  110. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-files.mjs +18 -0
  111. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +2 -0
  112. package/scaffold/.agent/skills/total-recall/plugins/mesh/test-support/memory-root.mjs +14 -0
  113. package/scaffold/.agent/skills/total-recall/plugins/openwiki/LICENSE +21 -0
  114. package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +35 -0
  115. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +124 -0
  116. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4097 -0
  117. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +12 -0
  118. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +165 -0
  119. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +127 -0
  120. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +18 -0
  121. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +279 -0
  122. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +539 -0
  123. package/scaffold/.agent/skills/total-recall/plugins/security/LICENSE +21 -0
  124. package/scaffold/.agent/skills/total-recall/plugins/security/README.md +67 -0
  125. package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +932 -0
  126. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3256 -0
  127. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +25 -0
  128. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +297 -0
  129. package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +4 -0
  130. package/scaffold/.agent/skills/total-recall/plugins/security/skills/security/SKILL.md +37 -0
  131. package/scaffold/.agent/skills/total-recall/plugins/security/src/android.mjs +21 -0
  132. package/scaffold/.agent/skills/total-recall/plugins/security/src/devices.mjs +94 -0
  133. package/scaffold/.agent/skills/total-recall/plugins/security/src/machines.mjs +164 -0
  134. package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +41 -0
  135. package/scaffold/.agent/skills/total-recall/plugins/security/src/windows.mjs +42 -0
  136. package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +18 -0
  137. package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +95 -0
  138. package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +98 -0
  139. package/scaffold/.agent/skills/total-recall/references/auth.md +1 -1
  140. package/src/cli/brief.mjs +33 -9
  141. package/src/cli/command.mjs +2 -7
  142. package/src/cli/config.mjs +1 -0
  143. package/src/cli/connect.mjs +5 -0
  144. package/src/cli/context.mjs +2 -1
  145. package/src/cli/daemon.mjs +36 -1
  146. package/src/cli/doctor.mjs +6 -0
  147. package/src/cli/harness.mjs +3 -0
  148. package/src/cli/help.mjs +26 -0
  149. package/src/cli/init.mjs +4 -4
  150. package/src/cli/memory-entry.mjs +1 -0
  151. package/src/cli/memory-init.mjs +2 -0
  152. package/src/cli/plugin/index.mjs +11 -0
  153. package/src/cli/plugin/install.mjs +2 -1
  154. package/src/cli/rebuild.mjs +3 -1
  155. package/src/cli/startup.mjs +2 -1
  156. package/src/cli/update.mjs +19 -0
  157. package/src/core/automatic-brain-updates.mjs +67 -0
  158. package/src/core/brain-updates.mjs +21 -2
  159. package/src/core/cli-commands.mjs +71 -0
  160. package/src/core/cli-maintenance.mjs +45 -0
  161. package/src/core/cli-quick-reference.mjs +20 -0
  162. package/src/core/cli-semantic-index.mjs +722 -0
  163. package/src/core/context-compiler.mjs +27 -1
  164. package/src/core/crons.mjs +2 -13
  165. package/src/core/daemon-control.mjs +6 -4
  166. package/src/core/daemon-loop.mjs +12 -8
  167. package/src/core/default-plugins.mjs +5 -0
  168. package/src/core/global-brain-updates.mjs +39 -0
  169. package/src/core/mesh-brain-updates.mjs +30 -0
  170. package/src/core/meta-harness.mjs +108 -0
  171. package/src/core/plugin-dependencies.mjs +2 -1
  172. package/src/core/plugin-loader.mjs +2 -1
  173. package/src/core/plugin-runner.mjs +12 -4
  174. package/src/core/plugin-setup.mjs +20 -0
  175. package/src/core/plugin-store.mjs +22 -4
  176. package/src/core/plugin-tasks.mjs +1 -1
  177. package/src/core/project-brain.mjs +2 -0
  178. package/src/core/project-status.mjs +64 -0
  179. package/src/core/required-plugins.mjs +46 -0
  180. package/src/core/scaffold-plugins.mjs +17 -3
  181. package/src/core/source-brain-updates.mjs +47 -0
  182. package/src/core/startup-health.mjs +5 -3
  183. package/src/core/surface.mjs +44 -10
  184. package/src/server/routes/extension.mjs +53 -8
  185. package/templates/onboarding-interview.md +1 -1
  186. package/frontend/dist/assets/index-SOVUlZqm.js +0 -5596
@@ -1,15 +1,15 @@
1
1
  /**
2
- * total-recall secret — manage API keys and credentials (not in the memory vault).
2
+ * total-recall auth secret — manage API keys and credentials (not in the memory vault).
3
3
  *
4
4
  * Usage:
5
- * npx total-recall secret set <key> <value> [--provider name] [--scope global|project]
6
- * npx total-recall secret get <key> # prints value (use carefully)
7
- * npx total-recall secret list
8
- * npx total-recall secret rotate <key> # prompts for the value
9
- * npx total-recall secret delete <key>
10
- * npx total-recall secret audit [--limit N]
11
- * npx total-recall secret usage [--days N]
12
- * npx total-recall secret usage-record --provider x --cost 0.01
5
+ * npx total-recall auth secret set <key> <value> [--provider name] [--scope global|project]
6
+ * npx total-recall auth secret get <key> # prints value (use carefully)
7
+ * npx total-recall auth secret list
8
+ * npx total-recall auth secret rotate <key> # prompts for the value
9
+ * npx total-recall auth secret delete <key>
10
+ * npx total-recall auth secret audit [--limit N]
11
+ * npx total-recall auth secret usage [--days N]
12
+ * npx total-recall auth secret usage-record --provider x --cost 0.01
13
13
  */
14
14
 
15
15
  import {
@@ -184,9 +184,9 @@ function readSecretValue(label) {
184
184
 
185
185
  function printHelp() {
186
186
  console.log(`
187
- total-recall secret — Secrets store (separate from memory vault)
187
+ total-recall auth secret — Secrets store (separate from memory vault)
188
188
 
189
- Usage: total-recall secret <command> [options]
189
+ Usage: total-recall auth secret <command> [options]
190
190
 
191
191
  Commands:
192
192
  set <key> [value] Store a secret (encrypted, unlock required; 0600 file)
@@ -220,7 +220,7 @@ function printHelp() {
220
220
  --no-global Only keys bound to this repo (skip unbound)
221
221
  --replace-all Overwrite entire .env (default: merge TR block only)
222
222
  --remote <name> Also push the SSOT projection to a configured remote
223
- target over SSH (see 'secret remote') instead of
223
+ target over SSH (see 'auth secret remote') instead of
224
224
  (or in addition to) a local file
225
225
  --dry-run Show keys that would be written
226
226
  remote <cmd> Manage remote (production) deploy targets — generic,
@@ -282,7 +282,7 @@ function printHelp() {
282
282
  --export-env After rotate, write .env to bound repos (and cwd if unbound)
283
283
  --export-all After rotate, export-env to all project-registry paths
284
284
  --remote <name> After rotate, also push to a configured remote target
285
- (production, etc.) over SSH — see 'secret remote'
285
+ (production, etc.) over SSH — see 'auth secret remote'
286
286
 
287
287
  set / rotate options:
288
288
  --provider <name> Optional provider tag (e.g. openai, anthropic)
@@ -549,6 +549,12 @@ function parseArgs(args) {
549
549
  case '--headless':
550
550
  out.headless = true;
551
551
  break;
552
+ case '--chrome':
553
+ out.chrome = true;
554
+ break;
555
+ case '--revoke-old':
556
+ out.revokeOld = true;
557
+ break;
552
558
  case '--json':
553
559
  out.json = true;
554
560
  break;
@@ -647,7 +653,7 @@ export default async function secretCli(argv) {
647
653
  process.exit(1);
648
654
  }
649
655
  if (opts.command !== 'rekey' && !opts.key) {
650
- console.error(`Usage: total-recall secret ${opts.command} <key> --stdin`);
656
+ console.error(`Usage: total-recall auth secret ${opts.command} <key> --stdin`);
651
657
  process.exit(1);
652
658
  }
653
659
  opts.value = readStdinValue();
@@ -750,7 +756,7 @@ export default async function secretCli(argv) {
750
756
 
751
757
  if (opts.command === 'set') {
752
758
  if (!opts.key || opts.value == null) {
753
- console.error('Usage: total-recall secret set <key> <value>');
759
+ console.error('Usage: total-recall auth secret set <key> <value>');
754
760
  process.exit(1);
755
761
  }
756
762
  await setSecret(brainDir, opts.key, opts.value, {
@@ -778,7 +784,7 @@ export default async function secretCli(argv) {
778
784
 
779
785
  if (opts.command === 'generate') {
780
786
  if (!opts.key) {
781
- console.error('Usage: total-recall secret generate <key> [--bytes 32] [--format hex|base64|base64url]');
787
+ console.error('Usage: total-recall auth secret generate <key> [--bytes 32] [--format hex|base64|base64url]');
782
788
  process.exit(1);
783
789
  }
784
790
  const crypto = await import('node:crypto');
@@ -831,7 +837,7 @@ export default async function secretCli(argv) {
831
837
  if (multiError.length) {
832
838
  console.log(
833
839
  ` ❌ ${multiError.length} key(s) bound to multiple repos — fix with:\n` +
834
- ` secret meta <KEY> --repo <one-repo>\n` +
840
+ ` auth secret meta <KEY> --repo <one-repo>\n` +
835
841
  ` or clear repos for Developer secrets\n`,
836
842
  );
837
843
  }
@@ -975,10 +981,10 @@ export default async function secretCli(argv) {
975
981
  }
976
982
  if (g.error) console.log(` → ${g.error}`);
977
983
  console.log(
978
- ` fix: rotate each key to a unique value, then: secret rotate <key> <new> --export-env`,
984
+ ` fix: rotate each key to a unique value, then: auth secret rotate <key> <new> --export-env`,
979
985
  );
980
986
  console.log(
981
- ` waive (rare): secret meta <key> --shared-ok # only if intentional mirror`,
987
+ ` waive (rare): auth secret meta <key> --shared-ok # only if intentional mirror`,
982
988
  );
983
989
  console.log('');
984
990
  }
@@ -991,7 +997,7 @@ export default async function secretCli(argv) {
991
997
 
992
998
  if (opts.command === 'meta') {
993
999
  if (!opts.key) {
994
- console.error('Usage: total-recall secret meta <key> [--repo ...] [--tier ...]');
1000
+ console.error('Usage: total-recall auth secret meta <key> [--repo ...] [--tier ...]');
995
1001
  process.exit(1);
996
1002
  }
997
1003
  const patch = {};
@@ -1046,15 +1052,15 @@ export default async function secretCli(argv) {
1046
1052
  const r = await enqueueRotationDueTasks(brainDir, { autoOnly: false });
1047
1053
  console.log(`\n 📋 Enqueued ${r.tasks.filter((t) => t.status === 'created').length} rotation assist task(s)`);
1048
1054
  }
1049
- console.log('\n Rotate: npx total-recall secret rotate <key> <new> --export-env');
1050
- console.log(' Browser assist: npx total-recall secret rotate-browser <key>');
1051
- console.log(' Enqueue tasks: npx total-recall secret rotation-due --enqueue\n');
1055
+ console.log('\n Rotate: npx total-recall auth secret rotate <key> <new> --export-env');
1056
+ console.log(' Browser assist: npx total-recall auth secret rotate-browser <key>');
1057
+ console.log(' Enqueue tasks: npx total-recall auth secret rotation-due --enqueue\n');
1052
1058
  return;
1053
1059
  }
1054
1060
 
1055
1061
  if (opts.command === 'get') {
1056
1062
  if (!opts.key) {
1057
- console.error('Usage: total-recall secret get <key>');
1063
+ console.error('Usage: total-recall auth secret get <key>');
1058
1064
  process.exit(1);
1059
1065
  }
1060
1066
  const project_id = currentProjectId();
@@ -1105,7 +1111,7 @@ export default async function secretCli(argv) {
1105
1111
  if (opts.command === 'remote') {
1106
1112
  if (!opts.subcommand) {
1107
1113
  console.error(
1108
- 'Usage: total-recall secret remote <add|list|remove|deploy> [...]\n' +
1114
+ 'Usage: total-recall auth secret remote <add|list|remove|deploy> [...]\n' +
1109
1115
  ' add <name> --host <h> --path </remote/dir> [--user root] [--port 22] [--filename .env] [--restart-cmd "..."]\n' +
1110
1116
  ' list\n' +
1111
1117
  ' remove <name>\n' +
@@ -1117,7 +1123,7 @@ export default async function secretCli(argv) {
1117
1123
  if (opts.subcommand === 'add') {
1118
1124
  if (!opts.key || !opts.remoteHost || !opts.path) {
1119
1125
  console.error(
1120
- 'Usage: total-recall secret remote add <name> --host <host> --path </remote/dir> [--user root] [--port 22] [--filename .env] [--restart-cmd "..."]',
1126
+ 'Usage: total-recall auth secret remote add <name> --host <host> --path </remote/dir> [--user root] [--port 22] [--filename .env] [--restart-cmd "..."]',
1121
1127
  );
1122
1128
  process.exit(1);
1123
1129
  }
@@ -1158,7 +1164,7 @@ export default async function secretCli(argv) {
1158
1164
 
1159
1165
  if (opts.subcommand === 'remove' || opts.subcommand === 'rm') {
1160
1166
  if (!opts.key) {
1161
- console.error('Usage: total-recall secret remote remove <name>');
1167
+ console.error('Usage: total-recall auth secret remote remove <name>');
1162
1168
  process.exit(1);
1163
1169
  }
1164
1170
  const r = removeRemoteTarget(brainDir, opts.key);
@@ -1168,7 +1174,7 @@ export default async function secretCli(argv) {
1168
1174
 
1169
1175
  if (opts.subcommand === 'deploy') {
1170
1176
  if (!opts.key) {
1171
- console.error('Usage: total-recall secret remote deploy <name> [--dry-run] [--keys k1,k2]');
1177
+ console.error('Usage: total-recall auth secret remote deploy <name> [--dry-run] [--keys k1,k2]');
1172
1178
  process.exit(1);
1173
1179
  }
1174
1180
  const r = await deployEnvToRemote(brainDir, opts.key, {
@@ -1193,7 +1199,7 @@ export default async function secretCli(argv) {
1193
1199
 
1194
1200
  if (opts.command === 'rotate') {
1195
1201
  if (!opts.key) {
1196
- console.error('Usage: total-recall secret rotate <key> [--export-env|--export-all|--remote <name>]');
1202
+ console.error('Usage: total-recall auth secret rotate <key> [--export-env|--export-all|--remote <name>]');
1197
1203
  console.error(' The new value is read from a prompt or stdin — never from argv.');
1198
1204
  process.exit(1);
1199
1205
  }
@@ -1220,7 +1226,7 @@ export default async function secretCli(argv) {
1220
1226
  `\n ⚠️ WARNING: the value was passed as a command-line argument.\n` +
1221
1227
  ` It is now in your shell history and was visible to any user\n` +
1222
1228
  ` running \`ps\` on this machine. Treat it as already disclosed\n` +
1223
- ` and rotate again using the prompt: total-recall secret rotate ${opts.key}\n`,
1229
+ ` and rotate again using the prompt: total-recall auth secret rotate ${opts.key}\n`,
1224
1230
  );
1225
1231
  }
1226
1232
  if (!opts.value) {
@@ -1277,7 +1283,7 @@ export default async function secretCli(argv) {
1277
1283
 
1278
1284
  if (opts.command === 'rotate-browser' || opts.command === 'rotate-auto') {
1279
1285
  if (!opts.key) {
1280
- console.error(`Usage: total-recall secret ${opts.command} <key>`);
1286
+ console.error(`Usage: total-recall auth secret ${opts.command} <key>`);
1281
1287
  process.exit(1);
1282
1288
  }
1283
1289
 
@@ -1296,6 +1302,9 @@ export default async function secretCli(argv) {
1296
1302
  console.log(`\n 🔐 Rotating ${opts.key}…\n`);
1297
1303
  const r = await run(brainDir, opts.key, {
1298
1304
  headless: !!opts.headless,
1305
+ chrome: !!opts.chrome,
1306
+ label: opts.label,
1307
+ revokeOld: !!opts.revokeOld,
1299
1308
  exportEnv: opts.exportEnv !== false,
1300
1309
  onStatus: (m) => console.log(` · ${m}`),
1301
1310
  });
@@ -1307,9 +1316,10 @@ export default async function secretCli(argv) {
1307
1316
  }
1308
1317
  console.log(`\n ✅ Rotated ${r.key}` + (r.method ? ` via ${r.method}` : ''));
1309
1318
  if (r.verified === true) console.log(' verified against provider API');
1319
+ if (r.revoked) console.log(` revoked ${r.revoked} deprecated credential(s) from provider console`);
1310
1320
  if (r.supervised) console.log(' (supervised — recipe not yet auto-verified)');
1311
1321
  if (r.exports?.length) console.log(` exported to ${r.exports.length} project(s)`);
1312
- if (r.revoke_hint) console.log(`\n ⚠️ ${r.revoke_hint}`);
1322
+ if (r.revoke_hint && !r.revoked) console.log(`\n ⚠️ ${r.revoke_hint}`);
1313
1323
  return;
1314
1324
  }
1315
1325
 
@@ -1347,7 +1357,7 @@ export default async function secretCli(argv) {
1347
1357
 
1348
1358
  if (opts.command === 'delete' || opts.command === 'rm') {
1349
1359
  if (!opts.key) {
1350
- console.error('Usage: total-recall secret delete <key>');
1360
+ console.error('Usage: total-recall auth secret delete <key>');
1351
1361
  process.exit(1);
1352
1362
  }
1353
1363
  const r = await deleteSecret(brainDir, opts.key);
@@ -1634,7 +1644,7 @@ export default async function secretCli(argv) {
1634
1644
  }
1635
1645
  if (opts.dryRun || (!opts.all && !opts.keys.length)) {
1636
1646
  console.log(
1637
- '\n Dry-run / preview. Import with:\n npx total-recall secret import-env --all\n npx total-recall secret import-env OPENAI_API_KEY ANTHROPIC_API_KEY\n',
1647
+ '\n Dry-run / preview. Import with:\n npx total-recall auth secret import-env --all\n npx total-recall auth secret import-env OPENAI_API_KEY ANTHROPIC_API_KEY\n',
1638
1648
  );
1639
1649
  return;
1640
1650
  }
@@ -23,7 +23,8 @@ export function createGoogleOAuth({ brainDir, load = loadSecretsSync, save = sav
23
23
  if (!values.GOOGLE_CLIENT_ID || !values.GOOGLE_CLIENT_SECRET) throw new Error('Google OAuth is not configured');
24
24
  const redirect = new URL(values.GOOGLE_REDIRECT_URI || '');
25
25
  if ((redirect.protocol !== 'https:' && !(redirect.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(redirect.hostname))) || redirect.username || redirect.password || redirect.search || redirect.hash || redirect.pathname !== '/auth/google/callback') throw new Error('Configure a trusted GOOGLE_REDIRECT_URI ending in /auth/google/callback');
26
- return { clientId: values.GOOGLE_CLIENT_ID, clientSecret: values.GOOGLE_CLIENT_SECRET, redirectUri: redirect.href };
26
+ const scope = values.GOOGLE_SCOPES || 'openid email profile';
27
+ return { clientId: values.GOOGLE_CLIENT_ID, clientSecret: values.GOOGLE_CLIENT_SECRET, redirectUri: redirect.href, scope };
27
28
  };
28
29
  const status = () => {
29
30
  const rows = state.read({}); assertNoSecretValues(rows);
@@ -69,16 +70,17 @@ export function createGoogleOAuth({ brainDir, load = loadSecretsSync, save = sav
69
70
  };
70
71
  return {
71
72
  config, status,
72
- begin() {
73
+ begin(options = {}) {
73
74
  const cfg = config();
74
75
  for (const [key, value] of pending) if (value.expires <= now()) pending.delete(key);
75
76
  if (pending.size >= 1000) throw new Error('Too many pending authorizations');
76
77
  const stateValue = crypto.randomBytes(32).toString('base64url');
77
78
  const browser = crypto.randomBytes(32).toString('base64url');
78
79
  const verifier = crypto.randomBytes(48).toString('base64url');
80
+ const scope = options.scope || cfg.scope;
79
81
  pending.set(stateValue, { browser: digest(browser), verifier, expires: now() + 600000, redirectUri: cfg.redirectUri });
80
82
  const url = new URL('https://accounts.google.com/o/oauth2/v2/auth');
81
- for (const [key, value] of Object.entries({ client_id: cfg.clientId, redirect_uri: cfg.redirectUri, response_type: 'code', scope: 'openid email profile',
83
+ for (const [key, value] of Object.entries({ client_id: cfg.clientId, redirect_uri: cfg.redirectUri, response_type: 'code', scope,
82
84
  state: stateValue, access_type: 'offline', prompt: 'consent select_account', code_challenge: crypto.createHash('sha256').update(verifier).digest('base64url'), code_challenge_method: 'S256' })) url.searchParams.set(key, value);
83
85
  return { url: url.href, redirectUri: cfg.redirectUri, browser };
84
86
  },
@@ -95,7 +97,7 @@ export function createGoogleOAuth({ brainDir, load = loadSecretsSync, save = sav
95
97
  if (user.email_verified !== true || typeof user.email !== 'string' || !user.email.includes('@')) throw new Error('Google identity is not verified');
96
98
  const allowed = load(brainDir).GOOGLE_ALLOWED_EMAILS;
97
99
  const emails = Array.isArray(allowed) ? allowed : typeof allowed === 'string' ? allowed.split(',') : [];
98
- if (!emails.some(email => typeof email === 'string' && email.trim().toLowerCase() === user.email.toLowerCase())) throw new Error('Google account is not authorized for this dashboard');
100
+ if (emails.length > 0 && !emails.some(email => typeof email === 'string' && email.trim().toLowerCase() === user.email.toLowerCase())) throw new Error('Google account is not authorized for this dashboard');
99
101
  commit(id, { ...tokens, email: user.email, name: typeof user.name === 'string' ? user.name : '' }, 'connected');
100
102
  return { sub: id, email: user.email, name: typeof user.name === 'string' ? user.name : '' };
101
103
  },
@@ -333,7 +333,7 @@ export const PROVIDER_CATALOG = [
333
333
  id: 'telnyx',
334
334
  name: 'Telnyx',
335
335
  docs_url: 'https://developers.telnyx.com/api',
336
- console_url: 'https://portal.telnyx.com/#/app/api-keys',
336
+ console_url: 'https://portal.telnyx.com/#/api-keys',
337
337
  key_patterns: ['TELNYX_API_KEY', 'TELNYX_SIP_PASSWORD'],
338
338
  schema: { auth: 'bearer', header: 'Authorization: Bearer <key>', env_keys: ['TELNYX_API_KEY'] },
339
339
  rotation: { class: 'provider_browser' },
@@ -165,10 +165,124 @@ export const ROTATION_RECIPES = {
165
165
 
166
166
  telnyx: {
167
167
  provider: 'telnyx',
168
- console_url: 'https://portal.telnyx.com/#/app/api-keys',
169
- verified: false,
170
- create_hint: 'Create API Key → copy the KEY… value shown once.',
171
- verify: (v) => probe('https://api.telnyx.com/v2/whoami', bearer(v)),
168
+ console_url: 'https://portal.telnyx.com/#/api-keys',
169
+ verified: true,
170
+ signed_in: 'button:has-text("Create API Key")',
171
+ signed_out: 'a:has-text("Log In"), input[name="email"]',
172
+ create_hint: 'Click "Create API Key", set tag, and copy the new key.',
173
+ revoke_hint: 'Click the Delete button on deprecated keys in the table.',
174
+ value_pattern: /^KEY[0-9A-Za-z_-]{30,}$/,
175
+ async create(page, { label }) {
176
+ // 1. Click Create API Key
177
+ await page.evaluate(() => {
178
+ const createBtn = Array.from(document.querySelectorAll('button')).find(
179
+ (b) => b.innerText.trim().toLowerCase() === 'create api key'
180
+ );
181
+ if (createBtn) createBtn.click();
182
+ });
183
+
184
+ // 2. Wait for dialog
185
+ let dialogReady = false;
186
+ for (let i = 0; i < 40; i++) {
187
+ await page.waitForTimeout(250);
188
+ dialogReady = await page.evaluate(() => {
189
+ const d = document.querySelector('[role=dialog]');
190
+ return !!(d && d.querySelector('input'));
191
+ });
192
+ if (dialogReady) break;
193
+ }
194
+ if (!dialogReady) return null;
195
+
196
+ // 3. Fill tag if provided
197
+ if (label) {
198
+ await page.evaluate(() => {
199
+ const d = document.querySelector('[role=dialog]');
200
+ const input = d?.querySelector('input');
201
+ if (input) input.focus();
202
+ });
203
+ if (typeof page.trustedKeystroke === 'function') {
204
+ await page.trustedKeystroke(label);
205
+ await page.waitForTimeout(300);
206
+ await page.trustedPress('return');
207
+ } else if (page.keyboard) {
208
+ await page.keyboard.type(label);
209
+ await page.keyboard.press('Enter');
210
+ }
211
+ await page.waitForTimeout(500);
212
+ }
213
+
214
+ // 4. Click Create inside dialog
215
+ await page.evaluate(() => {
216
+ const d = document.querySelector('[role=dialog]');
217
+ const confirmBtn = Array.from(d?.querySelectorAll('button') || []).find(
218
+ (b) => b.innerText.trim().toLowerCase() === 'create'
219
+ );
220
+ if (confirmBtn) confirmBtn.click();
221
+ });
222
+
223
+ // 5. Wait for "Copy to clipboard and close"
224
+ let copyReady = false;
225
+ for (let i = 0; i < 40; i++) {
226
+ await page.waitForTimeout(250);
227
+ copyReady = await page.evaluate(() => {
228
+ const d = document.querySelector('[role=dialog]');
229
+ const copyBtn = Array.from(d?.querySelectorAll('button') || []).find((b) =>
230
+ b.innerText.toLowerCase().includes('copy to clipboard')
231
+ );
232
+ if (copyBtn) {
233
+ copyBtn.focus();
234
+ return true;
235
+ }
236
+ return false;
237
+ });
238
+ if (copyReady) break;
239
+ }
240
+ if (!copyReady) return null;
241
+
242
+ // 6. Trigger trusted press to copy token to OS clipboard
243
+ if (typeof page.trustedPress === 'function') {
244
+ await page.trustedPress('space');
245
+ } else if (page.keyboard) {
246
+ await page.keyboard.press('Space');
247
+ }
248
+ await page.waitForTimeout(600);
249
+
250
+ // 7. Capture from clipboard
251
+ try {
252
+ const { execFileSync } = await import('node:child_process');
253
+ if (process.platform === 'darwin') {
254
+ const clip = execFileSync('pbpaste', [], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
255
+ if (clip && /^KEY[0-9A-Za-z_-]{30,}$/.test(clip)) return clip;
256
+ }
257
+ } catch {}
258
+
259
+ return null;
260
+ },
261
+ async revokeOld(page, { keepLabels = ['MASTER', 'FESTECH-PROD', 'FESTECH-LIVE', 'ULTRACHAT-PROD', 'GROCRM-PROD', 'JSN-PROD'] } = {}) {
262
+ return await page.evaluate(async (labelsToKeep) => {
263
+ let deleted = 0;
264
+ const deleteBtns = Array.from(document.querySelectorAll('button[aria-label=Delete]'));
265
+ for (const btn of deleteBtns) {
266
+ const row = btn.closest('div[class*=xlaxlx]') || btn.parentElement?.parentElement?.parentElement?.parentElement;
267
+ const text = row ? (row.innerText || '') : '';
268
+ const shouldKeep = labelsToKeep.some((l) => text.includes(l));
269
+ if (!shouldKeep && (text.includes('23 Sep 2026') || text.includes('Never used') || text.includes('2026'))) {
270
+ btn.click();
271
+ await new Promise((r) => setTimeout(r, 600));
272
+ const confirmBtn = Array.from(document.querySelectorAll('[role=dialog] button')).find(
273
+ (b) => b.innerText.trim().toLowerCase() === 'continue'
274
+ );
275
+ if (confirmBtn) {
276
+ confirmBtn.click();
277
+ deleted++;
278
+ await new Promise((r) => setTimeout(r, 1200));
279
+ }
280
+ }
281
+ }
282
+ return deleted;
283
+ }, keepLabels);
284
+ },
285
+ verify: (v) => probe('https://api.telnyx.com/v2/balance', bearer(v)),
172
286
  },
173
287
 
174
288
  fal: {
@@ -134,8 +134,8 @@ authRouter.get('/auth/google/callback', async (req, res) => {
134
134
  res.cookie('session', token, sessionCookieOptions(req, ttlHours * 60 * 60 * 1000));
135
135
  if (req.query.format === 'json' || req.headers.accept?.includes('application/json')) return res.json({ success: true, user: user.email, method: 'google_oauth2' });
136
136
  return res.redirect('/');
137
- } catch {
138
- logger.warn('auth', 'Google OAuth callback verification failed');
137
+ } catch (err) {
138
+ logger.warn('auth', `Google OAuth callback verification failed: ${err.message}`);
139
139
  return res.status(401).json({ error: 'Google authentication failed; restart sign-in' });
140
140
  }
141
141
  });
@@ -20,8 +20,8 @@ const END_MARK = '# END TOTAL-RECALL-SECRETS';
20
20
  function envHeader() {
21
21
  return `${BEGIN_MARK}
22
22
  # Generated by Total Recall — do not hand-edit secret values here.
23
- # SSOT: secrets store (npx total-recall secret …)
24
- # Re-export: npx total-recall secret export-env --path <repo>
23
+ # SSOT: secrets store (npx total-recall auth secret …)
24
+ # Re-export: npx total-recall auth secret export-env --path <repo>
25
25
  # ${new Date().toISOString()}
26
26
  `;
27
27
  }
@@ -162,7 +162,7 @@ export async function buildEnvProjection(brainDir, target = {}) {
162
162
 
163
163
  const body = `${envHeader()}${lines.join('\n')}${lines.length ? '\n' : ''}${END_MARK}\n`;
164
164
  const exampleBody = `# Example env — names only. Values live in Total Recall secrets store.
165
- # Generate real .env: npx total-recall secret export-env --path .
165
+ # Generate real .env: npx total-recall auth secret export-env --path .
166
166
  ${exampleLines.join('\n')}${exampleLines.length ? '\n' : ''}`;
167
167
 
168
168
  return {
@@ -164,7 +164,7 @@ export async function deployEnvToRemote(brainDir, targetName, opts = {}) {
164
164
  const target = targets.find((t) => t.name === targetName);
165
165
  if (!target) {
166
166
  throw new Error(
167
- `Unknown remote target "${targetName}". Configure one first: npx total-recall secret remote add ${targetName} --host <host> --path </remote/dir>`,
167
+ `Unknown remote target "${targetName}". Configure one first: npx total-recall auth secret remote add ${targetName} --host <host> --path </remote/dir>`,
168
168
  );
169
169
  }
170
170
 
@@ -49,7 +49,7 @@ export function buildBrowserRotatePrompt(secretMeta) {
49
49
  '3. Create a NEW API key (do not delete the old one yet).',
50
50
  '4. Copy the new key value ONCE — never paste into chat logs or vault markdown.',
51
51
  '5. Tell the human to run (or run via tool if value is only in a secure channel):',
52
- ` npx total-recall secret rotate ${secretMeta.key} "<NEW_VALUE>" --export-env`,
52
+ ` npx total-recall auth secret rotate ${secretMeta.key} "<NEW_VALUE>" --export-env`,
53
53
  '6. After TR confirms export succeeded, revoke/disable the OLD key in the console.',
54
54
  '',
55
55
  docsUrl ? `API docs: ${docsUrl}` : '',
@@ -124,7 +124,7 @@ export async function enqueueRotationDueTasks(brainDir, opts = {}) {
124
124
  `## After new value is obtained`,
125
125
  ``,
126
126
  '```bash',
127
- `npx total-recall secret rotate ${secret.key} "<NEW_VALUE>" --export-env`,
127
+ `npx total-recall auth secret rotate ${secret.key} "<NEW_VALUE>" --export-env`,
128
128
  '```',
129
129
  ].join('\n');
130
130
 
@@ -267,10 +267,18 @@ export async function getBrowserRotateAssist(brainDir, key) {
267
267
  async function captureFromClipboard(page) {
268
268
  try {
269
269
  const text = await page.evaluate(() => navigator.clipboard.readText());
270
- return typeof text === 'string' ? text.trim() : null;
271
- } catch {
272
- return null;
273
- }
270
+ if (typeof text === 'string' && text.trim()) return text.trim();
271
+ } catch {}
272
+
273
+ try {
274
+ const { execFileSync } = await import('node:child_process');
275
+ if (process.platform === 'darwin') {
276
+ const clip = execFileSync('pbpaste', [], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
277
+ if (clip) return clip;
278
+ }
279
+ } catch {}
280
+
281
+ return null;
274
282
  }
275
283
 
276
284
  /**
@@ -313,6 +321,8 @@ export async function rotateViaBrowser(brainDir, key, opts = {}) {
313
321
  const launched = await launchRotationContext(brainDir, {
314
322
  headless: opts.headless === true && recipe.verified,
315
323
  timeoutMs: opts.timeoutMs,
324
+ chrome: opts.chrome,
325
+ useChrome: opts.useChrome,
316
326
  });
317
327
  if (!launched.ok) return { ok: false, key, error: launched.error };
318
328
 
@@ -384,12 +394,20 @@ export async function rotateViaBrowser(brainDir, key, opts = {}) {
384
394
  });
385
395
  value = null; // drop the reference promptly
386
396
 
397
+ let revoked = 0;
398
+ if (opts.revokeOld && typeof recipe.revokeOld === 'function') {
399
+ say('revoking deprecated credentials from provider console');
400
+ revoked = await recipe.revokeOld(page, { keepLabels: [opts.label || 'total-recall', 'MASTER'] });
401
+ say(`revoked ${revoked} deprecated credential(s)`);
402
+ }
403
+
387
404
  return {
388
405
  ok: true,
389
406
  key,
390
407
  provider: plan.provider,
391
408
  supervised: !recipe.verified,
392
409
  verified,
410
+ revoked,
393
411
  exports: result.exports,
394
412
  revoke_hint: recipe.revoke_hint || 'Revoke the old credential in the console now that the new one is live.',
395
413
  };