total-recall-brain 3.41.2 → 3.41.4
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/bin/total-recall.mjs +19 -4
- package/frontend/dist/assets/{index-BG9NkVUy.css → index-BIQmqDKP.css} +1 -1
- package/frontend/dist/assets/{index-BtuiZqwP.js → index-CRzjQLu7.js} +7 -7
- package/frontend/dist/index.html +2 -2
- package/frontend/dist/sw.js +8 -5
- package/frontend/src/api/auth.ts +0 -3
- package/frontend/src/components/plugins/PluginPanel.tsx +7 -3
- package/frontend/src/components/plugins/card/CardEngine.tsx +1 -1
- package/frontend/src/pages/LoginPage.tsx +1 -41
- package/frontend/src/pages/PluginsPage.tsx +14 -7
- package/package.json +7 -5
- package/scaffold/.agent/skills/plugin/scripts/record.mjs +6 -3
- package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +197 -31
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +6 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +8 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +48 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/login-projections.mjs +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/mesh-secrets.mjs +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/secrets.mjs +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-carriers.mjs +76 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-prune.mjs +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-sync.mjs +53 -17
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/DESIGN.md +56 -8
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +1068 -204
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/panel.js +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +436 -274
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +12 -12
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +16 -2
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +461 -242
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +12 -12
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +1 -7
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +17 -2
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +426 -300
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +23 -8
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +6 -2
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/DESIGN.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +350 -283
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +23 -8
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +5 -1
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +31 -19
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +23 -10
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +5 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/grants.mjs +7 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +61 -38
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +57 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +220 -36
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/server/routes/mesh.mjs +7 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +65 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +534 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +139 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +2849 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +376 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adapters/dsh.mjs +446 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +398 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +152 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +82 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +90 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +415 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/tracker.mjs +133 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +233 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +845 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/capability/BRICK.md +64 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/capability/index.mjs +250 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +344 -14
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +2917 -140
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +9 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +223 -5
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +129 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +355 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +375 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +37 -10
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +986 -280
- package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -1
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +25 -10
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +6 -2
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +17 -3
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +38 -15
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +60 -34
- package/src/cli/command.mjs +23 -18
- package/src/cli/config.mjs +3 -0
- package/src/cli/connect.mjs +12 -116
- package/src/cli/daemon.mjs +23 -1
- package/src/cli/harness.mjs +0 -1
- package/src/cli/plugin/copies.mjs +8 -5
- package/src/cli/plugin/create.mjs +56 -3
- package/src/cli/plugin/index.mjs +40 -5
- package/src/cli/plugin/monetization.mjs +232 -12
- package/src/cli/preflight.mjs +161 -0
- package/src/cli/rebuild.mjs +22 -0
- package/src/cli/reset-password.mjs +3 -2
- package/src/cli/skill.mjs +8 -2
- package/src/cli/status.mjs +25 -7
- package/src/core/automatic-brain-updates.mjs +9 -3
- package/src/core/brain-updates.mjs +63 -5
- package/src/core/cli-commands.mjs +2 -0
- package/src/core/context-compiler.mjs +8 -1
- package/src/core/default-plugins.mjs +1 -1
- package/src/core/global-brain-updates.mjs +21 -1
- package/src/core/mesh-brain-updates.mjs +5 -2
- package/src/core/meta-harness.mjs +32 -79
- package/src/core/plugin-dependencies.mjs +6 -1
- package/src/core/plugin-runner.mjs +6 -1
- package/src/core/plugin-store.mjs +67 -1
- package/src/core/plugin-updates.mjs +1 -1
- package/src/core/portable-skills-maintenance.mjs +19 -0
- package/src/core/preflight-gate.mjs +77 -0
- package/src/core/preflight-receipt.mjs +123 -0
- package/src/core/required-plugins.mjs +7 -0
- package/src/core/runtime.mjs +0 -3
- package/src/core/skills-registry.mjs +33 -3
- package/src/core/surface.mjs +38 -1
- package/src/server/auth.mjs +41 -1
- package/src/server/memory-app.mjs +2 -1
- package/src/server/routes/auth.mjs +25 -1
- package/src/server/routes/plugins.mjs +17 -4
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { spawnSync } from 'node:child_process';
|
|
10
10
|
import fs from 'node:fs';
|
|
11
11
|
import path from 'node:path';
|
|
12
|
+
import { hostSecretsBrainDir } from './src/host-secrets-store.mjs';
|
|
12
13
|
|
|
13
14
|
function resolveTrPath() {
|
|
14
15
|
const localTr = path.join(process.cwd(), 'bin', 'total-recall.mjs');
|
|
@@ -19,13 +20,13 @@ function resolveTrPath() {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
async function resolveSecret(key) {
|
|
22
|
-
const { getSecret
|
|
23
|
-
const result = await getSecret(
|
|
23
|
+
const { getSecret } = await import('./src/secrets-store.mjs');
|
|
24
|
+
const result = await getSecret(hostSecretsBrainDir(), key, { actor: 'auth-cli', action: 'use' });
|
|
24
25
|
return result.found ? result.value : null;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
function resolveBrainDir() {
|
|
28
|
-
return
|
|
29
|
+
return hostSecretsBrainDir();
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
async function listStoredPasskeys() {
|
|
@@ -33,7 +34,11 @@ async function listStoredPasskeys() {
|
|
|
33
34
|
return listPasskeys(resolveBrainDir());
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
async function handleStatus(json) {
|
|
37
|
+
async function handleStatus(subArgs, json) {
|
|
38
|
+
if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
|
|
39
|
+
console.log('Usage: total-recall auth status [--json]');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
37
42
|
const passkeys = await listStoredPasskeys();
|
|
38
43
|
const googleClientId = await resolveSecret('GOOGLE_CLIENT_ID');
|
|
39
44
|
const googleClientSecret = await resolveSecret('GOOGLE_CLIENT_SECRET');
|
|
@@ -87,9 +92,15 @@ async function handleStatus(json) {
|
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
async function handlePasskey(subArgs, json) {
|
|
90
|
-
const passkeys = await listStoredPasskeys();
|
|
91
95
|
const action = subArgs[0] || 'list';
|
|
92
96
|
|
|
97
|
+
if (action === 'help' || action === '--help' || action === '-h') {
|
|
98
|
+
console.log('Usage: total-recall auth passkey [list|status|delete <id>] [--json]');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const passkeys = await listStoredPasskeys();
|
|
103
|
+
|
|
93
104
|
if (action === 'list') {
|
|
94
105
|
if (json) {
|
|
95
106
|
console.log(JSON.stringify({ count: passkeys.length, passkeys }, null, 2));
|
|
@@ -112,25 +123,80 @@ async function handlePasskey(subArgs, json) {
|
|
|
112
123
|
return;
|
|
113
124
|
}
|
|
114
125
|
|
|
126
|
+
if (action === 'delete' || action === 'remove' || action === 'rm') {
|
|
127
|
+
const id = subArgs[1];
|
|
128
|
+
if (!id) {
|
|
129
|
+
console.error('Credential ID required: total-recall auth passkey delete <id>');
|
|
130
|
+
process.exitCode = 1;
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const { deletePasskey } = await import('./src/webauthn-store.mjs');
|
|
134
|
+
const result = deletePasskey(resolveBrainDir(), id);
|
|
135
|
+
if (!result.found) {
|
|
136
|
+
console.error(`Passkey not found: ${id}`);
|
|
137
|
+
process.exitCode = 1;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (json) {
|
|
141
|
+
console.log(JSON.stringify({ success: true, deleted: id }));
|
|
142
|
+
} else {
|
|
143
|
+
console.log(`Passkey removed: ${id}`);
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
115
148
|
console.error(`Unknown passkey action: ${action}`);
|
|
116
149
|
process.exitCode = 1;
|
|
117
150
|
}
|
|
118
151
|
|
|
119
152
|
async function handleGoogle(subArgs, json) {
|
|
153
|
+
const action = subArgs[0] || 'status';
|
|
154
|
+
if (action === 'help' || action === '--help' || action === '-h') {
|
|
155
|
+
console.log('Usage: total-recall auth google [status|connect|accounts|refresh <id>|disconnect <id>] [--json]');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
120
158
|
const { createGoogleOAuth } = await import('./src/google-oauth.mjs');
|
|
121
159
|
const google = createGoogleOAuth({ brainDir: resolveBrainDir() });
|
|
122
|
-
|
|
123
|
-
|
|
160
|
+
if (action === 'accounts') {
|
|
161
|
+
const accounts = google.status();
|
|
162
|
+
if (json) {
|
|
163
|
+
console.log(JSON.stringify({ accounts }));
|
|
164
|
+
} else {
|
|
165
|
+
console.log(`Google Connected Accounts: ${accounts.length}`);
|
|
166
|
+
for (const a of accounts) {
|
|
167
|
+
console.log(` • [${a.status}] ${a.email || a.account_id} (Expires: ${a.expiry || 'never'})`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
124
172
|
if (action === 'refresh' || action === 'disconnect') {
|
|
125
|
-
if (!subArgs[1])
|
|
126
|
-
|
|
127
|
-
|
|
173
|
+
if (!subArgs[1]) {
|
|
174
|
+
console.error(`Google account identifier required: total-recall auth google ${action} <id>`);
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (action === 'refresh') {
|
|
179
|
+
await google.access(subArgs[1]);
|
|
180
|
+
const accounts = google.status();
|
|
181
|
+
if (json) console.log(JSON.stringify({ accounts }));
|
|
182
|
+
else console.log(`Account ${subArgs[1]} refreshed.`);
|
|
183
|
+
} else {
|
|
184
|
+
const res = await google.disconnect(subArgs[1]);
|
|
185
|
+
if (json) console.log(JSON.stringify(res));
|
|
186
|
+
else console.log(`Account ${subArgs[1]} disconnected.`);
|
|
187
|
+
}
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!['status', 'connect'].includes(action)) {
|
|
191
|
+
console.error(`Unknown action: ${action}. Use: total-recall auth google [status|connect|accounts|refresh|disconnect]`);
|
|
192
|
+
process.exitCode = 1;
|
|
128
193
|
return;
|
|
129
194
|
}
|
|
130
|
-
if (!['status', 'connect'].includes(action)) throw new Error('Use google status|connect|accounts|refresh|disconnect');
|
|
131
195
|
let cfg;
|
|
132
196
|
try { cfg = google.config(); } catch {
|
|
133
|
-
console.log(JSON.stringify({ configured: false, error: 'Configure GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET and GOOGLE_REDIRECT_URI in auth' }));
|
|
197
|
+
console.log(JSON.stringify({ configured: false, error: 'Configure GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET and GOOGLE_REDIRECT_URI in auth' }));
|
|
198
|
+
process.exitCode = 2;
|
|
199
|
+
return;
|
|
134
200
|
}
|
|
135
201
|
// Browser initiation sets its own HttpOnly state cookie; never manufacture a state-less consent URL in the CLI.
|
|
136
202
|
const authUrl = new URL('/auth/google', cfg.redirectUri).href;
|
|
@@ -138,7 +204,11 @@ async function handleGoogle(subArgs, json) {
|
|
|
138
204
|
console.log(json ? JSON.stringify(out) : `Open sign-in: ${authUrl}`);
|
|
139
205
|
}
|
|
140
206
|
|
|
141
|
-
async function handleProbe(json) {
|
|
207
|
+
async function handleProbe(subArgs, json) {
|
|
208
|
+
if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
|
|
209
|
+
console.log('Usage: total-recall auth probe [--json]');
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
142
212
|
const host = process.env.TR_HOST || 'http://localhost:3000';
|
|
143
213
|
const endpoints = ['/auth/status', '/auth/providers', '/health'];
|
|
144
214
|
const results = [];
|
|
@@ -182,7 +252,11 @@ async function handleProbe(json) {
|
|
|
182
252
|
if (!allOk) process.exitCode = 1;
|
|
183
253
|
}
|
|
184
254
|
|
|
185
|
-
async function handleSession(json) {
|
|
255
|
+
async function handleSession(subArgs, json) {
|
|
256
|
+
if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
|
|
257
|
+
console.log('Usage: total-recall auth session [--json]');
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
186
260
|
const brain = resolveBrainDir();
|
|
187
261
|
const secretExists = Boolean(await resolveSecret('SESSION_SECRET'));
|
|
188
262
|
|
|
@@ -208,7 +282,11 @@ async function handleSecret(subArgs, json) {
|
|
|
208
282
|
await secretCli(forwarded);
|
|
209
283
|
}
|
|
210
284
|
|
|
211
|
-
async function handleAudit(json) {
|
|
285
|
+
async function handleAudit(subArgs, json) {
|
|
286
|
+
if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
|
|
287
|
+
console.log('Usage: total-recall auth audit [--json]');
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
212
290
|
const brain = resolveBrainDir();
|
|
213
291
|
const webauthnPath = path.join(brain, 'config', 'webauthn.json');
|
|
214
292
|
const findings = [];
|
|
@@ -284,16 +362,41 @@ async function handleAudit(json) {
|
|
|
284
362
|
}
|
|
285
363
|
|
|
286
364
|
async function handleRotate(subArgs, json) {
|
|
365
|
+
if (Array.isArray(subArgs) && (subArgs.includes('--help') || subArgs.includes('-h') || subArgs[0] === 'help')) {
|
|
366
|
+
console.log('Usage: total-recall auth rotate [--collaboration] [--json]');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
287
369
|
const { createSessionCredentials } = await import('./src/session-credentials.mjs');
|
|
288
370
|
const { defaultBrainDir } = await import('./src/secrets-store.mjs');
|
|
289
371
|
const purpose = subArgs.includes('--collaboration') ? 'collaboration' : 'dashboard';
|
|
290
372
|
const result = createSessionCredentials({ brainDir: process.env.TR_SECRETS_BRAIN || defaultBrainDir() }).rotateSession(purpose);
|
|
291
373
|
console.log(json ? JSON.stringify(result) : 'Auth signing credential rotated; existing sessions are invalidated.');
|
|
292
374
|
}
|
|
375
|
+
|
|
293
376
|
async function handlePassword(args, json) {
|
|
294
|
-
if (args
|
|
377
|
+
if (!Array.isArray(args) || args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
378
|
+
console.log('Usage: total-recall auth password set --stdin [--force-reset]');
|
|
379
|
+
console.log(' total-recall auth password status [--json]');
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (args[0] === 'status') {
|
|
383
|
+
const { createSessionCredentials } = await import('./src/session-credentials.mjs');
|
|
384
|
+
const state = createSessionCredentials({ brainDir: resolveBrainDir() }).dashboardState();
|
|
385
|
+
if (json) {
|
|
386
|
+
console.log(JSON.stringify(state));
|
|
387
|
+
} else {
|
|
388
|
+
console.log(`Dashboard Password: ${state.configured ? 'Configured' : 'Not configured'}`);
|
|
389
|
+
if (state.forcePasswordReset) console.log(' • Password reset is currently enforced');
|
|
390
|
+
}
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (args[0] !== 'set' || !args.includes('--stdin') || args.slice(1).some(value => !['--stdin', '--force-reset'].includes(value))) {
|
|
394
|
+
console.error('Use: total-recall auth password set --stdin [--force-reset]');
|
|
395
|
+
process.exitCode = 1;
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
295
398
|
const chunks = []; let size = 0;
|
|
296
|
-
for await (const chunk of process.stdin) { size += chunk.length; if (size > 8192)
|
|
399
|
+
for await (const chunk of process.stdin) { size += chunk.length; if (size > 8192) { console.error('Password input exceeds limit'); process.exitCode = 1; return; } chunks.push(chunk); }
|
|
297
400
|
const password = Buffer.concat(chunks).toString('utf8').replace(/\r?\n$/, '');
|
|
298
401
|
const { createSessionCredentials } = await import('./src/session-credentials.mjs');
|
|
299
402
|
const result = await createSessionCredentials({ brainDir: resolveBrainDir() }).setDashboardPassword(password, { forcePasswordReset: args.includes('--force-reset') });
|
|
@@ -313,10 +416,10 @@ export async function run(argv = []) {
|
|
|
313
416
|
|
|
314
417
|
switch (command) {
|
|
315
418
|
case 'status':
|
|
316
|
-
await handleStatus(isJson);
|
|
419
|
+
await handleStatus(rest, isJson);
|
|
317
420
|
break;
|
|
318
421
|
case 'audit':
|
|
319
|
-
await handleAudit(isJson);
|
|
422
|
+
await handleAudit(rest, isJson);
|
|
320
423
|
break;
|
|
321
424
|
case 'rotate':
|
|
322
425
|
await handleRotate(rest, isJson);
|
|
@@ -328,32 +431,95 @@ export async function run(argv = []) {
|
|
|
328
431
|
await handlePasskey(rest, isJson);
|
|
329
432
|
break;
|
|
330
433
|
case 'key': {
|
|
434
|
+
if (rest.includes('--help') || rest.includes('-h') || rest[0] === 'help') {
|
|
435
|
+
console.log('Usage: total-recall auth key list [--json]');
|
|
436
|
+
console.log(' total-recall auth key issue <name> --scopes <csv> [--role <role>] [--kind <kind>] [--expires <date>] [--json]');
|
|
437
|
+
console.log(' total-recall auth key revoke <id> [--json]');
|
|
438
|
+
console.log(' total-recall auth key usage [id] [--json]');
|
|
439
|
+
console.log(' total-recall auth key roles [--json]');
|
|
440
|
+
console.log(' total-recall auth key role-set <name> --scopes <csv> [--json]');
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
331
443
|
const { createKeyStore } = await import('./src/access-keys.mjs');
|
|
332
444
|
const store = createKeyStore({ brainDir: resolveBrainDir() });
|
|
333
445
|
const [action = 'list', id] = rest;
|
|
334
446
|
const flag = name => { const at = rest.indexOf(name); return at < 0 ? undefined : rest[at + 1]; };
|
|
335
447
|
let result;
|
|
336
|
-
if (action === 'list')
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
448
|
+
if (action === 'list') {
|
|
449
|
+
const keys = store.loadKeys().map(({ token_hash, ...key }) => key);
|
|
450
|
+
if (isJson) {
|
|
451
|
+
console.log(JSON.stringify(keys));
|
|
452
|
+
} else {
|
|
453
|
+
console.log(`Access Keys: ${keys.length}`);
|
|
454
|
+
for (const k of keys) {
|
|
455
|
+
const status = k.revoked ? '[REVOKED]' : '[ACTIVE]';
|
|
456
|
+
console.log(` • ${status} ${k.name} (${k.token_prefix}…)`);
|
|
457
|
+
console.log(` ID: ${k.id}`);
|
|
458
|
+
console.log(` Scopes: ${(k.scopes || []).join(', ') || 'none'}`);
|
|
459
|
+
if (k.expires_at) console.log(` Expires: ${k.expires_at}`);
|
|
460
|
+
if (k.last_used_at) console.log(` Last used: ${k.last_used_at} (${k.hit_count} hits)`);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
break;
|
|
464
|
+
} else if (action === 'issue') {
|
|
465
|
+
result = store.issueKey(id, { scopes: flag('--scopes')?.split(','), role: flag('--role'), kind: flag('--kind'), expires_at: flag('--expires') });
|
|
466
|
+
} else if (action === 'revoke') {
|
|
467
|
+
result = store.revokeKey(id);
|
|
468
|
+
} else if (action === 'usage') {
|
|
469
|
+
result = store.listKeyUsage(id);
|
|
470
|
+
} else if (action === 'roles') {
|
|
471
|
+
result = store.loadRoles();
|
|
472
|
+
} else if (action === 'role-set') {
|
|
473
|
+
result = store.setRole(id, flag('--scopes')?.split(','));
|
|
474
|
+
} else {
|
|
475
|
+
console.error('Use: total-recall auth key list|issue|revoke|usage|roles|role-set');
|
|
476
|
+
process.exitCode = 1;
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
if (isJson) {
|
|
480
|
+
console.log(JSON.stringify(result));
|
|
481
|
+
} else {
|
|
482
|
+
if (action === 'issue') {
|
|
483
|
+
console.log(`Key issued: ${result.name} (${result.id})`);
|
|
484
|
+
console.log(`Token: ${result.token}`);
|
|
485
|
+
console.log('Store this token now; it will not be displayed again.');
|
|
486
|
+
} else if (action === 'revoke') {
|
|
487
|
+
console.log(`Key revoked: ${id}`);
|
|
488
|
+
} else {
|
|
489
|
+
console.log(JSON.stringify(result, null, 2));
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
break;
|
|
344
493
|
}
|
|
345
494
|
case 'agent': {
|
|
346
495
|
// Sign in an agent CLI that authenticates on its own terminal. The pty lives in
|
|
347
496
|
// scripts/agent-login-pty.py because Node has none and this plugin carries no pty library;
|
|
348
497
|
// this routes the plugin's own surface to it.
|
|
498
|
+
if (rest.includes('--help') || rest.includes('-h') || rest[0] === 'help') {
|
|
499
|
+
console.log('Usage: total-recall auth agent list [--json]');
|
|
500
|
+
console.log(' total-recall auth agent login <agent> [--code <code>] [--code-file <path>] [--url-file <path>] [--json]');
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
349
503
|
const { loginAgent, AGENT_LOGIN_PROFILES } = await import('./src/agent-login.mjs');
|
|
350
504
|
const [action = 'list', name] = rest;
|
|
351
505
|
const flag = (n) => { const at = rest.indexOf(n); return at < 0 ? undefined : rest[at + 1]; };
|
|
352
506
|
if (action === 'list') {
|
|
353
|
-
|
|
507
|
+
const list = Object.keys(AGENT_LOGIN_PROFILES).map((id) => ({ id, ...AGENT_LOGIN_PROFILES[id] }));
|
|
508
|
+
if (isJson) {
|
|
509
|
+
console.log(JSON.stringify(list));
|
|
510
|
+
} else {
|
|
511
|
+
console.log('Supported Agent Profiles:');
|
|
512
|
+
for (const item of list) {
|
|
513
|
+
console.log(` • ${item.id}: ${item.name || item.id}`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
if (action !== 'login') {
|
|
519
|
+
console.error('Use: total-recall auth agent list | total-recall auth agent login <agent>');
|
|
520
|
+
process.exitCode = 1;
|
|
354
521
|
break;
|
|
355
522
|
}
|
|
356
|
-
if (action !== 'login') throw new Error('Use auth agent list | auth agent login <agent> [--code <code>] [--code-file <path>] [--url-file <path>]');
|
|
357
523
|
const result = await loginAgent({
|
|
358
524
|
agent: name,
|
|
359
525
|
code: flag('--code'),
|
|
@@ -371,10 +537,10 @@ export async function run(argv = []) {
|
|
|
371
537
|
await handleGoogle(rest, isJson);
|
|
372
538
|
break;
|
|
373
539
|
case 'probe':
|
|
374
|
-
await handleProbe(isJson);
|
|
540
|
+
await handleProbe(rest, isJson);
|
|
375
541
|
break;
|
|
376
542
|
case 'session':
|
|
377
|
-
await handleSession(isJson);
|
|
543
|
+
await handleSession(rest, isJson);
|
|
378
544
|
break;
|
|
379
545
|
case 'secret':
|
|
380
546
|
case 'secrets':
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tr-plugin-auth",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "tr-plugin-auth",
|
|
9
|
-
"version": "1.2.
|
|
9
|
+
"version": "1.2.5",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@simplewebauthn/server": "^13.3.2",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"supertest": "^7.3.1",
|
|
21
|
-
"total-recall-brain": "3.41.
|
|
21
|
+
"total-recall-brain": "3.41.2",
|
|
22
22
|
"vitest": "^4.1.12"
|
|
23
23
|
}
|
|
24
24
|
},
|
|
@@ -3733,9 +3733,9 @@
|
|
|
3733
3733
|
}
|
|
3734
3734
|
},
|
|
3735
3735
|
"node_modules/total-recall-brain": {
|
|
3736
|
-
"version": "3.41.
|
|
3737
|
-
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.41.
|
|
3738
|
-
"integrity": "sha512-
|
|
3736
|
+
"version": "3.41.2",
|
|
3737
|
+
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.41.2.tgz",
|
|
3738
|
+
"integrity": "sha512-bwJFAFAyvk9ysf+5zhBzMxrKus1Hj1AygUWdBNt+vQbM2sfSEf6701sa30KwfGyU8+cMuN6YNT5Mn63mFZIi/w==",
|
|
3739
3739
|
"dev": true,
|
|
3740
3740
|
"license": "MIT",
|
|
3741
3741
|
"workspaces": [
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://github.com/gregiteen/total-recall/blob/main/metadata.plugin.schema.json",
|
|
3
3
|
"id": "auth",
|
|
4
4
|
"name": "Auth & Secrets",
|
|
5
|
-
"version": "1.2.
|
|
5
|
+
"version": "1.2.5",
|
|
6
6
|
"description": "Hardware WebAuthn biometric FIDO2 passkeys, OAuth session security, and access control.",
|
|
7
7
|
"author": "Total Recall contributors",
|
|
8
8
|
"license": "MIT",
|
|
@@ -154,6 +154,13 @@
|
|
|
154
154
|
"tag": "tr-auth-card",
|
|
155
155
|
"module": "ui/card.js",
|
|
156
156
|
"description": "Gatepass Card Face"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "panel",
|
|
160
|
+
"kind": "panel",
|
|
161
|
+
"tag": "tr-auth-panel",
|
|
162
|
+
"module": "ui/panel.js",
|
|
163
|
+
"description": "Gatepass Auth & Credentials Management Panel"
|
|
157
164
|
}
|
|
158
165
|
]
|
|
159
166
|
},
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
enqueueRotationDueTasks,
|
|
52
52
|
getBrowserRotateAssist,
|
|
53
53
|
} from './secrets-rotate.mjs';
|
|
54
|
+
import { findAmbientPasswordExports, removeAmbientPasswordExports, carrierPassword } from './secrets-carriers.mjs';
|
|
54
55
|
import {
|
|
55
56
|
loadRemoteTargets,
|
|
56
57
|
addRemoteTarget,
|
|
@@ -200,6 +201,7 @@ function printHelp() {
|
|
|
200
201
|
generate <key> Generate a random secret for outside integrations
|
|
201
202
|
get <key> [--json] Read selected-store value (audited); JSON reports found/value
|
|
202
203
|
list List keys + metadata only (no values)
|
|
204
|
+
prune Read-only report: unbound, errored and duplicate keys
|
|
203
205
|
rotate <key> Replace value and mark rotated. Prompts for the value;
|
|
204
206
|
--stdin reads fd0, --from-clipboard reads the OS
|
|
205
207
|
clipboard (use the browser you are already signed
|
|
@@ -286,6 +288,9 @@ function printHelp() {
|
|
|
286
288
|
--headscale-url <https-url>
|
|
287
289
|
rotation-due List keys overdue for rotation
|
|
288
290
|
--enqueue Create daemon tasks + browser-use prompts for each due key
|
|
291
|
+
carriers Find shell profiles and /etc files that export the master
|
|
292
|
+
password to every process; --remove-ambient deletes them
|
|
293
|
+
once an owner-only carrier holds the same password.
|
|
289
294
|
fingerprint Identify a credential without printing it: length, type
|
|
290
295
|
prefix, fingerprint and the store keys holding the same
|
|
291
296
|
value. --from-clipboard | --stdin; --expect-prefix <p>
|
|
@@ -643,6 +648,9 @@ function parseArgs(args) {
|
|
|
643
648
|
case '--stdin':
|
|
644
649
|
out.stdin = true;
|
|
645
650
|
break;
|
|
651
|
+
case '--remove-ambient':
|
|
652
|
+
out.removeAmbient = true;
|
|
653
|
+
break;
|
|
646
654
|
case '--app':
|
|
647
655
|
out.app = args[++i];
|
|
648
656
|
break;
|
|
@@ -1045,6 +1053,18 @@ export default async function secretCli(argv) {
|
|
|
1045
1053
|
return;
|
|
1046
1054
|
}
|
|
1047
1055
|
|
|
1056
|
+
if (opts.command === 'prune') {
|
|
1057
|
+
const { pruneCandidates, pruneSummary } = await import('./secrets-prune.mjs');
|
|
1058
|
+
const rows = (await listSecretsMeta(brainDir)).filter((r) => r.set);
|
|
1059
|
+
const candidates = pruneCandidates(rows);
|
|
1060
|
+
const sum = pruneSummary(rows, candidates);
|
|
1061
|
+
console.log(`\n prune report (read-only): ${sum.candidates} of ${sum.total} keys worth a look`);
|
|
1062
|
+
console.log(` unbound=${sum.unbound} error=${sum.errored} duplicated=${sum.duplicated}\n`);
|
|
1063
|
+
for (const c of candidates) console.log(` • ${c.key.padEnd(38)} ${c.provider || '-'} [${c.reasons.join('; ')}]`);
|
|
1064
|
+
console.log('\n Nothing was deleted. Remove a key with: auth secret delete <key>\n');
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1048
1068
|
if (opts.command === 'shared' || opts.command === 'shared-values' || opts.command === 'duplicates') {
|
|
1049
1069
|
const shared = await getSharedValueHealth(brainDir);
|
|
1050
1070
|
console.log(`\n ${shared.healthy ? '✅' : '❌'} ${shared.message}`);
|
|
@@ -1209,6 +1229,34 @@ export default async function secretCli(argv) {
|
|
|
1209
1229
|
return;
|
|
1210
1230
|
}
|
|
1211
1231
|
|
|
1232
|
+
if (opts.command === 'carriers') {
|
|
1233
|
+
// Files that make the master password ambient (shell profiles, /etc/environment).
|
|
1234
|
+
// Reports files and line numbers only; --remove-ambient deletes those exports once an
|
|
1235
|
+
// owner-only carrier is verified to hold the same password.
|
|
1236
|
+
if (opts.removeAmbient) {
|
|
1237
|
+
const r = removeAmbientPasswordExports();
|
|
1238
|
+
if (opts.json) console.log(JSON.stringify(r, null, 2));
|
|
1239
|
+
else {
|
|
1240
|
+
console.log(`\n ${r.removed.length ? '✅ Removed ambient master-password exports' : 'No ambient master-password exports found'}`);
|
|
1241
|
+
for (const row of r.removed) console.log(` ${row.file}: line ${row.lines.join(', ')}`);
|
|
1242
|
+
console.log('');
|
|
1243
|
+
}
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
const found = findAmbientPasswordExports();
|
|
1247
|
+
const carried = Boolean(carrierPassword());
|
|
1248
|
+
if (opts.json) console.log(JSON.stringify({ ambient: found, owner_only_carrier: carried }, null, 2));
|
|
1249
|
+
else {
|
|
1250
|
+
console.log(`\n Owner-only carrier holds the master password: ${carried ? 'yes' : 'NO'}`);
|
|
1251
|
+
if (!found.length) console.log(' No ambient exports in shell profiles or /etc');
|
|
1252
|
+
for (const row of found) console.log(` ❌ ${row.file}: line ${row.lines.join(', ')} exports it to every process started there`);
|
|
1253
|
+
if (found.length) console.log(' Remove with: total-recall auth secret carriers --remove-ambient');
|
|
1254
|
+
console.log('');
|
|
1255
|
+
}
|
|
1256
|
+
if (found.length) process.exitCode = 1;
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1212
1260
|
if (opts.command === 'fingerprint') {
|
|
1213
1261
|
// Identify a credential without seeing it: which store keys hold the same
|
|
1214
1262
|
// value, its length and its non-secret type prefix. Lets an operator check
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool logins derived from stored secrets.
|
|
3
|
+
*
|
|
4
|
+
* A secret that reaches a node (see secrets-sync.mjs) is only useful to people and
|
|
5
|
+
* agents there once the tool that uses it is signed in. This signs `gh` in from
|
|
6
|
+
* `github_token`, and wires git to it, so a token rotated once lands on every machine
|
|
7
|
+
* without anyone logging in by hand. Nothing here prints or logs the token.
|
|
8
|
+
*/
|
|
9
|
+
import { execFile } from 'node:child_process';
|
|
10
|
+
|
|
11
|
+
const TIMEOUT_MS = 20_000;
|
|
12
|
+
|
|
13
|
+
/** Run a command, optionally feeding stdin; never rejects. */
|
|
14
|
+
export function defaultRun(cmd, args, input) {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
const child = execFile(cmd, args, { timeout: TIMEOUT_MS, env: { ...process.env, GH_PROMPT_DISABLED: '1', GH_NO_UPDATE_NOTIFIER: '1' } }, (error, stdout, stderr) => {
|
|
17
|
+
resolve({ ok: !error, code: error?.code ?? 0, stdout: String(stdout || ''), stderr: String(stderr || '') });
|
|
18
|
+
});
|
|
19
|
+
child.stdin?.end(input === undefined ? undefined : String(input));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const firstLine = (text) => String(text || '').split('\n').map((l) => l.trim()).find(Boolean) || 'unknown error';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Sign `gh` in with `token` when it is not already, then point git at it.
|
|
27
|
+
* @returns {{ status: 'no-token'|'gh-missing'|'current'|'logged-in'|'failed', error?: string }}
|
|
28
|
+
*/
|
|
29
|
+
export async function projectGithubLogin({ token, run = defaultRun } = {}) {
|
|
30
|
+
const wanted = String(token || '').trim();
|
|
31
|
+
if (!wanted) return { status: 'no-token' };
|
|
32
|
+
|
|
33
|
+
const current = await run('gh', ['auth', 'token']);
|
|
34
|
+
if (current.code === 'ENOENT') return { status: 'gh-missing' };
|
|
35
|
+
if (current.ok && current.stdout.trim() === wanted) return { status: 'current' };
|
|
36
|
+
|
|
37
|
+
const login = await run('gh', ['auth', 'login', '--hostname', 'github.com', '--git-protocol', 'https', '--with-token'], wanted);
|
|
38
|
+
if (!login.ok) return { status: 'failed', error: firstLine(login.stderr).replaceAll(wanted, '***') };
|
|
39
|
+
|
|
40
|
+
await run('gh', ['auth', 'setup-git', '--hostname', 'github.com']);
|
|
41
|
+
return { status: 'logged-in' };
|
|
42
|
+
}
|
|
@@ -26,6 +26,24 @@ const FETCH_TIMEOUT_MS = 8_000;
|
|
|
26
26
|
|
|
27
27
|
/* ---------- leader side ---------- */
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* A grant key `repo:<name>` stands for every secret whose `repos` metadata names that
|
|
31
|
+
* repository, so a machine is given the keys its repositories use rather than a list of
|
|
32
|
+
* names or `*`. Resolved on each request, so rebinding a key takes effect on the next sync.
|
|
33
|
+
*/
|
|
34
|
+
export function expandRepoGrants(policy, rows) {
|
|
35
|
+
const byRepo = new Map();
|
|
36
|
+
for (const row of rows || []) for (const repo of row.repos || []) {
|
|
37
|
+
if (!byRepo.has(repo)) byRepo.set(repo, []);
|
|
38
|
+
byRepo.get(repo).push(row.key);
|
|
39
|
+
}
|
|
40
|
+
const grants = (policy?.grants || []).map((grant) => ({
|
|
41
|
+
...grant,
|
|
42
|
+
keys: (grant.keys || []).flatMap((key) => (String(key).startsWith('repo:') ? (byRepo.get(String(key).slice(5)) || []) : [key])),
|
|
43
|
+
}));
|
|
44
|
+
return { ...policy, grants };
|
|
45
|
+
}
|
|
46
|
+
|
|
29
47
|
function audit(brainDir, event) {
|
|
30
48
|
try {
|
|
31
49
|
appendAudit(brainDir, event);
|
|
@@ -26,7 +26,8 @@ import {
|
|
|
26
26
|
} from '../secrets-store.mjs';
|
|
27
27
|
import { logger } from '../logger.mjs';
|
|
28
28
|
import { getMeshSyncAuthorization, requireMeshSyncAuth } from '../mesh-auth.mjs';
|
|
29
|
-
import { createMeshIdentityMiddleware, createMeshListHandler, createMeshSecretHandler } from '../mesh-secrets.mjs';
|
|
29
|
+
import { createMeshIdentityMiddleware, createMeshListHandler, createMeshSecretHandler, expandRepoGrants } from '../mesh-secrets.mjs';
|
|
30
|
+
import { loadPolicy } from '../mesh-identity.mjs';
|
|
30
31
|
import { throttledFetch } from '../throttled-fetch.mjs';
|
|
31
32
|
import { listProviders, getProvider } from '../provider-catalog.mjs';
|
|
32
33
|
import {
|
|
@@ -90,15 +91,17 @@ router.get('/api/secrets/checksum', requireMeshSyncAuth, async (req, res) => {
|
|
|
90
91
|
// Mesh-identity secrets: the caller is the enrolled machine, not a shared
|
|
91
92
|
// token. Each node gets only the keys its grant allows; see mesh-secrets.mjs.
|
|
92
93
|
const requireMeshIdentity = createMeshIdentityMiddleware();
|
|
94
|
+
const meshPolicyFor = async (brainDir) => expandRepoGrants(await loadPolicy(brainDir), await listSecretsMeta(brainDir));
|
|
93
95
|
router.get(
|
|
94
96
|
'/api/secrets/mesh',
|
|
95
97
|
requireMeshIdentity,
|
|
96
98
|
createMeshListHandler({
|
|
97
99
|
brainDir: BRAIN_DIR,
|
|
100
|
+
policyFor: meshPolicyFor,
|
|
98
101
|
keysFor: async () => (await listSecretsMeta(BRAIN_DIR)).map((entry) => entry.key),
|
|
99
102
|
}),
|
|
100
103
|
);
|
|
101
|
-
router.get('/api/secrets/mesh/:key', requireMeshIdentity, createMeshSecretHandler({ brainDir: BRAIN_DIR }));
|
|
104
|
+
router.get('/api/secrets/mesh/:key', requireMeshIdentity, createMeshSecretHandler({ brainDir: BRAIN_DIR, policyFor: meshPolicyFor }));
|
|
102
105
|
|
|
103
106
|
router.get('/api/secrets/list', requireAuth, requireScope('keys:read', 'config:read'), async (req, res) => {
|
|
104
107
|
try {
|