@saadnvd1/agent-os 0.3.694 → 0.3.698

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.
Files changed (38) hide show
  1. package/app/api/peer/ping/route.ts +10 -0
  2. package/components/Settings/NoApprovalsSection.tsx +1 -1
  3. package/lib/__fixtures__/connect-transport.ts +93 -0
  4. package/lib/__fixtures__/peer-machine.ts +45 -0
  5. package/lib/__fixtures__/peer-rpc.ts +224 -0
  6. package/lib/approvals/enable.ts +1 -21
  7. package/lib/approvals/no-approvals.ts +1 -1
  8. package/lib/db/migrations/20261020T1258_peer_links.ts +49 -0
  9. package/lib/db/migrations/20261020T1345_peer_nonces.ts +18 -0
  10. package/lib/db/types.ts +2 -0
  11. package/lib/hosts/index.ts +28 -7
  12. package/lib/hosts/peer-socket.ts +3 -1
  13. package/lib/hosts/read-capped-bytes.ts +23 -0
  14. package/lib/hosts/remote-api.ts +112 -41
  15. package/lib/models/plan-step.ts +5 -13
  16. package/lib/peer/allowlist.ts +136 -0
  17. package/lib/peer/audit.ts +144 -0
  18. package/lib/peer/client.ts +188 -0
  19. package/lib/peer/envelope.ts +424 -0
  20. package/lib/peer/internal.ts +22 -0
  21. package/lib/peer/keys.ts +122 -0
  22. package/lib/peer/links.ts +96 -0
  23. package/lib/peer/nonce-store.ts +23 -0
  24. package/lib/peer/replay.ts +81 -0
  25. package/lib/peer/rpc-intake.ts +229 -0
  26. package/lib/peer/rpc-server.ts +351 -0
  27. package/lib/security/auth.ts +42 -5
  28. package/lib/security/gate.ts +35 -12
  29. package/lib/security/route-guard.ts +3 -1
  30. package/lib/session-artifacts/peer.ts +12 -1
  31. package/lib/tasks/contract/authorize.ts +4 -0
  32. package/lib/tasks/contract/plan.ts +1 -14
  33. package/lib/worktree-sweep/testTemp.ts +3 -0
  34. package/package.json +1 -1
  35. package/scripts/install-deps +6 -1
  36. package/scripts/lib/commands.sh +1 -1
  37. package/scripts/lib/wsl.test.ts +51 -1
  38. package/server.ts +18 -0
@@ -33,6 +33,12 @@ const TOOLS = [
33
33
  "id",
34
34
  ];
35
35
 
36
+ // Bash 4+ (Linux, CI) fails `set -e` on `((n++))` at 0; macOS's 3.2 doesn't.
37
+ const BASH =
38
+ ["/opt/homebrew/bin/bash", "/usr/local/bin/bash", "/bin/bash"].find(
39
+ existsSync
40
+ ) ?? "/bin/bash";
41
+
36
42
  let dir: string;
37
43
 
38
44
  function fake(name: string, body: string) {
@@ -48,7 +54,7 @@ function run(
48
54
  ) {
49
55
  writeFileSync(join(dir, "osrelease"), osrelease);
50
56
  return spawnSync(
51
- "/bin/bash",
57
+ BASH,
52
58
  [
53
59
  "-euo",
54
60
  "pipefail",
@@ -313,3 +319,47 @@ describe("installing prerequisites", () => {
313
319
  expect(r.stdout).not.toContain("Prerequisites installed");
314
320
  });
315
321
  });
322
+
323
+ describe("cmd_stop", () => {
324
+ // `ps` reports the server alive for the first `alive` checks, then gone.
325
+ // A server that takes a moment to exit used to trip `((count++))` (status 1
326
+ // at 0) under `set -e`, so stop exited 1 after the server had stopped.
327
+ const stop = (alive: number) => {
328
+ mkdirSync(join(dir, "home"), { recursive: true });
329
+ fake(
330
+ "ps",
331
+ `n=$(cat "${dir}/ps-count" 2>/dev/null || echo 0); echo $((n + 1)) > "${dir}/ps-count"; [ "$n" -lt ${alive} ]`
332
+ );
333
+ fake("sleep", "exit 0");
334
+ return run(
335
+ `AGENT_OS_HOME="${dir}/home"; PID_FILE="$AGENT_OS_HOME/agent-os.pid"; echo $$ > "$PID_FILE"; kill() { return 0; }; cmd_stop`,
336
+ ""
337
+ );
338
+ };
339
+
340
+ it("returns 0 when the server exits after a few checks", () => {
341
+ const r = stop(3);
342
+ expect(r.status).toBe(0);
343
+ expect(r.stdout).toContain("AgentOS stopped");
344
+ expect(existsSync(join(dir, "home", "agent-os.pid"))).toBe(false);
345
+ });
346
+
347
+ it("force kills and still returns 0 when it outlives the timeout", () => {
348
+ const r = stop(1000);
349
+ expect(r.status).toBe(0);
350
+ expect(r.stdout).toContain("Force killing");
351
+ });
352
+
353
+ it("has no bare ((x++)) in scripts/lib: it returns 1 at 0 under set -e", () => {
354
+ for (const f of [
355
+ "commands.sh",
356
+ "common.sh",
357
+ "machine.sh",
358
+ "prerequisites.sh",
359
+ ]) {
360
+ expect(readFileSync(join(LIB, f), "utf8"), f).not.toMatch(
361
+ /^\s*\(\(\s*\w+(\+\+|--)\s*\)\)\s*$/m
362
+ );
363
+ }
364
+ });
365
+ });
package/server.ts CHANGED
@@ -47,6 +47,7 @@ import {
47
47
  refuseNextInternal,
48
48
  } from "./lib/security/next-internals";
49
49
  import { lanEnabled } from "./lib/security/network-settings";
50
+ import { handlePeerRpc, isPeerRpc, rpcDeps } from "./lib/peer/rpc-server";
50
51
  import { startConnect } from "./lib/connect/serve";
51
52
  import { serverPort } from "./lib/connect/config";
52
53
  import { startUpdateChecks, updateChecksEnabled } from "./lib/updates/check";
@@ -150,6 +151,7 @@ app.prepare().then(async () => {
150
151
  .map((h) => h.trim().toLowerCase())
151
152
  .filter(Boolean);
152
153
  const auth = authPolicy();
154
+ const peerRpc = rpcDeps(port);
153
155
  const header = (v: string | string[] | undefined) =>
154
156
  Array.isArray(v) ? v[0] : v;
155
157
 
@@ -177,6 +179,22 @@ app.prepare().then(async () => {
177
179
  if (refuseNextInternal(req, res)) return;
178
180
  if (!gateRequest(req, res, auth)) return;
179
181
  if (demo && !gateDemoRequest(req, res)) return;
182
+ // A linked machine's envelope: verified, then re-issued to loopback.
183
+ if (isPeerRpc(req)) {
184
+ if (demo) {
185
+ res.statusCode = 404;
186
+ res.end();
187
+ return;
188
+ }
189
+ try {
190
+ await handlePeerRpc(req, res, peerRpc);
191
+ } catch (err) {
192
+ console.error("peer rpc failed:", err);
193
+ if (!res.headersSent) res.statusCode = 500;
194
+ res.end();
195
+ }
196
+ return;
197
+ }
180
198
  try {
181
199
  await compressJson(req, res);
182
200
  const parsedUrl = parse(req.url!, true);