total-recall-brain 3.39.2 → 3.39.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "total-recall-brain",
3
- "version": "3.39.2",
3
+ "version": "3.39.4",
4
4
  "description": "Portable SSSS memory, local retrieval and task instructions with explicit plugin loading.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tr-plugin-auth",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
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",
@@ -31,9 +31,12 @@
31
31
  "files": [
32
32
  "cli.mjs",
33
33
  "plugin.json",
34
- "src/",
35
- "ui/",
36
34
  "README.md",
37
- "LICENSE"
38
- ]
35
+ "src/",
36
+ "!src/**/*.spec.mjs",
37
+ "ui/"
38
+ ],
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
39
42
  }
@@ -258,5 +258,45 @@
258
258
  "status": {
259
259
  "command": "status",
260
260
  "refresh_seconds": 60
261
+ },
262
+ "distribution": {
263
+ "tier": "official",
264
+ "license": "open"
265
+ },
266
+ "card_layout": {
267
+ "template": "vault",
268
+ "regions": [
269
+ {
270
+ "widget": "status",
271
+ "title": "Status"
272
+ },
273
+ {
274
+ "widget": "live",
275
+ "title": "Live view"
276
+ },
277
+ {
278
+ "widget": "list",
279
+ "title": "Details",
280
+ "source": {
281
+ "command": "audit"
282
+ }
283
+ },
284
+ {
285
+ "widget": "actions",
286
+ "title": "Commands"
287
+ },
288
+ {
289
+ "widget": "settings",
290
+ "title": "Settings"
291
+ },
292
+ {
293
+ "widget": "credentials",
294
+ "title": "Credentials"
295
+ },
296
+ {
297
+ "widget": "activity",
298
+ "title": "Recent activity"
299
+ }
300
+ ]
261
301
  }
262
302
  }
@@ -471,6 +471,14 @@ function parseArgs(args) {
471
471
  .filter(Boolean),
472
472
  );
473
473
  break;
474
+ case '--keys':
475
+ out.keys.push(
476
+ ...String(args[++i] || '')
477
+ .split(',')
478
+ .map((s) => s.trim())
479
+ .filter(Boolean),
480
+ );
481
+ break;
474
482
  case '--tier':
475
483
  out.tier = args[++i];
476
484
  break;
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "tr-plugin-mesh",
3
3
  "version": "0.2.0",
4
- "private": true,
5
4
  "type": "module",
6
5
  "description": "Headscale and Tailscale SSH control-server mesh administration and node discovery",
7
6
  "scripts": {
@@ -17,5 +16,17 @@
17
16
  "license": "MIT",
18
17
  "dependencies": {
19
18
  "express": "^5.2.1"
19
+ },
20
+ "files": [
21
+ "cli.mjs",
22
+ "plugin.json",
23
+ "README.md",
24
+ "LICENSE",
25
+ "src/",
26
+ "!src/**/*.spec.mjs",
27
+ "ui/"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
20
31
  }
21
32
  }
@@ -272,5 +272,45 @@
272
272
  "status": {
273
273
  "command": "status",
274
274
  "refresh_seconds": 15
275
+ },
276
+ "distribution": {
277
+ "tier": "official",
278
+ "license": "open"
279
+ },
280
+ "card_layout": {
281
+ "template": "monitor",
282
+ "regions": [
283
+ {
284
+ "widget": "status",
285
+ "title": "Status"
286
+ },
287
+ {
288
+ "widget": "live",
289
+ "title": "Live view"
290
+ },
291
+ {
292
+ "widget": "list",
293
+ "title": "Details",
294
+ "source": {
295
+ "command": "view"
296
+ }
297
+ },
298
+ {
299
+ "widget": "actions",
300
+ "title": "Commands"
301
+ },
302
+ {
303
+ "widget": "settings",
304
+ "title": "Settings"
305
+ },
306
+ {
307
+ "widget": "credentials",
308
+ "title": "Credentials"
309
+ },
310
+ {
311
+ "widget": "activity",
312
+ "title": "Recent activity"
313
+ }
314
+ ]
275
315
  }
276
316
  }
@@ -18,6 +18,11 @@ export async function installPlugin(args = []) {
18
18
  console.log(`\n✅ Installed '${result.plugin.name}' (id: ${result.plugin.id}, v${result.plugin.version}) from ${from}`);
19
19
  console.log(` Location: ${result.plugin.dir}`);
20
20
  console.log(` sha256: ${result.sha256}`);
21
+ if (result.dependencies?.installed) {
22
+ console.log(` Dependencies: installed ${result.dependencies.packages.length} package(s)`);
23
+ } else if (result.dependencies?.reason && !['no package.json', 'no dependencies declared'].includes(result.dependencies.reason)) {
24
+ console.log(` Dependencies: NOT installed (${result.dependencies.reason})`);
25
+ }
21
26
  const cmds = syncPluginCommands(result.plugin.dir, { global: isGlobal });
22
27
  if (cmds.created.length) console.log(` Commands: ${cmds.created.map((c) => `total-recall ${c}`).join(', ')}`);
23
28
  for (const c of cmds.skipped) console.log(` Skipped command '${c.name}': ${c.reason}`);
@@ -293,6 +293,50 @@ export async function recompileAfterPluginMutation(projectRoot = process.cwd(),
293
293
  }
294
294
  }
295
295
 
296
+ /**
297
+ * Install a copied plugin's declared runtime dependencies.
298
+ *
299
+ * A plugin must carry its dependencies before the loader imports it: a single
300
+ * unresolvable import from any installed plugin aborts every plugin command in
301
+ * that scope, not just the plugin that owns it. Linked plugins are developer
302
+ * checkouts that keep their own `node_modules`, so they are never touched.
303
+ *
304
+ * @returns {Promise<{ installed: boolean, packages?: string[], reason?: string }>}
305
+ */
306
+ export async function installPluginDependencies(dest, id) {
307
+ const pkgPath = path.join(dest, 'package.json');
308
+ if (!fs.existsSync(pkgPath)) return { installed: false, reason: 'no package.json' };
309
+
310
+ let pkg;
311
+ try {
312
+ pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
313
+ } catch (err) {
314
+ throw new Error(`Plugin '${id}' was copied but its package.json is unreadable: ${err.message}`);
315
+ }
316
+ const packages = Object.keys(pkg.dependencies || {});
317
+ if (packages.length === 0) return { installed: false, reason: 'no dependencies declared' };
318
+
319
+ try {
320
+ await execFileAsync('npm', ['--version'], { timeout: 20_000 });
321
+ } catch {
322
+ console.warn(`⚠️ Plugin '${id}' declares ${packages.length} dependency(ies) but npm is unavailable. Run 'npm install --omit=dev' in ${dest}.`);
323
+ return { installed: false, reason: 'npm unavailable' };
324
+ }
325
+
326
+ try {
327
+ await execFileAsync('npm', ['install', '--omit=dev', '--no-audit', '--no-fund', '--loglevel=error'], {
328
+ cwd: dest,
329
+ timeout: 600_000,
330
+ maxBuffer: 16 * 1024 * 1024
331
+ });
332
+ } catch (err) {
333
+ const detail = (err.stderr || err.stdout || err.message || '').toString().trim()
334
+ .split('\n').filter(Boolean).slice(-3).join(' ');
335
+ throw new Error(`Plugin '${id}' was copied but its dependencies (${packages.join(', ')}) failed to install: ${detail}`);
336
+ }
337
+ return { installed: true, packages };
338
+ }
339
+
296
340
  /**
297
341
  * Install a plugin.
298
342
  * @param {string} source bundled id | public share URL | `peer:<host>/<id>` | git URL | local path
@@ -310,6 +354,7 @@ export async function installPlugin(source, options = {}) {
310
354
  let dest;
311
355
  let sourceRecord;
312
356
  let verifiedSha = null;
357
+ let dependencies = null;
313
358
 
314
359
  const resolved = resolveBundle ? await resolveBundle(source, options) : null;
315
360
  const bundled = !resolved && ID_PATTERN.test(source) ? listBundledPlugins().find((p) => p.id === source) : null;
@@ -361,6 +406,17 @@ export async function installPlugin(source, options = {}) {
361
406
  }
362
407
  }
363
408
 
409
+ if (sourceRecord.kind !== 'link') {
410
+ try {
411
+ dependencies = await installPluginDependencies(dest, manifest.id);
412
+ } catch (err) {
413
+ // A plugin copied without its dependencies would break every plugin
414
+ // command in this scope, so undo the copy instead of leaving it behind.
415
+ fs.rmSync(dest, { recursive: true, force: true });
416
+ throw err;
417
+ }
418
+ }
419
+
364
420
  const sha256 = verifiedSha || hashPluginDir(dest).sha256;
365
421
  const vaultRoot = vaultForPluginsDir(pluginsDir);
366
422
  try {
@@ -380,6 +436,7 @@ export async function installPlugin(source, options = {}) {
380
436
  plugin: { id: manifest.id, name: manifest.name, version: manifest.version, dir: dest, scope },
381
437
  source: sourceRecord,
382
438
  sha256,
439
+ dependencies,
383
440
  compilation
384
441
  };
385
442
  }
@@ -343,6 +343,38 @@ export function readRuleBudgets(skillsDir) {
343
343
  return out;
344
344
  }
345
345
 
346
+ const DEFAULT_CONTEXT_BUDGET = { total_tokens: 4000, bootstrap_total_tokens: 2500 };
347
+
348
+ /**
349
+ * Instruction-surface token budget from surface.yml files and the environment.
350
+ *
351
+ * The rule-section budgets above are characters, so a brain whose required
352
+ * rules are rich can need more text than the fixed token total allows and then
353
+ * write no surface at all. A project brain overrides the global brain;
354
+ * TR_CONTEXT_BUDGET_TOKENS overrides both, as JSON of the same keys.
355
+ */
356
+ export function readContextBudget(skillsDir) {
357
+ const out = {};
358
+ const dirs = [skillsDir ? path.join(skillsDir, 'total-recall') : null, globalBrainDir].filter(Boolean);
359
+ for (const dir of dirs.reverse()) {
360
+ try {
361
+ const cfg = yaml.parse(fs.readFileSync(path.join(dir, 'config', 'surface.yml'), 'utf8')) || {};
362
+ for (const k of Object.keys(DEFAULT_CONTEXT_BUDGET)) {
363
+ const v = Number(cfg?.budget?.[k]);
364
+ if (Number.isFinite(v) && v > 0) out[k] = v;
365
+ }
366
+ } catch {
367
+ /* no surface.yml in this brain */
368
+ }
369
+ }
370
+ try {
371
+ Object.assign(out, JSON.parse(process.env.TR_CONTEXT_BUDGET_TOKENS || '{}'));
372
+ } catch {
373
+ /* ignore a malformed override */
374
+ }
375
+ return { ...DEFAULT_CONTEXT_BUDGET, ...out };
376
+ }
377
+
346
378
  export function legacyRuleContributions(skillsDir) {
347
379
  if (!skillsDir) return [];
348
380
  return ['invariants', 'preferences', 'corrections'].map(name => ({
@@ -373,9 +405,11 @@ export function buildInstalledPluginsSummary(projectRoot = process.cwd()) {
373
405
  }
374
406
 
375
407
  export async function buildRulesBlock(skillsDir, nodes = [], {
376
- consumer = 'ide', derivedDir, vaultDir, projectRoot, actions = [], bootstrap = false, total = bootstrap ? 2500 : 4000,
408
+ consumer = 'ide', derivedDir, vaultDir, projectRoot, actions = [], bootstrap = false, total,
377
409
  } = {}) {
378
410
  const root = projectRoot || (skillsDir ? path.dirname(path.dirname(skillsDir)) : process.cwd());
411
+ const budget = readContextBudget(skillsDir);
412
+ const tokenBudget = total ?? (bootstrap ? budget.bootstrap_total_tokens : budget.total_tokens);
379
413
  const rules = selectRules(curatedRules(nodes), { actions, projectRoot: root, bootstrap });
380
414
  const contributions = [];
381
415
  const counts = category => rules.filter(n => n.category === category).length;
@@ -412,7 +446,7 @@ DO NOT SKIP THIS. Run Total Recall CLI help and context first before doing anyth
412
446
  const plugin = await assemblePluginContexts({ projectRoot: root, vaultDir, nodes, derivedDir });
413
447
  contributions.push({ id: 'plugins', text: plugin });
414
448
  }
415
- const result = assembleContext(contributions, { total });
449
+ const result = assembleContext(contributions, { total: tokenBudget });
416
450
  if (derivedDir) {
417
451
  fs.mkdirSync(derivedDir, { recursive: true });
418
452
  atomicWrite(path.join(derivedDir, bootstrap ? 'bootstrap-accounting.json' : 'context-accounting.json'), JSON.stringify({ ready: result.ready, ...result.stats }, null, 2));
@@ -1,14 +0,0 @@
1
- /** Per-worker isolation must exist before modules initialize config or logging. */
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { afterAll } from 'vitest';
6
-
7
- const previous = process.env._TR_TEST_AGENT_DIR;
8
- const fixture = fs.mkdtempSync(path.join(os.tmpdir(), 'tr-suite-brain-'));
9
- process.env._TR_TEST_AGENT_DIR = fixture;
10
- afterAll(() => {
11
- if (previous === undefined) delete process.env._TR_TEST_AGENT_DIR;
12
- else process.env._TR_TEST_AGENT_DIR = previous;
13
- fs.rmSync(fixture, { recursive: true, force: true });
14
- });