total-recall-brain 3.40.0 → 3.40.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/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 +18 -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 +272 -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/backup.mjs +71 -3
- 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
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline Composer — Dynamic Command Orchestration Engine
|
|
3
|
+
* Bespoke Animated Web Component Card Face with Live Flow Pipeline Visualizer.
|
|
4
|
+
*
|
|
5
|
+
* Zero emojis, authentic commercial brand identity, touch-first ergonomics (>=44px).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class PipelineComposerCard extends HTMLElement {
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
this.status = 'Ready';
|
|
12
|
+
this.executing = false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
connectedCallback() {
|
|
16
|
+
this.attachShadow({ mode: 'open' });
|
|
17
|
+
this.render();
|
|
18
|
+
this.initPipelineAnimation();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async execute(action, args = []) {
|
|
22
|
+
const out = this.shadowRoot.querySelector('#card-out');
|
|
23
|
+
const badge = this.shadowRoot.querySelector('#status-badge');
|
|
24
|
+
if (out) out.textContent = `[PIPELINE ROUTER] Invoking ${action.toUpperCase()}...`;
|
|
25
|
+
if (badge) badge.textContent = 'ROUTING';
|
|
26
|
+
this.executing = true;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const res = await this.host?.run(action, args);
|
|
30
|
+
let text = '';
|
|
31
|
+
if (typeof res === 'object') {
|
|
32
|
+
text = res.output || JSON.stringify(res, null, 2);
|
|
33
|
+
} else {
|
|
34
|
+
text = String(res || 'Execution completed without output.');
|
|
35
|
+
}
|
|
36
|
+
if (out) out.textContent = text.trim();
|
|
37
|
+
if (badge) badge.textContent = 'ONLINE';
|
|
38
|
+
} catch (err) {
|
|
39
|
+
if (out) out.textContent = `[PIPELINE FAULT] ${err.message}`;
|
|
40
|
+
if (badge) badge.textContent = 'ERROR';
|
|
41
|
+
} finally {
|
|
42
|
+
this.executing = false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
initPipelineAnimation() {
|
|
47
|
+
const canvas = this.shadowRoot.querySelector('#flow-canvas');
|
|
48
|
+
if (!canvas) return;
|
|
49
|
+
const ctx = canvas.getContext('2d');
|
|
50
|
+
if (!ctx) return;
|
|
51
|
+
|
|
52
|
+
let progress = 0;
|
|
53
|
+
const nodes = [
|
|
54
|
+
{ x: 30, y: 32, label: 'INPUT' },
|
|
55
|
+
{ x: 110, y: 32, label: 'TRANSFORM' },
|
|
56
|
+
{ x: 210, y: 32, label: 'FILTER' },
|
|
57
|
+
{ x: 300, y: 32, label: 'SINK' }
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const draw = () => {
|
|
61
|
+
if (!this.isConnected) return;
|
|
62
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
63
|
+
|
|
64
|
+
// Draw connection lines
|
|
65
|
+
ctx.strokeStyle = 'rgba(99, 102, 241, 0.25)';
|
|
66
|
+
ctx.lineWidth = 2;
|
|
67
|
+
ctx.beginPath();
|
|
68
|
+
ctx.moveTo(nodes[0].x, nodes[0].y);
|
|
69
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
70
|
+
ctx.lineTo(nodes[i].x, nodes[i].y);
|
|
71
|
+
}
|
|
72
|
+
ctx.stroke();
|
|
73
|
+
|
|
74
|
+
// Draw packet pulse
|
|
75
|
+
const totalLen = nodes[nodes.length - 1].x - nodes[0].x;
|
|
76
|
+
const packetX = nodes[0].x + (progress % 1) * totalLen;
|
|
77
|
+
ctx.fillStyle = this.executing ? '#38bdf8' : '#818cf8';
|
|
78
|
+
ctx.shadowColor = ctx.fillStyle;
|
|
79
|
+
ctx.shadowBlur = 8;
|
|
80
|
+
ctx.beginPath();
|
|
81
|
+
ctx.arc(packetX, 32, 4, 0, Math.PI * 2);
|
|
82
|
+
ctx.fill();
|
|
83
|
+
ctx.shadowBlur = 0;
|
|
84
|
+
|
|
85
|
+
// Draw stage nodes
|
|
86
|
+
for (const node of nodes) {
|
|
87
|
+
ctx.fillStyle = '#0f172a';
|
|
88
|
+
ctx.strokeStyle = '#6366f1';
|
|
89
|
+
ctx.lineWidth = 1.5;
|
|
90
|
+
ctx.beginPath();
|
|
91
|
+
ctx.arc(node.x, node.y, 8, 0, Math.PI * 2);
|
|
92
|
+
ctx.fill();
|
|
93
|
+
ctx.stroke();
|
|
94
|
+
|
|
95
|
+
ctx.fillStyle = '#c7d2fe';
|
|
96
|
+
ctx.font = '8px monospace';
|
|
97
|
+
ctx.textAlign = 'center';
|
|
98
|
+
ctx.fillText(node.label, node.x, node.y + 18);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
progress += this.executing ? 0.03 : 0.008;
|
|
102
|
+
requestAnimationFrame(draw);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
requestAnimationFrame(draw);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
render() {
|
|
109
|
+
this.shadowRoot.innerHTML = `
|
|
110
|
+
<style>
|
|
111
|
+
:host {
|
|
112
|
+
display: block;
|
|
113
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
114
|
+
background: #0b0f19;
|
|
115
|
+
color: #e0e7ff;
|
|
116
|
+
border-radius: 8px;
|
|
117
|
+
border: 1px solid rgba(99, 102, 241, 0.35);
|
|
118
|
+
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
|
|
119
|
+
padding: 18px;
|
|
120
|
+
box-sizing: border-box;
|
|
121
|
+
overflow: hidden;
|
|
122
|
+
}
|
|
123
|
+
.header {
|
|
124
|
+
display: flex;
|
|
125
|
+
justify-content: space-between;
|
|
126
|
+
align-items: flex-start;
|
|
127
|
+
margin-bottom: 14px;
|
|
128
|
+
}
|
|
129
|
+
.brand-meta {
|
|
130
|
+
display: flex;
|
|
131
|
+
flex-direction: column;
|
|
132
|
+
gap: 2px;
|
|
133
|
+
}
|
|
134
|
+
.title {
|
|
135
|
+
font-size: 16px;
|
|
136
|
+
font-weight: 700;
|
|
137
|
+
letter-spacing: 0.04em;
|
|
138
|
+
text-transform: uppercase;
|
|
139
|
+
color: #c7d2fe;
|
|
140
|
+
margin: 0;
|
|
141
|
+
}
|
|
142
|
+
.subtitle {
|
|
143
|
+
font-size: 11px;
|
|
144
|
+
color: #94a3b8;
|
|
145
|
+
letter-spacing: 0.02em;
|
|
146
|
+
margin: 0;
|
|
147
|
+
}
|
|
148
|
+
.badge {
|
|
149
|
+
display: inline-flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
justify-content: center;
|
|
152
|
+
font-size: 10px;
|
|
153
|
+
font-weight: 700;
|
|
154
|
+
letter-spacing: 0.08em;
|
|
155
|
+
text-transform: uppercase;
|
|
156
|
+
color: #818cf8;
|
|
157
|
+
background: rgba(99, 102, 241, 0.12);
|
|
158
|
+
border: 1px solid rgba(99, 102, 241, 0.3);
|
|
159
|
+
border-radius: 4px;
|
|
160
|
+
padding: 4px 8px;
|
|
161
|
+
}
|
|
162
|
+
.visual-display {
|
|
163
|
+
position: relative;
|
|
164
|
+
background: #030712;
|
|
165
|
+
border: 1px solid rgba(99, 102, 241, 0.2);
|
|
166
|
+
border-radius: 6px;
|
|
167
|
+
margin-bottom: 14px;
|
|
168
|
+
overflow: hidden;
|
|
169
|
+
}
|
|
170
|
+
canvas {
|
|
171
|
+
display: block;
|
|
172
|
+
width: 100%;
|
|
173
|
+
height: 64px;
|
|
174
|
+
}
|
|
175
|
+
.telemetry-row {
|
|
176
|
+
display: flex;
|
|
177
|
+
justify-content: space-between;
|
|
178
|
+
padding: 6px 10px;
|
|
179
|
+
background: rgba(255, 255, 255, 0.03);
|
|
180
|
+
border-top: 1px solid rgba(99, 102, 241, 0.15);
|
|
181
|
+
font-family: monospace;
|
|
182
|
+
font-size: 11px;
|
|
183
|
+
color: #cbd5e1;
|
|
184
|
+
}
|
|
185
|
+
.actions-grid {
|
|
186
|
+
display: grid;
|
|
187
|
+
grid-template-columns: repeat(3, 1fr);
|
|
188
|
+
gap: 8px;
|
|
189
|
+
margin-bottom: 12px;
|
|
190
|
+
}
|
|
191
|
+
.touch-btn {
|
|
192
|
+
min-height: 44px;
|
|
193
|
+
min-width: 44px;
|
|
194
|
+
display: inline-flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
justify-content: center;
|
|
197
|
+
background: rgba(99, 102, 241, 0.08);
|
|
198
|
+
border: 1px solid rgba(99, 102, 241, 0.25);
|
|
199
|
+
color: #c7d2fe;
|
|
200
|
+
font-size: 11px;
|
|
201
|
+
font-weight: 600;
|
|
202
|
+
letter-spacing: 0.04em;
|
|
203
|
+
text-transform: uppercase;
|
|
204
|
+
border-radius: 6px;
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
transition: background 0.15s ease, border-color 0.15s ease;
|
|
207
|
+
padding: 6px 8px;
|
|
208
|
+
text-align: center;
|
|
209
|
+
}
|
|
210
|
+
.touch-btn:hover {
|
|
211
|
+
background: rgba(99, 102, 241, 0.18);
|
|
212
|
+
border-color: rgba(99, 102, 241, 0.5);
|
|
213
|
+
}
|
|
214
|
+
.touch-btn:active {
|
|
215
|
+
transform: translateY(1px);
|
|
216
|
+
}
|
|
217
|
+
.output-box {
|
|
218
|
+
background: #020617;
|
|
219
|
+
border: 1px solid rgba(99, 102, 241, 0.2);
|
|
220
|
+
border-radius: 6px;
|
|
221
|
+
padding: 8px 10px;
|
|
222
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
223
|
+
font-size: 11px;
|
|
224
|
+
line-height: 1.45;
|
|
225
|
+
color: #94a3b8;
|
|
226
|
+
min-height: 48px;
|
|
227
|
+
max-height: 85px;
|
|
228
|
+
overflow-y: auto;
|
|
229
|
+
white-space: pre-wrap;
|
|
230
|
+
word-break: break-word;
|
|
231
|
+
}
|
|
232
|
+
</style>
|
|
233
|
+
|
|
234
|
+
<div class="header">
|
|
235
|
+
<div class="brand-meta">
|
|
236
|
+
<h2 class="title">Pipeline Composer</h2>
|
|
237
|
+
<p class="subtitle">Dynamic Command Orchestration Engine</p>
|
|
238
|
+
</div>
|
|
239
|
+
<div id="status-badge" class="badge">READY</div>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div class="visual-display">
|
|
243
|
+
<canvas id="flow-canvas" width="340" height="64"></canvas>
|
|
244
|
+
<div class="telemetry-row">
|
|
245
|
+
<span>Execution: Direct</span>
|
|
246
|
+
<span>Risk Gate: Fail-Closed</span>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<div class="actions-grid">
|
|
251
|
+
<button id="btn-list" class="touch-btn" type="button">Catalog</button>
|
|
252
|
+
<button id="btn-inspect" class="touch-btn" type="button">Inspect</button>
|
|
253
|
+
<button id="btn-history" class="touch-btn" type="button">History</button>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
<div id="card-out" class="output-box">Dynamic command registry operational. Ready for pipeline invocation.</div>
|
|
257
|
+
`;
|
|
258
|
+
|
|
259
|
+
this.shadowRoot.querySelector('#btn-list')?.addEventListener('click', () => {
|
|
260
|
+
this.execute('list');
|
|
261
|
+
});
|
|
262
|
+
this.shadowRoot.querySelector('#btn-inspect')?.addEventListener('click', () => {
|
|
263
|
+
this.execute('read', ['startup-check']);
|
|
264
|
+
});
|
|
265
|
+
this.shadowRoot.querySelector('#btn-history')?.addEventListener('click', () => {
|
|
266
|
+
this.execute('history');
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!customElements.get('tr-composable-cli-card')) {
|
|
272
|
+
customElements.define('tr-composable-cli-card', PipelineComposerCard);
|
|
273
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Plugin authors
|
|
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,20 @@
|
|
|
1
|
+
# Embeddings
|
|
2
|
+
|
|
3
|
+
> Local embedding model management: provider/model status, benchmark, index build and coverage
|
|
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 embeddings
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Peers install it with `npx total-recall plugin install peer:<this-node>/embeddings`.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Identifier**: `embeddings`
|
|
18
|
+
- **Version**: 0.1.0
|
|
19
|
+
- **Use cases**: operations, ai
|
|
20
|
+
- **CLI Command**: `npx total-recall embeddings`
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Embeddings CLI Handler
|
|
4
|
+
* Executed via: npx total-recall embeddings <subcommand>
|
|
5
|
+
*
|
|
6
|
+
* Owns local embedding provider discovery and index coverage for a brain:
|
|
7
|
+
* which provider/model actually serves vectors, whether that model is local
|
|
8
|
+
* (no text leaves the machine, no provider spend), and how much of the vault
|
|
9
|
+
* is embedded. Building the index itself stays with `total-recall compile`,
|
|
10
|
+
* which owns the embeddings.db lifecycle.
|
|
11
|
+
*/
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { spawnSync } from 'node:child_process';
|
|
15
|
+
|
|
16
|
+
const PLUGIN_ID = 'embeddings';
|
|
17
|
+
const PLUGIN_DIR = path.dirname(new URL(import.meta.url).pathname);
|
|
18
|
+
const PLUGIN_VERSION = JSON.parse(fs.readFileSync(path.join(PLUGIN_DIR, 'plugin.json'), 'utf8')).version;
|
|
19
|
+
const DEFAULT_URL = 'http://127.0.0.1:11434';
|
|
20
|
+
const HOSTED_PROVIDERS = ['OPENROUTER_API_KEY', 'GOOGLE_API_KEY', 'OPENAI_API_KEY'];
|
|
21
|
+
|
|
22
|
+
/** The brain this invocation targets: project brain when present, else global. */
|
|
23
|
+
function resolveVault() {
|
|
24
|
+
const candidates = [
|
|
25
|
+
path.join(process.cwd(), '.agent', 'skills', 'total-recall'),
|
|
26
|
+
path.join(process.env.HOME || '', '.agent', 'skills', 'total-recall'),
|
|
27
|
+
];
|
|
28
|
+
for (const dir of candidates) {
|
|
29
|
+
if (dir && fs.existsSync(path.join(dir, 'memory-vault'))) return dir;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ollamaUrl() {
|
|
35
|
+
const raw = process.env.TR_OLLAMA_URL || process.env.OLLAMA_HOST || DEFAULT_URL;
|
|
36
|
+
const withScheme = /^https?:\/\//i.test(raw) ? raw : `http://${raw}`;
|
|
37
|
+
try {
|
|
38
|
+
const u = new URL(withScheme);
|
|
39
|
+
if (!u.port) u.port = '11434';
|
|
40
|
+
return u.origin;
|
|
41
|
+
} catch { return DEFAULT_URL; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function probe(url, timeoutMs = 4000) {
|
|
45
|
+
try {
|
|
46
|
+
const res = await fetch(`${url}/api/tags`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
47
|
+
if (!res.ok) return { reachable: false, reason: `HTTP ${res.status}` };
|
|
48
|
+
const data = await res.json();
|
|
49
|
+
return { reachable: true, models: (data.models || []).map(m => m.name) };
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return { reachable: false, reason: err.message };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Pick the embedding-capable model with the vault's vector width, as the core does. */
|
|
56
|
+
async function selectModel(url, dims = 768) {
|
|
57
|
+
const tags = await probe(url);
|
|
58
|
+
if (!tags.reachable) return null;
|
|
59
|
+
for (const name of tags.models || []) {
|
|
60
|
+
try {
|
|
61
|
+
const res = await fetch(`${url}/api/show`, {
|
|
62
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
63
|
+
body: JSON.stringify({ model: name }), signal: AbortSignal.timeout(5000),
|
|
64
|
+
});
|
|
65
|
+
if (!res.ok) continue;
|
|
66
|
+
const info = await res.json();
|
|
67
|
+
const caps = info.capabilities || [];
|
|
68
|
+
// Older servers do not report capabilities; fall back to a name hint.
|
|
69
|
+
const capable = caps.includes('embedding') || /embed/i.test(name);
|
|
70
|
+
if (!capable) continue;
|
|
71
|
+
const width = Object.entries(info.model_info || {})
|
|
72
|
+
.filter(([k]) => k.endsWith('.embedding_length')).map(([, v]) => v)[0];
|
|
73
|
+
if (width && width !== dims) continue;
|
|
74
|
+
return { name, width: width || null };
|
|
75
|
+
} catch { /* try the next model */ }
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Coverage of the vault against its embeddings index, read without loading it all. */
|
|
81
|
+
function coverage(brainDir) {
|
|
82
|
+
const dbPath = path.join(brainDir, 'memory-derived', 'embeddings.db');
|
|
83
|
+
// No vault_nodes here: counting nodes is vaultNodeCount's job, and a
|
|
84
|
+
// placeholder key would clobber a real count when this object is spread.
|
|
85
|
+
const result = { embedded: null, model: null, db_bytes: null, percent: null };
|
|
86
|
+
if (fs.existsSync(dbPath)) result.db_bytes = fs.statSync(dbPath).size;
|
|
87
|
+
if (!fs.existsSync(dbPath)) return result;
|
|
88
|
+
const py = spawnSync('python3', ['-c', `
|
|
89
|
+
import sqlite3, json, sys
|
|
90
|
+
try:
|
|
91
|
+
c = sqlite3.connect(${JSON.stringify(dbPath)})
|
|
92
|
+
n = c.execute('select count(*) from vault_embeddings').fetchone()[0]
|
|
93
|
+
models = [r[0] for r in c.execute('select model, count(*) from vault_embeddings group by model order by count(*) desc')]
|
|
94
|
+
print(json.dumps({'embedded': n, 'models': models}))
|
|
95
|
+
except Exception as e:
|
|
96
|
+
print(json.dumps({'error': str(e)}))
|
|
97
|
+
`], { encoding: 'utf8', timeout: 20000 });
|
|
98
|
+
try {
|
|
99
|
+
const parsed = JSON.parse(py.stdout || '{}');
|
|
100
|
+
result.embedded = parsed.embedded ?? null;
|
|
101
|
+
result.model = parsed.models?.[0] ?? null;
|
|
102
|
+
} catch { /* leave nulls */ }
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function vaultNodeCount(brainDir) {
|
|
107
|
+
const vault = path.join(brainDir, 'memory-vault');
|
|
108
|
+
let count = 0;
|
|
109
|
+
const walk = (dir) => {
|
|
110
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
111
|
+
if (entry.name.startsWith('.')) continue;
|
|
112
|
+
const full = path.join(dir, entry.name);
|
|
113
|
+
if (entry.isDirectory()) walk(full);
|
|
114
|
+
else if (entry.name.endsWith('.md')) count++;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
try { walk(vault); } catch { return null; }
|
|
118
|
+
return count;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const IMPLEMENTED = {};
|
|
122
|
+
|
|
123
|
+
IMPLEMENTED.status = async (args) => {
|
|
124
|
+
const json = args.includes('--json');
|
|
125
|
+
const url = ollamaUrl();
|
|
126
|
+
const probed = await probe(url);
|
|
127
|
+
const model = probed.reachable ? await selectModel(url) : null;
|
|
128
|
+
const brain = resolveVault();
|
|
129
|
+
const report = {
|
|
130
|
+
plugin: PLUGIN_ID,
|
|
131
|
+
configured_url: url,
|
|
132
|
+
ollama: { reachable: probed.reachable, models: probed.models || [], reason: probed.reason || null },
|
|
133
|
+
selected_model: model?.name ?? null,
|
|
134
|
+
embedding_dims: model?.width ?? null,
|
|
135
|
+
local: Boolean(model),
|
|
136
|
+
// A hosted fallback is what the core uses when no local model answers.
|
|
137
|
+
hosted_fallback_keys: HOSTED_PROVIDERS.filter(k => Boolean(process.env[k])),
|
|
138
|
+
brain: brain ? {
|
|
139
|
+
dir: brain,
|
|
140
|
+
// Spread coverage FIRST: it returns its own vault_nodes placeholder which
|
|
141
|
+
// would otherwise clobber the counted value placed after it.
|
|
142
|
+
...coverage(brain),
|
|
143
|
+
vault_nodes: vaultNodeCount(brain),
|
|
144
|
+
} : null,
|
|
145
|
+
};
|
|
146
|
+
if (report.brain && report.brain.vault_nodes && report.brain.embedded != null) {
|
|
147
|
+
report.brain.percent = Math.round((report.brain.embedded / report.brain.vault_nodes) * 100);
|
|
148
|
+
}
|
|
149
|
+
if (json) { console.log(JSON.stringify(report, null, 1)); return; }
|
|
150
|
+
console.log(`Embeddings v${PLUGIN_VERSION}`);
|
|
151
|
+
console.log(` endpoint: ${url} ${probed.reachable ? '(reachable)' : `(unreachable: ${probed.reason})`}`);
|
|
152
|
+
console.log(` model: ${model ? `${model.name}${model.width ? ` (${model.width} dims)` : ''}` : 'none — would fall back to a hosted provider'}`);
|
|
153
|
+
console.log(` local: ${model ? 'yes — vectors never leave this machine' : 'NO — text would be sent to a provider'}`);
|
|
154
|
+
if (!model && report.hosted_fallback_keys.length) console.log(` fallback: ${report.hosted_fallback_keys.join(', ')}`);
|
|
155
|
+
if (report.brain) {
|
|
156
|
+
const b = report.brain;
|
|
157
|
+
console.log(` brain: ${b.dir}`);
|
|
158
|
+
console.log(` vault: ${b.vault_nodes} nodes`);
|
|
159
|
+
console.log(` embedded: ${b.embedded ?? 'no index'}${b.model ? ` (model: ${b.model})` : ''}${b.percent != null ? ` — ${b.percent}%` : ''}`);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
IMPLEMENTED.models = async (args) => {
|
|
164
|
+
const json = args.includes('--json');
|
|
165
|
+
const url = ollamaUrl();
|
|
166
|
+
const probed = await probe(url);
|
|
167
|
+
if (!probed.reachable) {
|
|
168
|
+
if (json) console.log(JSON.stringify({ url, reachable: false, reason: probed.reason, models: [] }));
|
|
169
|
+
else console.error(`Embeddings: ${url} is unreachable (${probed.reason}).`);
|
|
170
|
+
process.exitCode = 1;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const rows = [];
|
|
174
|
+
for (const name of probed.models || []) {
|
|
175
|
+
let width = null, capable = false;
|
|
176
|
+
try {
|
|
177
|
+
const res = await fetch(`${url}/api/show`, {
|
|
178
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
179
|
+
body: JSON.stringify({ model: name }), signal: AbortSignal.timeout(5000),
|
|
180
|
+
});
|
|
181
|
+
if (res.ok) {
|
|
182
|
+
const info = await res.json();
|
|
183
|
+
capable = (info.capabilities || []).includes('embedding') || /embed/i.test(name);
|
|
184
|
+
width = Object.entries(info.model_info || {}).filter(([k]) => k.endsWith('.embedding_length')).map(([, v]) => v)[0] ?? null;
|
|
185
|
+
}
|
|
186
|
+
} catch { /* report what is known */ }
|
|
187
|
+
rows.push({ name, embedding_capable: capable, dims: width });
|
|
188
|
+
}
|
|
189
|
+
if (json) { console.log(JSON.stringify({ url, reachable: true, models: rows }, null, 1)); return; }
|
|
190
|
+
console.log(`Embeddings v${PLUGIN_VERSION} — ${url}`);
|
|
191
|
+
for (const r of rows) console.log(` ${r.embedding_capable ? '●' : '○'} ${r.name}${r.dims ? ` (${r.dims} dims)` : ''}`);
|
|
192
|
+
if (!rows.some(r => r.embedding_capable)) console.log(' no embedding-capable model — run: embeddings pull');
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
IMPLEMENTED.pull = async (args) => {
|
|
196
|
+
const model = args.find(a => !a.startsWith('-')) || 'nomic-embed-text';
|
|
197
|
+
const json = args.includes('--json');
|
|
198
|
+
const url = ollamaUrl();
|
|
199
|
+
if (!json) console.log(`Embeddings: pulling ${model} from ${url} ...`);
|
|
200
|
+
const res = spawnSync('ollama', ['pull', model], {
|
|
201
|
+
encoding: 'utf8', timeout: 600000,
|
|
202
|
+
env: { ...process.env, OLLAMA_HOST: url.replace(/^https?:\/\//, '') },
|
|
203
|
+
});
|
|
204
|
+
const ok = res.status === 0;
|
|
205
|
+
if (json) { console.log(JSON.stringify({ model, ok, status: res.status, stderr: ok ? null : (res.stderr || '').slice(-400) })); if (!ok) process.exitCode = 1; return; }
|
|
206
|
+
if (!ok) { console.error(` failed: ${(res.stderr || res.stdout || '').trim().slice(-300)}`); process.exitCode = 1; return; }
|
|
207
|
+
console.log(` ✅ ${model} ready`);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
IMPLEMENTED.benchmark = async (args) => {
|
|
211
|
+
const json = args.includes('--json');
|
|
212
|
+
const countIdx = args.indexOf('--count');
|
|
213
|
+
const samples = Number(countIdx > -1 ? args[countIdx + 1] : 0) || 10;
|
|
214
|
+
const url = ollamaUrl();
|
|
215
|
+
const model = await selectModel(url);
|
|
216
|
+
if (!model) {
|
|
217
|
+
if (json) console.log(JSON.stringify({ ok: false, reason: 'no embedding-capable model available', url }));
|
|
218
|
+
else console.error(`Embeddings: no embedding-capable model at ${url}. Run: embeddings pull`);
|
|
219
|
+
process.exitCode = 1;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const text = 'Contact High Life Smoke Shop, 2485 Corporate Pkwy Burlington NC 27215, phone 336-828-7106, status active.';
|
|
223
|
+
const timings = [];
|
|
224
|
+
for (let i = 0; i < samples; i++) {
|
|
225
|
+
const t0 = Date.now();
|
|
226
|
+
try {
|
|
227
|
+
const res = await fetch(`${url}/api/embeddings`, {
|
|
228
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
229
|
+
body: JSON.stringify({ model: model.name, prompt: text }), signal: AbortSignal.timeout(60000),
|
|
230
|
+
});
|
|
231
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
232
|
+
await res.json();
|
|
233
|
+
timings.push(Date.now() - t0);
|
|
234
|
+
} catch (err) {
|
|
235
|
+
if (json) console.log(JSON.stringify({ ok: false, reason: err.message, url, model: model.name }));
|
|
236
|
+
else console.error(`Embeddings: benchmark failed: ${err.message}`);
|
|
237
|
+
process.exitCode = 1;
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const total = timings.reduce((a, b) => a + b, 0);
|
|
242
|
+
const avg = total / timings.length;
|
|
243
|
+
const brain = resolveVault();
|
|
244
|
+
const nodes = brain ? vaultNodeCount(brain) : null;
|
|
245
|
+
const report = {
|
|
246
|
+
ok: true, url, model: model.name, samples,
|
|
247
|
+
ms_per_embedding: Math.round(avg),
|
|
248
|
+
min_ms: Math.min(...timings), max_ms: Math.max(...timings),
|
|
249
|
+
vault_nodes: nodes,
|
|
250
|
+
projected_minutes: nodes ? Math.round((avg * nodes) / 60000) : null,
|
|
251
|
+
};
|
|
252
|
+
if (json) { console.log(JSON.stringify(report, null, 1)); return; }
|
|
253
|
+
console.log(`Embeddings v${PLUGIN_VERSION} — benchmark (${samples} samples, ${model.name})`);
|
|
254
|
+
console.log(` ${Math.round(avg)} ms/embedding (min ${Math.min(...timings)}, max ${Math.max(...timings)})`);
|
|
255
|
+
if (nodes) console.log(` ${nodes} vault nodes → about ${report.projected_minutes} minutes to embed fully`);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
IMPLEMENTED.coverage = async (args) => {
|
|
259
|
+
const json = args.includes('--json');
|
|
260
|
+
const brain = resolveVault();
|
|
261
|
+
if (!brain) {
|
|
262
|
+
if (json) console.log(JSON.stringify({ ok: false, reason: 'no brain found in the current directory or home' }));
|
|
263
|
+
else console.error('Embeddings: no brain found. Run from a project with .agent/skills/total-recall, or after total-recall init.');
|
|
264
|
+
process.exitCode = 1;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const nodes = vaultNodeCount(brain);
|
|
268
|
+
const cov = coverage(brain);
|
|
269
|
+
const report = { ok: true, brain, vault_nodes: nodes, embedded: cov.embedded, model: cov.model, db_bytes: cov.db_bytes,
|
|
270
|
+
percent: (nodes && cov.embedded != null) ? Math.round((cov.embedded / nodes) * 100) : null };
|
|
271
|
+
if (json) { console.log(JSON.stringify(report, null, 1)); return; }
|
|
272
|
+
console.log(`Embeddings v${PLUGIN_VERSION} — coverage`);
|
|
273
|
+
console.log(` brain: ${brain}`);
|
|
274
|
+
console.log(` vault: ${nodes} nodes`);
|
|
275
|
+
console.log(` embedded: ${cov.embedded ?? 'no index'}${cov.model ? ` (model: ${cov.model})` : ''}${report.percent != null ? ` — ${report.percent}%` : ''}`);
|
|
276
|
+
if (report.percent != null && report.percent < 100) console.log(' run: total-recall compile (builds the missing vectors)');
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export async function run(argv = []) {
|
|
280
|
+
const args = Array.isArray(argv) ? argv.slice(3) : [];
|
|
281
|
+
const sub = args[0] || 'status';
|
|
282
|
+
const json = args.includes('--json');
|
|
283
|
+
const wantsHelp = args.includes('--help') || args.includes('-h');
|
|
284
|
+
|
|
285
|
+
if (wantsHelp || sub === 'help') {
|
|
286
|
+
const subs = [...new Set(['status', ...Object.keys(IMPLEMENTED)])];
|
|
287
|
+
console.log(`Embeddings v${PLUGIN_VERSION}
|
|
288
|
+
|
|
289
|
+
Usage: total-recall embeddings <subcommand> [options]
|
|
290
|
+
|
|
291
|
+
Subcommands:
|
|
292
|
+
${subs.map((s) => ` ${s}`).join('\n')}
|
|
293
|
+
|
|
294
|
+
Options:
|
|
295
|
+
--json Machine-readable output
|
|
296
|
+
--help, -h Show this help
|
|
297
|
+
|
|
298
|
+
Environment:
|
|
299
|
+
TR_OLLAMA_URL / OLLAMA_HOST Ollama endpoint (default http://127.0.0.1:11434)
|
|
300
|
+
When unset, the core falls back to a hosted
|
|
301
|
+
provider and vault text leaves the machine.`);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (IMPLEMENTED[sub]) return IMPLEMENTED[sub](args.slice(1));
|
|
306
|
+
|
|
307
|
+
console.error(`Embeddings: "${sub}" is not implemented. Run 'total-recall embeddings --help' for available subcommands.`);
|
|
308
|
+
process.exitCode = 2;
|
|
309
|
+
}
|
|
310
|
+
export default run;
|