total-recall-brain 3.43.1 → 3.43.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "total-recall-brain",
3
- "version": "3.43.1",
3
+ "version": "3.43.2",
4
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": {
@@ -197,11 +197,13 @@ export function consumerState(root, pkg, version = null) {
197
197
  const declaredFiles = [];
198
198
  const pkgFile = path.join(root, 'package.json');
199
199
  let declared = null;
200
+ let devOnly = false;
200
201
  if (fs.existsSync(pkgFile)) {
201
202
  try {
202
203
  const j = JSON.parse(fs.readFileSync(pkgFile, 'utf8'));
203
204
  const all = { ...(j.dependencies || {}), ...(j.devDependencies || {}), ...(j.optionalDependencies || {}) };
204
205
  declared = all[pkg] ?? null;
206
+ devOnly = Boolean(j.devDependencies?.[pkg]) && !j.dependencies?.[pkg] && !j.optionalDependencies?.[pkg];
205
207
  if (declared) declaredFiles.push(pkgFile);
206
208
  } catch {
207
209
  /* unreadable package.json is reported as unknown, not as satisfied */
@@ -243,6 +245,10 @@ export function consumerState(root, pkg, version = null) {
243
245
  /* keep looking */
244
246
  }
245
247
  }
248
+ // A checkout with no copy of a dev dependency runs under the shared install. Nothing there is stale.
249
+ if (!installed && devOnly) {
250
+ return { root, declared, want, range, installed: null, satisfied: true, pinBehind: false, devNotInstalled: true, reason: 'dev-not-installed' };
251
+ }
246
252
  // What a consumer runs is what counts, and the version named is the question. A pin can lag on
247
253
  // purpose, because `update` installs plugin copies with --no-save. Comparing against the pin
248
254
  // would fail those and pass a consumer whose pin and install both lag.
@@ -285,6 +291,7 @@ export function verifyConsumers(pkg, version, opts = {}) {
285
291
  failing: failing.length,
286
292
  failures: failing.map((s) => ({ root: s.root, declared: s.declared, installed: s.installed, reason: s.reason })),
287
293
  pin_behind: relevant.filter((s) => s.pinBehind).map((s) => ({ root: s.root, declared: s.declared, installed: s.installed })),
294
+ dev_not_installed: relevant.filter((s) => s.devNotInstalled).map((s) => s.root),
288
295
  };
289
296
  }
290
297
 
@@ -369,6 +376,7 @@ export async function run(argv = process.argv, deps = {}) {
369
376
  }
370
377
  log(` ✓ ${pkg}@${version} verified: registry + ${consumers.consumers}/${consumers.consumers} consumers`);
371
378
  if (consumers.pin_behind.length) log(` ${consumers.pin_behind.length} run it with an older pin in package.json, such as ${consumers.pin_behind[0].root}`);
379
+ if (consumers.dev_not_installed.length) log(` ${consumers.dev_not_installed.length} keep no dev copy of it and run under the shared install`);
372
380
  continue;
373
381
  }
374
382
 
@@ -40,7 +40,9 @@ export async function runAutomaticBrainUpdates({brainDir,fleet=false,force=false
40
40
  if(!force && !isPackageAutoUpdateEnabled())return {skipped:true,reason:'disabled',results:[]};
41
41
  const file=path.join(brainDir,'memory-derived/automatic-brain-updates.json');
42
42
  let saved;try{saved=JSON.parse(fs.readFileSync(file,'utf8'));}catch{}
43
- const delay=saved?.failed?Math.min(intervalMs,5*60*1000):intervalMs;
43
+ // Fast retry is for this machine's own failures. A peer with no update transport fails every
44
+ // run, and counting it made maintenance run back to back forever.
45
+ const delay=(saved?.local_failed??saved?.failed)?Math.min(intervalMs,5*60*1000):intervalMs;
44
46
  if(!force && !skipThrottle && saved?.checkedAt && now()>=saved.checkedAt && now()-saved.checkedAt<delay)return {skipped:true,reason:'throttled',results:[]};
45
47
  fs.mkdirSync(path.dirname(file),{recursive:true});
46
48
  const lock=`${file}.lock`;
@@ -185,6 +185,22 @@ function targetSpec(key, version) {
185
185
  }
186
186
 
187
187
  /** Dry by default. Resolve releases once; install independently; verify actual resolution. */
188
+ /**
189
+ * A checkout that keeps no copy of a dev dependency runs under the shared install, so it has no
190
+ * old version to replace. Its pin stays current, and its dev tree is its owner's to install: one
191
+ * plugin's tree is about 460 MB, so restoring them all automatically could fill a disk.
192
+ */
193
+ const absentDevDep = (dep) => dep.section === 'devDependencies' && !dep.file;
194
+
195
+ /**
196
+ * The daemon runs with NODE_ENV=production, and under that npm and pnpm remove dev dependencies
197
+ * from whatever they install in. An install that wants a runtime tree says so with a flag.
198
+ */
199
+ function installEnv(env = process.env) {
200
+ const { NODE_ENV, npm_config_omit, NPM_CONFIG_OMIT, npm_config_production, NPM_CONFIG_PRODUCTION, ...kept } = env;
201
+ return kept;
202
+ }
203
+
188
204
  export async function updateAllBrains(opts = {}) {
189
205
  const dryRun = opts.dryRun !== false;
190
206
  const exec = opts.exec || spawnSync;
@@ -245,9 +261,12 @@ export async function updateAllBrains(opts = {}) {
245
261
  row.status = 'failed'; row.error = 'Newer local/preview package; refusing an implicit downgrade'; continue;
246
262
  }
247
263
  const shared = isLink(path.join(owner.owner, 'node_modules')) || isLink(path.join(consumer.root, 'node_modules'));
248
- const needsChange = shared || consumer.deps.some(dep => dep.version !== versions[dep.package]
249
- || (!consumer.runtimeOnly && dep.declared !== targetSpec(dep.key, versions[dep.package]))
250
- || !dep.file?.startsWith(path.join(owner.owner, 'node_modules') + path.sep));
264
+ const needsChange = shared || consumer.deps.some(dep => {
265
+ const pinLags = !consumer.runtimeOnly && dep.declared !== targetSpec(dep.key, versions[dep.package]);
266
+ if (absentDevDep(dep)) return pinLags;
267
+ return pinLags || dep.version !== versions[dep.package]
268
+ || !dep.file?.startsWith(path.join(owner.owner, 'node_modules') + path.sep);
269
+ });
251
270
  if (!needsChange) continue;
252
271
  row.status = dryRun ? 'would_update' : 'pending';
253
272
  if (!groups.has(owner.owner)) groups.set(owner.owner, { ...owner, consumers: [] });
@@ -264,8 +283,8 @@ export async function updateAllBrains(opts = {}) {
264
283
  const runtimeOnly = group.consumers.every(({ consumer }) => consumer.runtimeOnly);
265
284
  const unpinned = runtimeOnly && group.manager === 'npm';
266
285
  const needsInstall = group.consumers.some(({ consumer }) =>
267
- isLink(path.join(consumer.root, 'node_modules')) || consumer.deps.some(dep =>
268
- dep.version !== versions[dep.package] || !dep.file?.startsWith(path.join(group.owner, 'node_modules') + path.sep)));
286
+ isLink(path.join(consumer.root, 'node_modules')) || consumer.deps.some(dep => !absentDevDep(dep)
287
+ && (dep.version !== versions[dep.package] || !dep.file?.startsWith(path.join(group.owner, 'node_modules') + path.sep))));
269
288
  try {
270
289
  lockFd = acquireBrainUpdateLock(lock, opts.lockWaitMs === undefined ? {} : { waitMs: opts.lockWaitMs });
271
290
  for (const name of ['package-lock.json', 'npm-shrinkwrap.json', 'pnpm-lock.yaml']) {
@@ -294,7 +313,7 @@ export async function updateAllBrains(opts = {}) {
294
313
  if (unpinned) args.push('--no-save', ...new Set(group.consumers.flatMap(({ consumer }) =>
295
314
  consumer.deps.map(dep => `${dep.key}@${targetSpec(dep.key, versions[dep.package])}`))));
296
315
  else if (!needsInstall) args.push(group.manager === 'pnpm' ? '--lockfile-only' : '--package-lock-only');
297
- const install = exec(group.manager, args, { cwd: group.owner, encoding: 'utf8',
316
+ const install = exec(group.manager, args, { cwd: group.owner, env: installEnv(), encoding: 'utf8',
298
317
  timeout: 600000, maxBuffer: 8 * 1024 * 1024 });
299
318
  if (install.status !== 0) throw new Error(install.error?.message || install.stderr?.trim().slice(-1500) || `Install exited ${install.status}`);
300
319
  for (const { consumer, row } of group.consumers) {
@@ -309,6 +328,7 @@ export async function updateAllBrains(opts = {}) {
309
328
  const placed = (file) => Boolean(file) && allowedRoots.some((root) => file.startsWith(root));
310
329
  const bad = consumer.deps.find(dep => {
311
330
  const actual = installedBrainPackage(consumer.root, dep.key);
331
+ if (absentDevDep(dep) && !actual.file) return false;
312
332
  return actual.version !== versions[dep.package] || !placed(actual.file);
313
333
  });
314
334
  // A link is only a fault when it escapes every legitimate root; a self-contained one is fine.