total-recall-brain 3.41.1 → 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/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +197 -31
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +1456 -12
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +8 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +210 -27
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-catalog.mjs +10 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/rotation-capability.mjs +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +78 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-merge.mjs +131 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +111 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-store.mjs +26 -5
- 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/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/{src/core/headscale-server.mjs → capability/control-server.mjs} +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/capability/headscale-api.mjs +192 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/capability/index.mjs +46 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +9 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +23 -22
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +6 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +242 -8
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +14 -9
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/server.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/credentials.mjs +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/headscale-client.mjs +34 -181
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-auth.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/presence.mjs +5 -5
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/screen-view.mjs +78 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/DESIGN.md +211 -12
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/card.js +323 -388
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +12 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +7 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +6 -6
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +34 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/bootstrap.mjs +99 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/architecture.md +5 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/ide.md +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/memory.md +11 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/secrets.md +7 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/templates/openwiki/skills.md +7 -0
- 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/scaffold/.agent/skills/total-recall/references/auth.md +2 -0
- 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
|
@@ -456,6 +456,11 @@ export function deploySkill(brainDir, skillIdOrPath, opts = {}) {
|
|
|
456
456
|
if (layered && fs.existsSync(destDir) && fs.lstatSync(destDir).isSymbolicLink()) {
|
|
457
457
|
throw new Error(`Refusing layered deploy into symlink: ${destDir}`);
|
|
458
458
|
}
|
|
459
|
+
if (opts.dryRun) return { success: true, dryRun: true, skillId: resolved.id, destDir,
|
|
460
|
+
sourcePath: resolved.sourcePath, layered, existed, routeCore: Boolean(opts.routeCore), actions: [
|
|
461
|
+
{ op: 'replace-managed', from: layered && existed ? path.join(resolved.sourcePath, 'core') : resolved.sourcePath, to: layered && existed ? path.join(destDir, 'core') : destDir },
|
|
462
|
+
...(opts.routeCore && layered ? [{ op: 'route-core', path: path.join(destDir, 'SKILL.md') }] : []),
|
|
463
|
+
] };
|
|
459
464
|
const replacement = layered && existed
|
|
460
465
|
? replaceSkillDir(path.join(resolved.sourcePath, 'core'), path.join(destDir, 'core'), {
|
|
461
466
|
requiredFile: null,
|
|
@@ -463,6 +468,14 @@ export function deploySkill(brainDir, skillIdOrPath, opts = {}) {
|
|
|
463
468
|
})
|
|
464
469
|
: replaceSkillDir(resolved.sourcePath, destDir, { managedFiles: priorInstall?.managed_files || [] });
|
|
465
470
|
|
|
471
|
+
if (opts.routeCore && layered) {
|
|
472
|
+
const wrapper = path.join(destDir, 'SKILL.md');
|
|
473
|
+
const current = fs.readFileSync(wrapper, 'utf8');
|
|
474
|
+
const marker = '<!-- total-recall:managed-core-route:v1 -->';
|
|
475
|
+
if (!current.includes(marker)) fs.writeFileSync(wrapper, current.replace(/\s*$/, '') +
|
|
476
|
+
`\n\n${marker}\nRead the [maintained portable core](core/SKILL.md) for current protocol contracts; preserve repository-specific instructions above.\n`);
|
|
477
|
+
}
|
|
478
|
+
|
|
466
479
|
let adaptResult = { adapted: false };
|
|
467
480
|
if (opts.adapt && (!layered || !existed)) {
|
|
468
481
|
const openwikiDir =
|
|
@@ -536,7 +549,7 @@ function installDetail(entry, inst) {
|
|
|
536
549
|
const liveHash = exists ? hashSkillContent(inst.path) : null;
|
|
537
550
|
const liveCoreHash = layered && exists ? hashSkillLayer(inst.path, 'core') : null;
|
|
538
551
|
const liveRepoHash = layered && exists ? hashSkillLayer(inst.path, 'repo') : null;
|
|
539
|
-
const drift = layered
|
|
552
|
+
const drift = !exists ? true : layered
|
|
540
553
|
? liveCoreHash !== (entry?.core_hash || inst.core_hash)
|
|
541
554
|
: legacyUnderLayered
|
|
542
555
|
? false
|
|
@@ -548,7 +561,9 @@ function installDetail(entry, inst) {
|
|
|
548
561
|
return {
|
|
549
562
|
...inst,
|
|
550
563
|
exists,
|
|
564
|
+
required: !inst.obsolete,
|
|
551
565
|
live_hash: liveHash,
|
|
566
|
+
verification: !exists ? 'missing' : legacyUnderLayered ? 'not_adopted' : drift ? 'drifted' : 'verified',
|
|
552
567
|
...(layered ? {
|
|
553
568
|
core_hash: liveCoreHash,
|
|
554
569
|
repo_hash: liveRepoHash,
|
|
@@ -576,7 +591,7 @@ export function skillStatus(brainDir, skillId) {
|
|
|
576
591
|
} catch (err) {
|
|
577
592
|
// One unreadable install (a symlinked layer, a moved repo) must not hide
|
|
578
593
|
// the status of every other install.
|
|
579
|
-
return { ...inst, exists: fs.existsSync(inst.path), error: err.message, drift:
|
|
594
|
+
return { ...inst, exists: fs.existsSync(inst.path), error: err.message, drift: true, verification: 'error' };
|
|
580
595
|
}
|
|
581
596
|
});
|
|
582
597
|
|
|
@@ -593,7 +608,11 @@ export function skillStatus(brainDir, skillId) {
|
|
|
593
608
|
localPath,
|
|
594
609
|
installs: installDetails,
|
|
595
610
|
install_count: installDetails.length,
|
|
596
|
-
any_drift: installDetails.some((i) => i.drift),
|
|
611
|
+
any_drift: installDetails.some((i) => !i.obsolete && i.drift),
|
|
612
|
+
verified: Boolean(entry && localPath) && installDetails.every((i) => i.obsolete || (i.verification === 'verified' && (!i.contract || i.contract.ok))),
|
|
613
|
+
missing_count: installDetails.filter((i) => !i.obsolete && !i.exists).length,
|
|
614
|
+
obsolete_count: installDetails.filter((i) => i.obsolete).length,
|
|
615
|
+
error_count: installDetails.filter((i) => i.error).length,
|
|
597
616
|
any_contract_failure: installDetails.some((i) => i.contract && !i.contract.ok),
|
|
598
617
|
};
|
|
599
618
|
}
|
|
@@ -1269,6 +1288,17 @@ export function syncSkillTwoWay(brainDir, skillId, opts = {}) {
|
|
|
1269
1288
|
}
|
|
1270
1289
|
|
|
1271
1290
|
const { entry, locations } = collectSkillLocations(brainDir, skillId);
|
|
1291
|
+
const missingInstalls = loadRegistry(brainDir).installs.filter((inst) =>
|
|
1292
|
+
inst.skill_id === skillId && !inst.obsolete && !fs.existsSync(path.join(inst.path, 'SKILL.md')),
|
|
1293
|
+
);
|
|
1294
|
+
if (missingInstalls.length) {
|
|
1295
|
+
return {
|
|
1296
|
+
skillId, in_sync: false, dryRun,
|
|
1297
|
+
error: 'Recorded skill destinations are missing; reconcile existing repositories and deploy explicitly before synchronization',
|
|
1298
|
+
missing: missingInstalls.map((inst) => ({ path: inst.path, repo: inst.repo, repository_exists: Boolean(inst.repo && fs.existsSync(inst.repo)) })),
|
|
1299
|
+
locations: locations.length, actions: [],
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1272
1302
|
if (!locations.length) {
|
|
1273
1303
|
return { skillId, skipped: true, reason: 'no live copies found' };
|
|
1274
1304
|
}
|
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);
|