chainlesschain 0.37.10 → 0.37.12
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 +166 -10
- package/package.json +1 -1
- package/src/commands/a2a.js +374 -0
- package/src/commands/bi.js +240 -0
- package/src/commands/cowork.js +317 -0
- package/src/commands/economy.js +375 -0
- package/src/commands/evolution.js +398 -0
- package/src/commands/hmemory.js +273 -0
- package/src/commands/hook.js +260 -0
- package/src/commands/init.js +184 -0
- package/src/commands/lowcode.js +320 -0
- package/src/commands/plugin.js +55 -2
- package/src/commands/sandbox.js +366 -0
- package/src/commands/skill.js +254 -201
- package/src/commands/workflow.js +359 -0
- package/src/commands/zkp.js +277 -0
- package/src/index.js +44 -0
- package/src/lib/a2a-protocol.js +371 -0
- package/src/lib/agent-coordinator.js +273 -0
- package/src/lib/agent-economy.js +369 -0
- package/src/lib/app-builder.js +377 -0
- package/src/lib/bi-engine.js +299 -0
- package/src/lib/cowork/ab-comparator-cli.js +180 -0
- package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
- package/src/lib/cowork/debate-review-cli.js +144 -0
- package/src/lib/cowork/decision-kb-cli.js +153 -0
- package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
- package/src/lib/cowork-adapter.js +106 -0
- package/src/lib/evolution-system.js +508 -0
- package/src/lib/hierarchical-memory.js +471 -0
- package/src/lib/hook-manager.js +387 -0
- package/src/lib/plugin-manager.js +118 -0
- package/src/lib/project-detector.js +53 -0
- package/src/lib/sandbox-v2.js +503 -0
- package/src/lib/service-container.js +183 -0
- package/src/lib/skill-loader.js +274 -0
- package/src/lib/workflow-engine.js +503 -0
- package/src/lib/zkp-engine.js +241 -0
- package/src/repl/agent-repl.js +117 -112
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Sandbox v2 commands
|
|
3
|
+
* chainlesschain sandbox create|exec|destroy|list|audit|quota|monitor
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import { logger } from "../lib/logger.js";
|
|
9
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
10
|
+
import {
|
|
11
|
+
createSandbox,
|
|
12
|
+
executeSandbox,
|
|
13
|
+
destroySandbox,
|
|
14
|
+
listSandboxes,
|
|
15
|
+
getAuditLog,
|
|
16
|
+
getSandbox,
|
|
17
|
+
setQuota,
|
|
18
|
+
monitorBehavior,
|
|
19
|
+
} from "../lib/sandbox-v2.js";
|
|
20
|
+
|
|
21
|
+
export function registerSandboxCommand(program) {
|
|
22
|
+
const sandbox = program
|
|
23
|
+
.command("sandbox")
|
|
24
|
+
.description("Security sandbox v2 — isolated agent execution environments");
|
|
25
|
+
|
|
26
|
+
// sandbox create <agent-id>
|
|
27
|
+
sandbox
|
|
28
|
+
.command("create")
|
|
29
|
+
.description("Create a new sandbox for an agent")
|
|
30
|
+
.argument("<agent-id>", "Agent ID to sandbox")
|
|
31
|
+
.option("--allow-read <paths>", "Comma-separated allowed read paths")
|
|
32
|
+
.option("--allow-write <paths>", "Comma-separated allowed write paths")
|
|
33
|
+
.option("--allowed-hosts <hosts>", "Comma-separated allowed network hosts")
|
|
34
|
+
.option("--json", "Output as JSON")
|
|
35
|
+
.action(async (agentId, options) => {
|
|
36
|
+
try {
|
|
37
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
38
|
+
if (!ctx.db) {
|
|
39
|
+
logger.error("Database not available");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const db = ctx.db.getDatabase();
|
|
43
|
+
const spinner = ora("Creating sandbox...").start();
|
|
44
|
+
|
|
45
|
+
const perms = {};
|
|
46
|
+
if (options.allowRead || options.allowWrite) {
|
|
47
|
+
perms.fileSystem = {
|
|
48
|
+
read: options.allowRead
|
|
49
|
+
? options.allowRead.split(",").map((p) => p.trim())
|
|
50
|
+
: ["/tmp"],
|
|
51
|
+
write: options.allowWrite
|
|
52
|
+
? options.allowWrite.split(",").map((p) => p.trim())
|
|
53
|
+
: ["/tmp"],
|
|
54
|
+
denied: ["/etc", "/usr", "/sys"],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (options.allowedHosts) {
|
|
58
|
+
perms.network = {
|
|
59
|
+
allowed: options.allowedHosts.split(",").map((h) => h.trim()),
|
|
60
|
+
denied: [],
|
|
61
|
+
maxConnections: 10,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const sandboxOpts =
|
|
66
|
+
Object.keys(perms).length > 0 ? { permissions: perms } : {};
|
|
67
|
+
const result = createSandbox(db, agentId, sandboxOpts);
|
|
68
|
+
spinner.succeed("Sandbox created");
|
|
69
|
+
|
|
70
|
+
if (options.json) {
|
|
71
|
+
console.log(JSON.stringify(result, null, 2));
|
|
72
|
+
} else {
|
|
73
|
+
logger.log(chalk.bold("Sandbox Created:"));
|
|
74
|
+
logger.log(` ID: ${chalk.cyan(result.id)}`);
|
|
75
|
+
logger.log(` Status: ${chalk.green(result.status)}`);
|
|
76
|
+
logger.log(
|
|
77
|
+
` Quota: CPU=${result.quota.cpu}, Memory=${(result.quota.memory / 1024 / 1024).toFixed(0)}MB`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
await shutdown();
|
|
82
|
+
} catch (err) {
|
|
83
|
+
logger.error(`Failed: ${err.message}`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// sandbox exec <sandbox-id> <code>
|
|
89
|
+
sandbox
|
|
90
|
+
.command("exec")
|
|
91
|
+
.description("Execute code within a sandbox")
|
|
92
|
+
.argument("<sandbox-id>", "Sandbox ID")
|
|
93
|
+
.argument("<code>", "Code to execute")
|
|
94
|
+
.option("--json", "Output as JSON")
|
|
95
|
+
.action(async (sandboxId, code, options) => {
|
|
96
|
+
try {
|
|
97
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
98
|
+
if (!ctx.db) {
|
|
99
|
+
logger.error("Database not available");
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
const db = ctx.db.getDatabase();
|
|
103
|
+
const spinner = ora("Executing in sandbox...").start();
|
|
104
|
+
|
|
105
|
+
const result = executeSandbox(db, sandboxId, code);
|
|
106
|
+
spinner.succeed("Execution complete");
|
|
107
|
+
|
|
108
|
+
if (options.json) {
|
|
109
|
+
console.log(JSON.stringify(result, null, 2));
|
|
110
|
+
} else {
|
|
111
|
+
logger.log(chalk.bold("Execution Result:"));
|
|
112
|
+
logger.log(` Output: ${result.output}`);
|
|
113
|
+
logger.log(
|
|
114
|
+
` Exit Code: ${result.exitCode === 0 ? chalk.green(0) : chalk.red(result.exitCode)}`,
|
|
115
|
+
);
|
|
116
|
+
logger.log(` Duration: ${result.duration}ms`);
|
|
117
|
+
logger.log(` CPU Used: ${result.resourceUsage.cpu}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await shutdown();
|
|
121
|
+
} catch (err) {
|
|
122
|
+
logger.error(`Failed: ${err.message}`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// sandbox destroy <sandbox-id>
|
|
128
|
+
sandbox
|
|
129
|
+
.command("destroy")
|
|
130
|
+
.description("Destroy a sandbox")
|
|
131
|
+
.argument("<sandbox-id>", "Sandbox ID to destroy")
|
|
132
|
+
.option("--json", "Output as JSON")
|
|
133
|
+
.action(async (sandboxId, options) => {
|
|
134
|
+
try {
|
|
135
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
136
|
+
if (!ctx.db) {
|
|
137
|
+
logger.error("Database not available");
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
const db = ctx.db.getDatabase();
|
|
141
|
+
|
|
142
|
+
const result = destroySandbox(db, sandboxId);
|
|
143
|
+
|
|
144
|
+
if (options.json) {
|
|
145
|
+
console.log(JSON.stringify(result, null, 2));
|
|
146
|
+
} else {
|
|
147
|
+
logger.log(chalk.yellow(`Sandbox ${sandboxId} destroyed.`));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
await shutdown();
|
|
151
|
+
} catch (err) {
|
|
152
|
+
logger.error(`Failed: ${err.message}`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// sandbox list
|
|
158
|
+
sandbox
|
|
159
|
+
.command("list")
|
|
160
|
+
.description("List active sandboxes")
|
|
161
|
+
.option("--json", "Output as JSON")
|
|
162
|
+
.action(async (options) => {
|
|
163
|
+
try {
|
|
164
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
165
|
+
if (!ctx.db) {
|
|
166
|
+
logger.error("Database not available");
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
const db = ctx.db.getDatabase();
|
|
170
|
+
const sandboxes = listSandboxes(db);
|
|
171
|
+
|
|
172
|
+
if (options.json) {
|
|
173
|
+
console.log(JSON.stringify(sandboxes, null, 2));
|
|
174
|
+
} else if (sandboxes.length === 0) {
|
|
175
|
+
logger.info("No active sandboxes.");
|
|
176
|
+
} else {
|
|
177
|
+
logger.log(chalk.bold(`Active Sandboxes (${sandboxes.length}):\n`));
|
|
178
|
+
for (const s of sandboxes) {
|
|
179
|
+
logger.log(` ${chalk.cyan(s.id)}`);
|
|
180
|
+
logger.log(
|
|
181
|
+
` Agent: ${s.agentId} Status: ${chalk.green(s.status)}`,
|
|
182
|
+
);
|
|
183
|
+
logger.log(
|
|
184
|
+
` CPU: ${s.resourceUsage.cpu}/${s.quota.cpu} Memory: ${s.resourceUsage.memory}/${s.quota.memory}`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
await shutdown();
|
|
190
|
+
} catch (err) {
|
|
191
|
+
logger.error(`Failed: ${err.message}`);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// sandbox audit [sandbox-id]
|
|
197
|
+
sandbox
|
|
198
|
+
.command("audit")
|
|
199
|
+
.description("Show audit log for sandboxes")
|
|
200
|
+
.argument("[sandbox-id]", "Optional sandbox ID to filter")
|
|
201
|
+
.option("--action <name>", "Filter by action type")
|
|
202
|
+
.option("--limit <n>", "Limit entries", parseInt)
|
|
203
|
+
.option("--json", "Output as JSON")
|
|
204
|
+
.action(async (sandboxId, options) => {
|
|
205
|
+
try {
|
|
206
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
207
|
+
if (!ctx.db) {
|
|
208
|
+
logger.error("Database not available");
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
const db = ctx.db.getDatabase();
|
|
212
|
+
|
|
213
|
+
const entries = getAuditLog(db, sandboxId || null, {
|
|
214
|
+
action: options.action,
|
|
215
|
+
limit: options.limit,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (options.json) {
|
|
219
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
220
|
+
} else if (entries.length === 0) {
|
|
221
|
+
logger.info("No audit entries found.");
|
|
222
|
+
} else {
|
|
223
|
+
logger.log(chalk.bold(`Audit Log (${entries.length} entries):\n`));
|
|
224
|
+
for (const e of entries) {
|
|
225
|
+
const ts = chalk.gray(e.timestamp);
|
|
226
|
+
const action = chalk.yellow(e.action);
|
|
227
|
+
logger.log(` ${ts} ${action} sandbox=${e.sandboxId}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
await shutdown();
|
|
232
|
+
} catch (err) {
|
|
233
|
+
logger.error(`Failed: ${err.message}`);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// sandbox quota <sandbox-id>
|
|
239
|
+
sandbox
|
|
240
|
+
.command("quota")
|
|
241
|
+
.description("Show or set sandbox quota")
|
|
242
|
+
.argument("<sandbox-id>", "Sandbox ID")
|
|
243
|
+
.option("--cpu <n>", "Set CPU quota", parseInt)
|
|
244
|
+
.option("--memory <n>", "Set memory quota in MB", parseInt)
|
|
245
|
+
.option("--json", "Output as JSON")
|
|
246
|
+
.action(async (sandboxId, options) => {
|
|
247
|
+
try {
|
|
248
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
249
|
+
if (!ctx.db) {
|
|
250
|
+
logger.error("Database not available");
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
const db = ctx.db.getDatabase();
|
|
254
|
+
|
|
255
|
+
if (options.cpu || options.memory) {
|
|
256
|
+
const current = getSandbox(db, sandboxId);
|
|
257
|
+
if (!current) {
|
|
258
|
+
logger.error("Sandbox not found");
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
const newQuota = { ...current.quota };
|
|
262
|
+
if (options.cpu) newQuota.cpu = options.cpu;
|
|
263
|
+
if (options.memory) newQuota.memory = options.memory * 1024 * 1024;
|
|
264
|
+
|
|
265
|
+
const result = setQuota(db, sandboxId, newQuota);
|
|
266
|
+
if (options.json) {
|
|
267
|
+
console.log(JSON.stringify(result, null, 2));
|
|
268
|
+
} else {
|
|
269
|
+
logger.log(chalk.green("Quota updated."));
|
|
270
|
+
logger.log(
|
|
271
|
+
` CPU: ${newQuota.cpu} Memory: ${(newQuota.memory / 1024 / 1024).toFixed(0)}MB`,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
const info = getSandbox(db, sandboxId);
|
|
276
|
+
if (!info) {
|
|
277
|
+
logger.error("Sandbox not found");
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
if (options.json) {
|
|
281
|
+
console.log(
|
|
282
|
+
JSON.stringify(
|
|
283
|
+
{ quota: info.quota, resourceUsage: info.resourceUsage },
|
|
284
|
+
null,
|
|
285
|
+
2,
|
|
286
|
+
),
|
|
287
|
+
);
|
|
288
|
+
} else {
|
|
289
|
+
logger.log(chalk.bold("Quota:"));
|
|
290
|
+
logger.log(
|
|
291
|
+
` CPU: ${info.resourceUsage.cpu} / ${info.quota.cpu}`,
|
|
292
|
+
);
|
|
293
|
+
logger.log(
|
|
294
|
+
` Memory: ${info.resourceUsage.memory} / ${info.quota.memory}`,
|
|
295
|
+
);
|
|
296
|
+
logger.log(
|
|
297
|
+
` Storage: ${info.resourceUsage.storage} / ${info.quota.storage}`,
|
|
298
|
+
);
|
|
299
|
+
logger.log(
|
|
300
|
+
` Network: ${info.resourceUsage.network} / ${info.quota.network}`,
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
await shutdown();
|
|
306
|
+
} catch (err) {
|
|
307
|
+
logger.error(`Failed: ${err.message}`);
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// sandbox monitor <sandbox-id>
|
|
313
|
+
sandbox
|
|
314
|
+
.command("monitor")
|
|
315
|
+
.description("Monitor sandbox behavior and detect suspicious patterns")
|
|
316
|
+
.argument("<sandbox-id>", "Sandbox ID to monitor")
|
|
317
|
+
.option("--json", "Output as JSON")
|
|
318
|
+
.action(async (sandboxId, options) => {
|
|
319
|
+
try {
|
|
320
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
321
|
+
if (!ctx.db) {
|
|
322
|
+
logger.error("Database not available");
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
const db = ctx.db.getDatabase();
|
|
326
|
+
const spinner = ora("Analyzing behavior...").start();
|
|
327
|
+
|
|
328
|
+
const result = monitorBehavior(db, sandboxId);
|
|
329
|
+
spinner.succeed("Analysis complete");
|
|
330
|
+
|
|
331
|
+
if (options.json) {
|
|
332
|
+
console.log(JSON.stringify(result, null, 2));
|
|
333
|
+
} else {
|
|
334
|
+
logger.log(chalk.bold("Behavior Analysis:"));
|
|
335
|
+
logger.log(` Total Events: ${result.totalEvents}`);
|
|
336
|
+
const riskColor =
|
|
337
|
+
result.riskScore > 50
|
|
338
|
+
? chalk.red
|
|
339
|
+
: result.riskScore > 20
|
|
340
|
+
? chalk.yellow
|
|
341
|
+
: chalk.green;
|
|
342
|
+
logger.log(` Risk Score: ${riskColor(result.riskScore)}/100`);
|
|
343
|
+
|
|
344
|
+
if (result.patterns.length > 0) {
|
|
345
|
+
logger.log(chalk.bold("\n Detected Patterns:"));
|
|
346
|
+
for (const p of result.patterns) {
|
|
347
|
+
const sev =
|
|
348
|
+
p.severity === "high"
|
|
349
|
+
? chalk.red(p.severity)
|
|
350
|
+
: chalk.yellow(p.severity);
|
|
351
|
+
logger.log(
|
|
352
|
+
` - ${p.type} (count: ${p.count}, severity: ${sev})`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
logger.log(chalk.green("\n No suspicious patterns detected."));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
await shutdown();
|
|
361
|
+
} catch (err) {
|
|
362
|
+
logger.error(`Failed: ${err.message}`);
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|