chainlesschain 0.37.12 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/commands/agent.js +7 -1
- package/src/commands/ask.js +24 -9
- package/src/commands/chat.js +7 -1
- package/src/commands/cli-anything.js +266 -0
- package/src/commands/compliance.js +216 -0
- package/src/commands/dao.js +312 -0
- package/src/commands/dlp.js +278 -0
- package/src/commands/evomap.js +558 -0
- package/src/commands/hardening.js +230 -0
- package/src/commands/matrix.js +168 -0
- package/src/commands/nostr.js +185 -0
- package/src/commands/pqc.js +162 -0
- package/src/commands/scim.js +218 -0
- package/src/commands/serve.js +109 -0
- package/src/commands/siem.js +156 -0
- package/src/commands/social.js +480 -0
- package/src/commands/terraform.js +148 -0
- package/src/constants.js +1 -0
- package/src/index.js +60 -0
- package/src/lib/autonomous-agent.js +487 -0
- package/src/lib/cli-anything-bridge.js +379 -0
- package/src/lib/cli-context-engineering.js +472 -0
- package/src/lib/compliance-manager.js +290 -0
- package/src/lib/content-recommender.js +205 -0
- package/src/lib/dao-governance.js +296 -0
- package/src/lib/dlp-engine.js +304 -0
- package/src/lib/evomap-client.js +135 -0
- package/src/lib/evomap-federation.js +240 -0
- package/src/lib/evomap-governance.js +250 -0
- package/src/lib/evomap-manager.js +227 -0
- package/src/lib/git-integration.js +1 -1
- package/src/lib/hardening-manager.js +275 -0
- package/src/lib/llm-providers.js +14 -1
- package/src/lib/matrix-bridge.js +196 -0
- package/src/lib/nostr-bridge.js +195 -0
- package/src/lib/permanent-memory.js +370 -0
- package/src/lib/plan-mode.js +211 -0
- package/src/lib/pqc-manager.js +196 -0
- package/src/lib/scim-manager.js +212 -0
- package/src/lib/session-manager.js +38 -0
- package/src/lib/siem-exporter.js +137 -0
- package/src/lib/social-manager.js +283 -0
- package/src/lib/task-model-selector.js +232 -0
- package/src/lib/terraform-manager.js +201 -0
- package/src/lib/ws-server.js +474 -0
- package/src/repl/agent-repl.js +796 -41
- package/src/repl/chat-repl.js +14 -6
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-Anything Bridge — discovers and registers CLI-Anything generated tools
|
|
3
|
+
* as ChainlessChain managed-layer skills.
|
|
4
|
+
*
|
|
5
|
+
* CLI-Anything (https://github.com/HKUDS/CLI-Anything) generates Agent-native
|
|
6
|
+
* CLI wrappers for arbitrary software. This bridge scans for those wrappers
|
|
7
|
+
* on the user's PATH and turns each one into a SKILL.md + handler.js pair
|
|
8
|
+
* that the existing 4-layer skill-loader picks up automatically.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { execSync } from "child_process";
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { getElectronUserDataDir } from "./paths.js";
|
|
15
|
+
|
|
16
|
+
/* ---------- _deps injection (Vitest CJS mock pattern) ---------- */
|
|
17
|
+
export const _deps = { execSync, fs, path };
|
|
18
|
+
|
|
19
|
+
/* ----------------------------------------------------------------
|
|
20
|
+
* Database helpers
|
|
21
|
+
* ---------------------------------------------------------------- */
|
|
22
|
+
|
|
23
|
+
const CREATE_TABLE_SQL = `
|
|
24
|
+
CREATE TABLE IF NOT EXISTS cli_anything_tools (
|
|
25
|
+
id TEXT PRIMARY KEY,
|
|
26
|
+
name TEXT NOT NULL UNIQUE,
|
|
27
|
+
software_path TEXT,
|
|
28
|
+
cli_command TEXT NOT NULL,
|
|
29
|
+
version TEXT DEFAULT '1.0.0',
|
|
30
|
+
description TEXT,
|
|
31
|
+
subcommands TEXT,
|
|
32
|
+
skill_name TEXT,
|
|
33
|
+
status TEXT DEFAULT 'discovered',
|
|
34
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
35
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
36
|
+
)`;
|
|
37
|
+
|
|
38
|
+
export function ensureCliAnythingTables(db) {
|
|
39
|
+
db.exec(CREATE_TABLE_SQL);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ----------------------------------------------------------------
|
|
43
|
+
* Python / CLI-Anything detection
|
|
44
|
+
* ---------------------------------------------------------------- */
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Detect a usable Python interpreter.
|
|
48
|
+
* Returns { found: boolean, command?: string, version?: string }.
|
|
49
|
+
*/
|
|
50
|
+
export function detectPython() {
|
|
51
|
+
const candidates =
|
|
52
|
+
process.platform === "win32"
|
|
53
|
+
? ["python", "python3", "py"]
|
|
54
|
+
: ["python3", "python"];
|
|
55
|
+
|
|
56
|
+
for (const cmd of candidates) {
|
|
57
|
+
try {
|
|
58
|
+
const ver = _deps
|
|
59
|
+
.execSync(`${cmd} --version`, {
|
|
60
|
+
encoding: "utf-8",
|
|
61
|
+
timeout: 10000,
|
|
62
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
63
|
+
})
|
|
64
|
+
.trim();
|
|
65
|
+
const match = ver.match(/Python\s+([\d.]+)/i);
|
|
66
|
+
if (match) {
|
|
67
|
+
return { found: true, command: cmd, version: match[1] };
|
|
68
|
+
}
|
|
69
|
+
} catch (_err) {
|
|
70
|
+
// This candidate not available — try next
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { found: false };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Check whether the `cli-anything` Python package is installed.
|
|
78
|
+
* Returns { installed: boolean, version?: string }.
|
|
79
|
+
*/
|
|
80
|
+
export function detectCliAnything() {
|
|
81
|
+
const py = detectPython();
|
|
82
|
+
if (!py.found) return { installed: false };
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const out = _deps.execSync(`${py.command} -m pip show cli-anything`, {
|
|
86
|
+
encoding: "utf-8",
|
|
87
|
+
timeout: 15000,
|
|
88
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
89
|
+
});
|
|
90
|
+
const verMatch = out.match(/Version:\s*([\d.]+)/i);
|
|
91
|
+
return {
|
|
92
|
+
installed: true,
|
|
93
|
+
version: verMatch ? verMatch[1] : "unknown",
|
|
94
|
+
pythonCommand: py.command,
|
|
95
|
+
};
|
|
96
|
+
} catch (_err) {
|
|
97
|
+
return { installed: false, pythonCommand: py.command };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Install CLI-Anything via pip.
|
|
103
|
+
*/
|
|
104
|
+
export function installCliAnything(pythonCmd) {
|
|
105
|
+
_deps.execSync(`${pythonCmd} -m pip install cli-anything`, {
|
|
106
|
+
encoding: "utf-8",
|
|
107
|
+
timeout: 120000,
|
|
108
|
+
stdio: "inherit",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* ----------------------------------------------------------------
|
|
113
|
+
* Tool scanning
|
|
114
|
+
* ---------------------------------------------------------------- */
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Scan PATH for executables matching `cli-anything-*`.
|
|
118
|
+
* Returns an array of { name, command, path }.
|
|
119
|
+
*/
|
|
120
|
+
export function scanPathForTools() {
|
|
121
|
+
const results = [];
|
|
122
|
+
const seen = new Set();
|
|
123
|
+
const dirs = (process.env.PATH || "").split(_deps.path.delimiter);
|
|
124
|
+
|
|
125
|
+
for (const dir of dirs) {
|
|
126
|
+
try {
|
|
127
|
+
const entries = _deps.fs.readdirSync(dir);
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
const baseName = entry.replace(/\.exe$/i, "");
|
|
130
|
+
if (!baseName.startsWith("cli-anything-")) continue;
|
|
131
|
+
if (seen.has(baseName)) continue;
|
|
132
|
+
seen.add(baseName);
|
|
133
|
+
|
|
134
|
+
const toolName = baseName.replace(/^cli-anything-/, "");
|
|
135
|
+
results.push({
|
|
136
|
+
name: toolName,
|
|
137
|
+
command: baseName,
|
|
138
|
+
path: _deps.path.join(dir, entry),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
} catch (_err) {
|
|
142
|
+
// Directory not readable — skip
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return results;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Parse `--help` output of a cli-anything generated tool.
|
|
150
|
+
* Returns { description, subcommands: [{ name, description }] }.
|
|
151
|
+
*/
|
|
152
|
+
export function parseToolHelp(command) {
|
|
153
|
+
let helpText;
|
|
154
|
+
try {
|
|
155
|
+
helpText = _deps.execSync(`${command} --help`, {
|
|
156
|
+
encoding: "utf-8",
|
|
157
|
+
timeout: 15000,
|
|
158
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
159
|
+
});
|
|
160
|
+
} catch (err) {
|
|
161
|
+
const raw = err.stdout || err.stderr || "";
|
|
162
|
+
helpText = typeof raw === "string" ? raw : raw.toString("utf-8");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const lines = helpText.split("\n").map((l) => l.trimEnd());
|
|
166
|
+
|
|
167
|
+
// First non-empty, non-"usage:" line is typically the description
|
|
168
|
+
let description = "";
|
|
169
|
+
for (const line of lines) {
|
|
170
|
+
const trimmed = line.trim();
|
|
171
|
+
if (!trimmed) continue;
|
|
172
|
+
if (/^usage:/i.test(trimmed)) continue;
|
|
173
|
+
if (/^options?:/i.test(trimmed)) break;
|
|
174
|
+
if (/^commands?:/i.test(trimmed)) break;
|
|
175
|
+
description = trimmed;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Parse subcommands section
|
|
180
|
+
const subcommands = [];
|
|
181
|
+
let inCommands = false;
|
|
182
|
+
for (const line of lines) {
|
|
183
|
+
const trimmed = line.trim();
|
|
184
|
+
if (/^commands?:/i.test(trimmed) || /^subcommands?:/i.test(trimmed)) {
|
|
185
|
+
inCommands = true;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (inCommands) {
|
|
189
|
+
if (!trimmed || /^options?:/i.test(trimmed)) break;
|
|
190
|
+
const match = trimmed.match(/^(\S+)\s+(.*)/);
|
|
191
|
+
if (match) {
|
|
192
|
+
subcommands.push({ name: match[1], description: match[2].trim() });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
description: description || `CLI-Anything tool: ${command}`,
|
|
199
|
+
subcommands,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ----------------------------------------------------------------
|
|
204
|
+
* Skill registration / removal
|
|
205
|
+
* ---------------------------------------------------------------- */
|
|
206
|
+
|
|
207
|
+
function _skillDir(toolName) {
|
|
208
|
+
const userData = getElectronUserDataDir();
|
|
209
|
+
return _deps.path.join(userData, "skills", `cli-anything-${toolName}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Generate SKILL.md content for a CLI-Anything tool.
|
|
214
|
+
*/
|
|
215
|
+
export function _generateSkillMd(name, helpData) {
|
|
216
|
+
const subs = (helpData.subcommands || [])
|
|
217
|
+
.map((s) => `- **${s.name}**: ${s.description}`)
|
|
218
|
+
.join("\n");
|
|
219
|
+
|
|
220
|
+
return `---
|
|
221
|
+
name: cli-anything-${name}
|
|
222
|
+
display-name: CLI-Anything ${name}
|
|
223
|
+
description: ${helpData.description || `Agent-native CLI for ${name}`}
|
|
224
|
+
version: 1.0.0
|
|
225
|
+
category: integration
|
|
226
|
+
tags: [cli-anything, ${name}, external-tool]
|
|
227
|
+
user-invocable: true
|
|
228
|
+
handler: handler.js
|
|
229
|
+
capabilities: [shell-exec]
|
|
230
|
+
os: [linux, darwin, win32]
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
# cli-anything-${name}
|
|
234
|
+
|
|
235
|
+
Auto-registered by \`chainlesschain cli-anything register ${name}\`.
|
|
236
|
+
|
|
237
|
+
${helpData.description || ""}
|
|
238
|
+
|
|
239
|
+
${subs ? `## Subcommands\n\n${subs}\n` : ""}
|
|
240
|
+
## Usage
|
|
241
|
+
|
|
242
|
+
Pass the subcommand and arguments as plain text input:
|
|
243
|
+
|
|
244
|
+
\`\`\`
|
|
245
|
+
/skill cli-anything-${name} <subcommand> [args...]
|
|
246
|
+
\`\`\`
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Generate handler.js content for a CLI-Anything tool.
|
|
252
|
+
*/
|
|
253
|
+
export function _generateHandlerJs(name, command) {
|
|
254
|
+
return `"use strict";
|
|
255
|
+
const { execSync } = require("child_process");
|
|
256
|
+
|
|
257
|
+
module.exports = {
|
|
258
|
+
async execute(task, context) {
|
|
259
|
+
const input = (task?.params?.input || task?.action || "").trim();
|
|
260
|
+
if (!input) return { success: false, error: "No input provided" };
|
|
261
|
+
try {
|
|
262
|
+
const output = execSync(\`${command} \${input}\`, {
|
|
263
|
+
encoding: "utf-8",
|
|
264
|
+
timeout: 60000,
|
|
265
|
+
cwd: context?.projectRoot || process.cwd(),
|
|
266
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
267
|
+
});
|
|
268
|
+
let result;
|
|
269
|
+
try { result = JSON.parse(output); } catch (_e) { result = { output: output.trim() }; }
|
|
270
|
+
return { success: true, result, message: "Completed" };
|
|
271
|
+
} catch (err) {
|
|
272
|
+
const errMsg = typeof err.stderr === "string" ? err.stderr : (err.stderr?.toString("utf-8") || "");
|
|
273
|
+
return { success: false, error: errMsg || err.message };
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Register a CLI-Anything tool as a managed-layer skill.
|
|
282
|
+
*/
|
|
283
|
+
export function registerTool(db, toolName, opts = {}) {
|
|
284
|
+
const command = opts.command || `cli-anything-${toolName}`;
|
|
285
|
+
const helpData = opts.helpData || parseToolHelp(command);
|
|
286
|
+
const force = opts.force || false;
|
|
287
|
+
|
|
288
|
+
const dir = _skillDir(toolName);
|
|
289
|
+
|
|
290
|
+
// Check existing
|
|
291
|
+
const existing = db
|
|
292
|
+
.prepare("SELECT id FROM cli_anything_tools WHERE name = ?")
|
|
293
|
+
.get(toolName);
|
|
294
|
+
if (existing && !force) {
|
|
295
|
+
throw new Error(
|
|
296
|
+
`Tool "${toolName}" already registered. Use --force to overwrite.`,
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Write skill files
|
|
301
|
+
_deps.fs.mkdirSync(dir, { recursive: true });
|
|
302
|
+
_deps.fs.writeFileSync(
|
|
303
|
+
_deps.path.join(dir, "SKILL.md"),
|
|
304
|
+
_generateSkillMd(toolName, helpData),
|
|
305
|
+
"utf-8",
|
|
306
|
+
);
|
|
307
|
+
_deps.fs.writeFileSync(
|
|
308
|
+
_deps.path.join(dir, "handler.js"),
|
|
309
|
+
_generateHandlerJs(toolName, command),
|
|
310
|
+
"utf-8",
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
// Upsert DB record
|
|
314
|
+
const id = existing ? existing.id : `clia-${toolName}-${Date.now()}`;
|
|
315
|
+
const skillName = `cli-anything-${toolName}`;
|
|
316
|
+
const subcommandsJson = JSON.stringify(helpData.subcommands || []);
|
|
317
|
+
|
|
318
|
+
if (existing) {
|
|
319
|
+
db.prepare(
|
|
320
|
+
`
|
|
321
|
+
UPDATE cli_anything_tools
|
|
322
|
+
SET cli_command = ?, description = ?, subcommands = ?,
|
|
323
|
+
skill_name = ?, status = 'registered', updated_at = datetime('now')
|
|
324
|
+
WHERE name = ?
|
|
325
|
+
`,
|
|
326
|
+
).run(command, helpData.description, subcommandsJson, skillName, toolName);
|
|
327
|
+
} else {
|
|
328
|
+
db.prepare(
|
|
329
|
+
`
|
|
330
|
+
INSERT INTO cli_anything_tools (id, name, software_path, cli_command, description, subcommands, skill_name, status)
|
|
331
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 'registered')
|
|
332
|
+
`,
|
|
333
|
+
).run(
|
|
334
|
+
id,
|
|
335
|
+
toolName,
|
|
336
|
+
opts.softwarePath || null,
|
|
337
|
+
command,
|
|
338
|
+
helpData.description,
|
|
339
|
+
subcommandsJson,
|
|
340
|
+
skillName,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return { id, skillName, dir, subcommands: helpData.subcommands || [] };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Remove a registered CLI-Anything tool.
|
|
349
|
+
*/
|
|
350
|
+
export function removeTool(db, toolName) {
|
|
351
|
+
const row = db
|
|
352
|
+
.prepare("SELECT id FROM cli_anything_tools WHERE name = ?")
|
|
353
|
+
.get(toolName);
|
|
354
|
+
if (!row) {
|
|
355
|
+
throw new Error(`Tool "${toolName}" is not registered.`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Remove skill directory
|
|
359
|
+
const dir = _skillDir(toolName);
|
|
360
|
+
try {
|
|
361
|
+
_deps.fs.rmSync(dir, { recursive: true, force: true });
|
|
362
|
+
} catch (_err) {
|
|
363
|
+
// Directory may already be gone
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
db.prepare("DELETE FROM cli_anything_tools WHERE name = ?").run(toolName);
|
|
367
|
+
return { removed: true, toolName };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* List all registered CLI-Anything tools.
|
|
372
|
+
*/
|
|
373
|
+
export function listTools(db) {
|
|
374
|
+
return db
|
|
375
|
+
.prepare(
|
|
376
|
+
"SELECT id, name, cli_command, description, skill_name, status, created_at, updated_at FROM cli_anything_tools ORDER BY name",
|
|
377
|
+
)
|
|
378
|
+
.all();
|
|
379
|
+
}
|