nothumanallowed 14.1.75 → 14.1.76
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "14.1.
|
|
3
|
+
"version": "14.1.76",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '14.1.
|
|
8
|
+
export const VERSION = '14.1.76';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -74,10 +74,12 @@ class SandboxManager {
|
|
|
74
74
|
* @param {string} projectDir
|
|
75
75
|
* @param {(event: object) => void} emit
|
|
76
76
|
*/
|
|
77
|
-
async start(projectName, projectDir, emit) {
|
|
77
|
+
async start(projectName, projectDir, emit, _attempt = 1) {
|
|
78
|
+
const MAX_RETRIES = 3;
|
|
79
|
+
|
|
78
80
|
// Kill any existing sandbox
|
|
79
81
|
if (this.isRunning()) {
|
|
80
|
-
emit({ type: '
|
|
82
|
+
emit({ type: 'phase', phase: 'cleanup', msg: 'Stopping previous sandbox...' });
|
|
81
83
|
await this.stop();
|
|
82
84
|
}
|
|
83
85
|
|
|
@@ -86,7 +88,8 @@ class SandboxManager {
|
|
|
86
88
|
return;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
// ──
|
|
91
|
+
// ── Phase 1: Shims ────────────────────────────────────────────────────
|
|
92
|
+
emit({ type: 'phase', phase: 'shims', msg: 'Injecting runtime shims (pg, redis, mongoose, helmet...)' });
|
|
90
93
|
const shimDir = path.join(projectDir, '.nha-shims');
|
|
91
94
|
ensureDir(shimDir);
|
|
92
95
|
_writeShims(shimDir);
|
|
@@ -96,34 +99,36 @@ class SandboxManager {
|
|
|
96
99
|
emit({ type: 'error', msg: 'No entry point found (server.js / app.js / index.js).' });
|
|
97
100
|
return;
|
|
98
101
|
}
|
|
102
|
+
emit({ type: 'status', msg: `Entry point: ${entryFile}` });
|
|
99
103
|
|
|
100
|
-
// ──
|
|
104
|
+
// ── Phase 2: Dependencies ─────────────────────────────────────────────
|
|
101
105
|
if (fs.existsSync(path.join(projectDir, 'package.json'))) {
|
|
102
|
-
emit({ type: '
|
|
106
|
+
emit({ type: 'phase', phase: 'deps', msg: 'Installing dependencies...' });
|
|
103
107
|
try {
|
|
104
|
-
await execAsync('npm install --prefer-offline --no-audit --no-fund', {
|
|
108
|
+
const { stdout } = await execAsync('npm install --prefer-offline --no-audit --no-fund 2>&1', {
|
|
105
109
|
cwd: projectDir,
|
|
106
110
|
timeout: 120_000,
|
|
107
111
|
env: { ...process.env, NODE_ENV: 'development' },
|
|
108
112
|
});
|
|
109
|
-
|
|
113
|
+
const added = stdout.match(/added (\d+) package/)?.[1] || '0';
|
|
114
|
+
emit({ type: 'status', msg: `Dependencies installed (${added} packages)` });
|
|
110
115
|
} catch (e) {
|
|
111
|
-
emit({ type: 'warn', msg: `npm install warning: ${e.message.slice(0,
|
|
116
|
+
emit({ type: 'warn', msg: `npm install warning: ${e.message.slice(0, 300)}` });
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
|
|
115
|
-
// ──
|
|
120
|
+
// ── Phase 3: Start server ─────────────────────────────────────────────
|
|
116
121
|
const port = await _findFreePort(4000, 4999);
|
|
117
122
|
if (!port) {
|
|
118
123
|
emit({ type: 'error', msg: 'No free ports available in range 4000-4999.' });
|
|
119
124
|
return;
|
|
120
125
|
}
|
|
121
126
|
|
|
122
|
-
emit({ type: '
|
|
123
|
-
|
|
124
|
-
// Patch entry file to use our shims and bind to the found port
|
|
127
|
+
emit({ type: 'phase', phase: 'start', msg: `Starting server on port ${port}...` });
|
|
125
128
|
const patchedEntry = _patchEntry(projectDir, entryFile, shimDir, port);
|
|
126
129
|
|
|
130
|
+
// Capture stderr for missing module detection
|
|
131
|
+
let stderrBuf = '';
|
|
127
132
|
const proc = spawn('node', [patchedEntry], {
|
|
128
133
|
cwd: projectDir,
|
|
129
134
|
env: {
|
|
@@ -149,22 +154,62 @@ class SandboxManager {
|
|
|
149
154
|
|
|
150
155
|
proc.stderr.on('data', (d) => {
|
|
151
156
|
const line = d.toString().trim();
|
|
157
|
+
stderrBuf += d.toString();
|
|
152
158
|
if (line) emit({ type: 'log', msg: `[stderr] ${line}` });
|
|
153
159
|
});
|
|
154
160
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
// Wait for exit or healthy
|
|
162
|
+
const exitPromise = new Promise((resolve) => {
|
|
163
|
+
proc.once('exit', (code) => {
|
|
164
|
+
if (this._sandbox?.proc === proc) this._sandbox = null;
|
|
165
|
+
resolve(code ?? -1);
|
|
166
|
+
});
|
|
158
167
|
});
|
|
159
168
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
const healthy = await Promise.race([
|
|
170
|
+
_waitForPort(port, 15_000).then((h) => h ? 'healthy' : 'timeout'),
|
|
171
|
+
exitPromise.then((code) => ({ exitCode: code })),
|
|
172
|
+
]);
|
|
173
|
+
|
|
174
|
+
if (healthy === 'healthy') {
|
|
175
|
+
if (this._sandbox) this._sandbox.healthy = true;
|
|
176
|
+
emit({ type: 'phase', phase: 'ready', msg: `Server running on port ${port}` });
|
|
164
177
|
emit({ type: 'ready', port });
|
|
165
|
-
|
|
166
|
-
emit({ type: 'warn', msg: 'Sandbox started but port not yet bound — may still be loading.' });
|
|
178
|
+
return;
|
|
167
179
|
}
|
|
180
|
+
|
|
181
|
+
if (healthy === 'timeout') {
|
|
182
|
+
emit({ type: 'warn', msg: 'Server started but port not yet bound — may still be loading.' });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Crash handling — auto-fix missing modules ─────────────────────────
|
|
187
|
+
const exitCode = typeof healthy === 'object' ? healthy.exitCode : -1;
|
|
188
|
+
emit({ type: 'status', msg: `Process exited with code ${exitCode}` });
|
|
189
|
+
|
|
190
|
+
// Extract missing module name from stderr
|
|
191
|
+
const missingMatch = stderrBuf.match(/Cannot find module ['"]([^'"]+)['"]/);
|
|
192
|
+
if (missingMatch && _attempt < MAX_RETRIES) {
|
|
193
|
+
const missingMod = missingMatch[1];
|
|
194
|
+
// Skip shim-able or built-in modules
|
|
195
|
+
if (!missingMod.startsWith('.') && !missingMod.startsWith('/') && !missingMod.startsWith('node:')) {
|
|
196
|
+
const pkgName = missingMod.startsWith('@') ? missingMod.split('/').slice(0, 2).join('/') : missingMod.split('/')[0];
|
|
197
|
+
emit({ type: 'phase', phase: 'autofix', msg: `Missing module "${pkgName}" — installing...` });
|
|
198
|
+
try {
|
|
199
|
+
await execAsync(`npm install --save ${pkgName} --no-audit --no-fund`, {
|
|
200
|
+
cwd: projectDir,
|
|
201
|
+
timeout: 60_000,
|
|
202
|
+
env: { ...process.env, NODE_ENV: 'development' },
|
|
203
|
+
});
|
|
204
|
+
emit({ type: 'status', msg: `Installed ${pkgName} — retrying (attempt ${_attempt + 1}/${MAX_RETRIES})...` });
|
|
205
|
+
return this.start(projectName, projectDir, emit, _attempt + 1);
|
|
206
|
+
} catch (installErr) {
|
|
207
|
+
emit({ type: 'warn', msg: `Failed to install ${pkgName}: ${installErr.message.slice(0, 200)}` });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
emit({ type: 'error', msg: _attempt >= MAX_RETRIES ? `Failed after ${MAX_RETRIES} attempts` : 'Server crashed on startup' });
|
|
168
213
|
}
|
|
169
214
|
}
|
|
170
215
|
|