@openape/nest 1.1.1 → 2.0.0
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 +101 -243
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
+
import { watch } from "fs";
|
|
4
5
|
import process3 from "process";
|
|
5
6
|
|
|
6
|
-
// src/lib/intent-channel.ts
|
|
7
|
-
import { readdirSync, readFileSync as readFileSync2, renameSync, statSync, unlinkSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, chmodSync } from "fs";
|
|
8
|
-
import { homedir as homedir2 } from "os";
|
|
9
|
-
import { join as join2 } from "path";
|
|
10
|
-
|
|
11
|
-
// src/api/agents.ts
|
|
12
|
-
import { execFile } from "child_process";
|
|
13
|
-
import { promisify } from "util";
|
|
14
|
-
|
|
15
7
|
// src/lib/registry.ts
|
|
16
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
17
9
|
import { homedir } from "os";
|
|
@@ -31,220 +23,23 @@ function readRegistry() {
|
|
|
31
23
|
return emptyRegistry();
|
|
32
24
|
}
|
|
33
25
|
}
|
|
34
|
-
function writeRegistry(reg) {
|
|
35
|
-
mkdirSync(REGISTRY_DIR, { recursive: true });
|
|
36
|
-
writeFileSync(REGISTRY_PATH, `${JSON.stringify(reg, null, 2)}
|
|
37
|
-
`, { mode: 384 });
|
|
38
|
-
}
|
|
39
26
|
function listAgents() {
|
|
40
27
|
return readRegistry().agents;
|
|
41
28
|
}
|
|
42
|
-
function findAgent(name) {
|
|
43
|
-
return readRegistry().agents.find((a) => a.name === name);
|
|
44
|
-
}
|
|
45
|
-
function upsertAgent(entry) {
|
|
46
|
-
const reg = readRegistry();
|
|
47
|
-
const existing = reg.agents.findIndex((a) => a.name === entry.name);
|
|
48
|
-
if (existing >= 0) reg.agents[existing] = entry;
|
|
49
|
-
else reg.agents.push(entry);
|
|
50
|
-
writeRegistry(reg);
|
|
51
|
-
}
|
|
52
|
-
function removeAgent(name) {
|
|
53
|
-
const reg = readRegistry();
|
|
54
|
-
const before = reg.agents.length;
|
|
55
|
-
reg.agents = reg.agents.filter((a) => a.name !== name);
|
|
56
|
-
if (reg.agents.length === before) return false;
|
|
57
|
-
writeRegistry(reg);
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// src/api/agents.ts
|
|
62
|
-
var execFileAsync = promisify(execFile);
|
|
63
|
-
var NAME_REGEX = /^[a-z][a-z0-9-]{0,23}$/;
|
|
64
|
-
async function handleAgentSpawn(ctx) {
|
|
65
|
-
const body = ctx.body;
|
|
66
|
-
const name = typeof body?.name === "string" ? body.name : "";
|
|
67
|
-
if (!NAME_REGEX.test(name)) {
|
|
68
|
-
throw new Error(`name must match ${NAME_REGEX} (got "${name}")`);
|
|
69
|
-
}
|
|
70
|
-
if (findAgent(name)) {
|
|
71
|
-
throw new Error(`agent "${name}" is already registered with this nest`);
|
|
72
|
-
}
|
|
73
|
-
const args = ["run", "--as", "root", "--wait", "--", "apes", "agents", "spawn", name];
|
|
74
|
-
const includeBridge = body?.bridge !== false;
|
|
75
|
-
if (includeBridge) {
|
|
76
|
-
args.push("--bridge");
|
|
77
|
-
if (typeof body?.bridgeKey === "string") args.push("--bridge-key", body.bridgeKey);
|
|
78
|
-
if (typeof body?.bridgeBaseUrl === "string") args.push("--bridge-base-url", body.bridgeBaseUrl);
|
|
79
|
-
if (typeof body?.bridgeModel === "string") args.push("--bridge-model", body.bridgeModel);
|
|
80
|
-
}
|
|
81
|
-
ctx.log(`nest: spawning agent "${name}" via apes...`);
|
|
82
|
-
const { stdout: _stdout } = await execFileAsync(ctx.apesBin, args, { maxBuffer: 4 * 1024 * 1024 });
|
|
83
|
-
const uid = await readUidFromDscl(name);
|
|
84
|
-
const entry = {
|
|
85
|
-
name,
|
|
86
|
-
uid,
|
|
87
|
-
home: `/Users/${name}`,
|
|
88
|
-
email: "",
|
|
89
|
-
// filled in on first sync — we don't know it locally
|
|
90
|
-
registeredAt: Math.floor(Date.now() / 1e3),
|
|
91
|
-
bridge: includeBridge ? {
|
|
92
|
-
baseUrl: typeof body?.bridgeBaseUrl === "string" ? body.bridgeBaseUrl : void 0,
|
|
93
|
-
apiKey: typeof body?.bridgeKey === "string" ? body.bridgeKey : void 0,
|
|
94
|
-
model: typeof body?.bridgeModel === "string" ? body.bridgeModel : void 0
|
|
95
|
-
} : void 0
|
|
96
|
-
};
|
|
97
|
-
upsertAgent(entry);
|
|
98
|
-
await ctx.supervisor.reconcile(listAgents());
|
|
99
|
-
return { name, email: entry.email, uid, home: entry.home };
|
|
100
|
-
}
|
|
101
|
-
async function handleAgentDestroy(ctx, name) {
|
|
102
|
-
if (!NAME_REGEX.test(name)) throw new Error(`invalid agent name "${name}"`);
|
|
103
|
-
const entry = findAgent(name);
|
|
104
|
-
if (!entry) throw new Error(`agent "${name}" not registered with this nest`);
|
|
105
|
-
ctx.log(`nest: destroying agent "${name}"...`);
|
|
106
|
-
const args = ["run", "--as", "root", "--", "apes", "agents", "destroy", name, "--force"];
|
|
107
|
-
await execFileAsync(ctx.apesBin, args, { maxBuffer: 4 * 1024 * 1024 });
|
|
108
|
-
removeAgent(name);
|
|
109
|
-
await ctx.supervisor.reconcile(listAgents());
|
|
110
|
-
return { name, removed: true };
|
|
111
|
-
}
|
|
112
|
-
async function readUidFromDscl(name) {
|
|
113
|
-
try {
|
|
114
|
-
const { stdout } = await execFileAsync("/usr/bin/dscl", [".", "-read", `/Users/${name}`, "UniqueID"]);
|
|
115
|
-
const match = stdout.match(/UniqueID:\s*(\d+)/);
|
|
116
|
-
if (match) return Number(match[1]);
|
|
117
|
-
} catch {
|
|
118
|
-
}
|
|
119
|
-
return -1;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// src/lib/intent-channel.ts
|
|
123
|
-
var POLL_MS = 1e3;
|
|
124
|
-
var INTENTS_DIR = join2(homedir2(), "intents");
|
|
125
|
-
var IntentChannel = class {
|
|
126
|
-
constructor(deps) {
|
|
127
|
-
this.deps = deps;
|
|
128
|
-
mkdirSync2(INTENTS_DIR, { recursive: true });
|
|
129
|
-
chmodSync(INTENTS_DIR, 504);
|
|
130
|
-
}
|
|
131
|
-
timer;
|
|
132
|
-
inflight = /* @__PURE__ */ new Set();
|
|
133
|
-
start() {
|
|
134
|
-
if (this.timer) return;
|
|
135
|
-
this.timer = setInterval(() => void this.tick(), POLL_MS);
|
|
136
|
-
this.deps.log(`intent-channel: polling ${INTENTS_DIR}`);
|
|
137
|
-
}
|
|
138
|
-
stop() {
|
|
139
|
-
if (this.timer) clearInterval(this.timer);
|
|
140
|
-
this.timer = void 0;
|
|
141
|
-
}
|
|
142
|
-
async tick() {
|
|
143
|
-
let entries;
|
|
144
|
-
try {
|
|
145
|
-
entries = readdirSync(INTENTS_DIR);
|
|
146
|
-
} catch {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
for (const f of entries) {
|
|
150
|
-
if (!f.endsWith(".json")) continue;
|
|
151
|
-
if (this.inflight.has(f)) continue;
|
|
152
|
-
this.inflight.add(f);
|
|
153
|
-
void this.process(f).finally(() => this.inflight.delete(f));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
async process(filename) {
|
|
157
|
-
const path = join2(INTENTS_DIR, filename);
|
|
158
|
-
let intent;
|
|
159
|
-
try {
|
|
160
|
-
const raw = readFileSync2(path, "utf8");
|
|
161
|
-
intent = JSON.parse(raw);
|
|
162
|
-
} catch (err) {
|
|
163
|
-
this.deps.log(`intent-channel: failed to read ${filename}: ${err instanceof Error ? err.message : String(err)}`);
|
|
164
|
-
try {
|
|
165
|
-
unlinkSync(path);
|
|
166
|
-
} catch {
|
|
167
|
-
}
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
this.deps.log(`intent-channel: processing ${intent.action} (id=${intent.id})`);
|
|
171
|
-
let response;
|
|
172
|
-
try {
|
|
173
|
-
const ctx = {
|
|
174
|
-
url: new URL("intent:/"),
|
|
175
|
-
body: intent,
|
|
176
|
-
log: this.deps.log,
|
|
177
|
-
apesBin: this.deps.apesBin,
|
|
178
|
-
caller: "<intent-channel>",
|
|
179
|
-
grantId: intent.id,
|
|
180
|
-
supervisor: this.deps.supervisor
|
|
181
|
-
};
|
|
182
|
-
let result;
|
|
183
|
-
switch (intent.action) {
|
|
184
|
-
case "spawn":
|
|
185
|
-
result = await handleAgentSpawn(ctx);
|
|
186
|
-
break;
|
|
187
|
-
case "destroy":
|
|
188
|
-
result = await handleAgentDestroy(ctx, intent.name);
|
|
189
|
-
break;
|
|
190
|
-
case "list":
|
|
191
|
-
result = { agents: listAgents() };
|
|
192
|
-
break;
|
|
193
|
-
default:
|
|
194
|
-
throw new Error(`unknown action: ${intent.action ?? "<undefined>"}`);
|
|
195
|
-
}
|
|
196
|
-
response = { ok: true, result };
|
|
197
|
-
} catch (err) {
|
|
198
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
199
|
-
this.deps.log(`intent-channel: ${intent.action} failed: ${msg}`);
|
|
200
|
-
response = { ok: false, error: msg };
|
|
201
|
-
}
|
|
202
|
-
const respTmp = `${path.replace(/\.json$/, "")}.response.tmp`;
|
|
203
|
-
const respFinal = `${path.replace(/\.json$/, "")}.response`;
|
|
204
|
-
writeFileSync2(respTmp, `${JSON.stringify(response)}
|
|
205
|
-
`, { mode: 432 });
|
|
206
|
-
renameSync(respTmp, respFinal);
|
|
207
|
-
try {
|
|
208
|
-
unlinkSync(path);
|
|
209
|
-
} catch {
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
function reapStaleResponses(log2) {
|
|
214
|
-
let entries;
|
|
215
|
-
try {
|
|
216
|
-
entries = readdirSync(INTENTS_DIR);
|
|
217
|
-
} catch {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
const now = Date.now();
|
|
221
|
-
for (const f of entries) {
|
|
222
|
-
if (!f.endsWith(".response")) continue;
|
|
223
|
-
const path = join2(INTENTS_DIR, f);
|
|
224
|
-
try {
|
|
225
|
-
const st = statSync(path);
|
|
226
|
-
if (now - st.mtimeMs > 60 * 60 * 1e3) {
|
|
227
|
-
unlinkSync(path);
|
|
228
|
-
log2(`intent-channel: reaped stale ${f}`);
|
|
229
|
-
}
|
|
230
|
-
} catch {
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
29
|
|
|
235
30
|
// src/lib/pm2-supervisor.ts
|
|
236
|
-
import { execFile
|
|
237
|
-
import { mkdirSync as
|
|
238
|
-
import { join as
|
|
31
|
+
import { execFile } from "child_process";
|
|
32
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
33
|
+
import { join as join2 } from "path";
|
|
239
34
|
import process from "process";
|
|
240
|
-
import { promisify
|
|
241
|
-
var
|
|
35
|
+
import { promisify } from "util";
|
|
36
|
+
var execFileAsync = promisify(execFile);
|
|
242
37
|
var AGENTS_DIR = "/var/openape/agents";
|
|
243
38
|
function pm2AppName(agentName) {
|
|
244
39
|
return `openape-bridge-${agentName}`;
|
|
245
40
|
}
|
|
246
41
|
function ecosystemPath(agentName) {
|
|
247
|
-
return
|
|
42
|
+
return join2(AGENTS_DIR, agentName, "ecosystem.config.js");
|
|
248
43
|
}
|
|
249
44
|
function ecosystemContents(apesBin, agentName) {
|
|
250
45
|
void apesBin;
|
|
@@ -264,6 +59,21 @@ module.exports = {
|
|
|
264
59
|
}
|
|
265
60
|
`;
|
|
266
61
|
}
|
|
62
|
+
function startScriptPath(agentName) {
|
|
63
|
+
return join2(AGENTS_DIR, agentName, "start.sh");
|
|
64
|
+
}
|
|
65
|
+
function startScriptContents(agentName) {
|
|
66
|
+
const ecosystem = ecosystemPath(agentName);
|
|
67
|
+
const log2 = `/var/log/openape/${agentName}-pm2.log`;
|
|
68
|
+
return `#!/bin/bash
|
|
69
|
+
# Auto-generated by Pm2Supervisor for agent '${agentName}'.
|
|
70
|
+
set -e
|
|
71
|
+
export HOME="/Users/${agentName}"
|
|
72
|
+
export PM2_HOME="$HOME/.pm2"
|
|
73
|
+
mkdir -p "$(dirname "${log2}")"
|
|
74
|
+
exec pm2 startOrReload ${ecosystem} >> ${log2} 2>&1 < /dev/null
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
267
77
|
var Pm2Supervisor = class {
|
|
268
78
|
constructor(deps) {
|
|
269
79
|
this.deps = deps;
|
|
@@ -278,7 +88,7 @@ var Pm2Supervisor = class {
|
|
|
278
88
|
try {
|
|
279
89
|
await this.startOrReload(agent.name);
|
|
280
90
|
} catch (err) {
|
|
281
|
-
this.deps.log(`pm2-supervisor: ${agent.name}
|
|
91
|
+
this.deps.log(`pm2-supervisor: ${agent.name} reconcile errored: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`);
|
|
282
92
|
} finally {
|
|
283
93
|
this.inflight.delete(agent.name);
|
|
284
94
|
}
|
|
@@ -300,30 +110,65 @@ var Pm2Supervisor = class {
|
|
|
300
110
|
async stopAll() {
|
|
301
111
|
}
|
|
302
112
|
async startOrReload(agentName) {
|
|
303
|
-
|
|
304
|
-
const dir =
|
|
305
|
-
|
|
113
|
+
mkdirSync2(AGENTS_DIR, { recursive: true, mode: 493 });
|
|
114
|
+
const dir = join2(AGENTS_DIR, agentName);
|
|
115
|
+
mkdirSync2(dir, { recursive: true, mode: 493 });
|
|
306
116
|
const path = ecosystemPath(agentName);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
117
|
+
writeFileSync2(path, ecosystemContents(this.deps.apesBin, agentName), { mode: 420 });
|
|
118
|
+
const startPath = startScriptPath(agentName);
|
|
119
|
+
writeFileSync2(startPath, startScriptContents(agentName), { mode: 493 });
|
|
120
|
+
void path;
|
|
121
|
+
try {
|
|
122
|
+
await this.runAsAgent(agentName, ["bash", startPath]);
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
let online = false;
|
|
126
|
+
try {
|
|
127
|
+
const { stdout } = await this.runAsAgent(agentName, ["pm2", "jlist"]);
|
|
128
|
+
const json = stdout.match(/\[\s*\{.*\}\s*\]/s)?.[0];
|
|
129
|
+
if (json) {
|
|
130
|
+
const list = JSON.parse(json);
|
|
131
|
+
online = list.some((p) => p.name === pm2AppName(agentName) && p.pm2_env?.status === "online");
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
if (online) {
|
|
136
|
+
this.deps.log(`pm2-supervisor: ${agentName} bridge online (pm2)`);
|
|
137
|
+
} else {
|
|
138
|
+
this.deps.log(`pm2-supervisor: ${agentName} bridge NOT online \u2014 see /var/log/openape/${agentName}-pm2.log`);
|
|
139
|
+
}
|
|
310
140
|
}
|
|
311
141
|
/** Run a pm2 subcommand AS the agent — escapes-helper does the
|
|
312
|
-
* setuid switch, then exec's pm2 in the agent's uid.
|
|
142
|
+
* setuid switch, then exec's pm2 in the agent's uid.
|
|
143
|
+
*
|
|
144
|
+
* cwd: the agent process inherits cwd from the spawning Nest
|
|
145
|
+
* daemon, whose cwd is /var/openape/nest (mode 750, no access for
|
|
146
|
+
* other uids). Without setting cwd to a world-readable dir, the
|
|
147
|
+
* child's first `process.cwd()` call (which Node does internally
|
|
148
|
+
* during module loading) throws EACCES. /tmp is the most portable
|
|
149
|
+
* always-writable location.
|
|
150
|
+
*/
|
|
313
151
|
async runAsAgent(agentName, args) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
152
|
+
try {
|
|
153
|
+
return await execFileAsync(
|
|
154
|
+
this.deps.apesBin,
|
|
155
|
+
["run", "--as", agentName, "--wait", "--", ...args],
|
|
156
|
+
{ maxBuffer: 1024 * 1024, env: process.env, timeout: 6e4, cwd: "/tmp" }
|
|
157
|
+
);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
const e = err;
|
|
160
|
+
const detail = (e.stderr ?? "").trim().split("\n").slice(-3).join(" / ");
|
|
161
|
+
const stdoutDetail = (e.stdout ?? "").trim().split("\n").slice(-2).join(" / ");
|
|
162
|
+
throw new Error(`${e.message?.split("\n")[0] ?? "execFile failed"} | stderr: ${detail || "<empty>"} | stdout: ${stdoutDetail || "<empty>"}`);
|
|
163
|
+
}
|
|
319
164
|
}
|
|
320
165
|
};
|
|
321
166
|
|
|
322
167
|
// src/lib/troop-sync.ts
|
|
323
|
-
import { execFile as
|
|
168
|
+
import { execFile as execFile2 } from "child_process";
|
|
324
169
|
import process2 from "process";
|
|
325
|
-
import { promisify as
|
|
326
|
-
var
|
|
170
|
+
import { promisify as promisify2 } from "util";
|
|
171
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
327
172
|
var TICK_MS = 5 * 60 * 1e3;
|
|
328
173
|
var TroopSync = class {
|
|
329
174
|
constructor(deps) {
|
|
@@ -357,7 +202,7 @@ var TroopSync = class {
|
|
|
357
202
|
}
|
|
358
203
|
async syncOne(name) {
|
|
359
204
|
try {
|
|
360
|
-
await
|
|
205
|
+
await execFileAsync2(
|
|
361
206
|
this.deps.apesBin,
|
|
362
207
|
["run", "--as", name, "--wait", "--", "apes", "agents", "sync"],
|
|
363
208
|
{ maxBuffer: 1024 * 1024, env: process2.env, timeout: 6e4 }
|
|
@@ -370,34 +215,47 @@ var TroopSync = class {
|
|
|
370
215
|
|
|
371
216
|
// src/index.ts
|
|
372
217
|
var APES_BIN = process3.env.OPENAPE_APES_BIN ?? "apes";
|
|
218
|
+
var RECONCILE_DEBOUNCE_MS = 1e3;
|
|
373
219
|
function log(line) {
|
|
374
220
|
process3.stderr.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
375
221
|
`);
|
|
376
222
|
}
|
|
377
223
|
var supervisor = new Pm2Supervisor({ apesBin: APES_BIN, log });
|
|
378
224
|
var troopSync = new TroopSync({ apesBin: APES_BIN, log });
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
});
|
|
225
|
+
async function reconcile() {
|
|
226
|
+
try {
|
|
227
|
+
await supervisor.reconcile(listAgents());
|
|
228
|
+
log("nest: pm2-supervisor reconciled with registry");
|
|
229
|
+
} catch (err) {
|
|
230
|
+
log(`nest: reconcile failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
void reconcile();
|
|
385
234
|
troopSync.start();
|
|
386
|
-
|
|
387
|
-
|
|
235
|
+
var reconcileTimer;
|
|
236
|
+
try {
|
|
237
|
+
watch(REGISTRY_PATH, () => {
|
|
238
|
+
if (reconcileTimer) clearTimeout(reconcileTimer);
|
|
239
|
+
reconcileTimer = setTimeout(() => {
|
|
240
|
+
void reconcile();
|
|
241
|
+
}, RECONCILE_DEBOUNCE_MS);
|
|
242
|
+
});
|
|
243
|
+
log(`nest: watching ${REGISTRY_PATH} for registry changes`);
|
|
244
|
+
} catch (err) {
|
|
245
|
+
log(`nest: registry watch failed (${err instanceof Error ? err.message : String(err)}) \u2014 falling back to 5s poll`);
|
|
246
|
+
setInterval(() => {
|
|
247
|
+
void reconcile();
|
|
248
|
+
}, 5e3).unref();
|
|
249
|
+
}
|
|
388
250
|
process3.on("SIGTERM", () => {
|
|
389
251
|
log("nest: SIGTERM \u2014 stopping");
|
|
390
|
-
void supervisor.stopAll();
|
|
391
252
|
troopSync.stop();
|
|
392
|
-
|
|
393
|
-
clearInterval(reaperTimer);
|
|
253
|
+
if (reconcileTimer) clearTimeout(reconcileTimer);
|
|
394
254
|
process3.exit(0);
|
|
395
255
|
});
|
|
396
256
|
process3.on("SIGINT", () => {
|
|
397
257
|
log("nest: SIGINT \u2014 stopping");
|
|
398
|
-
void supervisor.stopAll();
|
|
399
258
|
troopSync.stop();
|
|
400
|
-
|
|
401
|
-
clearInterval(reaperTimer);
|
|
259
|
+
if (reconcileTimer) clearTimeout(reconcileTimer);
|
|
402
260
|
process3.exit(0);
|
|
403
261
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openape/nest",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
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",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"ofetch": "^1.4.1",
|
|
20
|
-
"@openape/
|
|
21
|
-
"@openape/
|
|
20
|
+
"@openape/cli-auth": "0.4.0",
|
|
21
|
+
"@openape/core": "0.16.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@antfu/eslint-config": "^7.6.1",
|