total-recall-brain 3.41.2 → 3.41.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/total-recall.mjs +9 -3
- package/frontend/dist/assets/{index-BG9NkVUy.css → index-BIQmqDKP.css} +1 -1
- package/frontend/dist/assets/{index-BtuiZqwP.js → index-DFenQQEQ.js} +74 -79
- 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 +2 -2
- 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 +4 -4
- 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/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 +4 -4
- 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 +4 -4
- 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 +4 -4
- 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 +4 -4
- 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 +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +16 -2
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +380 -298
- package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -1
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +6 -6
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +1 -1
- 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/daemon.mjs +23 -1
- package/src/cli/plugin/copies.mjs +8 -5
- package/src/cli/plugin/index.mjs +25 -2
- 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/global-brain-updates.mjs +21 -1
- package/src/core/mesh-brain-updates.mjs +5 -2
- package/src/core/meta-harness.mjs +16 -41
- 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/required-plugins.mjs +7 -0
- package/src/core/skills-registry.mjs +33 -3
- 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
package/src/server/auth.mjs
CHANGED
|
@@ -6,4 +6,44 @@ import { findValidKeyByToken, keyHasAnyScope, recordKeyUsage, recordKeyOutcome }
|
|
|
6
6
|
import { logger } from '../core/logger.mjs';
|
|
7
7
|
const owner = await loadPluginModule('auth', 'server-auth');
|
|
8
8
|
export const BCRYPT_COST = owner.BCRYPT_COST;
|
|
9
|
-
|
|
9
|
+
const base = owner.createServerAuth({ brainDir, authBrainDir: process.env.TR_SECRETS_BRAIN || brainDir, nodeEnv, watchdog, findValidKeyByToken, keyHasAnyScope, recordKeyUsage, recordKeyOutcome, logger });
|
|
10
|
+
export const { loadSecurityConfig, apiRateLimiter, authRateLimiter, sandboxRateLimiter, ingestRateLimiter, isLoopbackIp, isMeshIp, isLocalRequest, requireHttps, requireAuthOrLocal, corsOptions, requireScope, requireSandboxEnabled, sessionCookieOptions, loginHandler, changePasswordHandler, mintStepUpToken, verifyStepUpToken, verifyDashboardPassword, logoutHandler, signSessionToken, dashboardAuthState } = base;
|
|
11
|
+
|
|
12
|
+
const hostName = (value) => String(value || '').replace(/^https?:\/\//, '').replace(/[/?#].*$/, '').replace(/:\d+$/, '').replace(/^\[(.*)\]$/, '$1');
|
|
13
|
+
|
|
14
|
+
let alwaysRequired = false;
|
|
15
|
+
|
|
16
|
+
/** Headless hosts (the memory-only runtime) never offer the open-local mode. */
|
|
17
|
+
export function enforceLogin() {
|
|
18
|
+
alwaysRequired = true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Dashboard login is optional for a local install. It is required when
|
|
23
|
+
* `dashboard.require_login` is true, or, when unset, once a dashboard password
|
|
24
|
+
* exists. When not required, only a genuinely local browser or CLI is let in:
|
|
25
|
+
* loopback socket, loopback Host header and no foreign Origin (which blocks DNS
|
|
26
|
+
* rebinding and cross-site requests). Bearer tokens and every other client
|
|
27
|
+
* (mesh, tunnel, remote) still authenticate normally.
|
|
28
|
+
*/
|
|
29
|
+
export function loginRequired() {
|
|
30
|
+
if (alwaysRequired) return true;
|
|
31
|
+
const configured = loadSecurityConfig().dashboard?.require_login;
|
|
32
|
+
return typeof configured === 'boolean' ? configured : dashboardAuthState().configured;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isOpenLocalRequest(req) {
|
|
36
|
+
if (req.headers.authorization) return false;
|
|
37
|
+
if (!isLocalRequest(req)) return false;
|
|
38
|
+
if (!isLoopbackIp(hostName(req.headers.host))) return false;
|
|
39
|
+
const origin = req.headers.origin;
|
|
40
|
+
return !origin || isLoopbackIp(hostName(origin));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function requireAuth(req, res, next) {
|
|
44
|
+
if (!loginRequired() && isOpenLocalRequest(req)) {
|
|
45
|
+
req.auth = { type: 'dashboard_session', local: true };
|
|
46
|
+
return next();
|
|
47
|
+
}
|
|
48
|
+
return base.requireAuth(req, res, next);
|
|
49
|
+
}
|
|
@@ -4,7 +4,7 @@ import cors from 'cors';
|
|
|
4
4
|
import cookieParser from 'cookie-parser';
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import { requireHttps, corsOptions, apiRateLimiter, requireAuthOrLocal } from './auth.mjs';
|
|
7
|
+
import { requireHttps, corsOptions, apiRateLimiter, requireAuthOrLocal, enforceLogin } from './auth.mjs';
|
|
8
8
|
import { memoryRouter } from './routes/memory.mjs';
|
|
9
9
|
import { authRouter } from './routes/auth.mjs';
|
|
10
10
|
import { keysRouter } from './routes/keys.mjs';
|
|
@@ -17,6 +17,7 @@ import { getNodes } from '../core/vault-cache.mjs';
|
|
|
17
17
|
const { version } = JSON.parse(fs.readFileSync(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf8'));
|
|
18
18
|
|
|
19
19
|
export function createMemoryApp() {
|
|
20
|
+
enforceLogin();
|
|
20
21
|
const app = express();
|
|
21
22
|
app.disable('x-powered-by');
|
|
22
23
|
app.set('trust proxy', 'loopback');
|
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
/** Required auth plugin adapter for dashboard authentication routes. */
|
|
2
|
+
import express from 'express';
|
|
2
3
|
import * as auth from '../auth.mjs';
|
|
3
4
|
import { logger } from '../../core/logger.mjs';
|
|
4
5
|
import { BRAIN_DIR } from './_shared.mjs';
|
|
5
6
|
import { loadPluginModule } from '../../core/plugin-runtime.mjs';
|
|
6
7
|
const { createAuthRouter } = await loadPluginModule('auth', 'auth-router');
|
|
7
|
-
|
|
8
|
+
const pluginRouter = createAuthRouter({ auth, logger, BRAIN_DIR });
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Total Recall has no Google sign-in: the plugin serves it for apps that do,
|
|
12
|
+
* so this router refuses those routes and drops Google from provider discovery.
|
|
13
|
+
*/
|
|
14
|
+
export const authRouter = express.Router();
|
|
15
|
+
authRouter.use('/auth/google', (_req, res) => res.status(404).json({ error: 'Google sign-in is not available in Total Recall' }));
|
|
16
|
+
authRouter.use('/auth/providers', (req, res, next) => {
|
|
17
|
+
if (req.method !== 'GET') return next();
|
|
18
|
+
const json = res.json.bind(res);
|
|
19
|
+
res.json = (body) => {
|
|
20
|
+
if (body && typeof body === 'object') delete body.google;
|
|
21
|
+
return json(body);
|
|
22
|
+
};
|
|
23
|
+
next();
|
|
24
|
+
});
|
|
25
|
+
authRouter.use('/auth/status', (req, res, next) => {
|
|
26
|
+
if (req.method !== 'GET') return next();
|
|
27
|
+
const json = res.json.bind(res);
|
|
28
|
+
res.json = (body) => json({ ...body, login_required: auth.loginRequired() });
|
|
29
|
+
next();
|
|
30
|
+
});
|
|
31
|
+
authRouter.use(pluginRouter);
|
|
@@ -307,15 +307,28 @@ router.patch("/api/plugins/:id/card-face", requireAuth, requireScope("config:wri
|
|
|
307
307
|
/**
|
|
308
308
|
* POST /api/plugins/:id/run body: { subcommand?, args? }
|
|
309
309
|
* Runs the plugin's CLI handler in a child process. Executing plugin code is a
|
|
310
|
-
* write-level action
|
|
310
|
+
* write-level action unless the installed manifest explicitly declares the
|
|
311
|
+
* exact operation read-only. Alternate execution and credential input remain
|
|
312
|
+
* write-level regardless of that declaration.
|
|
311
313
|
*/
|
|
312
|
-
router.post("/api/plugins/:id/run", requireAuth,
|
|
314
|
+
router.post("/api/plugins/:id/run", requireAuth, (req, res, next) => {
|
|
315
|
+
const { subcommand, args = [], stdin, execution } = req.body || {};
|
|
316
|
+
try {
|
|
317
|
+
const plugin = getPluginById(req.params.id, projectRoot());
|
|
318
|
+
const operation = plugin?.valid && plugin.manifest?.cli?.subcommands?.find(item => item.name === subcommand);
|
|
319
|
+
res.locals.executionPlugin = plugin;
|
|
320
|
+
const read = typeof subcommand === 'string' && Array.isArray(args) && args.every(arg => typeof arg === 'string') &&
|
|
321
|
+
operation?.effect === 'read' && stdin === undefined && !execution &&
|
|
322
|
+
!operation.args?.some(arg => arg.secret);
|
|
323
|
+
return requireScope(read ? 'config:read' : 'config:write')(req, res, next);
|
|
324
|
+
} catch (error) { badRequest(res, error.message); }
|
|
325
|
+
}, async (req, res) => {
|
|
313
326
|
try {
|
|
314
327
|
const { subcommand = "", args = [], stdin, execution } = req.body || {};
|
|
315
328
|
if (typeof subcommand !== "string" || !Array.isArray(args) || args.some((a) => typeof a !== "string")) {
|
|
316
329
|
return badRequest(res, "subcommand must be a string and args an array of strings");
|
|
317
330
|
}
|
|
318
|
-
const plugin =
|
|
331
|
+
const plugin = res.locals.executionPlugin;
|
|
319
332
|
if (!plugin) return res.status(404).json({ success: false, error: `Plugin ${req.params.id} not found` });
|
|
320
333
|
if (!plugin.valid) return badRequest(res, `Plugin ${plugin.id} has an invalid manifest: ${plugin.errors.join("; ")}`);
|
|
321
334
|
if (!plugin.manifest?.cli?.handler) return badRequest(res, `Plugin ${plugin.id} does not declare a CLI handler`);
|
|
@@ -330,7 +343,7 @@ router.post("/api/plugins/:id/run", requireAuth, requireScope("config:write"), a
|
|
|
330
343
|
return res.json({ success: result.ok, pluginId: plugin.id, ...result });
|
|
331
344
|
}
|
|
332
345
|
|
|
333
|
-
const result = await runPluginCommand(plugin, { subcommand, args, ...(stdin !== undefined ? { stdinText: stdin } : {}), cwd: projectRoot(), secretsBrainDir: process.env.TR_SECRETS_BRAIN || globalBrainDir || BRAIN_DIR });
|
|
346
|
+
const result = await runPluginCommand(plugin, { subcommand, args, ...(stdin !== undefined ? { stdinText: stdin } : {}), cwd: projectRoot(), secretsBrainDir: process.env.TR_SECRETS_BRAIN || globalBrainDir || BRAIN_DIR, maxOutputBytes: 4 * 1024 * 1024 });
|
|
334
347
|
res.json({ success: result.ok, pluginId: plugin.id, ...result });
|
|
335
348
|
} catch (err) {
|
|
336
349
|
badRequest(res, err.message);
|