genesis-ai-cli 7.4.5
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/.env.example +78 -0
- package/README.md +282 -0
- package/dist/src/active-inference/actions.d.ts +75 -0
- package/dist/src/active-inference/actions.js +250 -0
- package/dist/src/active-inference/autonomous-loop.d.ts +103 -0
- package/dist/src/active-inference/autonomous-loop.js +289 -0
- package/dist/src/active-inference/core.d.ts +85 -0
- package/dist/src/active-inference/core.js +555 -0
- package/dist/src/active-inference/demo-autonomous-loop.d.ts +8 -0
- package/dist/src/active-inference/demo-autonomous-loop.js +338 -0
- package/dist/src/active-inference/demo-value-integration.d.ts +8 -0
- package/dist/src/active-inference/demo-value-integration.js +174 -0
- package/dist/src/active-inference/index.d.ts +32 -0
- package/dist/src/active-inference/index.js +88 -0
- package/dist/src/active-inference/integration.d.ts +114 -0
- package/dist/src/active-inference/integration.js +698 -0
- package/dist/src/active-inference/memory-integration.d.ts +51 -0
- package/dist/src/active-inference/memory-integration.js +232 -0
- package/dist/src/active-inference/observations.d.ts +67 -0
- package/dist/src/active-inference/observations.js +147 -0
- package/dist/src/active-inference/test-active-inference.d.ts +8 -0
- package/dist/src/active-inference/test-active-inference.js +320 -0
- package/dist/src/active-inference/test-value-integration.d.ts +6 -0
- package/dist/src/active-inference/test-value-integration.js +168 -0
- package/dist/src/active-inference/types.d.ts +150 -0
- package/dist/src/active-inference/types.js +59 -0
- package/dist/src/active-inference/value-integration.d.ts +164 -0
- package/dist/src/active-inference/value-integration.js +459 -0
- package/dist/src/agents/base-agent.d.ts +53 -0
- package/dist/src/agents/base-agent.js +178 -0
- package/dist/src/agents/builder.d.ts +67 -0
- package/dist/src/agents/builder.js +537 -0
- package/dist/src/agents/critic.d.ts +35 -0
- package/dist/src/agents/critic.js +322 -0
- package/dist/src/agents/ethicist.d.ts +54 -0
- package/dist/src/agents/ethicist.js +393 -0
- package/dist/src/agents/explorer.d.ts +26 -0
- package/dist/src/agents/explorer.js +216 -0
- package/dist/src/agents/feeling.d.ts +41 -0
- package/dist/src/agents/feeling.js +320 -0
- package/dist/src/agents/index.d.ts +111 -0
- package/dist/src/agents/index.js +222 -0
- package/dist/src/agents/memory.d.ts +69 -0
- package/dist/src/agents/memory.js +404 -0
- package/dist/src/agents/message-bus.d.ts +88 -0
- package/dist/src/agents/message-bus.js +267 -0
- package/dist/src/agents/narrator.d.ts +90 -0
- package/dist/src/agents/narrator.js +473 -0
- package/dist/src/agents/planner.d.ts +38 -0
- package/dist/src/agents/planner.js +341 -0
- package/dist/src/agents/predictor.d.ts +73 -0
- package/dist/src/agents/predictor.js +506 -0
- package/dist/src/agents/sensor.d.ts +88 -0
- package/dist/src/agents/sensor.js +377 -0
- package/dist/src/agents/test-agents.d.ts +6 -0
- package/dist/src/agents/test-agents.js +73 -0
- package/dist/src/agents/types.d.ts +194 -0
- package/dist/src/agents/types.js +7 -0
- package/dist/src/brain/index.d.ts +185 -0
- package/dist/src/brain/index.js +843 -0
- package/dist/src/brain/trace.d.ts +91 -0
- package/dist/src/brain/trace.js +327 -0
- package/dist/src/brain/types.d.ts +165 -0
- package/dist/src/brain/types.js +51 -0
- package/dist/src/cli/chat.d.ts +237 -0
- package/dist/src/cli/chat.js +1959 -0
- package/dist/src/cli/dispatcher.d.ts +182 -0
- package/dist/src/cli/dispatcher.js +718 -0
- package/dist/src/cli/human-loop.d.ts +170 -0
- package/dist/src/cli/human-loop.js +543 -0
- package/dist/src/cli/index.d.ts +12 -0
- package/dist/src/cli/index.js +28 -0
- package/dist/src/cli/interactive.d.ts +141 -0
- package/dist/src/cli/interactive.js +757 -0
- package/dist/src/cli/ui.d.ts +205 -0
- package/dist/src/cli/ui.js +632 -0
- package/dist/src/consciousness/attention-schema.d.ts +154 -0
- package/dist/src/consciousness/attention-schema.js +432 -0
- package/dist/src/consciousness/global-workspace.d.ts +149 -0
- package/dist/src/consciousness/global-workspace.js +422 -0
- package/dist/src/consciousness/index.d.ts +186 -0
- package/dist/src/consciousness/index.js +476 -0
- package/dist/src/consciousness/phi-calculator.d.ts +119 -0
- package/dist/src/consciousness/phi-calculator.js +445 -0
- package/dist/src/consciousness/phi-decisions.d.ts +169 -0
- package/dist/src/consciousness/phi-decisions.js +383 -0
- package/dist/src/consciousness/phi-monitor.d.ts +153 -0
- package/dist/src/consciousness/phi-monitor.js +465 -0
- package/dist/src/consciousness/types.d.ts +260 -0
- package/dist/src/consciousness/types.js +44 -0
- package/dist/src/daemon/dream-mode.d.ts +115 -0
- package/dist/src/daemon/dream-mode.js +470 -0
- package/dist/src/daemon/index.d.ts +162 -0
- package/dist/src/daemon/index.js +542 -0
- package/dist/src/daemon/maintenance.d.ts +139 -0
- package/dist/src/daemon/maintenance.js +549 -0
- package/dist/src/daemon/process.d.ts +82 -0
- package/dist/src/daemon/process.js +442 -0
- package/dist/src/daemon/scheduler.d.ts +90 -0
- package/dist/src/daemon/scheduler.js +494 -0
- package/dist/src/daemon/types.d.ts +213 -0
- package/dist/src/daemon/types.js +50 -0
- package/dist/src/epistemic/index.d.ts +74 -0
- package/dist/src/epistemic/index.js +225 -0
- package/dist/src/grounding/epistemic-stack.d.ts +100 -0
- package/dist/src/grounding/epistemic-stack.js +408 -0
- package/dist/src/grounding/feedback.d.ts +98 -0
- package/dist/src/grounding/feedback.js +276 -0
- package/dist/src/grounding/index.d.ts +123 -0
- package/dist/src/grounding/index.js +224 -0
- package/dist/src/grounding/verifier.d.ts +149 -0
- package/dist/src/grounding/verifier.js +484 -0
- package/dist/src/healing/detector.d.ts +110 -0
- package/dist/src/healing/detector.js +436 -0
- package/dist/src/healing/fixer.d.ts +138 -0
- package/dist/src/healing/fixer.js +572 -0
- package/dist/src/healing/index.d.ts +23 -0
- package/dist/src/healing/index.js +43 -0
- package/dist/src/hooks/index.d.ts +135 -0
- package/dist/src/hooks/index.js +317 -0
- package/dist/src/index.d.ts +23 -0
- package/dist/src/index.js +1266 -0
- package/dist/src/kernel/index.d.ts +155 -0
- package/dist/src/kernel/index.js +795 -0
- package/dist/src/kernel/invariants.d.ts +153 -0
- package/dist/src/kernel/invariants.js +355 -0
- package/dist/src/kernel/test-kernel.d.ts +6 -0
- package/dist/src/kernel/test-kernel.js +108 -0
- package/dist/src/kernel/test-real-mcp.d.ts +10 -0
- package/dist/src/kernel/test-real-mcp.js +295 -0
- package/dist/src/llm/index.d.ts +146 -0
- package/dist/src/llm/index.js +428 -0
- package/dist/src/llm/router.d.ts +136 -0
- package/dist/src/llm/router.js +510 -0
- package/dist/src/mcp/index.d.ts +85 -0
- package/dist/src/mcp/index.js +657 -0
- package/dist/src/mcp/resilient.d.ts +139 -0
- package/dist/src/mcp/resilient.js +417 -0
- package/dist/src/memory/cache.d.ts +118 -0
- package/dist/src/memory/cache.js +356 -0
- package/dist/src/memory/cognitive-workspace.d.ts +231 -0
- package/dist/src/memory/cognitive-workspace.js +521 -0
- package/dist/src/memory/consolidation.d.ts +99 -0
- package/dist/src/memory/consolidation.js +443 -0
- package/dist/src/memory/episodic.d.ts +114 -0
- package/dist/src/memory/episodic.js +394 -0
- package/dist/src/memory/forgetting.d.ts +134 -0
- package/dist/src/memory/forgetting.js +324 -0
- package/dist/src/memory/index.d.ts +211 -0
- package/dist/src/memory/index.js +367 -0
- package/dist/src/memory/indexer.d.ts +123 -0
- package/dist/src/memory/indexer.js +479 -0
- package/dist/src/memory/procedural.d.ts +136 -0
- package/dist/src/memory/procedural.js +479 -0
- package/dist/src/memory/semantic.d.ts +132 -0
- package/dist/src/memory/semantic.js +497 -0
- package/dist/src/memory/types.d.ts +193 -0
- package/dist/src/memory/types.js +15 -0
- package/dist/src/orchestrator.d.ts +65 -0
- package/dist/src/orchestrator.js +317 -0
- package/dist/src/persistence/index.d.ts +257 -0
- package/dist/src/persistence/index.js +763 -0
- package/dist/src/pipeline/executor.d.ts +51 -0
- package/dist/src/pipeline/executor.js +695 -0
- package/dist/src/pipeline/index.d.ts +7 -0
- package/dist/src/pipeline/index.js +11 -0
- package/dist/src/self-production.d.ts +67 -0
- package/dist/src/self-production.js +205 -0
- package/dist/src/subagents/executor.d.ts +58 -0
- package/dist/src/subagents/executor.js +283 -0
- package/dist/src/subagents/index.d.ts +37 -0
- package/dist/src/subagents/index.js +53 -0
- package/dist/src/subagents/registry.d.ts +23 -0
- package/dist/src/subagents/registry.js +167 -0
- package/dist/src/subagents/types.d.ts +79 -0
- package/dist/src/subagents/types.js +14 -0
- package/dist/src/tools/bash.d.ts +139 -0
- package/dist/src/tools/bash.js +583 -0
- package/dist/src/tools/edit.d.ts +125 -0
- package/dist/src/tools/edit.js +424 -0
- package/dist/src/tools/git.d.ts +179 -0
- package/dist/src/tools/git.js +504 -0
- package/dist/src/tools/index.d.ts +21 -0
- package/dist/src/tools/index.js +163 -0
- package/dist/src/types.d.ts +145 -0
- package/dist/src/types.js +7 -0
- package/dist/src/world-model/decoder.d.ts +163 -0
- package/dist/src/world-model/decoder.js +517 -0
- package/dist/src/world-model/digital-twin.d.ts +219 -0
- package/dist/src/world-model/digital-twin.js +695 -0
- package/dist/src/world-model/encoder.d.ts +141 -0
- package/dist/src/world-model/encoder.js +564 -0
- package/dist/src/world-model/index.d.ts +221 -0
- package/dist/src/world-model/index.js +772 -0
- package/dist/src/world-model/predictor.d.ts +161 -0
- package/dist/src/world-model/predictor.js +681 -0
- package/dist/src/world-model/test-value-jepa.d.ts +8 -0
- package/dist/src/world-model/test-value-jepa.js +430 -0
- package/dist/src/world-model/types.d.ts +341 -0
- package/dist/src/world-model/types.js +69 -0
- package/dist/src/world-model/value-jepa.d.ts +247 -0
- package/dist/src/world-model/value-jepa.js +622 -0
- package/dist/test/brain.test.d.ts +11 -0
- package/dist/test/brain.test.js +358 -0
- package/dist/test/cli/dispatcher.test.d.ts +4 -0
- package/dist/test/cli/dispatcher.test.js +332 -0
- package/dist/test/cli/human-loop.test.d.ts +4 -0
- package/dist/test/cli/human-loop.test.js +270 -0
- package/dist/test/grounding/feedback.test.d.ts +4 -0
- package/dist/test/grounding/feedback.test.js +462 -0
- package/dist/test/grounding/verifier.test.d.ts +4 -0
- package/dist/test/grounding/verifier.test.js +442 -0
- package/dist/test/grounding.test.d.ts +6 -0
- package/dist/test/grounding.test.js +246 -0
- package/dist/test/healing/detector.test.d.ts +4 -0
- package/dist/test/healing/detector.test.js +266 -0
- package/dist/test/healing/fixer.test.d.ts +4 -0
- package/dist/test/healing/fixer.test.js +369 -0
- package/dist/test/integration.test.d.ts +5 -0
- package/dist/test/integration.test.js +290 -0
- package/dist/test/tools/bash.test.d.ts +4 -0
- package/dist/test/tools/bash.test.js +348 -0
- package/dist/test/tools/edit.test.d.ts +4 -0
- package/dist/test/tools/edit.test.js +350 -0
- package/dist/test/tools/git.test.d.ts +4 -0
- package/dist/test/tools/git.test.js +350 -0
- package/package.json +60 -0
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Genesis Secure Bash Executor
|
|
4
|
+
*
|
|
5
|
+
* Provides sandboxed command execution with:
|
|
6
|
+
* - Command validation (whitelist/blacklist)
|
|
7
|
+
* - Dangerous pattern detection
|
|
8
|
+
* - Timeout enforcement
|
|
9
|
+
* - Working directory confinement
|
|
10
|
+
* - Output streaming
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.BashTool = exports.DEFAULT_SANDBOX_CONFIG = void 0;
|
|
47
|
+
exports.getBashTool = getBashTool;
|
|
48
|
+
exports.resetBashTool = resetBashTool;
|
|
49
|
+
exports.bash = bash;
|
|
50
|
+
exports.validateCommand = validateCommand;
|
|
51
|
+
exports.exec = exec;
|
|
52
|
+
const child_process_1 = require("child_process");
|
|
53
|
+
const events_1 = require("events");
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Default Configuration
|
|
58
|
+
// ============================================================================
|
|
59
|
+
exports.DEFAULT_SANDBOX_CONFIG = {
|
|
60
|
+
allowedCommands: [
|
|
61
|
+
// File operations (read-only safe)
|
|
62
|
+
'ls', 'cat', 'head', 'tail', 'less', 'more', 'file', 'stat', 'wc',
|
|
63
|
+
'find', 'locate', 'which', 'whereis', 'tree',
|
|
64
|
+
// Text processing
|
|
65
|
+
'grep', 'awk', 'sed', 'cut', 'sort', 'uniq', 'tr', 'diff', 'comm',
|
|
66
|
+
'jq', 'yq', 'xargs',
|
|
67
|
+
// Development tools
|
|
68
|
+
'node', 'npm', 'npx', 'yarn', 'pnpm', 'bun',
|
|
69
|
+
'python', 'python3', 'pip', 'pip3',
|
|
70
|
+
'git', 'gh',
|
|
71
|
+
'tsc', 'tsx', 'esbuild', 'vite', 'webpack',
|
|
72
|
+
'jest', 'vitest', 'mocha', 'pytest',
|
|
73
|
+
'eslint', 'prettier', 'biome',
|
|
74
|
+
'cargo', 'rustc', 'go', 'make', 'cmake',
|
|
75
|
+
// System info
|
|
76
|
+
'echo', 'printf', 'date', 'time', 'whoami', 'pwd', 'env', 'printenv',
|
|
77
|
+
'uname', 'hostname', 'df', 'du', 'free', 'top', 'ps', 'pgrep',
|
|
78
|
+
'sleep', 'exit', 'true', 'false', 'test', '[', 'timeout',
|
|
79
|
+
// File management (with caution)
|
|
80
|
+
'mkdir', 'touch', 'cp', 'mv', 'ln',
|
|
81
|
+
// Archive
|
|
82
|
+
'tar', 'zip', 'unzip', 'gzip', 'gunzip',
|
|
83
|
+
// Network (controlled)
|
|
84
|
+
'curl', 'wget', 'ping', 'dig', 'nslookup', 'host',
|
|
85
|
+
// Docker (common in dev)
|
|
86
|
+
'docker', 'docker-compose', 'podman',
|
|
87
|
+
// Kubernetes
|
|
88
|
+
'kubectl', 'helm', 'k9s',
|
|
89
|
+
// Cloud CLIs
|
|
90
|
+
'aws', 'gcloud', 'az', 'vercel', 'netlify', 'fly', 'railway',
|
|
91
|
+
// Databases
|
|
92
|
+
'psql', 'mysql', 'mongosh', 'redis-cli', 'sqlite3',
|
|
93
|
+
// Misc dev
|
|
94
|
+
'openssl', 'ssh-keygen', 'base64', 'md5', 'shasum', 'sha256sum',
|
|
95
|
+
],
|
|
96
|
+
blockedPatterns: [
|
|
97
|
+
// Destructive file operations
|
|
98
|
+
/\brm\s+(-[rf]+\s+)*[\/~]/, // rm with paths
|
|
99
|
+
/\brm\s+-rf?\s/, // rm -rf or rm -r
|
|
100
|
+
/\brmdir\b/, // rmdir
|
|
101
|
+
/>\s*\/dev\/sd[a-z]/, // Write to disk devices
|
|
102
|
+
/\bmkfs\b/, // Format filesystem
|
|
103
|
+
/\bdd\s+if=/, // dd (disk destroyer)
|
|
104
|
+
// Privilege escalation
|
|
105
|
+
/\bsudo\b/, // sudo
|
|
106
|
+
/\bsu\s+-?\s*\w/, // su to another user
|
|
107
|
+
/\bchmod\s+[0-7]*777/, // chmod 777
|
|
108
|
+
/\bchown\b/, // chown
|
|
109
|
+
/\bchgrp\b/, // chgrp
|
|
110
|
+
// System modification
|
|
111
|
+
/\bsystemctl\b/, // systemctl
|
|
112
|
+
/\bservice\b/, // service
|
|
113
|
+
/\blaunchctl\b/, // macOS launchctl
|
|
114
|
+
/\bcrontab\b/, // crontab
|
|
115
|
+
/\bvisudo\b/, // visudo
|
|
116
|
+
// Network abuse
|
|
117
|
+
/\bcurl\s+.*\|\s*(ba)?sh/, // curl | sh (pipe to shell)
|
|
118
|
+
/\bwget\s+.*\|\s*(ba)?sh/, // wget | sh
|
|
119
|
+
/\bnc\s+-[el]/, // netcat listen
|
|
120
|
+
/\bnetcat\b/, // netcat
|
|
121
|
+
/\bsocat\b/, // socat
|
|
122
|
+
/\biptables\b/, // iptables
|
|
123
|
+
/\bufw\b/, // ufw
|
|
124
|
+
// Shell escape / injection
|
|
125
|
+
/;\s*sh\b/, // ; sh
|
|
126
|
+
/;\s*bash\b/, // ; bash
|
|
127
|
+
/\$\([^)]*sh\)/, // $(sh)
|
|
128
|
+
/`[^`]*sh`/, // `sh`
|
|
129
|
+
/\beval\b/, // eval
|
|
130
|
+
/\bexec\b/, // exec
|
|
131
|
+
/\bsource\b/, // source
|
|
132
|
+
/\b\.\s+\//, // . /path (source)
|
|
133
|
+
// Fork bomb / resource exhaustion
|
|
134
|
+
/:\(\)\{.*:\|:.*\}/, // :(){ :|:& };:
|
|
135
|
+
/\bfork\b/, // fork
|
|
136
|
+
/\bwhile\s+true.*done/, // while true; do; done (careful)
|
|
137
|
+
// Sensitive files
|
|
138
|
+
/\/etc\/passwd/, // passwd file
|
|
139
|
+
/\/etc\/shadow/, // shadow file
|
|
140
|
+
/\/etc\/sudoers/, // sudoers
|
|
141
|
+
/~\/\.ssh\/id_/, // SSH private keys
|
|
142
|
+
/\.env\b.*password/i, // .env passwords
|
|
143
|
+
// History manipulation
|
|
144
|
+
/\bhistory\s+-c/, // Clear history
|
|
145
|
+
/\bshred\b/, // Secure delete
|
|
146
|
+
// Kill signals
|
|
147
|
+
/\bkill\s+-9\s+1\b/, // kill -9 1 (init)
|
|
148
|
+
/\bkillall\b/, // killall
|
|
149
|
+
/\bpkill\b/, // pkill (without constraints)
|
|
150
|
+
],
|
|
151
|
+
maxTimeout: 120000, // 2 minutes
|
|
152
|
+
workingDirectory: process.cwd(),
|
|
153
|
+
allowNetwork: true,
|
|
154
|
+
allowExternalWrites: false,
|
|
155
|
+
maxOutputSize: 1024 * 1024, // 1MB
|
|
156
|
+
};
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Bash Tool Class
|
|
159
|
+
// ============================================================================
|
|
160
|
+
class BashTool extends events_1.EventEmitter {
|
|
161
|
+
config;
|
|
162
|
+
backgroundTasks = new Map();
|
|
163
|
+
taskCounter = 0;
|
|
164
|
+
constructor(config) {
|
|
165
|
+
super();
|
|
166
|
+
this.config = { ...exports.DEFAULT_SANDBOX_CONFIG, ...config };
|
|
167
|
+
}
|
|
168
|
+
// --------------------------------------------------------------------------
|
|
169
|
+
// Validation
|
|
170
|
+
// --------------------------------------------------------------------------
|
|
171
|
+
/**
|
|
172
|
+
* Validate a command against security rules
|
|
173
|
+
*/
|
|
174
|
+
validate(command) {
|
|
175
|
+
const trimmed = command.trim();
|
|
176
|
+
// Empty command
|
|
177
|
+
if (!trimmed) {
|
|
178
|
+
return { valid: false, reason: 'Empty command', severity: 'blocked' };
|
|
179
|
+
}
|
|
180
|
+
// Check blocked patterns first
|
|
181
|
+
for (const pattern of this.config.blockedPatterns) {
|
|
182
|
+
if (pattern.test(trimmed)) {
|
|
183
|
+
return {
|
|
184
|
+
valid: false,
|
|
185
|
+
reason: `Blocked pattern detected: ${pattern.toString()}`,
|
|
186
|
+
severity: 'blocked',
|
|
187
|
+
matchedPattern: pattern.toString(),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Extract base command
|
|
192
|
+
const baseCommand = this.extractBaseCommand(trimmed);
|
|
193
|
+
// Check if base command is in allowed list
|
|
194
|
+
if (!this.config.allowedCommands.includes(baseCommand)) {
|
|
195
|
+
// Check if it's a path to an allowed command
|
|
196
|
+
const basename = path.basename(baseCommand);
|
|
197
|
+
if (!this.config.allowedCommands.includes(basename)) {
|
|
198
|
+
return {
|
|
199
|
+
valid: false,
|
|
200
|
+
reason: `Command not in allowed list: ${baseCommand}`,
|
|
201
|
+
severity: 'blocked',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Check for pipe chains
|
|
206
|
+
if (trimmed.includes('|')) {
|
|
207
|
+
const pipedCommands = trimmed.split('|').map(c => this.extractBaseCommand(c.trim()));
|
|
208
|
+
for (const cmd of pipedCommands) {
|
|
209
|
+
if (cmd && !this.config.allowedCommands.includes(cmd)) {
|
|
210
|
+
const basename = path.basename(cmd);
|
|
211
|
+
if (!this.config.allowedCommands.includes(basename)) {
|
|
212
|
+
return {
|
|
213
|
+
valid: false,
|
|
214
|
+
reason: `Piped command not allowed: ${cmd}`,
|
|
215
|
+
severity: 'blocked',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Check for command chains (&&, ||, ;)
|
|
222
|
+
const chainedCommands = trimmed.split(/&&|\|\||;/).map(c => this.extractBaseCommand(c.trim()));
|
|
223
|
+
for (const cmd of chainedCommands) {
|
|
224
|
+
if (cmd && !this.config.allowedCommands.includes(cmd)) {
|
|
225
|
+
const basename = path.basename(cmd);
|
|
226
|
+
if (!this.config.allowedCommands.includes(basename)) {
|
|
227
|
+
return {
|
|
228
|
+
valid: false,
|
|
229
|
+
reason: `Chained command not allowed: ${cmd}`,
|
|
230
|
+
severity: 'blocked',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Warning for potentially dangerous but allowed
|
|
236
|
+
const warningPatterns = [
|
|
237
|
+
{ pattern: /\brm\b/, reason: 'rm command - verify target' },
|
|
238
|
+
{ pattern: />\s*[^|]/, reason: 'File redirection - verify target' },
|
|
239
|
+
{ pattern: /\bgit\s+push\s+--force/, reason: 'Force push - verify intent' },
|
|
240
|
+
{ pattern: /\bgit\s+reset\s+--hard/, reason: 'Hard reset - verify intent' },
|
|
241
|
+
];
|
|
242
|
+
for (const { pattern, reason } of warningPatterns) {
|
|
243
|
+
if (pattern.test(trimmed)) {
|
|
244
|
+
return { valid: true, reason, severity: 'warning' };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return { valid: true, severity: 'safe' };
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Extract the base command from a command string
|
|
251
|
+
*/
|
|
252
|
+
extractBaseCommand(command) {
|
|
253
|
+
// Remove leading env vars
|
|
254
|
+
const withoutEnv = command.replace(/^(\w+=\S+\s+)+/, '');
|
|
255
|
+
// Get first word
|
|
256
|
+
const parts = withoutEnv.trim().split(/\s+/);
|
|
257
|
+
return parts[0] || '';
|
|
258
|
+
}
|
|
259
|
+
// --------------------------------------------------------------------------
|
|
260
|
+
// Execution
|
|
261
|
+
// --------------------------------------------------------------------------
|
|
262
|
+
/**
|
|
263
|
+
* Execute a command with sandboxing
|
|
264
|
+
*/
|
|
265
|
+
async execute(command, options = {}) {
|
|
266
|
+
const startTime = Date.now();
|
|
267
|
+
// Validate first
|
|
268
|
+
const validation = this.validate(command);
|
|
269
|
+
if (!validation.valid) {
|
|
270
|
+
return {
|
|
271
|
+
success: false,
|
|
272
|
+
exitCode: -1,
|
|
273
|
+
stdout: '',
|
|
274
|
+
stderr: validation.reason || 'Command blocked by security policy',
|
|
275
|
+
duration: Date.now() - startTime,
|
|
276
|
+
truncated: false,
|
|
277
|
+
killed: false,
|
|
278
|
+
error: validation.reason,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
// Emit warning if applicable
|
|
282
|
+
if (validation.severity === 'warning') {
|
|
283
|
+
this.emit('warning', { command, reason: validation.reason });
|
|
284
|
+
}
|
|
285
|
+
// Determine working directory
|
|
286
|
+
const cwd = options.cwd || this.config.workingDirectory;
|
|
287
|
+
// Verify cwd exists and is within allowed scope
|
|
288
|
+
if (!fs.existsSync(cwd)) {
|
|
289
|
+
return {
|
|
290
|
+
success: false,
|
|
291
|
+
exitCode: -1,
|
|
292
|
+
stdout: '',
|
|
293
|
+
stderr: `Working directory does not exist: ${cwd}`,
|
|
294
|
+
duration: Date.now() - startTime,
|
|
295
|
+
truncated: false,
|
|
296
|
+
killed: false,
|
|
297
|
+
error: 'Invalid working directory',
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
// If background mode, handle separately
|
|
301
|
+
if (options.background) {
|
|
302
|
+
return this.executeBackground(command, options);
|
|
303
|
+
}
|
|
304
|
+
// Execute synchronously with timeout
|
|
305
|
+
return this.executeSync(command, {
|
|
306
|
+
...options,
|
|
307
|
+
cwd,
|
|
308
|
+
timeout: options.timeout || this.config.maxTimeout,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Execute command synchronously
|
|
313
|
+
*/
|
|
314
|
+
executeSync(command, options) {
|
|
315
|
+
return new Promise((resolve) => {
|
|
316
|
+
const startTime = Date.now();
|
|
317
|
+
let stdout = '';
|
|
318
|
+
let stderr = '';
|
|
319
|
+
let killed = false;
|
|
320
|
+
let truncated = false;
|
|
321
|
+
const env = {
|
|
322
|
+
...process.env,
|
|
323
|
+
...options.env,
|
|
324
|
+
// Prevent some shell shenanigans
|
|
325
|
+
SHELL: '/bin/bash',
|
|
326
|
+
TERM: 'xterm-256color',
|
|
327
|
+
};
|
|
328
|
+
const child = (0, child_process_1.spawn)('bash', ['-c', command], {
|
|
329
|
+
cwd: options.cwd,
|
|
330
|
+
env,
|
|
331
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
332
|
+
});
|
|
333
|
+
// Set timeout
|
|
334
|
+
const timeoutId = setTimeout(() => {
|
|
335
|
+
killed = true;
|
|
336
|
+
child.kill('SIGTERM');
|
|
337
|
+
setTimeout(() => child.kill('SIGKILL'), 1000);
|
|
338
|
+
}, options.timeout);
|
|
339
|
+
// Collect stdout
|
|
340
|
+
child.stdout.on('data', (data) => {
|
|
341
|
+
const chunk = data.toString();
|
|
342
|
+
if (stdout.length + chunk.length > this.config.maxOutputSize) {
|
|
343
|
+
truncated = true;
|
|
344
|
+
stdout += chunk.slice(0, this.config.maxOutputSize - stdout.length);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
stdout += chunk;
|
|
348
|
+
}
|
|
349
|
+
this.emit('stdout', chunk);
|
|
350
|
+
});
|
|
351
|
+
// Collect stderr
|
|
352
|
+
child.stderr.on('data', (data) => {
|
|
353
|
+
const chunk = data.toString();
|
|
354
|
+
if (stderr.length + chunk.length > this.config.maxOutputSize) {
|
|
355
|
+
truncated = true;
|
|
356
|
+
stderr += chunk.slice(0, this.config.maxOutputSize - stderr.length);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
stderr += chunk;
|
|
360
|
+
}
|
|
361
|
+
this.emit('stderr', chunk);
|
|
362
|
+
});
|
|
363
|
+
// Handle completion
|
|
364
|
+
child.on('close', (code) => {
|
|
365
|
+
clearTimeout(timeoutId);
|
|
366
|
+
const duration = Date.now() - startTime;
|
|
367
|
+
resolve({
|
|
368
|
+
success: code === 0 && !killed,
|
|
369
|
+
exitCode: code ?? -1,
|
|
370
|
+
stdout: stdout.trim(),
|
|
371
|
+
stderr: stderr.trim(),
|
|
372
|
+
duration,
|
|
373
|
+
truncated,
|
|
374
|
+
killed,
|
|
375
|
+
error: killed ? 'Command killed due to timeout' : undefined,
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
child.on('error', (err) => {
|
|
379
|
+
clearTimeout(timeoutId);
|
|
380
|
+
resolve({
|
|
381
|
+
success: false,
|
|
382
|
+
exitCode: -1,
|
|
383
|
+
stdout: '',
|
|
384
|
+
stderr: err.message,
|
|
385
|
+
duration: Date.now() - startTime,
|
|
386
|
+
truncated: false,
|
|
387
|
+
killed: false,
|
|
388
|
+
error: err.message,
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Execute command in background
|
|
395
|
+
*/
|
|
396
|
+
executeBackground(command, options) {
|
|
397
|
+
const taskId = `task-${++this.taskCounter}-${Date.now()}`;
|
|
398
|
+
const startTime = Date.now();
|
|
399
|
+
const cwd = options.cwd || this.config.workingDirectory;
|
|
400
|
+
const env = {
|
|
401
|
+
...process.env,
|
|
402
|
+
...options.env,
|
|
403
|
+
};
|
|
404
|
+
const child = (0, child_process_1.spawn)('bash', ['-c', command], {
|
|
405
|
+
cwd,
|
|
406
|
+
env,
|
|
407
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
408
|
+
detached: true,
|
|
409
|
+
});
|
|
410
|
+
const task = {
|
|
411
|
+
id: taskId,
|
|
412
|
+
command,
|
|
413
|
+
process: child,
|
|
414
|
+
startTime,
|
|
415
|
+
stdout: '',
|
|
416
|
+
stderr: '',
|
|
417
|
+
status: 'running',
|
|
418
|
+
};
|
|
419
|
+
this.backgroundTasks.set(taskId, task);
|
|
420
|
+
// Collect output
|
|
421
|
+
child.stdout.on('data', (data) => {
|
|
422
|
+
task.stdout += data.toString();
|
|
423
|
+
this.emit('background-stdout', { taskId, data: data.toString() });
|
|
424
|
+
});
|
|
425
|
+
child.stderr.on('data', (data) => {
|
|
426
|
+
task.stderr += data.toString();
|
|
427
|
+
this.emit('background-stderr', { taskId, data: data.toString() });
|
|
428
|
+
});
|
|
429
|
+
child.on('close', (code) => {
|
|
430
|
+
task.status = code === 0 ? 'completed' : 'failed';
|
|
431
|
+
task.exitCode = code ?? -1;
|
|
432
|
+
this.emit('background-complete', { taskId, exitCode: code });
|
|
433
|
+
});
|
|
434
|
+
child.on('error', (err) => {
|
|
435
|
+
task.status = 'failed';
|
|
436
|
+
task.stderr += err.message;
|
|
437
|
+
this.emit('background-error', { taskId, error: err.message });
|
|
438
|
+
});
|
|
439
|
+
return Promise.resolve({
|
|
440
|
+
success: true,
|
|
441
|
+
exitCode: 0,
|
|
442
|
+
stdout: `Background task started: ${taskId}`,
|
|
443
|
+
stderr: '',
|
|
444
|
+
duration: 0,
|
|
445
|
+
truncated: false,
|
|
446
|
+
killed: false,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
// --------------------------------------------------------------------------
|
|
450
|
+
// Background Task Management
|
|
451
|
+
// --------------------------------------------------------------------------
|
|
452
|
+
/**
|
|
453
|
+
* Get output from a background task
|
|
454
|
+
*/
|
|
455
|
+
getTaskOutput(taskId, block = true, timeout = 30000) {
|
|
456
|
+
const task = this.backgroundTasks.get(taskId);
|
|
457
|
+
if (!task)
|
|
458
|
+
return Promise.resolve(null);
|
|
459
|
+
if (!block || task.status !== 'running') {
|
|
460
|
+
return Promise.resolve(task);
|
|
461
|
+
}
|
|
462
|
+
// Block until complete or timeout
|
|
463
|
+
return new Promise((resolve) => {
|
|
464
|
+
const startTime = Date.now();
|
|
465
|
+
const check = () => {
|
|
466
|
+
const t = this.backgroundTasks.get(taskId);
|
|
467
|
+
if (!t || t.status !== 'running' || Date.now() - startTime > timeout) {
|
|
468
|
+
resolve(t || null);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
setTimeout(check, 100);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
check();
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Kill a background task
|
|
479
|
+
*/
|
|
480
|
+
killTask(taskId) {
|
|
481
|
+
const task = this.backgroundTasks.get(taskId);
|
|
482
|
+
if (!task || task.status !== 'running')
|
|
483
|
+
return false;
|
|
484
|
+
task.process.kill('SIGTERM');
|
|
485
|
+
setTimeout(() => {
|
|
486
|
+
if (task.status === 'running') {
|
|
487
|
+
task.process.kill('SIGKILL');
|
|
488
|
+
}
|
|
489
|
+
}, 1000);
|
|
490
|
+
task.status = 'killed';
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* List all background tasks
|
|
495
|
+
*/
|
|
496
|
+
listTasks() {
|
|
497
|
+
return Array.from(this.backgroundTasks.values());
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Clean up completed tasks
|
|
501
|
+
*/
|
|
502
|
+
cleanupTasks() {
|
|
503
|
+
let cleaned = 0;
|
|
504
|
+
for (const [id, task] of this.backgroundTasks) {
|
|
505
|
+
if (task.status !== 'running') {
|
|
506
|
+
this.backgroundTasks.delete(id);
|
|
507
|
+
cleaned++;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return cleaned;
|
|
511
|
+
}
|
|
512
|
+
// --------------------------------------------------------------------------
|
|
513
|
+
// Configuration
|
|
514
|
+
// --------------------------------------------------------------------------
|
|
515
|
+
/**
|
|
516
|
+
* Update sandbox configuration
|
|
517
|
+
*/
|
|
518
|
+
updateConfig(config) {
|
|
519
|
+
this.config = { ...this.config, ...config };
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Add command to allowed list
|
|
523
|
+
*/
|
|
524
|
+
allowCommand(command) {
|
|
525
|
+
if (!this.config.allowedCommands.includes(command)) {
|
|
526
|
+
this.config.allowedCommands.push(command);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Add pattern to blocked list
|
|
531
|
+
*/
|
|
532
|
+
blockPattern(pattern) {
|
|
533
|
+
this.config.blockedPatterns.push(pattern);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Get current configuration
|
|
537
|
+
*/
|
|
538
|
+
getConfig() {
|
|
539
|
+
return { ...this.config };
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
exports.BashTool = BashTool;
|
|
543
|
+
// ============================================================================
|
|
544
|
+
// Singleton Instance
|
|
545
|
+
// ============================================================================
|
|
546
|
+
let bashToolInstance = null;
|
|
547
|
+
function getBashTool(config) {
|
|
548
|
+
if (!bashToolInstance) {
|
|
549
|
+
bashToolInstance = new BashTool(config);
|
|
550
|
+
}
|
|
551
|
+
else if (config) {
|
|
552
|
+
bashToolInstance.updateConfig(config);
|
|
553
|
+
}
|
|
554
|
+
return bashToolInstance;
|
|
555
|
+
}
|
|
556
|
+
function resetBashTool() {
|
|
557
|
+
bashToolInstance = null;
|
|
558
|
+
}
|
|
559
|
+
// ============================================================================
|
|
560
|
+
// Convenience Functions
|
|
561
|
+
// ============================================================================
|
|
562
|
+
/**
|
|
563
|
+
* Execute a command with default settings
|
|
564
|
+
*/
|
|
565
|
+
async function bash(command, options) {
|
|
566
|
+
return getBashTool().execute(command, options);
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Validate a command without executing
|
|
570
|
+
*/
|
|
571
|
+
function validateCommand(command) {
|
|
572
|
+
return getBashTool().validate(command);
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Execute command and return stdout only (throws on error)
|
|
576
|
+
*/
|
|
577
|
+
async function exec(command, options) {
|
|
578
|
+
const result = await bash(command, options);
|
|
579
|
+
if (!result.success) {
|
|
580
|
+
throw new Error(result.stderr || result.error || 'Command failed');
|
|
581
|
+
}
|
|
582
|
+
return result.stdout;
|
|
583
|
+
}
|