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
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "total-recall-brain",
|
|
3
|
-
"version": "3.40.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.40.2",
|
|
4
|
+
"description": "The Vibe Coding Brain & Autonomous Software Factory: memory, rules, secrets, and 33 visual plugins automatically managed across all your devices.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"total-recall": "bin/total-recall.mjs",
|
|
8
|
+
"vibebrain": "bin/total-recall.mjs",
|
|
8
9
|
"antigravity": "bin/antigravity.mjs",
|
|
9
10
|
"total-recall-memory": "bin/total-recall-memory.mjs"
|
|
10
11
|
},
|
|
@@ -99,7 +100,8 @@
|
|
|
99
100
|
"@testing-library/react": "^16.3.2",
|
|
100
101
|
"@testing-library/user-event": "^14.6.1",
|
|
101
102
|
"@vitejs/plugin-react": "^6.0.3",
|
|
102
|
-
"@vitest/coverage-v8": "^4.1.
|
|
103
|
+
"@vitest/coverage-v8": "^4.1.12",
|
|
104
|
+
"gray-matter": "^4.0.3",
|
|
103
105
|
"jsdom": "^29.1.1",
|
|
104
106
|
"react": "^19.2.7",
|
|
105
107
|
"react-dom": "^19.2.7",
|
|
@@ -107,8 +109,7 @@
|
|
|
107
109
|
"recharts": "^3.9.2",
|
|
108
110
|
"skills": "^1.5.7",
|
|
109
111
|
"supertest": "^7.2.2",
|
|
110
|
-
"vitest": "^4.1.
|
|
111
|
-
"gray-matter": "^4.0.3"
|
|
112
|
+
"vitest": "^4.1.12"
|
|
112
113
|
},
|
|
113
114
|
"workspaces": [
|
|
114
115
|
"frontend"
|
|
@@ -86,7 +86,7 @@ npx total-recall agent spawn claude "Answer from memory only" --no-tools
|
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
Claude Code authenticates headlessly with `CLAUDE_CODE_OAUTH_TOKEN` (create it with
|
|
89
|
-
`claude setup-token`, store it with `total-recall secret set CLAUDE_CODE_OAUTH_TOKEN --stdin`).
|
|
89
|
+
`claude setup-token`, store it with `total-recall auth secret set CLAUDE_CODE_OAUTH_TOKEN --stdin`).
|
|
90
90
|
A spawn reads it from the secret store when the environment lacks it, so SSH and
|
|
91
91
|
launchd sessions that cannot reach a login keychain still authenticate.
|
|
92
92
|
|
|
@@ -15,6 +15,6 @@ npx total-recall agent spawn claude "Answer from memory only" --no-tools
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Claude Code authenticates headlessly with `CLAUDE_CODE_OAUTH_TOKEN` (create it with
|
|
18
|
-
`claude setup-token`, store it with `total-recall secret set CLAUDE_CODE_OAUTH_TOKEN --stdin`).
|
|
18
|
+
`claude setup-token`, store it with `total-recall auth secret set CLAUDE_CODE_OAUTH_TOKEN --stdin`).
|
|
19
19
|
A spawn reads it from the secret store when the environment lacks it, so SSH and
|
|
20
20
|
launchd sessions that cannot reach a login keychain still authenticate.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tr-plugin-auth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "tr-plugin-auth",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.2",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@simplewebauthn/server": "^13.3.2",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"express": "^5.2.1",
|
|
15
15
|
"express-rate-limit": "^8.5.1",
|
|
16
16
|
"jsonwebtoken": "^9.0.3",
|
|
17
|
-
"total-recall-brain": "
|
|
17
|
+
"total-recall-brain": "3.40.1",
|
|
18
18
|
"yaml": "^2.9.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
@@ -695,9 +695,9 @@
|
|
|
695
695
|
},
|
|
696
696
|
"node_modules/@ssss/cli": {
|
|
697
697
|
"name": "@gregiteen/ssss-cli",
|
|
698
|
-
"version": "0.
|
|
699
|
-
"resolved": "https://registry.npmjs.org/@gregiteen/ssss-cli/-/ssss-cli-0.
|
|
700
|
-
"integrity": "sha512-
|
|
698
|
+
"version": "0.11.2",
|
|
699
|
+
"resolved": "https://registry.npmjs.org/@gregiteen/ssss-cli/-/ssss-cli-0.11.2.tgz",
|
|
700
|
+
"integrity": "sha512-DTkBPCOv9uN79YoRtsa/GnXPbbeHOU5PQcQ995axHg+SLWdkL6xmrTRs5Xxshcje/Iv6+lKkYOc9UL+Arn9kHg==",
|
|
701
701
|
"license": "ISC",
|
|
702
702
|
"bin": {
|
|
703
703
|
"ssss": "scripts/ssss.mjs"
|
|
@@ -3578,16 +3578,16 @@
|
|
|
3578
3578
|
}
|
|
3579
3579
|
},
|
|
3580
3580
|
"node_modules/total-recall-brain": {
|
|
3581
|
-
"version": "3.
|
|
3582
|
-
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.
|
|
3583
|
-
"integrity": "sha512-
|
|
3581
|
+
"version": "3.40.1",
|
|
3582
|
+
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.40.1.tgz",
|
|
3583
|
+
"integrity": "sha512-tZMnDHXsMT1sa698oF26Np/G4k+Cn4ZXpTdo+BTNOvUMYEhxWR+7Tl+upHLMhcEEPuEM0EiAObL53a9JELzGag==",
|
|
3584
3584
|
"license": "MIT",
|
|
3585
3585
|
"workspaces": [
|
|
3586
3586
|
"frontend"
|
|
3587
3587
|
],
|
|
3588
3588
|
"dependencies": {
|
|
3589
3589
|
"@simplewebauthn/server": "^13.3.2",
|
|
3590
|
-
"@ssss/cli": "npm:@gregiteen/ssss-cli@0.
|
|
3590
|
+
"@ssss/cli": "npm:@gregiteen/ssss-cli@0.11.2",
|
|
3591
3591
|
"bcrypt": "^6.0.0",
|
|
3592
3592
|
"better-sqlite3": "^12.11.1",
|
|
3593
3593
|
"cookie-parser": "^1.4.7",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tr-plugin-auth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "White-label Total Recall auth plugin: Passkey biometric FIDO2 and Google OAuth2 authentication.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "cli.mjs",
|
|
@@ -15,12 +15,7 @@
|
|
|
15
15
|
"google"
|
|
16
16
|
],
|
|
17
17
|
"license": "MIT",
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"vitest": "^5.0.3",
|
|
20
|
-
"supertest": "^7.2.2"
|
|
21
|
-
},
|
|
22
18
|
"dependencies": {
|
|
23
|
-
"total-recall-brain": "^3.39.1",
|
|
24
19
|
"yaml": "^2.9.0",
|
|
25
20
|
"bcrypt": "^6.0.0",
|
|
26
21
|
"jsonwebtoken": "^9.0.3",
|
|
@@ -14,20 +14,297 @@
|
|
|
14
14
|
|
|
15
15
|
import fs from 'node:fs';
|
|
16
16
|
import path from 'node:path';
|
|
17
|
+
import { execFileSync } from 'node:child_process';
|
|
17
18
|
|
|
18
19
|
const PROFILE_DIRNAME = 'browser-profile';
|
|
19
20
|
|
|
20
21
|
const PROFILE_README = `# TR browser profile — DO NOT SYNC, DO NOT COPY
|
|
21
22
|
|
|
22
23
|
This directory holds live authenticated sessions (Stripe, GitHub, cloud consoles)
|
|
23
|
-
used by \`total-recall secret rotate-auto\`.
|
|
24
|
+
used by \`total-recall auth secret rotate-auto\`.
|
|
24
25
|
|
|
25
26
|
Treat it as equivalent to secrets.enc:
|
|
26
27
|
- never commit it
|
|
27
28
|
- never include it in backups that leave this machine
|
|
28
|
-
- delete it with \`total-recall secret browser-logout\` when no longer needed
|
|
29
|
+
- delete it with \`total-recall auth secret browser-logout\` when no longer needed
|
|
29
30
|
`;
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Check if Google Chrome is currently running on macOS.
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
export function isMacChromeRunning() {
|
|
37
|
+
if (process.platform !== 'darwin') return false;
|
|
38
|
+
try {
|
|
39
|
+
const res = execFileSync('pgrep', ['-x', 'Google Chrome'], {
|
|
40
|
+
encoding: 'utf8',
|
|
41
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
42
|
+
});
|
|
43
|
+
return !!res.trim();
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Native macOS Chrome driver using AppleScript/JXA. Connects to the operator's
|
|
51
|
+
* already-running, authenticated Google Chrome instance so rotations can drive
|
|
52
|
+
* authenticated consoles without needing to re-login.
|
|
53
|
+
*/
|
|
54
|
+
export function createMacChromeContext(opts = {}) {
|
|
55
|
+
let activeUrl = '';
|
|
56
|
+
|
|
57
|
+
const runJxa = (js) => {
|
|
58
|
+
return execFileSync('osascript', ['-l', 'JavaScript', '-e', js], {
|
|
59
|
+
encoding: 'utf8',
|
|
60
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
61
|
+
}).trim();
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const page = {
|
|
65
|
+
async goto(url) {
|
|
66
|
+
activeUrl = url;
|
|
67
|
+
let host = '';
|
|
68
|
+
try {
|
|
69
|
+
host = new URL(url).hostname;
|
|
70
|
+
} catch {}
|
|
71
|
+
const script = `
|
|
72
|
+
const chrome = Application("Google Chrome");
|
|
73
|
+
let target = null;
|
|
74
|
+
for (const w of chrome.windows()) {
|
|
75
|
+
for (const t of w.tabs()) {
|
|
76
|
+
if (t.url().includes("${host}")) {
|
|
77
|
+
target = t;
|
|
78
|
+
w.activeTabIndex = w.tabs.indexOf(t) + 1;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (target) break;
|
|
83
|
+
}
|
|
84
|
+
if (!target) {
|
|
85
|
+
const w = chrome.windows()[0] || chrome.Window().make();
|
|
86
|
+
target = chrome.Tab({ url: "${url}" });
|
|
87
|
+
w.tabs.push(target);
|
|
88
|
+
} else if (target.url() !== "${url}") {
|
|
89
|
+
target.url = "${url}";
|
|
90
|
+
}
|
|
91
|
+
target.url();
|
|
92
|
+
`;
|
|
93
|
+
try {
|
|
94
|
+
runJxa(script);
|
|
95
|
+
} catch {}
|
|
96
|
+
await page.waitForTimeout(1500);
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
async url() {
|
|
100
|
+
let host = '';
|
|
101
|
+
try {
|
|
102
|
+
host = new URL(activeUrl || 'https://portal.telnyx.com').hostname;
|
|
103
|
+
} catch {}
|
|
104
|
+
const script = `
|
|
105
|
+
const chrome = Application("Google Chrome");
|
|
106
|
+
let u = "";
|
|
107
|
+
for (const w of chrome.windows()) {
|
|
108
|
+
for (const t of w.tabs()) {
|
|
109
|
+
if (t.url().includes("${host}")) {
|
|
110
|
+
u = t.url();
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (u) break;
|
|
115
|
+
}
|
|
116
|
+
u;
|
|
117
|
+
`;
|
|
118
|
+
try {
|
|
119
|
+
const res = runJxa(script);
|
|
120
|
+
if (res) return res;
|
|
121
|
+
} catch {}
|
|
122
|
+
return activeUrl;
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
async waitForTimeout(ms) {
|
|
126
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
async trustedKeystroke(text) {
|
|
130
|
+
if (process.platform === 'darwin') {
|
|
131
|
+
const escaped = String(text).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
132
|
+
const script = `
|
|
133
|
+
tell application "Google Chrome" to activate
|
|
134
|
+
delay 0.1
|
|
135
|
+
tell application "System Events"
|
|
136
|
+
keystroke "${escaped}"
|
|
137
|
+
end tell
|
|
138
|
+
`;
|
|
139
|
+
try {
|
|
140
|
+
execFileSync('osascript', ['-e', script], { stdio: 'ignore' });
|
|
141
|
+
} catch {}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
async trustedPress(keyName) {
|
|
146
|
+
if (process.platform === 'darwin') {
|
|
147
|
+
let code = 'space';
|
|
148
|
+
if (keyName === 'Enter' || keyName === 'return') code = 'return';
|
|
149
|
+
else if (keyName === 'space' || keyName === 'Space') code = 'space';
|
|
150
|
+
const script = `
|
|
151
|
+
tell application "Google Chrome" to activate
|
|
152
|
+
delay 0.1
|
|
153
|
+
tell application "System Events"
|
|
154
|
+
keystroke ${code}
|
|
155
|
+
end tell
|
|
156
|
+
`;
|
|
157
|
+
try {
|
|
158
|
+
execFileSync('osascript', ['-e', script], { stdio: 'ignore' });
|
|
159
|
+
} catch {}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
async evaluate(fn, ...args) {
|
|
164
|
+
let host = '';
|
|
165
|
+
try {
|
|
166
|
+
host = new URL(activeUrl || 'https://portal.telnyx.com').hostname;
|
|
167
|
+
} catch {}
|
|
168
|
+
|
|
169
|
+
const evalChrome = (code) => {
|
|
170
|
+
const script = `
|
|
171
|
+
const chrome = Application("Google Chrome");
|
|
172
|
+
let res = null;
|
|
173
|
+
for (const w of chrome.windows()) {
|
|
174
|
+
for (const t of w.tabs()) {
|
|
175
|
+
if (t.url().includes("${host}")) {
|
|
176
|
+
res = t.execute({ javascript: ${JSON.stringify(code)} });
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (res !== null) break;
|
|
181
|
+
}
|
|
182
|
+
JSON.stringify(res);
|
|
183
|
+
`;
|
|
184
|
+
try {
|
|
185
|
+
const out = runJxa(script);
|
|
186
|
+
return out ? JSON.parse(out) : null;
|
|
187
|
+
} catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const fnStr = typeof fn === 'function' ? `(${fn.toString()})(${args.map((a) => JSON.stringify(a)).join(',')})` : String(fn);
|
|
193
|
+
const wrapped = `
|
|
194
|
+
(() => {
|
|
195
|
+
window.__tr_pending = true;
|
|
196
|
+
window.__tr_result = null;
|
|
197
|
+
window.__tr_error = null;
|
|
198
|
+
try {
|
|
199
|
+
const out = ${fnStr};
|
|
200
|
+
if (out && typeof out.then === 'function') {
|
|
201
|
+
out.then(
|
|
202
|
+
r => { window.__tr_result = r; window.__tr_pending = false; },
|
|
203
|
+
e => { window.__tr_error = String(e); window.__tr_pending = false; }
|
|
204
|
+
);
|
|
205
|
+
} else {
|
|
206
|
+
window.__tr_result = out;
|
|
207
|
+
window.__tr_pending = false;
|
|
208
|
+
}
|
|
209
|
+
} catch (err) {
|
|
210
|
+
window.__tr_error = String(err);
|
|
211
|
+
window.__tr_pending = false;
|
|
212
|
+
}
|
|
213
|
+
})()
|
|
214
|
+
`;
|
|
215
|
+
|
|
216
|
+
evalChrome(wrapped);
|
|
217
|
+
|
|
218
|
+
const deadline = Date.now() + 35_000;
|
|
219
|
+
while (Date.now() < deadline) {
|
|
220
|
+
const pending = evalChrome('window.__tr_pending');
|
|
221
|
+
if (pending === false) {
|
|
222
|
+
const err = evalChrome('window.__tr_error');
|
|
223
|
+
if (err) throw new Error(err);
|
|
224
|
+
return evalChrome('window.__tr_result');
|
|
225
|
+
}
|
|
226
|
+
await page.waitForTimeout(200);
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
locator(selector) {
|
|
232
|
+
return {
|
|
233
|
+
first() {
|
|
234
|
+
return this;
|
|
235
|
+
},
|
|
236
|
+
filter() {
|
|
237
|
+
return this;
|
|
238
|
+
},
|
|
239
|
+
async isVisible() {
|
|
240
|
+
return (
|
|
241
|
+
(await page.evaluate((sel) => {
|
|
242
|
+
const el = document.querySelector(sel);
|
|
243
|
+
return !!(el && (el.offsetWidth || el.offsetHeight || el.getClientRects().length));
|
|
244
|
+
}, selector)) || false
|
|
245
|
+
);
|
|
246
|
+
},
|
|
247
|
+
async waitFor(options = {}) {
|
|
248
|
+
const timeout = options.timeout ?? 10_000;
|
|
249
|
+
const started = Date.now();
|
|
250
|
+
while (Date.now() - started < timeout) {
|
|
251
|
+
const vis = await this.isVisible();
|
|
252
|
+
if (vis) return true;
|
|
253
|
+
await page.waitForTimeout(250);
|
|
254
|
+
}
|
|
255
|
+
return false;
|
|
256
|
+
},
|
|
257
|
+
async count() {
|
|
258
|
+
return (await page.evaluate((sel) => document.querySelectorAll(sel).length, selector)) || 0;
|
|
259
|
+
},
|
|
260
|
+
async click() {
|
|
261
|
+
return await page.evaluate((sel) => {
|
|
262
|
+
const el = document.querySelector(sel);
|
|
263
|
+
if (el) el.click();
|
|
264
|
+
}, selector);
|
|
265
|
+
},
|
|
266
|
+
async fill(text) {
|
|
267
|
+
return await page.evaluate(
|
|
268
|
+
({ sel, text }) => {
|
|
269
|
+
const el = document.querySelector(sel);
|
|
270
|
+
if (el) {
|
|
271
|
+
el.focus();
|
|
272
|
+
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
|
|
273
|
+
if (setter) setter.call(el, text);
|
|
274
|
+
else el.value = text;
|
|
275
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
276
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
{ sel: selector, text }
|
|
280
|
+
);
|
|
281
|
+
},
|
|
282
|
+
async press(key) {
|
|
283
|
+
return await page.evaluate(
|
|
284
|
+
({ sel, key }) => {
|
|
285
|
+
const el = document.querySelector(sel);
|
|
286
|
+
if (el) {
|
|
287
|
+
el.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true }));
|
|
288
|
+
el.dispatchEvent(new KeyboardEvent('keyup', { key, bubbles: true }));
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
{ sel: selector, key }
|
|
292
|
+
);
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
isMacChrome: true,
|
|
300
|
+
pages: () => [page],
|
|
301
|
+
newPage: async () => page,
|
|
302
|
+
grantPermissions: async () => {},
|
|
303
|
+
close: async () => {},
|
|
304
|
+
setDefaultTimeout: () => {},
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
31
308
|
/**
|
|
32
309
|
* Load chromium if playwright is installed. Mirrors source-adapters.getPlaywright
|
|
33
310
|
* so browser support stays an optional dependency.
|
|
@@ -76,6 +353,16 @@ export function ensureProfileDir(brainDir) {
|
|
|
76
353
|
* @returns {Promise<{ ok: boolean, context?: any, error?: string, profileDir?: string }>}
|
|
77
354
|
*/
|
|
78
355
|
export async function launchRotationContext(brainDir, opts = {}) {
|
|
356
|
+
// If Chrome is already running on macOS and not opted out, use the operator's active session
|
|
357
|
+
if (opts.useChrome !== false && (opts.chrome || isMacChromeRunning())) {
|
|
358
|
+
return {
|
|
359
|
+
ok: true,
|
|
360
|
+
context: createMacChromeContext(opts),
|
|
361
|
+
profileDir: 'running-google-chrome',
|
|
362
|
+
browser: 'running Google Chrome (macOS active session)',
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
79
366
|
const chromium = await getChromium();
|
|
80
367
|
if (!chromium) {
|
|
81
368
|
return {
|
|
@@ -88,12 +375,30 @@ export async function launchRotationContext(brainDir, opts = {}) {
|
|
|
88
375
|
const profileDir = ensureProfileDir(brainDir);
|
|
89
376
|
// Headed by default: provider logins routinely require human 2FA, and a
|
|
90
377
|
// headless first-run would simply hang on an invisible challenge.
|
|
91
|
-
|
|
378
|
+
//
|
|
379
|
+
// But a headed launch needs a display, and a server usually has none: the window cannot open,
|
|
380
|
+
// Chromium exits at once, and launchPersistentContext reports only "Target page, context or
|
|
381
|
+
// browser has been closed" for every candidate browser. That reads as "no browser installed"
|
|
382
|
+
// and left the operator with a live, un-rotated credential. With no display there is nothing
|
|
383
|
+
// for a human to see anyway, so fall back to headless rather than fail.
|
|
384
|
+
const headless = opts.headless === true || (process.platform === 'linux' && !process.env.DISPLAY);
|
|
92
385
|
|
|
93
386
|
const base = {
|
|
94
387
|
headless,
|
|
95
388
|
viewport: { width: 1440, height: 900 },
|
|
96
|
-
|
|
389
|
+
// Chromium refuses to start as root with its sandbox on, and it dies so fast that
|
|
390
|
+
// launchPersistentContext reports only "Target page, context or browser has been closed" —
|
|
391
|
+
// the exact failure every rotation attempt hit on this host, from every candidate browser,
|
|
392
|
+
// which reads as "no browser installed" and is why the key was never rotated. Add the flags
|
|
393
|
+
// root needs, and keep /dev/shm out of it because it is small in a container and stalls the
|
|
394
|
+
// renderer.
|
|
395
|
+
args: [
|
|
396
|
+
'--disable-blink-features=AutomationControlled',
|
|
397
|
+
'--no-first-run',
|
|
398
|
+
...(typeof process.getuid === 'function' && process.getuid() === 0
|
|
399
|
+
? ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
|
|
400
|
+
: []),
|
|
401
|
+
],
|
|
97
402
|
};
|
|
98
403
|
|
|
99
404
|
// Playwright's bundled Chromium is not available on every host — it has
|