total-recall-brain 3.40.1 → 3.40.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/README.md +152 -41
- package/bin/total-recall.mjs +16 -76
- package/docs/reference/cli-reference.md +10 -10
- package/extension/background.js +6 -2
- package/extension/config.js +8 -0
- package/extension/lib/brain-client.js +29 -4
- package/extension/options/options.html +1 -0
- package/extension/sidepanel/sidepanel.html +1 -0
- package/frontend/dist/assets/{index-CvQRdT2j.css → index-AGUes-VG.css} +1 -1
- package/frontend/dist/assets/index-Bt5iVPZj.js +5618 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/src/api/keys.ts +9 -2
- package/frontend/src/api/plugins.ts +4 -0
- package/frontend/src/components/plugins/card/CardEngine.tsx +2 -1
- package/frontend/src/components/plugins/card/card.css +1 -1
- package/frontend/src/pages/IntegrationsPage.tsx +315 -8
- package/frontend/src/pages/PluginsPage.tsx +532 -87
- package/package.json +6 -5
- package/scaffold/.agent/skills/meta-harness/references/optimized/original.md +1 -1
- package/scaffold/.agent/skills/meta-harness/references/optimized/section-09.md +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +10 -10
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +0 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/browser-session.mjs +309 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +44 -34
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/google-oauth.mjs +6 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-catalog.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-rotation-recipes.mjs +118 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/auth.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-rotate.mjs +24 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +164 -123
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +65 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +76 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +290 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +446 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +152 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +71 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +175 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +413 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/README.md +84 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +79 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/check.mjs +6 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/report.mjs +6 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +27 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/snapshot.mjs +163 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +757 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/config.schema.json +86 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/detect.mjs +89 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +145 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +210 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/state.mjs +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +2869 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +28 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +186 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +368 -0
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +129 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/README.md +73 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/cli.mjs +375 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/echo-tool.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/group.mjs +262 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/project-id.mjs +74 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +2869 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +31 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +221 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/schemas/cli_command.schema.json +107 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/skills/composable-cli/SKILL.md +63 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +313 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compile.mjs +19 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compose.mjs +144 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/contract.mjs +110 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/library.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/registry.mjs +192 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/resolve.mjs +221 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/risk.mjs +126 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/ssss-documents.mjs +29 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +273 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/README.md +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/cli.mjs +310 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +2826 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +11 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +62 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +297 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +43 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/cli.mjs +258 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4005 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +125 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +278 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +9 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +6 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +4721 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -21
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +14 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +10 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/headscale-client.mjs +5 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +4 -13
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-access-verify.mjs +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-files.mjs +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +2 -0
- package/scaffold/.agent/skills/total-recall/plugins/mesh/test-support/memory-root.mjs +14 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +124 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4097 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +12 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +165 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +127 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +279 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +539 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/LICENSE +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/README.md +67 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +932 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3256 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +25 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +297 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +4 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/skills/security/SKILL.md +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/android.mjs +21 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/devices.mjs +94 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/machines.mjs +164 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +41 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/src/windows.mjs +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +95 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +98 -0
- package/scaffold/.agent/skills/total-recall/references/auth.md +1 -1
- package/src/cli/brief.mjs +33 -9
- package/src/cli/command.mjs +2 -7
- package/src/cli/config.mjs +1 -0
- package/src/cli/connect.mjs +5 -0
- package/src/cli/context.mjs +2 -1
- package/src/cli/daemon.mjs +36 -1
- package/src/cli/doctor.mjs +6 -0
- package/src/cli/harness.mjs +3 -0
- package/src/cli/help.mjs +26 -0
- package/src/cli/init.mjs +4 -4
- package/src/cli/memory-entry.mjs +1 -0
- package/src/cli/memory-init.mjs +2 -0
- package/src/cli/plugin/index.mjs +11 -0
- package/src/cli/plugin/install.mjs +2 -1
- package/src/cli/rebuild.mjs +3 -1
- package/src/cli/startup.mjs +2 -1
- package/src/cli/update.mjs +19 -0
- package/src/core/automatic-brain-updates.mjs +67 -0
- package/src/core/brain-updates.mjs +21 -2
- package/src/core/cli-commands.mjs +71 -0
- package/src/core/cli-maintenance.mjs +45 -0
- package/src/core/cli-quick-reference.mjs +20 -0
- package/src/core/cli-semantic-index.mjs +722 -0
- package/src/core/context-compiler.mjs +27 -1
- package/src/core/crons.mjs +2 -13
- package/src/core/daemon-control.mjs +6 -4
- package/src/core/daemon-loop.mjs +12 -8
- package/src/core/default-plugins.mjs +5 -0
- package/src/core/global-brain-updates.mjs +39 -0
- package/src/core/mesh-brain-updates.mjs +30 -0
- package/src/core/meta-harness.mjs +108 -0
- package/src/core/plugin-dependencies.mjs +2 -1
- package/src/core/plugin-loader.mjs +2 -1
- package/src/core/plugin-runner.mjs +12 -4
- package/src/core/plugin-setup.mjs +20 -0
- package/src/core/plugin-store.mjs +22 -4
- package/src/core/plugin-tasks.mjs +1 -1
- package/src/core/project-brain.mjs +2 -0
- package/src/core/project-status.mjs +64 -0
- package/src/core/required-plugins.mjs +46 -0
- package/src/core/scaffold-plugins.mjs +17 -3
- package/src/core/source-brain-updates.mjs +47 -0
- package/src/core/startup-health.mjs +5 -3
- package/src/core/surface.mjs +44 -10
- package/src/server/routes/extension.mjs +53 -8
- package/templates/onboarding-interview.md +1 -1
- package/frontend/dist/assets/index-SOVUlZqm.js +0 -5596
|
@@ -1,32 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tr-plugin-mesh",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"private": true,
|
|
4
5
|
"type": "module",
|
|
5
6
|
"description": "Headscale and Tailscale SSH control-server mesh administration and node discovery",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"test": "node --test test/*.test.mjs"
|
|
8
|
-
"test:runtime": "vitest run"
|
|
9
|
-
},
|
|
10
|
-
"devDependencies": {
|
|
11
|
-
"total-recall-brain": "^3.39.1",
|
|
12
|
-
"vitest": "^4.1.11",
|
|
13
|
-
"supertest": "^7.2.2",
|
|
14
|
-
"jsdom": "^29.1.1"
|
|
8
|
+
"test": "node --test test/*.test.mjs"
|
|
15
9
|
},
|
|
16
10
|
"license": "MIT",
|
|
17
11
|
"dependencies": {
|
|
18
12
|
"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"
|
|
31
13
|
}
|
|
32
14
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://github.com/gregiteen/total-recall/blob/main/metadata.plugin.schema.json",
|
|
3
3
|
"id": "mesh",
|
|
4
4
|
"name": "Headscale",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.2",
|
|
6
6
|
"description": "Control-server mesh administration, Tailscale SSH, and peer node discovery.",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"branding": {
|
|
@@ -81,6 +81,18 @@
|
|
|
81
81
|
"command": "mesh",
|
|
82
82
|
"handler": "./cli.mjs",
|
|
83
83
|
"subcommands": [
|
|
84
|
+
{
|
|
85
|
+
"name": "file",
|
|
86
|
+
"description": "Transfer a file through authenticated mesh access and verify its destination hash",
|
|
87
|
+
"effect": "write",
|
|
88
|
+
"output": "json",
|
|
89
|
+
"args": [
|
|
90
|
+
{"name":"operation","label":"Operation","type":"string","positional":true,"required":true},
|
|
91
|
+
{"name":"node","label":"Node","type":"string","positional":true,"required":true},
|
|
92
|
+
{"name":"source","label":"Source","type":"string","positional":true,"required":true},
|
|
93
|
+
{"name":"destination","label":"Destination","type":"string","positional":true,"required":true}
|
|
94
|
+
]
|
|
95
|
+
},
|
|
84
96
|
{
|
|
85
97
|
"name": "status",
|
|
86
98
|
"description": "Inspect live mesh state",
|
|
@@ -113,6 +125,7 @@
|
|
|
113
125
|
},
|
|
114
126
|
{
|
|
115
127
|
"name": "access",
|
|
128
|
+
"timeout_ms": 180000,
|
|
116
129
|
"description": "Mesh access operation",
|
|
117
130
|
"effect": "write"
|
|
118
131
|
},
|
|
@@ -19,6 +19,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
19
19
|
import { resolveBrainDir, parseLayerFlag } from './agent-dir.mjs';
|
|
20
20
|
import {
|
|
21
21
|
describeHeadscaleAvailability,
|
|
22
|
+
headscaleFetch,
|
|
22
23
|
createHeadscalePreAuthKey,
|
|
23
24
|
getHeadscalePolicy,
|
|
24
25
|
setHeadscalePolicy,
|
|
@@ -54,6 +55,10 @@ function printHelp() {
|
|
|
54
55
|
Record how to reach a node
|
|
55
56
|
access import Propose access from ~/.ssh/config (--apply to save)
|
|
56
57
|
access sync Learn login accounts from every peer already reachable
|
|
58
|
+
access verify [--all-pairs] [--json]
|
|
59
|
+
Prove execution from each configured machine to every other
|
|
60
|
+
file put <node> <source> <destination> [--mode 600]
|
|
61
|
+
Atomically transfer a file and verify its SHA-256
|
|
57
62
|
access discover [node] Find a working login with this machine's keys and record it
|
|
58
63
|
ping [node] Measure round-trip latency to a node (or all nodes) (--json)
|
|
59
64
|
leader Show cluster leader election status and current leader (--json)
|
|
@@ -427,6 +432,11 @@ export default async function meshCli(argv = []) {
|
|
|
427
432
|
|
|
428
433
|
if (command === 'access') {
|
|
429
434
|
const target = args.shift();
|
|
435
|
+
if(target==='verify'){
|
|
436
|
+
const {verifyMeshAccess}=await import('../core/mesh-access-verify.mjs');
|
|
437
|
+
const report=await verifyMeshAccess({allPairs:args.includes('--all-pairs')});
|
|
438
|
+
console.log(JSON.stringify(report,null,2));if(!report.ok)process.exitCode=1;return;
|
|
439
|
+
}
|
|
430
440
|
if (!target) {
|
|
431
441
|
fail('`access` requires a node name, or `import`.');
|
|
432
442
|
return;
|
|
@@ -272,8 +272,12 @@ export function buildMeshSshPolicy({ allowRoot = false } = {}) {
|
|
|
272
272
|
ssh: [
|
|
273
273
|
{
|
|
274
274
|
action: 'accept',
|
|
275
|
+
// autogroup:member, not autogroup:self. 'self' limits a member to SSH on their own
|
|
276
|
+
// devices, so no node on the mesh could reach any other one and every cross-machine
|
|
277
|
+
// operation fell back to key auth that had never been set up — which reads as the peer
|
|
278
|
+
// being unreachable. The point of this policy is that members reach each other.
|
|
275
279
|
src: ['autogroup:member'],
|
|
276
|
-
dst: ['autogroup:
|
|
280
|
+
dst: ['autogroup:member'],
|
|
277
281
|
users: allowRoot ? ['autogroup:nonroot', 'root'] : ['autogroup:nonroot'],
|
|
278
282
|
},
|
|
279
283
|
],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { getMeshPeers, getMeshSelf
|
|
3
|
+
import { getMeshPeers, getMeshSelf } from './mesh.mjs';
|
|
4
4
|
import { brainDir } from './config.mjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -70,27 +70,18 @@ function compareNodes(a, b) {
|
|
|
70
70
|
* each node wrote a private, node-local lease document and could elect itself.
|
|
71
71
|
*/
|
|
72
72
|
export async function getLeaderInfo() {
|
|
73
|
-
|
|
74
|
-
// entity variable: it lives in the vault's mesh-node document and is absent
|
|
75
|
-
// from raw Tailscale status. getMeshPeers() reads only that status, so its
|
|
76
|
-
// `brain_port` was always null and every caller fell back to port 3000 --
|
|
77
|
-
// while the leader was reachable the whole time on its real port.
|
|
78
|
-
const nodes = listEnrichedMeshNodes().filter((node) => node.online);
|
|
73
|
+
const nodes = getMeshPeers({ includeSelf: true }).filter((node) => node.online);
|
|
79
74
|
if (nodes.length === 0) return null;
|
|
80
75
|
|
|
81
76
|
const pin = leaderPin();
|
|
82
77
|
if (pin) {
|
|
83
78
|
const pinned = nodes.find((node) => node.ip === pin);
|
|
84
79
|
if (!pinned) return null;
|
|
85
|
-
return { hostname: pinned.hostname, ip: pinned.ip,
|
|
80
|
+
return { hostname: pinned.hostname, ip: pinned.ip, strategy: 'pinned-mesh-ip' };
|
|
86
81
|
}
|
|
87
82
|
|
|
88
83
|
const leader = [...nodes].sort(compareNodes)[0];
|
|
89
|
-
|
|
90
|
-
// serve on the default port. It was dropped here, so callers such as
|
|
91
|
-
// secrets-sync fell back to 3000 and reported "fetch failed" against a leader
|
|
92
|
-
// that was up and reachable the whole time on a different port.
|
|
93
|
-
return { hostname: leader.hostname, ip: leader.ip, brain_port: leader.brain_port ?? null, strategy: 'lowest-mesh-ip' };
|
|
84
|
+
return { hostname: leader.hostname, ip: leader.ip, strategy: 'lowest-mesh-ip' };
|
|
94
85
|
}
|
|
95
86
|
|
|
96
87
|
/**
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Prove execution from every configured online machine; metadata is not access proof. */
|
|
2
|
+
const quote=value=>"'"+String(value).replaceAll("'","'\\''")+"'";
|
|
3
|
+
export async function verifyMeshAccess({allPairs=false,listNodes,exec}={}){
|
|
4
|
+
if(!listNodes || !exec){const mesh=await import('./mesh.mjs');listNodes ||= ()=>mesh.listEnrichedMeshNodes();exec ||= mesh.execMeshCommand;}
|
|
5
|
+
const nodes=listNodes();const machines=nodes.filter(node=>node.ip && node.access?.ssh_user);
|
|
6
|
+
const self=nodes.find(node=>node.self);
|
|
7
|
+
if(!self)throw new Error('Local mesh identity is unavailable');
|
|
8
|
+
const sources=allPairs?machines:[self];const results=[];
|
|
9
|
+
for(const source of sources)for(const destination of machines){
|
|
10
|
+
if(source.ip===destination.ip)continue;
|
|
11
|
+
const row={source:source.ip,destination:destination.ip,status:'unverified'};results.push(row);
|
|
12
|
+
if(source.online===false || destination.online===false){row.status='offline';continue;}
|
|
13
|
+
try{
|
|
14
|
+
const command=source.self?'id -un':`cd "$HOME" && total-recall mesh exec ${quote(destination.ip)} ${quote('id -un')}`;
|
|
15
|
+
const result=await exec(source.self?destination.ip:source.ip,command,{timeoutMs:20000});
|
|
16
|
+
const actual=result.stdout?.trim();
|
|
17
|
+
row.status=result.success && actual===destination.access.ssh_user?'verified':'failed';
|
|
18
|
+
if(row.status==='failed')row.error=result.stderr || `Expected recorded login ${destination.access.ssh_user}, received ${actual || 'no identity'}`;
|
|
19
|
+
}catch(error){row.status='failed';row.error=error.message;}
|
|
20
|
+
}
|
|
21
|
+
return {ok:results.length>0 && results.every(row=>row.status==='verified'),observedAt:new Date().toISOString(),results,unconfigured:nodes.filter(node=>node.ip && !node.access?.ssh_user).map(node=>({node:node.ip,online:node.online,reason:'No SSH account is configured; execution not verified'}))};
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Bounded atomic file transfer over the authenticated mesh execution owner. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
export async function putMeshFile({node,source,destination,mode=0o600,exec}={}){
|
|
5
|
+
if(!node || !source || typeof destination!=='string' || !destination || destination.includes('\0'))throw new Error('Node, source and destination are required');
|
|
6
|
+
if(!Number.isInteger(mode) || mode<0 || mode>0o777)throw new Error('Invalid file mode');
|
|
7
|
+
const bytes=fs.readFileSync(source);if(bytes.length>256*1024)throw new Error('Mesh file exceeds 256 KiB transfer budget');
|
|
8
|
+
const sha256=crypto.createHash('sha256').update(bytes).digest('hex');
|
|
9
|
+
exec ||= (await import('./mesh.mjs')).execMeshCommand;
|
|
10
|
+
const payload=JSON.stringify({destination,mode,data:bytes.toString('base64'),sha256});
|
|
11
|
+
const program=`const fs=require('node:fs'),path=require('node:path'),os=require('node:os'),crypto=require('node:crypto');let input='';for await(const chunk of process.stdin)input+=chunk;const p=JSON.parse(input);const f=p.destination.startsWith('~/')?path.join(os.homedir(),p.destination.slice(2)):path.resolve(p.destination);const b=Buffer.from(p.data,'base64');if(crypto.createHash('sha256').update(b).digest('hex')!==p.sha256)throw Error('Transfer hash mismatch');fs.mkdirSync(path.dirname(f),{recursive:true,mode:448});const t=f+'.tr-transfer-'+process.pid;fs.writeFileSync(t,b,{mode:p.mode,flag:'wx'});fs.renameSync(t,f);const actual=crypto.createHash('sha256').update(fs.readFileSync(f)).digest('hex');if(actual!==p.sha256)throw Error('Destination verification failed');console.log(JSON.stringify({path:f,bytes:b.length,sha256:actual}));`;
|
|
12
|
+
const command=`node --input-type=commonjs -e '${('(async()=>{'+program+'})().catch(()=>{console.error("Mesh transfer failed");process.exitCode=1;});').replaceAll("'","'\\''")}'`;
|
|
13
|
+
const result=await exec(node,command,{timeoutMs:20000,input:payload});
|
|
14
|
+
if(!result.success)throw new Error(result.stderr || 'Mesh file transfer failed');
|
|
15
|
+
let observed;try{observed=JSON.parse(result.stdout);}catch{throw new Error('Invalid mesh file verification response');}
|
|
16
|
+
if(observed.sha256!==sha256 || observed.bytes!==bytes.length)throw new Error('Remote file verification mismatch');
|
|
17
|
+
return {ok:true,node,...observed};
|
|
18
|
+
}
|
|
@@ -701,6 +701,8 @@ export async function execMeshCommand(target, command, options = {}) {
|
|
|
701
701
|
|
|
702
702
|
return new Promise((resolve, reject) => {
|
|
703
703
|
const proc = spawn('ssh', sshArgs);
|
|
704
|
+
proc.stdin.on('error', error => { if(error.code!=='EPIPE') logger.error('mesh', 'Mesh stdin transport failed'); });
|
|
705
|
+
proc.stdin.end(options.input || '');
|
|
704
706
|
let stdout = '';
|
|
705
707
|
let stderr = '';
|
|
706
708
|
let timedOut = false;
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Greg Iteen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# OpenWiki
|
|
2
|
+
|
|
3
|
+
> Multi-repo knowledge graph and documentation wiki engine for Total Recall
|
|
4
|
+
|
|
5
|
+
## Sharing
|
|
6
|
+
|
|
7
|
+
Offer it to your other Total Recall nodes over the mesh:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx total-recall plugin share openwiki
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Peers install it with `npx total-recall plugin install peer:<this-node>/openwiki`.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Identifier**: `openwiki`
|
|
18
|
+
- **Version**: 0.1.0
|
|
19
|
+
- **SSSS Category**: `openwiki-hubs`
|
|
20
|
+
- **CLI Command**: `npx total-recall openwiki`
|
|
21
|
+
- **UI**: `ui/panel.js` custom element with DESIGN.md tokens
|
|
22
|
+
|
|
23
|
+
## Use it two ways
|
|
24
|
+
|
|
25
|
+
This plugin is a reusable brick, so a new project does not start from zero.
|
|
26
|
+
|
|
27
|
+
**Add it to a project (for apps you build and ship).** From the project directory:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
total-recall plugin vendor https://github.com/gregiteen/tr-plugin-openwiki.git
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This copies the plugin's source, skills, tests and docs into `bricks/openwiki` and records it in `tr-bricks.json`. Your project then owns that code: edit it, test it in your own gates, and ship it with no runtime dependency on this plugin or on Total Recall. Combine as many bricks as the project needs. List them with `total-recall plugin bricks`.
|
|
34
|
+
|
|
35
|
+
**Use it directly with Total Recall (for your own machines and workflow).** Install it with `total-recall plugin install https://github.com/gregiteen/tr-plugin-openwiki.git` and run `total-recall openwiki`. Manage its credentials with `total-recall auth secret` and its skills with `total-recall skill`.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OpenWiki CLI Handler
|
|
4
|
+
* Executed via: npx total-recall openwiki <subcommand>
|
|
5
|
+
*/
|
|
6
|
+
// Scaffold: report honestly what exists. Add each real subcommand to
|
|
7
|
+
// IMPLEMENTED and to plugin.json "cli.subcommands" as it is built.
|
|
8
|
+
|
|
9
|
+
import { spawnSync } from "node:child_process";
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
|
|
13
|
+
const TR = process.env.TOTAL_RECALL_BIN || "total-recall";
|
|
14
|
+
|
|
15
|
+
async function probeCommand(args) {
|
|
16
|
+
const json = args.includes("--json");
|
|
17
|
+
const host = process.env.TOTAL_RECALL_URL || "http://127.0.0.1:3000";
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
try {
|
|
20
|
+
const res = await fetch(`${host}/health`, { signal: AbortSignal.timeout(3000) });
|
|
21
|
+
const latency_ms = Date.now() - start;
|
|
22
|
+
const ok = res.ok;
|
|
23
|
+
const status = res.status;
|
|
24
|
+
const result = { status, ok, latency_ms, endpoint: "/health" };
|
|
25
|
+
if (json) console.log(JSON.stringify(result));
|
|
26
|
+
else console.log(`Health probe: HTTP ${status} (${latency_ms}ms) from ${host}`);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
const latency_ms = Date.now() - start;
|
|
29
|
+
const result = { status: 503, ok: false, latency_ms, error: err.message };
|
|
30
|
+
if (json) console.log(JSON.stringify(result));
|
|
31
|
+
else console.error(`Health probe failure: ${err.message} (${latency_ms}ms)`);
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function hubsCommand(args) {
|
|
37
|
+
const json = args.includes("--json");
|
|
38
|
+
const targetDir = path.resolve(process.cwd(), "openwiki");
|
|
39
|
+
const hubs = [];
|
|
40
|
+
|
|
41
|
+
if (fs.existsSync(targetDir)) {
|
|
42
|
+
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
43
|
+
for (const ent of entries) {
|
|
44
|
+
if (ent.isFile() && ent.name.endsWith(".md")) {
|
|
45
|
+
const fullPath = path.join(targetDir, ent.name);
|
|
46
|
+
const content = fs.readFileSync(fullPath, "utf8");
|
|
47
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
48
|
+
const title = titleMatch ? titleMatch[1].trim() : ent.name.replace(/\.md$/, "");
|
|
49
|
+
const lines = content.split("\n").length;
|
|
50
|
+
hubs.push({
|
|
51
|
+
title,
|
|
52
|
+
path: path.relative(process.cwd(), fullPath),
|
|
53
|
+
lines,
|
|
54
|
+
size_bytes: content.length
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (json) {
|
|
61
|
+
console.log(JSON.stringify({ hubs, count: hubs.length, directory: targetDir }));
|
|
62
|
+
} else {
|
|
63
|
+
if (hubs.length === 0) {
|
|
64
|
+
console.log(`No markdown hubs found in ${targetDir}`);
|
|
65
|
+
} else {
|
|
66
|
+
console.log(`Discovered ${hubs.length} OpenWiki Hub(s) in ${targetDir}:`);
|
|
67
|
+
for (const h of hubs) {
|
|
68
|
+
console.log(` - ${h.title}: ${h.path} (${h.lines} lines, ${h.size_bytes} bytes)`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
import { runOpenWikiIngest } from "./src/ingest.mjs";
|
|
75
|
+
|
|
76
|
+
async function ingestCommand(args) {
|
|
77
|
+
const result = await runOpenWikiIngest(args);
|
|
78
|
+
if (!result.ok) {
|
|
79
|
+
process.exitCode = 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const IMPLEMENTED = {};
|
|
84
|
+
IMPLEMENTED.probe = probeCommand;
|
|
85
|
+
IMPLEMENTED.hubs = hubsCommand;
|
|
86
|
+
IMPLEMENTED.ingest = ingestCommand;
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
export async function run(argv = []) {
|
|
90
|
+
const args = Array.isArray(argv) ? argv.slice(3) : [];
|
|
91
|
+
const sub = args[0] || "status";
|
|
92
|
+
const json = args.includes("--json");
|
|
93
|
+
|
|
94
|
+
if (sub === "status") {
|
|
95
|
+
const status = { plugin: "openwiki", implemented: Object.keys(IMPLEMENTED) };
|
|
96
|
+
if (json) console.log(JSON.stringify(status));
|
|
97
|
+
else console.log(status.implemented.length
|
|
98
|
+
? `OpenWiki: implemented subcommands: ${status.implemented.join(", ")}`
|
|
99
|
+
: `OpenWiki: scaffold only — no subcommands implemented yet.`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (sub === "help" || sub === "--help" || sub === "-h") {
|
|
104
|
+
console.log(`
|
|
105
|
+
OpenWiki — Multi-repo knowledge graph and documentation wiki engine
|
|
106
|
+
|
|
107
|
+
Usage:
|
|
108
|
+
total-recall openwiki <subcommand> [options]
|
|
109
|
+
|
|
110
|
+
Subcommands:
|
|
111
|
+
status Show OpenWiki plugin status
|
|
112
|
+
probe [--json] Check subsystem health
|
|
113
|
+
hubs [--json] List indexed documentation hubs
|
|
114
|
+
ingest <path> [options] Ingest Markdown files into knowledge graph
|
|
115
|
+
`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (IMPLEMENTED[sub]) return IMPLEMENTED[sub](args.slice(1));
|
|
120
|
+
|
|
121
|
+
console.error(`OpenWiki: "${sub}" is not implemented.`);
|
|
122
|
+
process.exitCode = 2;
|
|
123
|
+
}
|
|
124
|
+
export default run;
|