@openape/nest 1.1.0 → 1.1.2
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/dist/index.mjs +62 -11
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -239,7 +239,7 @@ import { join as join3 } from "path";
|
|
|
239
239
|
import process from "process";
|
|
240
240
|
import { promisify as promisify2 } from "util";
|
|
241
241
|
var execFileAsync2 = promisify2(execFile2);
|
|
242
|
-
var AGENTS_DIR = "/var/openape/
|
|
242
|
+
var AGENTS_DIR = "/var/openape/agents";
|
|
243
243
|
function pm2AppName(agentName) {
|
|
244
244
|
return `openape-bridge-${agentName}`;
|
|
245
245
|
}
|
|
@@ -264,6 +264,21 @@ module.exports = {
|
|
|
264
264
|
}
|
|
265
265
|
`;
|
|
266
266
|
}
|
|
267
|
+
function startScriptPath(agentName) {
|
|
268
|
+
return join3(AGENTS_DIR, agentName, "start.sh");
|
|
269
|
+
}
|
|
270
|
+
function startScriptContents(agentName) {
|
|
271
|
+
const ecosystem = ecosystemPath(agentName);
|
|
272
|
+
const log2 = `/var/log/openape/${agentName}-pm2.log`;
|
|
273
|
+
return `#!/bin/bash
|
|
274
|
+
# Auto-generated by Pm2Supervisor for agent '${agentName}'.
|
|
275
|
+
set -e
|
|
276
|
+
export HOME="/Users/${agentName}"
|
|
277
|
+
export PM2_HOME="$HOME/.pm2"
|
|
278
|
+
mkdir -p "$(dirname "${log2}")"
|
|
279
|
+
exec pm2 startOrReload ${ecosystem} >> ${log2} 2>&1 < /dev/null
|
|
280
|
+
`;
|
|
281
|
+
}
|
|
267
282
|
var Pm2Supervisor = class {
|
|
268
283
|
constructor(deps) {
|
|
269
284
|
this.deps = deps;
|
|
@@ -278,7 +293,7 @@ var Pm2Supervisor = class {
|
|
|
278
293
|
try {
|
|
279
294
|
await this.startOrReload(agent.name);
|
|
280
295
|
} catch (err) {
|
|
281
|
-
this.deps.log(`pm2-supervisor: ${agent.name}
|
|
296
|
+
this.deps.log(`pm2-supervisor: ${agent.name} reconcile errored: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`);
|
|
282
297
|
} finally {
|
|
283
298
|
this.inflight.delete(agent.name);
|
|
284
299
|
}
|
|
@@ -300,21 +315,57 @@ var Pm2Supervisor = class {
|
|
|
300
315
|
async stopAll() {
|
|
301
316
|
}
|
|
302
317
|
async startOrReload(agentName) {
|
|
318
|
+
mkdirSync3(AGENTS_DIR, { recursive: true, mode: 493 });
|
|
303
319
|
const dir = join3(AGENTS_DIR, agentName);
|
|
304
|
-
mkdirSync3(dir, { recursive: true });
|
|
320
|
+
mkdirSync3(dir, { recursive: true, mode: 493 });
|
|
305
321
|
const path = ecosystemPath(agentName);
|
|
306
322
|
writeFileSync3(path, ecosystemContents(this.deps.apesBin, agentName), { mode: 420 });
|
|
307
|
-
|
|
308
|
-
|
|
323
|
+
const startPath = startScriptPath(agentName);
|
|
324
|
+
writeFileSync3(startPath, startScriptContents(agentName), { mode: 493 });
|
|
325
|
+
void path;
|
|
326
|
+
try {
|
|
327
|
+
await this.runAsAgent(agentName, ["bash", startPath]);
|
|
328
|
+
} catch {
|
|
329
|
+
}
|
|
330
|
+
let online = false;
|
|
331
|
+
try {
|
|
332
|
+
const { stdout } = await this.runAsAgent(agentName, ["pm2", "jlist"]);
|
|
333
|
+
const json = stdout.match(/\[\s*\{.*\}\s*\]/s)?.[0];
|
|
334
|
+
if (json) {
|
|
335
|
+
const list = JSON.parse(json);
|
|
336
|
+
online = list.some((p) => p.name === pm2AppName(agentName) && p.pm2_env?.status === "online");
|
|
337
|
+
}
|
|
338
|
+
} catch {
|
|
339
|
+
}
|
|
340
|
+
if (online) {
|
|
341
|
+
this.deps.log(`pm2-supervisor: ${agentName} bridge online (pm2)`);
|
|
342
|
+
} else {
|
|
343
|
+
this.deps.log(`pm2-supervisor: ${agentName} bridge NOT online \u2014 see /var/log/openape/${agentName}-pm2.log`);
|
|
344
|
+
}
|
|
309
345
|
}
|
|
310
346
|
/** Run a pm2 subcommand AS the agent — escapes-helper does the
|
|
311
|
-
* setuid switch, then exec's pm2 in the agent's uid.
|
|
347
|
+
* setuid switch, then exec's pm2 in the agent's uid.
|
|
348
|
+
*
|
|
349
|
+
* cwd: the agent process inherits cwd from the spawning Nest
|
|
350
|
+
* daemon, whose cwd is /var/openape/nest (mode 750, no access for
|
|
351
|
+
* other uids). Without setting cwd to a world-readable dir, the
|
|
352
|
+
* child's first `process.cwd()` call (which Node does internally
|
|
353
|
+
* during module loading) throws EACCES. /tmp is the most portable
|
|
354
|
+
* always-writable location.
|
|
355
|
+
*/
|
|
312
356
|
async runAsAgent(agentName, args) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
357
|
+
try {
|
|
358
|
+
return await execFileAsync2(
|
|
359
|
+
this.deps.apesBin,
|
|
360
|
+
["run", "--as", agentName, "--wait", "--", ...args],
|
|
361
|
+
{ maxBuffer: 1024 * 1024, env: process.env, timeout: 6e4, cwd: "/tmp" }
|
|
362
|
+
);
|
|
363
|
+
} catch (err) {
|
|
364
|
+
const e = err;
|
|
365
|
+
const detail = (e.stderr ?? "").trim().split("\n").slice(-3).join(" / ");
|
|
366
|
+
const stdoutDetail = (e.stdout ?? "").trim().split("\n").slice(-2).join(" / ");
|
|
367
|
+
throw new Error(`${e.message?.split("\n")[0] ?? "execFile failed"} | stderr: ${detail || "<empty>"} | stdout: ${stdoutDetail || "<empty>"}`);
|
|
368
|
+
}
|
|
318
369
|
}
|
|
319
370
|
};
|
|
320
371
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openape/nest",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "OpenApe Nest — local control-plane daemon that supervises agent processes on this computer. Talks to troop SP for ownership state, spawns/destroys agents via DDISA always-grants, supervises chat-bridge children (replacing per-agent launchd plists).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|