total-recall-brain 3.32.3 → 3.32.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.32.3",
3
+ "version": "3.32.4",
4
4
  "description": "Personal AI system — database-free, SSSS-powered intelligence engine deployable to any POSIX host.",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -170,7 +170,7 @@ function printHelp() {
170
170
  `);
171
171
  }
172
172
 
173
- function resolveTargetDir(isGlobal) {
173
+ export function resolveTargetDir(isGlobal) {
174
174
  if (isGlobal) {
175
175
  try {
176
176
  const gBrain = resolveBrainLayer('global');
@@ -235,7 +235,7 @@ function existingTag(file, key) {
235
235
  * Composable instructions: the surfaces list every command, so any change to
236
236
  * the command set recompiles them (project layer, or every project for global).
237
237
  */
238
- function recompileSurfaces(isGlobal) {
238
+ export function recompileSurfaces(isGlobal) {
239
239
  if (process.env.TR_COMMAND_NO_COMPILE === '1' || !process.argv[1]) return;
240
240
  try {
241
241
  const child = spawn(process.execPath, [process.argv[1], 'compile', isGlobal ? '--global' : '--project'], { detached: true, stdio: 'ignore' });
@@ -246,6 +246,48 @@ function recompileSurfaces(isGlobal) {
246
246
  }
247
247
  }
248
248
 
249
+ /**
250
+ * Register every `commands` entry of an installed plugin as a composable command.
251
+ * A command that already exists and was generated from this plugin is refreshed;
252
+ * one that exists from anywhere else is left alone and reported.
253
+ */
254
+ export function syncPluginCommands(pluginDir, { global: isGlobal = false } = {}) {
255
+ const manifest = JSON.parse(fs.readFileSync(path.join(fs.realpathSync(pluginDir), 'plugin.json'), 'utf8'));
256
+ const commandsDir = resolveTargetDir(isGlobal);
257
+ const result = { created: [], skipped: [] };
258
+ for (const entry of manifest.commands || []) {
259
+ const file = path.join(commandsDir, `${entry.name}.mjs`);
260
+ if (fs.existsSync(file)) {
261
+ if (!fs.readFileSync(file, 'utf8').startsWith(`// Generated from plugin ${manifest.id};`)) {
262
+ result.skipped.push({ name: entry.name, reason: 'a different command with this name exists' });
263
+ continue;
264
+ }
265
+ fs.rmSync(file);
266
+ }
267
+ generatePluginCommand(pluginDir, entry.name, commandsDir);
268
+ result.created.push(entry.name);
269
+ }
270
+ if (result.created.length) recompileSurfaces(isGlobal);
271
+ return result;
272
+ }
273
+
274
+ /** Remove the commands a plugin generated (only files carrying its generated header). */
275
+ export function removePluginCommands(pluginId, { global: isGlobal = false } = {}) {
276
+ const commandsDir = resolveTargetDir(isGlobal);
277
+ const removed = [];
278
+ if (!fs.existsSync(commandsDir)) return removed;
279
+ for (const name of fs.readdirSync(commandsDir)) {
280
+ if (!name.endsWith('.mjs')) continue;
281
+ const file = path.join(commandsDir, name);
282
+ if (fs.readFileSync(file, 'utf8').startsWith(`// Generated from plugin ${pluginId};`)) {
283
+ fs.rmSync(file);
284
+ removed.push(name.replace(/\.mjs$/, ''));
285
+ }
286
+ }
287
+ if (removed.length) recompileSurfaces(isGlobal);
288
+ return removed;
289
+ }
290
+
249
291
  export default async function commandCmd(rawArgs = []) {
250
292
  const isGlobal = rawArgs.includes('--global') || rawArgs.includes('-g');
251
293
  const asJson = rawArgs.includes('--json');
@@ -1,4 +1,5 @@
1
1
  import { installPlugin as install } from '../../core/plugin-store.mjs';
2
+ import { syncPluginCommands } from '../command.mjs';
2
3
 
3
4
  export async function installPlugin(args = []) {
4
5
  const isGlobal = args.includes('--global') || args.includes('-g');
@@ -16,7 +17,11 @@ export async function installPlugin(args = []) {
16
17
  const from = result.source.kind === 'peer' ? `peer ${result.source.peer_hostname}` : result.source.kind;
17
18
  console.log(`\n✅ Installed '${result.plugin.name}' (id: ${result.plugin.id}, v${result.plugin.version}) from ${from}`);
18
19
  console.log(` Location: ${result.plugin.dir}`);
19
- console.log(` sha256: ${result.sha256}\n`);
20
+ console.log(` sha256: ${result.sha256}`);
21
+ const cmds = syncPluginCommands(result.plugin.dir, { global: isGlobal });
22
+ if (cmds.created.length) console.log(` Commands: ${cmds.created.map((c) => `total-recall ${c}`).join(', ')}`);
23
+ for (const c of cmds.skipped) console.log(` Skipped command '${c.name}': ${c.reason}`);
24
+ console.log('');
20
25
  } catch (err) {
21
26
  console.error(`❌ ${err.message}`);
22
27
  process.exit(1);
@@ -1,4 +1,5 @@
1
1
  import { uninstallPlugin, setPluginShared } from '../../core/plugin-store.mjs';
2
+ import { removePluginCommands } from '../command.mjs';
2
3
  import { publicPluginShareUrl } from '../../core/plugin-public.mjs';
3
4
 
4
5
  export async function removePlugin(args = []) {
@@ -13,7 +14,10 @@ export async function removePlugin(args = []) {
13
14
 
14
15
  try {
15
16
  const result = await uninstallPlugin(id, { projectRoot: process.cwd(), global: isGlobal ? true : undefined });
16
- console.log(`\n✅ Removed plugin '${id}' (${result.scope}) from ${result.dir}\n`);
17
+ const removed = removePluginCommands(id, { global: result.scope === 'global' });
18
+ console.log(`\n✅ Removed plugin '${id}' (${result.scope}) from ${result.dir}`);
19
+ if (removed.length) console.log(` Removed commands: ${removed.join(', ')}`);
20
+ console.log('');
17
21
  } catch (err) {
18
22
  console.error(`❌ ${err.message}`);
19
23
  process.exit(1);
@@ -10,6 +10,7 @@ import fs from 'node:fs';
10
10
  import path from 'node:path';
11
11
  import os from 'node:os';
12
12
  import { setSecret, listSecretsMeta, updateSecretMeta } from './secrets-store.mjs';
13
+ import { providerForKeyName } from './provider-catalog.mjs';
13
14
 
14
15
  /**
15
16
  * @deprecated Kept as empty/export for older callers. Import does NOT use this
@@ -108,13 +109,9 @@ export function inferProvider(key) {
108
109
  if (!key) return null;
109
110
  const k = String(key);
110
111
 
111
- // Lazy catalog match (optional enrichment only)
112
- try {
113
- // Dynamic import avoided in sync path — inline thin match from catalog module if already loadable
114
- // Use sync require-free approach: duplicate-free segment heuristic only here for speed.
115
- } catch {
116
- /* ignore */
117
- }
112
+ // A known provider anywhere in the key name wins (JSN_SMTP2GO_API_KEY -> smtp2go).
113
+ const known = providerForKeyName(k);
114
+ if (known) return known.id;
118
115
 
119
116
  // Strip common prefixes that are not the vendor
120
117
  let base = k