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.
- package/README.md +152 -41
- package/bin/total-recall.mjs +16 -76
- package/docs/reference/cli-reference.md +10 -10
- package/extension/background.js +6 -2
- package/extension/config.js +8 -0
- package/extension/lib/brain-client.js +29 -4
- package/extension/options/options.html +1 -0
- package/extension/sidepanel/sidepanel.html +1 -0
- package/frontend/dist/assets/{index-CvQRdT2j.css → index-AGUes-VG.css} +1 -1
- package/frontend/dist/assets/index-Bt5iVPZj.js +5618 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/src/api/keys.ts +9 -2
- package/frontend/src/api/plugins.ts +4 -0
- package/frontend/src/components/plugins/card/CardEngine.tsx +2 -1
- package/frontend/src/components/plugins/card/card.css +1 -1
- package/frontend/src/pages/IntegrationsPage.tsx +315 -8
- package/frontend/src/pages/PluginsPage.tsx +532 -87
- package/package.json +6 -5
- package/scaffold/.agent/skills/meta-harness/references/optimized/original.md +1 -1
- package/scaffold/.agent/skills/meta-harness/references/optimized/section-09.md +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +10 -10
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +0 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/browser-session.mjs +309 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +44 -34
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/google-oauth.mjs +6 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-catalog.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-rotation-recipes.mjs +118 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/auth.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-rotate.mjs +24 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +164 -123
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +65 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +76 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +290 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +446 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +152 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +71 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +175 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/README.md +84 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +79 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/check.mjs +6 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/report.mjs +6 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +27 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/snapshot.mjs +163 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +757 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/config.schema.json +86 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/detect.mjs +89 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +145 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +210 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/state.mjs +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +2869 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +28 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +186 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +272 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +129 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/README.md +73 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/cli.mjs +375 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/echo-tool.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/group.mjs +262 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/project-id.mjs +74 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +2869 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +31 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +221 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/schemas/cli_command.schema.json +107 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/skills/composable-cli/SKILL.md +63 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +313 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compile.mjs +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compose.mjs +144 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/contract.mjs +110 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/library.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/registry.mjs +192 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/resolve.mjs +221 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/risk.mjs +126 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/ssss-documents.mjs +29 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +273 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/README.md +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/cli.mjs +310 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +2826 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +11 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +62 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +297 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +43 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/cli.mjs +258 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4005 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +125 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +278 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +9 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +6 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +4721 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -21
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +14 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/headscale-client.mjs +5 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +4 -13
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-access-verify.mjs +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-files.mjs +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/test-support/memory-root.mjs +14 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +124 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4097 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +12 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +165 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +127 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +279 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +539 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/README.md +67 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +932 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3256 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +297 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +4 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/skills/security/SKILL.md +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/android.mjs +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/devices.mjs +94 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/machines.mjs +164 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +41 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/windows.mjs +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +95 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +98 -0
- package/scaffold/.agent/skills/total-recall/references/auth.md +1 -1
- package/src/cli/brief.mjs +33 -9
- package/src/cli/command.mjs +2 -7
- package/src/cli/config.mjs +1 -0
- package/src/cli/connect.mjs +5 -0
- package/src/cli/context.mjs +2 -1
- package/src/cli/daemon.mjs +36 -1
- package/src/cli/doctor.mjs +6 -0
- package/src/cli/harness.mjs +3 -0
- package/src/cli/help.mjs +26 -0
- package/src/cli/init.mjs +4 -4
- package/src/cli/memory-entry.mjs +1 -0
- package/src/cli/memory-init.mjs +2 -0
- package/src/cli/plugin/index.mjs +11 -0
- package/src/cli/plugin/install.mjs +2 -1
- package/src/cli/rebuild.mjs +3 -1
- package/src/cli/startup.mjs +2 -1
- package/src/cli/update.mjs +19 -0
- package/src/core/automatic-brain-updates.mjs +67 -0
- package/src/core/brain-updates.mjs +21 -2
- package/src/core/cli-commands.mjs +71 -0
- package/src/core/cli-maintenance.mjs +45 -0
- package/src/core/cli-quick-reference.mjs +20 -0
- package/src/core/cli-semantic-index.mjs +722 -0
- package/src/core/context-compiler.mjs +27 -1
- package/src/core/crons.mjs +2 -13
- package/src/core/daemon-control.mjs +6 -4
- package/src/core/daemon-loop.mjs +12 -8
- package/src/core/default-plugins.mjs +5 -0
- package/src/core/global-brain-updates.mjs +39 -0
- package/src/core/mesh-brain-updates.mjs +30 -0
- package/src/core/meta-harness.mjs +108 -0
- package/src/core/plugin-dependencies.mjs +2 -1
- package/src/core/plugin-loader.mjs +2 -1
- package/src/core/plugin-runner.mjs +12 -4
- package/src/core/plugin-setup.mjs +20 -0
- package/src/core/plugin-store.mjs +22 -4
- package/src/core/plugin-tasks.mjs +1 -1
- package/src/core/project-brain.mjs +2 -0
- package/src/core/project-status.mjs +64 -0
- package/src/core/required-plugins.mjs +46 -0
- package/src/core/scaffold-plugins.mjs +17 -3
- package/src/core/source-brain-updates.mjs +47 -0
- package/src/core/startup-health.mjs +5 -3
- package/src/core/surface.mjs +44 -10
- package/src/server/routes/extension.mjs +53 -8
- package/templates/onboarding-interview.md +1 -1
- 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
|
-
|
|
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
|
|
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/#/
|
|
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/#/
|
|
169
|
-
verified:
|
|
170
|
-
|
|
171
|
-
|
|
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',
|
|
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
|
-
|
|
271
|
-
} catch {
|
|
272
|
-
|
|
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
|
};
|