total-recall-brain 3.28.0 → 3.28.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/frontend/src/App.spec.tsx +4 -0
- package/frontend/src/pages/PluginsPage.spec.tsx +10 -2
- package/package.json +1 -1
- package/scaffold/openwiki/README.md +10 -0
- package/scaffold/openwiki/architecture.md +5 -0
- package/scaffold/openwiki/ide.md +10 -0
- package/scaffold/openwiki/memory.md +11 -0
- package/scaffold/openwiki/secrets.md +7 -0
- package/scaffold/openwiki/skills.md +7 -0
- package/src/cli/connect.mjs +13 -8
- package/src/cli/init.mjs +51 -27
- package/src/cli/skill-projection.mjs +67 -16
- package/src/cli/status.mjs +42 -31
- package/src/core/daemon-loop.mjs +68 -15
- package/src/core/embeddings.mjs +14 -4
- package/src/core/fact-seeker.mjs +95 -3
- package/src/core/headscale-client.mjs +30 -6
- package/src/core/leader-election.mjs +49 -0
- package/src/core/research-queue.mjs +22 -0
- package/src/core/research.mjs +71 -0
- package/src/core/runtime.mjs +65 -25
- package/src/core/sandbox.mjs +27 -3
- package/src/core/secret-integration-research.mjs +31 -20
- package/src/core/skills-registry.mjs +2 -0
- package/src/server/route-manifest.json +48 -0
package/src/core/sandbox.mjs
CHANGED
|
@@ -47,11 +47,31 @@ function canUseMacSandbox(profile) {
|
|
|
47
47
|
return macSandboxProbe;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Strip quoted argument payloads.
|
|
52
|
+
*
|
|
53
|
+
* TR dispatches CLI agents with an argv array (`spawn(binary, args)`), so a
|
|
54
|
+
* quoted argument is ONE argument and a `|` inside it is inert data — there is no
|
|
55
|
+
* shell to interpret it. Prompts routinely embed vault fact bodies, and two facts
|
|
56
|
+
* documenting the legitimate `curl … | bash` install line made every deliberation
|
|
57
|
+
* run fail with a bogus "Piping download tool to shell blocked" security
|
|
58
|
+
* exception. Strip quoted payloads before the shell-syntax checks so prompt text
|
|
59
|
+
* is not mistaken for a pipeline.
|
|
60
|
+
*/
|
|
61
|
+
function stripQuotedArgs(line) {
|
|
62
|
+
return line.replace(/"(?:\\.|[^"\\])*"/g, '""').replace(/'(?:\\.|[^'\\])*'/g, "''");
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
/**
|
|
51
66
|
* Command Sanitizer / Whitelist Execution Validator
|
|
52
67
|
* Blocks high-risk actions (destructive deletes, reverse shells, shell pipes)
|
|
68
|
+
*
|
|
69
|
+
* @param {string} commandLine
|
|
70
|
+
* @param {{ argvSpawn?: boolean }} [opts] `argvSpawn: true` means the caller
|
|
71
|
+
* passes the command as an argument vector rather than to a shell, so quoted
|
|
72
|
+
* content cannot act as shell syntax.
|
|
53
73
|
*/
|
|
54
|
-
export function validateCommand(commandLine) {
|
|
74
|
+
export function validateCommand(commandLine, { argvSpawn = false } = {}) {
|
|
55
75
|
if (typeof commandLine !== 'string') return true;
|
|
56
76
|
const trimmed = commandLine.trim();
|
|
57
77
|
|
|
@@ -68,16 +88,20 @@ export function validateCommand(commandLine) {
|
|
|
68
88
|
}
|
|
69
89
|
}
|
|
70
90
|
|
|
91
|
+
// Shell syntax only exists when a shell runs the line. Under an argv spawn the
|
|
92
|
+
// quoted payload is a single inert argument, so scan the unquoted remainder.
|
|
93
|
+
const scanned = argvSpawn ? stripQuotedArgs(trimmed) : trimmed;
|
|
94
|
+
|
|
71
95
|
// Piping download tools directly into shell
|
|
72
96
|
// Split strings to bypass static security grep audit false positives while keeping full safety checks
|
|
73
97
|
const pipeToShellRegex = new RegExp("(?:cu" + "rl|wget|fetch)\\s+.*\\s*\\|\\s*(?:ba" + "sh|sh|zsh)", "i");
|
|
74
|
-
const isPipeToShell = pipeToShellRegex.test(
|
|
98
|
+
const isPipeToShell = pipeToShellRegex.test(scanned);
|
|
75
99
|
if (isPipeToShell) {
|
|
76
100
|
throw new Error(`Security Exception: Piping download tool to shell blocked: "${commandLine}"`);
|
|
77
101
|
}
|
|
78
102
|
|
|
79
103
|
// Reverse shells and network exfiltration tools
|
|
80
|
-
const isReverseShell = /\b(?:nc|netcat)\s+-/i.test(
|
|
104
|
+
const isReverseShell = /\b(?:nc|netcat)\s+-/i.test(scanned) || /bash\s+-i/i.test(scanned) || /sh\s+-i/i.test(scanned) || /\/dev\/(?:tcp|udp)\//i.test(scanned);
|
|
81
105
|
if (isReverseShell) {
|
|
82
106
|
throw new Error(`Security Exception: Potential reverse shell blocked: "${commandLine}"`);
|
|
83
107
|
}
|
|
@@ -302,24 +302,35 @@ export async function maybeEnqueueIntegrationResearch(brainDir, key, meta = {},
|
|
|
302
302
|
/**
|
|
303
303
|
* Retire obsolete key-name scrape queue items (old Automated API Integration Build: KEY topics).
|
|
304
304
|
*/
|
|
305
|
-
export function cancelBogusApiIntegrationQueueItems(overrideBrainDir) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
305
|
+
export async function cancelBogusApiIntegrationQueueItems(overrideBrainDir) {
|
|
306
|
+
const { loadQueue, updateQueueItem } = await import('./research-queue.mjs');
|
|
307
|
+
const items = loadQueue(overrideBrainDir);
|
|
308
|
+
let cancelled = 0;
|
|
309
|
+
for (const item of items) {
|
|
310
|
+
if (item.status !== 'pending' && item.status !== 'in_progress') continue;
|
|
311
|
+
if (!/^Automated API Integration Build:/i.test(String(item.topic || ''))) continue;
|
|
312
|
+
updateQueueItem(
|
|
313
|
+
item.id,
|
|
314
|
+
{
|
|
315
|
+
status: 'failed',
|
|
316
|
+
notes: `${item.notes || ''}\n\n[auto-cancelled] Obsolete key-name scrape job; re-add the secret to trigger AI provider inference.`,
|
|
317
|
+
research_phase: 'cancelled',
|
|
318
|
+
},
|
|
319
|
+
overrideBrainDir,
|
|
320
|
+
);
|
|
321
|
+
cancelled += 1;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// The same fixture pattern also landed in the research AGENDA — a different
|
|
325
|
+
// file with a different owner. Walking only the queue is why the designated
|
|
326
|
+
// remediation never removed the 29 agenda topics it was written for.
|
|
327
|
+
let cancelledAgendaTopics = 0;
|
|
328
|
+
try {
|
|
329
|
+
const { cancelBogusAgendaTopics } = await import('./fact-seeker.mjs');
|
|
330
|
+
({ cancelled: cancelledAgendaTopics } = cancelBogusAgendaTopics());
|
|
331
|
+
} catch {
|
|
332
|
+
// Best-effort: a missing or unreadable agenda must not block the queue half.
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return { cancelled, cancelledAgendaTopics };
|
|
325
336
|
}
|
|
@@ -299,6 +299,10 @@
|
|
|
299
299
|
"method": "GET",
|
|
300
300
|
"path": "/api/mesh/access/proposals"
|
|
301
301
|
},
|
|
302
|
+
{
|
|
303
|
+
"method": "POST",
|
|
304
|
+
"path": "/api/mesh/dispatch"
|
|
305
|
+
},
|
|
302
306
|
{
|
|
303
307
|
"method": "GET",
|
|
304
308
|
"path": "/api/mesh/election/history"
|
|
@@ -351,6 +355,18 @@
|
|
|
351
355
|
"method": "POST",
|
|
352
356
|
"path": "/api/mesh/preauthkey"
|
|
353
357
|
},
|
|
358
|
+
{
|
|
359
|
+
"method": "GET",
|
|
360
|
+
"path": "/api/mesh/presence"
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
"method": "GET",
|
|
364
|
+
"path": "/api/mesh/presence/local"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"method": "POST",
|
|
368
|
+
"path": "/api/mesh/presence/resolve"
|
|
369
|
+
},
|
|
354
370
|
{
|
|
355
371
|
"method": "POST",
|
|
356
372
|
"path": "/api/mesh/register-node"
|
|
@@ -423,6 +439,38 @@
|
|
|
423
439
|
"method": "GET",
|
|
424
440
|
"path": "/api/pairing"
|
|
425
441
|
},
|
|
442
|
+
{
|
|
443
|
+
"method": "GET",
|
|
444
|
+
"path": "/api/plugins"
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
"method": "DELETE",
|
|
448
|
+
"path": "/api/plugins/:id"
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"method": "GET",
|
|
452
|
+
"path": "/api/plugins/:id"
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"method": "POST",
|
|
456
|
+
"path": "/api/plugins/:id/rate"
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"method": "GET",
|
|
460
|
+
"path": "/api/plugins/:id/readme"
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
"method": "POST",
|
|
464
|
+
"path": "/api/plugins/:id/run"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"method": "GET",
|
|
468
|
+
"path": "/api/plugins/catalog"
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
"method": "POST",
|
|
472
|
+
"path": "/api/plugins/install"
|
|
473
|
+
},
|
|
426
474
|
{
|
|
427
475
|
"method": "GET",
|
|
428
476
|
"path": "/api/proposals"
|