tr-plugin-meta-harness 0.0.0-stage → 0.3.0
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 +64 -2
- package/capability/BRICK.md +59 -0
- package/capability/index.mjs +534 -0
- package/cli.mjs +139 -0
- package/package.json +20 -4
- package/plugin.json +376 -0
- package/skills/meta-harness/SKILL.md +37 -0
- package/src/adapters/dsh.mjs +446 -0
- package/src/alerts.mjs +23 -0
- package/src/coordinator.mjs +398 -0
- package/src/documentation.mjs +152 -0
- package/src/executor.mjs +82 -0
- package/src/learner.mjs +90 -0
- package/src/router.mjs +415 -0
- package/src/runtime.mjs +36 -0
- package/src/setup.mjs +35 -0
- package/src/tracker.mjs +133 -0
- package/src/worker.mjs +233 -0
- package/ui/DESIGN.md +32 -0
- package/ui/card.js +845 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH CLI — query DeepSeek Harness state from Total Recall.
|
|
3
|
+
* Ran as `total-recall cli-agents runtime dsh <subcommand>`
|
|
4
|
+
* @module @total-recall/plugin-dsh
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
|
|
12
|
+
// The DSH Web/desktop GUI binds an EPHEMERAL port chosen at launch (observed
|
|
13
|
+
// 49661 -> 49703 -> 52406 within one session), so no fixed port list can ever
|
|
14
|
+
// be correct. Earlier revisions hardcoded [53626, 54866, 54055] and therefore
|
|
15
|
+
// reported "DeepSeek Harness is not running" against a live GUI.
|
|
16
|
+
//
|
|
17
|
+
// Discover by PROCESS IDENTITY instead: find the DSH process, then read the
|
|
18
|
+
// port it is actually listening on. The debugger port (9229/9230) is excluded.
|
|
19
|
+
const DSH_RUNTIME = process.env.DSH_RUNTIME || defaultRuntime();
|
|
20
|
+
const DEBUG_PORTS = new Set([9229, 9230]);
|
|
21
|
+
/** Process-name fragments that identify a DSH surface. */
|
|
22
|
+
const DSH_PROCESS_HINTS = ['Harness Dev.app', 'dsh-desktop', '@deepseek-ai/dsh'];
|
|
23
|
+
|
|
24
|
+
/** Locate the DSH checkout when DSH_RUNTIME is unset. */
|
|
25
|
+
function defaultRuntime() {
|
|
26
|
+
const candidates = [
|
|
27
|
+
process.env.DSH_CHECKOUT,
|
|
28
|
+
path.join(os.homedir(), 'github', 'deepseek-harness'),
|
|
29
|
+
path.join(os.homedir(), 'deepseek-harness')
|
|
30
|
+
].filter(Boolean);
|
|
31
|
+
for (const dir of candidates) {
|
|
32
|
+
if (fs.existsSync(path.join(dir, 'packages'))) return dir;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** PIDs whose command line looks like a DSH surface. */
|
|
38
|
+
function dshPids() {
|
|
39
|
+
const res = spawnSync('ps', ['-Ao', 'pid=,command='], { timeout: 4000, encoding: 'utf8' });
|
|
40
|
+
if (res.status !== 0 || !res.stdout) return [];
|
|
41
|
+
const pids = [];
|
|
42
|
+
for (const line of res.stdout.split('\n')) {
|
|
43
|
+
const trimmed = line.trim();
|
|
44
|
+
if (!trimmed) continue;
|
|
45
|
+
const match = /^(\d+)\s+(.*)$/.exec(trimmed);
|
|
46
|
+
if (!match) continue;
|
|
47
|
+
const [, pid, command] = match;
|
|
48
|
+
// Skip the CLI itself and any grep-like helper.
|
|
49
|
+
if (command.includes('tr-plugin-dsh') || command.includes('lsof') || command.includes('ps -Ao')) continue;
|
|
50
|
+
if (DSH_PROCESS_HINTS.some((hint) => command.includes(hint))) pids.push(Number(pid));
|
|
51
|
+
}
|
|
52
|
+
return pids;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Listening ports held by the given PIDs, debugger ports excluded. */
|
|
56
|
+
function listenPorts(pids) {
|
|
57
|
+
const ports = [];
|
|
58
|
+
for (const pid of pids) {
|
|
59
|
+
const res = spawnSync('lsof', ['-nP', '-iTCP', '-sTCP:LISTEN', '-a', '-p', String(pid)],
|
|
60
|
+
{ timeout: 4000, encoding: 'utf8' });
|
|
61
|
+
if (res.status !== 0 || !res.stdout) continue;
|
|
62
|
+
for (const line of res.stdout.split('\n').slice(1)) {
|
|
63
|
+
// lsof columns end with `TCP host:port (LISTEN)`; match the address form
|
|
64
|
+
// directly rather than assuming the port is the last field, which yields
|
|
65
|
+
// the literal '(LISTEN)'.
|
|
66
|
+
const match = /TCP\s+\S*:(\d+)\s+\(LISTEN\)/.exec(line);
|
|
67
|
+
if (!match) continue;
|
|
68
|
+
const port = Number(match[1]);
|
|
69
|
+
if (port && !DEBUG_PORTS.has(port) && !ports.includes(port)) ports.push(port);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return ports;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Is this port the DSH Web/desktop GUI?
|
|
77
|
+
*
|
|
78
|
+
* The GUI is not always open: it answers with an authentication notice
|
|
79
|
+
* ("dsh web authentication required; reopen the URL printed by dsh web") rather
|
|
80
|
+
* than the boot payload, so probing for `__DSH_BOOT__` alone would reject the
|
|
81
|
+
* real GUI and fall through to the Electron devtools port.
|
|
82
|
+
*/
|
|
83
|
+
function servesDsh(port) {
|
|
84
|
+
if (fetchBootInfo(port) !== null) return true;
|
|
85
|
+
const res = spawnSync('curl', ['-s', '-m', '4', `http://127.0.0.1:${port}/`],
|
|
86
|
+
{ timeout: 5000, encoding: 'utf8' });
|
|
87
|
+
return res.status === 0 && typeof res.stdout === 'string' && res.stdout.includes('dsh web');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function findDsh() {
|
|
91
|
+
const pids = dshPids();
|
|
92
|
+
if (pids.length === 0) return null;
|
|
93
|
+
|
|
94
|
+
// Prefer a PID whose port actually serves the DSH boot payload; fall back to
|
|
95
|
+
// the first PID that merely holds a listening socket.
|
|
96
|
+
let fallback = null;
|
|
97
|
+
for (const pid of pids) {
|
|
98
|
+
for (const port of listenPorts([pid])) {
|
|
99
|
+
const entry = () => {
|
|
100
|
+
const cmd = spawnSync('ps', ['-p', String(pid), '-o', 'comm='], { timeout: 2000, encoding: 'utf8' });
|
|
101
|
+
const startTime = spawnSync('ps', ['-p', String(pid), '-o', 'lstart='], { timeout: 2000, encoding: 'utf8' });
|
|
102
|
+
return {
|
|
103
|
+
pid,
|
|
104
|
+
port,
|
|
105
|
+
name: (cmd.stdout || '').toString().trim(),
|
|
106
|
+
started: (startTime.stdout || '').toString().trim()
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
if (servesDsh(port)) return entry();
|
|
110
|
+
fallback ??= entry();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return fallback;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// --- Model configuration -----------------------------------------------------
|
|
117
|
+
//
|
|
118
|
+
// The configured default model lives in each profile's cordis.patch.yml under
|
|
119
|
+
// `- id: agent-default-model`. DSH keeps profiles in more than one home: the
|
|
120
|
+
// shared ~/.dsh and the desktop app's isolated build home. Reading only one is
|
|
121
|
+
// how a stale default (V3) stayed invisible in the others.
|
|
122
|
+
|
|
123
|
+
const DEFAULT_MODEL_PREF = path.join(os.homedir(), '.dsh', 'default-model.txt');
|
|
124
|
+
|
|
125
|
+
/** Every DSH home that holds profiles, in precedence order. */
|
|
126
|
+
function profileHomes() {
|
|
127
|
+
const homes = [path.join(os.homedir(), '.dsh')];
|
|
128
|
+
if (DSH_RUNTIME) {
|
|
129
|
+
homes.push(path.join(DSH_RUNTIME, 'apps', 'desktop', '.desktop-build', 'development', 'home'));
|
|
130
|
+
}
|
|
131
|
+
return homes;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Read the default model + provider from one profile patch. */
|
|
135
|
+
function readProfileDefault(patchFile) {
|
|
136
|
+
try {
|
|
137
|
+
const text = fs.readFileSync(patchFile, 'utf8');
|
|
138
|
+
const block = text.split('- id: agent-default-model')[1];
|
|
139
|
+
if (!block) return null;
|
|
140
|
+
const model = /^\s+model:\s*(\S+)/m.exec(block);
|
|
141
|
+
const provider = /^\s+provider:\s*(\S+)/m.exec(block);
|
|
142
|
+
if (!model) return null;
|
|
143
|
+
return { model: model[1], provider: provider ? provider[1] : 'unknown' };
|
|
144
|
+
} catch { return null; }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Every profile's configured default across all homes. */
|
|
148
|
+
function allProfileDefaults() {
|
|
149
|
+
const found = [];
|
|
150
|
+
for (const home of profileHomes()) {
|
|
151
|
+
const dir = path.join(home, 'profiles');
|
|
152
|
+
if (!fs.existsSync(dir)) continue;
|
|
153
|
+
for (const name of fs.readdirSync(dir)) {
|
|
154
|
+
const patch = path.join(dir, name, 'cordis.patch.yml');
|
|
155
|
+
if (!fs.existsSync(patch)) continue;
|
|
156
|
+
const def = readProfileDefault(patch);
|
|
157
|
+
if (def) found.push({ home: home.replace(os.homedir(), '~'), profile: name, ...def });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return found;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Catalogue cached by the model refresh script, when present. */
|
|
164
|
+
function cachedCatalogue() {
|
|
165
|
+
const file = path.join(os.homedir(), '.dsh', 'openrouter-models.json');
|
|
166
|
+
try {
|
|
167
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
168
|
+
const list = Array.isArray(parsed) ? parsed : parsed.data;
|
|
169
|
+
return Array.isArray(list) ? list : [];
|
|
170
|
+
} catch { return []; }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Provider packages compiled into the DSH tree. */
|
|
174
|
+
function providerPackages() {
|
|
175
|
+
const providers = [];
|
|
176
|
+
if (!DSH_RUNTIME) return providers;
|
|
177
|
+
const providerDir = path.join(DSH_RUNTIME, 'node_modules', '@deepseek-ai');
|
|
178
|
+
if (!fs.existsSync(providerDir)) return providers;
|
|
179
|
+
for (const pkg of fs.readdirSync(providerDir)) {
|
|
180
|
+
if (!(pkg.includes('llm') || pkg.includes('model') || pkg.includes('provider'))) continue;
|
|
181
|
+
const pkgPath = path.join(providerDir, pkg, 'package.json');
|
|
182
|
+
if (!fs.existsSync(pkgPath)) continue;
|
|
183
|
+
try {
|
|
184
|
+
const meta = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
185
|
+
providers.push({ name: meta.name, version: meta.version, desc: (meta.description || '').slice(0, 80) });
|
|
186
|
+
} catch { /* unreadable package */ }
|
|
187
|
+
}
|
|
188
|
+
return providers;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function calcUptime(started) {
|
|
192
|
+
if (!started) return 'unknown';
|
|
193
|
+
const start = new Date(started);
|
|
194
|
+
const now = new Date();
|
|
195
|
+
const diff = (now - start) / 1000;
|
|
196
|
+
const days = Math.floor(diff / 86400);
|
|
197
|
+
const hours = Math.floor((diff % 86400) / 3600);
|
|
198
|
+
const mins = Math.floor((diff % 3600) / 60);
|
|
199
|
+
const parts = [];
|
|
200
|
+
if (days > 0) parts.push(`${days}d`);
|
|
201
|
+
if (hours > 0) parts.push(`${hours}h`);
|
|
202
|
+
parts.push(`${mins}m`);
|
|
203
|
+
return parts.join(' ');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function fetchBootInfo(port) {
|
|
207
|
+
try {
|
|
208
|
+
const res = spawnSync('curl', ['-s', `http://127.0.0.1:${port}/`], { timeout: 5000, encoding: 'utf8' });
|
|
209
|
+
if (res.status !== 0) return null;
|
|
210
|
+
const html = res.stdout;
|
|
211
|
+
// Extract __DSH_BOOT__ JSON
|
|
212
|
+
const idx = html.indexOf('__DSH_BOOT__');
|
|
213
|
+
if (idx === -1) return null;
|
|
214
|
+
const brace = html.indexOf('{', idx);
|
|
215
|
+
let depth = 0, end = brace;
|
|
216
|
+
for (let i = brace; i < html.length; i++) {
|
|
217
|
+
if (html[i] === '{') depth++;
|
|
218
|
+
else if (html[i] === '}') depth--;
|
|
219
|
+
if (depth === 0) { end = i + 1; break; }
|
|
220
|
+
}
|
|
221
|
+
const boot = JSON.parse(html.slice(brace, end));
|
|
222
|
+
return boot;
|
|
223
|
+
} catch { return null; }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function run(argv) {
|
|
227
|
+
const subcommand = argv.includes('--help') || argv.includes('-h') ? 'help' : argv[3] || 'help';
|
|
228
|
+
|
|
229
|
+
const dsh = findDsh();
|
|
230
|
+
// `model`/`models` read profile files on disk, so they work without a running
|
|
231
|
+
// DSH; every other subcommand needs the live process.
|
|
232
|
+
const FILE_ONLY = new Set(['model', 'models']);
|
|
233
|
+
if (!dsh && subcommand !== 'help' && !FILE_ONLY.has(subcommand)) {
|
|
234
|
+
console.error(' DeepSeek Harness is not running.');
|
|
235
|
+
process.exitCode = 1;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const bootInfo = dsh ? fetchBootInfo(dsh.port) : null;
|
|
240
|
+
|
|
241
|
+
switch (subcommand) {
|
|
242
|
+
|
|
243
|
+
case 'status': {
|
|
244
|
+
const mem = spawnSync('ps', ['-p', String(dsh.pid), '-o', 'rss='], { timeout: 2000 });
|
|
245
|
+
const rss = parseInt(mem.stdout.toString().trim()) || 0;
|
|
246
|
+
const uptime = calcUptime(dsh.started);
|
|
247
|
+
|
|
248
|
+
console.log(`\n DeepSeek Harness — Runtime\n`);
|
|
249
|
+
console.log(` PID: ${dsh.pid}`);
|
|
250
|
+
console.log(` Memory: ${(rss / 1024).toFixed(1)} MB`);
|
|
251
|
+
console.log(` Port: ${dsh.port}`);
|
|
252
|
+
console.log(` Process: ${dsh.name}`);
|
|
253
|
+
console.log(` Uptime: ${uptime}`);
|
|
254
|
+
console.log(` Revision: ${bootInfo?.rev || 'unknown'}`);
|
|
255
|
+
console.log(` Plugins: ${bootInfo?.entries?.length || 'unknown'} client plugins loaded`);
|
|
256
|
+
console.log(` Web GUI: ${process.env.DSH_WEB_URL || 'http://127.0.0.1:' + dsh.port}\n`);
|
|
257
|
+
return { data: { pid: dsh.pid, port: dsh.port, memory: (rss / 1024).toFixed(1), uptime, revision: bootInfo?.rev } };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
case 'sessions': {
|
|
261
|
+
// DSH sessions are Cordis-managed. Report what we can observe externally
|
|
262
|
+
const elapsed = calcUptime(dsh.started);
|
|
263
|
+
const pluginCount = bootInfo?.entries?.length || 0;
|
|
264
|
+
|
|
265
|
+
console.log(`\n DeepSeek Harness — Sessions\n`);
|
|
266
|
+
console.log(` Status: Running since ${dsh.started} (${elapsed})`);
|
|
267
|
+
console.log(` Web GUI: ${process.env.DSH_WEB_URL || 'http://127.0.0.1:' + dsh.port}`);
|
|
268
|
+
console.log(` API Port: ${dsh.port}`);
|
|
269
|
+
console.log(` Revision: ${bootInfo?.rev || 'unknown'}`);
|
|
270
|
+
console.log(` Plugins: ${pluginCount} active`);
|
|
271
|
+
console.log(`\n Open the Web GUI to view and manage active sessions.`);
|
|
272
|
+
console.log(` Session state, conversation logs, and tool usage are`);
|
|
273
|
+
console.log(` available through the browser interface.\n`);
|
|
274
|
+
return { data: { status: 'running', port: dsh.port, uptime: elapsed, plugins: pluginCount, revision: bootInfo?.rev } };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
case 'models': {
|
|
278
|
+
const providers = providerPackages();
|
|
279
|
+
const defaults = allProfileDefaults();
|
|
280
|
+
const catalogue = cachedCatalogue();
|
|
281
|
+
|
|
282
|
+
console.log(`\n DeepSeek Harness — Models\n`);
|
|
283
|
+
|
|
284
|
+
console.log(` Configured default model (per profile):\n`);
|
|
285
|
+
if (defaults.length === 0) {
|
|
286
|
+
console.log(` (no profile declares agent-default-model)\n`);
|
|
287
|
+
} else {
|
|
288
|
+
for (const d of defaults) {
|
|
289
|
+
console.log(` ${d.home}/${d.profile}`);
|
|
290
|
+
console.log(` model: ${d.model}`);
|
|
291
|
+
console.log(` provider: ${d.provider}`);
|
|
292
|
+
}
|
|
293
|
+
const distinct = [...new Set(defaults.map((d) => d.model))];
|
|
294
|
+
if (distinct.length > 1) {
|
|
295
|
+
console.log(`\n WARNING profiles disagree: ${distinct.join(', ')}`);
|
|
296
|
+
console.log(` A refresh that hardcodes a model id will revert these.`);
|
|
297
|
+
}
|
|
298
|
+
console.log();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
let pref = '';
|
|
302
|
+
try { pref = fs.readFileSync(DEFAULT_MODEL_PREF, 'utf8').trim(); } catch { /* unset */ }
|
|
303
|
+
console.log(` Persisted preference: ${pref || '(unset)'}`);
|
|
304
|
+
console.log(` ${DEFAULT_MODEL_PREF}`);
|
|
305
|
+
console.log(` The model refresh keeps this model instead of a hardcoded id.\n`);
|
|
306
|
+
|
|
307
|
+
console.log(` Catalogue cache: ${catalogue.length} models`);
|
|
308
|
+
console.log(` ~/.dsh/openrouter-models.json\n`);
|
|
309
|
+
|
|
310
|
+
console.log(` Provider packages: ${providers.length}\n`);
|
|
311
|
+
for (const p of providers) {
|
|
312
|
+
console.log(` • ${p.name} v${p.version}`);
|
|
313
|
+
if (p.desc) console.log(` ${p.desc}`);
|
|
314
|
+
}
|
|
315
|
+
console.log(`\n Cache the catalogue for offline reads: total-recall cli-agents runtime dsh model --cache\n`);
|
|
316
|
+
return { data: { defaults, preference: pref || null, catalogueSize: catalogue.length, providers: providers.map((p) => p.name) } };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
case 'model': {
|
|
320
|
+
// Model caching: read live configuration and (optionally) cache it.
|
|
321
|
+
const defaults = allProfileDefaults();
|
|
322
|
+
const catalogue = cachedCatalogue();
|
|
323
|
+
const asJson = argv.includes('--json');
|
|
324
|
+
const wantCache = argv.includes('--cache');
|
|
325
|
+
|
|
326
|
+
const payload = {
|
|
327
|
+
generatedAt: new Date().toISOString(),
|
|
328
|
+
runtime: DSH_RUNTIME || null,
|
|
329
|
+
preference: (() => {
|
|
330
|
+
try { return fs.readFileSync(DEFAULT_MODEL_PREF, 'utf8').trim(); } catch { return null; }
|
|
331
|
+
})(),
|
|
332
|
+
defaults,
|
|
333
|
+
catalogueSize: catalogue.length
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
if (wantCache) {
|
|
337
|
+
// Cache through SSSS memory so `total-recall recall` can answer model
|
|
338
|
+
// questions offline and on any mesh node.
|
|
339
|
+
const summary = [
|
|
340
|
+
`DSH model configuration cached ${payload.generatedAt}.`,
|
|
341
|
+
payload.preference ? `Persisted preference: ${payload.preference}.` : 'No persisted preference file.',
|
|
342
|
+
...defaults.map((d) => `${d.home}/${d.profile} -> ${d.model} (${d.provider}).`),
|
|
343
|
+
`OpenRouter catalogue cache holds ${catalogue.length} models.`
|
|
344
|
+
].join(' ');
|
|
345
|
+
const res = spawnSync('total-recall', [
|
|
346
|
+
'remember', 'fact', summary,
|
|
347
|
+
'--title', 'DSH model configuration (cached)',
|
|
348
|
+
'--slug', 'dsh-model-configuration-cache',
|
|
349
|
+
'--tags', 'dsh,model,cache,openrouter',
|
|
350
|
+
'--global'
|
|
351
|
+
], { timeout: 60000, encoding: 'utf8' });
|
|
352
|
+
payload.cached = res.status === 0;
|
|
353
|
+
payload.cacheOutput = (res.stdout || '').trim().split('\n').slice(-1)[0] || '';
|
|
354
|
+
if (res.status !== 0) {
|
|
355
|
+
console.error(` Cache write failed: ${(res.stderr || '').trim().split('\n')[0] || 'unknown error'}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (asJson) {
|
|
360
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
361
|
+
return { data: payload };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
console.log(`\n DeepSeek Harness — Model Cache\n`);
|
|
365
|
+
console.log(` Preference: ${payload.preference || '(unset)'}`);
|
|
366
|
+
console.log(` Profiles: ${defaults.length}`);
|
|
367
|
+
for (const d of defaults) {
|
|
368
|
+
console.log(` • ${d.home}/${d.profile} -> ${d.model}`);
|
|
369
|
+
}
|
|
370
|
+
console.log(` Catalogue: ${catalogue.length} models cached`);
|
|
371
|
+
if (wantCache) {
|
|
372
|
+
console.log(` SSSS cache: ${payload.cached ? 'written' : 'FAILED'}`);
|
|
373
|
+
} else {
|
|
374
|
+
console.log(`\n Write the cache: total-recall cli-agents runtime dsh model --cache`);
|
|
375
|
+
}
|
|
376
|
+
console.log();
|
|
377
|
+
return { data: payload };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
case 'plugins': {
|
|
381
|
+
const entries = bootInfo?.entries || [];
|
|
382
|
+
|
|
383
|
+
console.log(`\n DeepSeek Harness — Client Plugins\n`);
|
|
384
|
+
console.log(` Total: ${entries.length} plugins loaded\n`);
|
|
385
|
+
for (const e of entries) {
|
|
386
|
+
const deps = e.inject?.length ? ` [deps: ${e.inject.join(', ')}]` : '';
|
|
387
|
+
const imm = e.immediately ? ' [immediate]' : '';
|
|
388
|
+
console.log(` • ${e.id}${imm}${deps}`);
|
|
389
|
+
}
|
|
390
|
+
console.log(`\n Dynamic Cordis Plugins (from current session):`);
|
|
391
|
+
console.log(` Use cordis_inspect_self inside a DSH session to list them.\n`);
|
|
392
|
+
return { data: { pluginCount: entries.length, plugins: entries.map(e => e.id) } };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
case 'tools': {
|
|
396
|
+
// Parse available plug-in packages for tool-related ones
|
|
397
|
+
const pluginDir = path.join(DSH_RUNTIME, 'node_modules', '@deepseek-ai');
|
|
398
|
+
const tools = [];
|
|
399
|
+
if (fs.existsSync(pluginDir)) {
|
|
400
|
+
for (const pkg of fs.readdirSync(pluginDir)) {
|
|
401
|
+
if (pkg.includes('tool') || pkg.includes('skill') || pkg.includes('command')) {
|
|
402
|
+
const pkgPath = path.join(pluginDir, pkg, 'package.json');
|
|
403
|
+
if (fs.existsSync(pkgPath)) {
|
|
404
|
+
try {
|
|
405
|
+
const meta = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
406
|
+
tools.push({ name: meta.name, version: meta.version, desc: (meta.description || '').slice(0, 100) });
|
|
407
|
+
} catch {}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Known tool packages specific to DSH
|
|
414
|
+
const knownTools = []; // Only observed packages are reported; no fabricated live tool registry.
|
|
415
|
+
|
|
416
|
+
console.log(`\n DeepSeek Harness — Tool Registry\n`);
|
|
417
|
+
console.log(` Built-in Agent Tools:\n`);
|
|
418
|
+
for (const t of knownTools) {
|
|
419
|
+
console.log(` • ${t.name} — ${t.desc}`);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (tools.length > 0) {
|
|
423
|
+
console.log(`\n Tool-related Packages:\n`);
|
|
424
|
+
for (const t of tools) {
|
|
425
|
+
console.log(` • ${t.name} v${t.version}`);
|
|
426
|
+
if (t.desc) console.log(` ${t.desc}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
console.log(`\n To see the complete live tool registry:`);
|
|
430
|
+
console.log(` Use cordis_inspect_list inside a DSH session, or`);
|
|
431
|
+
console.log(` check the Settings → Plugins panel in the Web GUI.\n`);
|
|
432
|
+
return { data: { builtIn: knownTools.map(t => t.name), packages: tools.map(t => t.name) } };
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
default:
|
|
436
|
+
console.log(`\n DeepSeek Harness — Total Recall Plugin\n`);
|
|
437
|
+
console.log('Usage: total-recall cli-agents runtime dsh <subcommand>\n');
|
|
438
|
+
console.log(' dsh status DSH runtime status (PID, port, memory, uptime)');
|
|
439
|
+
console.log(' dsh sessions Session status and Web GUI info');
|
|
440
|
+
console.log(' dsh tools Registered tool inventory');
|
|
441
|
+
console.log(' dsh models Configured default model per profile + providers');
|
|
442
|
+
console.log(' dsh model Model cache (--cache writes an SSSS node, --json)');
|
|
443
|
+
console.log(' dsh plugins Client plugin inventory\n');
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
}
|
package/src/alerts.mjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator Alerts Dispatcher
|
|
3
|
+
* Bridges to Total Recall alerts plugin or system notifications.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
export async function notifyAlert(alert = {}) {
|
|
9
|
+
const { title = 'Meta Harness Notification', message = '', severity = 'info' } = alert;
|
|
10
|
+
|
|
11
|
+
// Attempt to invoke total-recall alerts if present
|
|
12
|
+
try {
|
|
13
|
+
const totalRecallBin = process.env.TOTAL_RECALL_BIN || 'total-recall';
|
|
14
|
+
execFileSync(totalRecallBin, ['alerts', 'send', '--title', title, '--message', message, '--severity', severity], {
|
|
15
|
+
stdio: 'pipe',
|
|
16
|
+
timeout: 5000
|
|
17
|
+
});
|
|
18
|
+
return { ok: true, dispatchedVia: 'total-recall-alerts' };
|
|
19
|
+
} catch {
|
|
20
|
+
// Fallback: console log without crashing
|
|
21
|
+
return { ok: true, dispatchedVia: 'fallback-logged', logged: { title, message, severity } };
|
|
22
|
+
}
|
|
23
|
+
}
|