@aria-cli/tools 1.0.9 → 1.0.11
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 +9 -5
- package/src/__tests__/web-fetch-download.test.ts +0 -433
- package/src/__tests__/web-tools.test.ts +0 -619
- package/src/ask-user-interaction.ts +0 -33
- package/src/cache/web-cache.ts +0 -110
- package/src/definitions/arion.ts +0 -118
- package/src/definitions/browser/browser.ts +0 -502
- package/src/definitions/browser/index.ts +0 -5
- package/src/definitions/browser/pw-downloads.ts +0 -142
- package/src/definitions/browser/pw-interactions.ts +0 -282
- package/src/definitions/browser/pw-responses.ts +0 -98
- package/src/definitions/browser/pw-session.ts +0 -405
- package/src/definitions/browser/pw-shared.ts +0 -85
- package/src/definitions/browser/pw-snapshot.ts +0 -383
- package/src/definitions/browser/pw-state.ts +0 -101
- package/src/definitions/browser/types.ts +0 -203
- package/src/definitions/code-intelligence.ts +0 -526
- package/src/definitions/core.ts +0 -118
- package/src/definitions/delegation.ts +0 -567
- package/src/definitions/deploy.ts +0 -73
- package/src/definitions/filesystem.ts +0 -217
- package/src/definitions/frg.ts +0 -67
- package/src/definitions/index.ts +0 -28
- package/src/definitions/memory.ts +0 -150
- package/src/definitions/messaging.ts +0 -734
- package/src/definitions/meta.ts +0 -392
- package/src/definitions/network.ts +0 -179
- package/src/definitions/outlook.ts +0 -318
- package/src/definitions/patch/apply-patch.ts +0 -235
- package/src/definitions/patch/fuzzy-match.ts +0 -217
- package/src/definitions/patch/index.ts +0 -1
- package/src/definitions/patch/patch-parser.ts +0 -297
- package/src/definitions/patch/sandbox-paths.ts +0 -129
- package/src/definitions/process/index.ts +0 -5
- package/src/definitions/process/process-registry.ts +0 -303
- package/src/definitions/process/process.ts +0 -456
- package/src/definitions/process/pty-keys.ts +0 -298
- package/src/definitions/process/session-slug.ts +0 -147
- package/src/definitions/quip.ts +0 -225
- package/src/definitions/search.ts +0 -67
- package/src/definitions/session-history.ts +0 -79
- package/src/definitions/shell.ts +0 -202
- package/src/definitions/slack.ts +0 -211
- package/src/definitions/web.ts +0 -119
- package/src/executors/apply-patch.ts +0 -1035
- package/src/executors/arion.ts +0 -199
- package/src/executors/code-intelligence.ts +0 -1179
- package/src/executors/deploy.ts +0 -1066
- package/src/executors/filesystem.ts +0 -1428
- package/src/executors/frg-freshness.ts +0 -743
- package/src/executors/frg.ts +0 -394
- package/src/executors/index.ts +0 -280
- package/src/executors/learning-meta.ts +0 -1367
- package/src/executors/lsp-client.ts +0 -355
- package/src/executors/memory.ts +0 -978
- package/src/executors/meta.ts +0 -293
- package/src/executors/process-registry.ts +0 -570
- package/src/executors/pty-session-store.ts +0 -43
- package/src/executors/pty.ts +0 -342
- package/src/executors/restart.ts +0 -133
- package/src/executors/search-freshness.ts +0 -249
- package/src/executors/search-types.ts +0 -98
- package/src/executors/search.ts +0 -89
- package/src/executors/self-diagnose.ts +0 -552
- package/src/executors/session-history.ts +0 -435
- package/src/executors/shell-safety.ts +0 -519
- package/src/executors/shell.ts +0 -1243
- package/src/executors/utils.ts +0 -40
- package/src/executors/web.ts +0 -786
- package/src/extraction/content-extraction.ts +0 -281
- package/src/extraction/index.ts +0 -5
- package/src/headless-control-contract.ts +0 -1149
- package/src/index.ts +0 -788
- package/src/local-control-http-auth.ts +0 -2
- package/src/mcp/client.ts +0 -218
- package/src/mcp/connection.ts +0 -568
- package/src/mcp/index.ts +0 -11
- package/src/mcp/jsonrpc.ts +0 -195
- package/src/mcp/types.ts +0 -199
- package/src/network-control-adapter.ts +0 -88
- package/src/network-runtime/address-types.ts +0 -218
- package/src/network-runtime/db-owner-fencing.ts +0 -91
- package/src/network-runtime/delivery-receipts.ts +0 -372
- package/src/network-runtime/direct-endpoint-authority.ts +0 -35
- package/src/network-runtime/index.ts +0 -316
- package/src/network-runtime/local-control-contract.ts +0 -784
- package/src/network-runtime/node-store-contract.ts +0 -46
- package/src/network-runtime/pair-route-contract.ts +0 -97
- package/src/network-runtime/peer-capabilities.ts +0 -48
- package/src/network-runtime/peer-principal-ref.ts +0 -20
- package/src/network-runtime/peer-state-machine.ts +0 -160
- package/src/network-runtime/protocol-schemas.ts +0 -265
- package/src/network-runtime/runtime-bootstrap-contract.ts +0 -83
- package/src/outlook/desktop-session.ts +0 -409
- package/src/policy.ts +0 -171
- package/src/providers/brave.ts +0 -80
- package/src/providers/duckduckgo.ts +0 -199
- package/src/providers/exa.ts +0 -85
- package/src/providers/firecrawl.ts +0 -77
- package/src/providers/index.ts +0 -8
- package/src/providers/jina.ts +0 -70
- package/src/providers/router.ts +0 -121
- package/src/providers/search-provider.ts +0 -74
- package/src/providers/tavily.ts +0 -74
- package/src/quip/desktop-session.ts +0 -435
- package/src/registry/index.ts +0 -1
- package/src/registry/registry.ts +0 -905
- package/src/runtime-socket-local-control-client.ts +0 -632
- package/src/security/dns-normalization.ts +0 -34
- package/src/security/dns-pinning.ts +0 -138
- package/src/security/external-content.ts +0 -129
- package/src/security/ssrf.ts +0 -207
- package/src/slack/desktop-session.ts +0 -493
- package/src/tool-factory.ts +0 -91
- package/src/types.ts +0 -1341
- package/src/utils/retry.ts +0 -163
- package/src/utils/safe-parse-json.ts +0 -176
- package/src/utils/url.ts +0 -20
- package/tests/benchmarks/registry.bench.ts +0 -57
- package/tests/cache/web-cache.test.ts +0 -147
- package/tests/critical-integration.test.ts +0 -1465
- package/tests/definitions/apply-patch.test.ts +0 -586
- package/tests/definitions/browser.test.ts +0 -495
- package/tests/definitions/delegation-pause-resume.test.ts +0 -758
- package/tests/definitions/execution.test.ts +0 -671
- package/tests/definitions/messaging-inbox-scope.test.ts +0 -229
- package/tests/definitions/messaging.test.ts +0 -1468
- package/tests/definitions/outlook.test.ts +0 -30
- package/tests/definitions/process.test.ts +0 -469
- package/tests/definitions/slack.test.ts +0 -28
- package/tests/definitions/tool-inventory.test.ts +0 -218
- package/tests/e2e/delegation-quest-orchestration.e2e.test.ts +0 -433
- package/tests/e2e/memory-tool-discovery-contract.e2e.test.ts +0 -81
- package/tests/executors/apply-patch.test.ts +0 -538
- package/tests/executors/arion.test.ts +0 -309
- package/tests/executors/conversation-primitives.test.ts +0 -250
- package/tests/executors/deploy.test.ts +0 -746
- package/tests/executors/filesystem-tools.test.ts +0 -357
- package/tests/executors/filesystem.test.ts +0 -959
- package/tests/executors/frg-freshness.test.ts +0 -136
- package/tests/executors/frg-merge.test.ts +0 -70
- package/tests/executors/frg-session-content.test.ts +0 -40
- package/tests/executors/frg.test.ts +0 -56
- package/tests/executors/memory-bugfixes.test.ts +0 -257
- package/tests/executors/memory-real-memoria.integration.test.ts +0 -316
- package/tests/executors/memory.test.ts +0 -853
- package/tests/executors/meta-tools.test.ts +0 -411
- package/tests/executors/meta.test.ts +0 -683
- package/tests/executors/path-containment.test.ts +0 -51
- package/tests/executors/process-registry.test.ts +0 -505
- package/tests/executors/pty.test.ts +0 -664
- package/tests/executors/quest-security.test.ts +0 -249
- package/tests/executors/read-file-media.test.ts +0 -230
- package/tests/executors/recall-knowledge-schema.test.ts +0 -209
- package/tests/executors/recall-tags.test.ts +0 -278
- package/tests/executors/remember-null-safety.contract.test.ts +0 -41
- package/tests/executors/restart.test.ts +0 -67
- package/tests/executors/search-unified.test.ts +0 -381
- package/tests/executors/session-history.test.ts +0 -340
- package/tests/executors/session-transcript.test.ts +0 -561
- package/tests/executors/shell-abort.test.ts +0 -416
- package/tests/executors/shell-env-blocklist.test.ts +0 -648
- package/tests/executors/shell-env-process.test.ts +0 -245
- package/tests/executors/shell-process-registry.test.ts +0 -334
- package/tests/executors/shell-tools.test.ts +0 -393
- package/tests/executors/shell.test.ts +0 -690
- package/tests/executors/web-abort-vs-timeout.test.ts +0 -213
- package/tests/executors/web-integration.test.ts +0 -633
- package/tests/executors/web-symlink.test.ts +0 -18
- package/tests/executors/web.test.ts +0 -1400
- package/tests/executors/write-stdin.test.ts +0 -145
- package/tests/extraction/content-extraction.test.ts +0 -153
- package/tests/guards/tools-default-test-lane.integration.test.ts +0 -21
- package/tests/guards/tools-package-test-commands.e2e.test.ts +0 -43
- package/tests/guards/tools-test-lane-manifest.contract.test.ts +0 -76
- package/tests/guards/tools-vitest-workspace-alias.contract.test.ts +0 -63
- package/tests/helpers/async-waits.ts +0 -53
- package/tests/integration/headless-control-contract.integration.test.ts +0 -153
- package/tests/integration/memory-tool-schema-parity.integration.test.ts +0 -67
- package/tests/integration/meta-tools-round-trip.integration.test.ts +0 -506
- package/tests/integration/quest-round-trip.test.ts +0 -303
- package/tests/integration/registry-executor-flow.test.ts +0 -85
- package/tests/integration.test.ts +0 -177
- package/tests/loading-tier.test.ts +0 -126
- package/tests/mcp/client-reconnect.test.ts +0 -267
- package/tests/mcp/connection.test.ts +0 -846
- package/tests/mcp/injectable-logger.test.ts +0 -83
- package/tests/mcp/jsonrpc.test.ts +0 -109
- package/tests/mcp/lifecycle.test.ts +0 -879
- package/tests/network-runtime/address-types.contract.test.ts +0 -143
- package/tests/network-runtime/continuity-bind-schema.contract.test.ts +0 -203
- package/tests/network-runtime/local-control-contract.test.ts +0 -869
- package/tests/network-runtime/local-control-invite-token.contract.test.ts +0 -146
- package/tests/network-runtime/node-store-contract.test.ts +0 -11
- package/tests/network-runtime/pair-protocol-nodeid.contract.test.ts +0 -15
- package/tests/network-runtime/peer-state-machine.contract.test.ts +0 -148
- package/tests/network-runtime/protocol-schemas.contract.test.ts +0 -512
- package/tests/network-runtime/relay-pending-nodeid.contract.test.ts +0 -62
- package/tests/network-runtime/runtime-bootstrap-contract.test.ts +0 -227
- package/tests/network-runtime/runtime-socket-local-control-client.test.ts +0 -621
- package/tests/network-runtime/wait-for-message-script.test.ts +0 -288
- package/tests/parallel.test.ts +0 -71
- package/tests/policy.test.ts +0 -184
- package/tests/print-default-test-lane.ts +0 -14
- package/tests/print-test-lane-manifest.ts +0 -22
- package/tests/providers/brave.test.ts +0 -159
- package/tests/providers/duckduckgo.test.ts +0 -207
- package/tests/providers/exa.test.ts +0 -175
- package/tests/providers/firecrawl.test.ts +0 -168
- package/tests/providers/jina.test.ts +0 -144
- package/tests/providers/router.test.ts +0 -328
- package/tests/providers/tavily.test.ts +0 -165
- package/tests/registry/discovery.test.ts +0 -154
- package/tests/registry/injectable-logger.test.ts +0 -230
- package/tests/registry/input-validation.test.ts +0 -361
- package/tests/registry/interface-completeness.test.ts +0 -85
- package/tests/registry/mcp-integration.test.ts +0 -103
- package/tests/registry/mcp-read-only-hint.test.ts +0 -60
- package/tests/registry/memoria-discovery.test.ts +0 -390
- package/tests/registry/nested-validation.test.ts +0 -283
- package/tests/registry/pseudo-tool-filtering.test.ts +0 -258
- package/tests/registry/registration-lifecycle.test.ts +0 -133
- package/tests/registry-validation.test.ts +0 -424
- package/tests/registry.test.ts +0 -460
- package/tests/security/dns-pinning.test.ts +0 -162
- package/tests/security/external-content.test.ts +0 -144
- package/tests/security/ssrf.test.ts +0 -118
- package/tests/shell-safety-integration.test.ts +0 -32
- package/tests/shell-safety.test.ts +0 -365
- package/tests/slack/desktop-session.test.ts +0 -50
- package/tests/test-lane-manifest.ts +0 -440
- package/tests/test-utils.ts +0 -27
- package/tests/tool-factory.test.ts +0 -188
- package/tests/utils/retry.test.ts +0 -231
- package/tests/utils/url.test.ts +0 -63
- package/tsconfig.cjs.json +0 -24
- package/tsconfig.json +0 -12
- package/vitest.config.ts +0 -55
- package/vitest.e2e.config.ts +0 -24
- package/vitest.integration.config.ts +0 -24
- package/vitest.native.config.ts +0 -24
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @aria/tools - isPathWithinBase unit tests
|
|
3
|
-
*
|
|
4
|
-
* Tests the security-critical path containment check directly.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect } from "vitest";
|
|
8
|
-
import { isPathWithinBase } from "../../src/executors/utils.js";
|
|
9
|
-
|
|
10
|
-
describe("isPathWithinBase", () => {
|
|
11
|
-
it("should accept exact base match", () => {
|
|
12
|
-
expect(isPathWithinBase("/home/user/project", "/home/user/project")).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("should accept child paths", () => {
|
|
16
|
-
expect(isPathWithinBase("/home/user/project/src/index.ts", "/home/user/project")).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("should accept deeply nested child paths", () => {
|
|
20
|
-
expect(isPathWithinBase("/home/user/project/src/a/b/c/d.ts", "/home/user/project")).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("should reject sibling directories", () => {
|
|
24
|
-
expect(isPathWithinBase("/home/user/other", "/home/user/project")).toBe(false);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("should reject prefix-collision paths (base-evil)", () => {
|
|
28
|
-
expect(isPathWithinBase("/home/user/project-evil/file.ts", "/home/user/project")).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("should reject parent directory", () => {
|
|
32
|
-
expect(isPathWithinBase("/home/user", "/home/user/project")).toBe(false);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should handle trailing separator on base", () => {
|
|
36
|
-
expect(isPathWithinBase("/home/user/project/file.ts", "/home/user/project/")).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("should handle trailing separator on both", () => {
|
|
40
|
-
expect(isPathWithinBase("/home/user/project/", "/home/user/project/")).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("should handle root as base", () => {
|
|
44
|
-
// Root as base allows everything — this is the correct behavior for "/"
|
|
45
|
-
expect(isPathWithinBase("/any/path", "/")).toBe(true);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("should reject completely unrelated paths", () => {
|
|
49
|
-
expect(isPathWithinBase("/tmp/something", "/home/user/project")).toBe(false);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @aria/tools - SpawnedProcessRegistry tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for tracking and killing spawned child processes.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
8
|
-
import { spawn } from "node:child_process";
|
|
9
|
-
import * as fs from "node:fs";
|
|
10
|
-
import * as os from "node:os";
|
|
11
|
-
import * as path from "node:path";
|
|
12
|
-
import { SpawnedProcessRegistry } from "../../src/executors/process-registry.js";
|
|
13
|
-
|
|
14
|
-
const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
|
|
15
|
-
const SIGTERM_IGNORING_NODE_SCRIPT =
|
|
16
|
-
"const fs = require('node:fs'); process.on('SIGTERM', () => {}); fs.writeFileSync(process.env.READY_FILE, 'ready'); setInterval(() => {}, 1000);";
|
|
17
|
-
|
|
18
|
-
function isProcessAlive(pid: number): boolean {
|
|
19
|
-
try {
|
|
20
|
-
process.kill(pid, 0);
|
|
21
|
-
return true;
|
|
22
|
-
} catch {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Spawn a detached sleep process for testing. Returns its PID.
|
|
29
|
-
* Caller is responsible for cleanup.
|
|
30
|
-
*/
|
|
31
|
-
function spawnSleepProcess(seconds = 60): number {
|
|
32
|
-
const proc = spawn("sleep", [String(seconds)], {
|
|
33
|
-
detached: true,
|
|
34
|
-
stdio: "ignore",
|
|
35
|
-
});
|
|
36
|
-
proc.unref();
|
|
37
|
-
return proc.pid!;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function waitForFile(filePath: string, timeoutMs = 5_000): Promise<void> {
|
|
41
|
-
const deadline = Date.now() + timeoutMs;
|
|
42
|
-
while (Date.now() < deadline) {
|
|
43
|
-
if (fs.existsSync(filePath)) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
await sleep(25);
|
|
47
|
-
}
|
|
48
|
-
throw new Error(`Timed out waiting for ${filePath}`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function spawnSigtermIgnoringNodeProcess(): Promise<{ pid: number; tempDir: string }> {
|
|
52
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "aria-proc-reg-"));
|
|
53
|
-
const readyFile = path.join(tempDir, "ready");
|
|
54
|
-
const proc = spawn(process.execPath, ["-e", SIGTERM_IGNORING_NODE_SCRIPT], {
|
|
55
|
-
detached: true,
|
|
56
|
-
stdio: "ignore",
|
|
57
|
-
env: {
|
|
58
|
-
...process.env,
|
|
59
|
-
READY_FILE: readyFile,
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
proc.unref();
|
|
63
|
-
await waitForFile(readyFile);
|
|
64
|
-
return { pid: proc.pid!, tempDir };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
describe("SpawnedProcessRegistry", () => {
|
|
68
|
-
let registry: SpawnedProcessRegistry;
|
|
69
|
-
const pidsToCleanup: number[] = [];
|
|
70
|
-
const tempDirsToCleanup: string[] = [];
|
|
71
|
-
|
|
72
|
-
beforeEach(() => {
|
|
73
|
-
registry = new SpawnedProcessRegistry();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
afterEach(() => {
|
|
77
|
-
// Clean up any spawned processes
|
|
78
|
-
for (const pid of pidsToCleanup) {
|
|
79
|
-
try {
|
|
80
|
-
process.kill(pid, "SIGKILL");
|
|
81
|
-
} catch {
|
|
82
|
-
// Process may already be dead
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
pidsToCleanup.length = 0;
|
|
86
|
-
for (const tempDir of tempDirsToCleanup) {
|
|
87
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
88
|
-
}
|
|
89
|
-
tempDirsToCleanup.length = 0;
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe("add/remove/has", () => {
|
|
93
|
-
it("tracks added PIDs", () => {
|
|
94
|
-
registry.add(1234);
|
|
95
|
-
expect(registry.has(1234)).toBe(true);
|
|
96
|
-
expect(registry.size).toBe(1);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("removes PIDs", () => {
|
|
100
|
-
registry.add(1234);
|
|
101
|
-
registry.remove(1234);
|
|
102
|
-
expect(registry.has(1234)).toBe(false);
|
|
103
|
-
expect(registry.size).toBe(0);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("handles removing non-existent PID gracefully", () => {
|
|
107
|
-
registry.remove(9999);
|
|
108
|
-
expect(registry.size).toBe(0);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("tracks multiple PIDs", () => {
|
|
112
|
-
registry.add(100);
|
|
113
|
-
registry.add(200);
|
|
114
|
-
registry.add(300);
|
|
115
|
-
expect(registry.size).toBe(3);
|
|
116
|
-
expect(registry.getAll()).toEqual(expect.arrayContaining([100, 200, 300]));
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("deduplicates same PID", () => {
|
|
120
|
-
registry.add(1234);
|
|
121
|
-
registry.add(1234);
|
|
122
|
-
expect(registry.size).toBe(1);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
describe("getAll", () => {
|
|
127
|
-
it("returns empty array when no PIDs tracked", () => {
|
|
128
|
-
expect(registry.getAll()).toEqual([]);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("returns snapshot of tracked PIDs", () => {
|
|
132
|
-
registry.add(10);
|
|
133
|
-
registry.add(20);
|
|
134
|
-
const pids = registry.getAll();
|
|
135
|
-
expect(pids).toHaveLength(2);
|
|
136
|
-
expect(pids).toContain(10);
|
|
137
|
-
expect(pids).toContain(20);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe("process metadata", () => {
|
|
142
|
-
it("stores metadata for tracked processes", () => {
|
|
143
|
-
registry.add(1234, {
|
|
144
|
-
command: "sleep",
|
|
145
|
-
args: ["60"],
|
|
146
|
-
cwd: "/tmp/test",
|
|
147
|
-
interactive: false,
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
const processes = registry.listProcesses();
|
|
151
|
-
expect(processes).toHaveLength(1);
|
|
152
|
-
expect(processes[0]).toMatchObject({
|
|
153
|
-
pid: 1234,
|
|
154
|
-
command: "sleep",
|
|
155
|
-
args: ["60"],
|
|
156
|
-
cwd: "/tmp/test",
|
|
157
|
-
interactive: false,
|
|
158
|
-
status: "running",
|
|
159
|
-
});
|
|
160
|
-
expect(typeof processes[0].startedAt).toBe("string");
|
|
161
|
-
expect(processes[0].runtimeMs).toBeGreaterThanOrEqual(0);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe("killAll", () => {
|
|
166
|
-
it("returns empty array when no PIDs tracked", async () => {
|
|
167
|
-
const killed = await registry.killAll();
|
|
168
|
-
expect(killed).toEqual([]);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("kills a real spawned process", async () => {
|
|
172
|
-
const pid = spawnSleepProcess();
|
|
173
|
-
pidsToCleanup.push(pid);
|
|
174
|
-
|
|
175
|
-
expect(isProcessAlive(pid)).toBe(true);
|
|
176
|
-
|
|
177
|
-
registry.add(pid);
|
|
178
|
-
const killed = await registry.killAll();
|
|
179
|
-
|
|
180
|
-
expect(killed).toContain(pid);
|
|
181
|
-
|
|
182
|
-
// Wait for process to actually die
|
|
183
|
-
await sleep(200);
|
|
184
|
-
expect(isProcessAlive(pid)).toBe(false);
|
|
185
|
-
}, 10000);
|
|
186
|
-
|
|
187
|
-
it("kills multiple real processes", async () => {
|
|
188
|
-
const pid1 = spawnSleepProcess();
|
|
189
|
-
const pid2 = spawnSleepProcess();
|
|
190
|
-
pidsToCleanup.push(pid1, pid2);
|
|
191
|
-
|
|
192
|
-
registry.add(pid1);
|
|
193
|
-
registry.add(pid2);
|
|
194
|
-
|
|
195
|
-
expect(isProcessAlive(pid1)).toBe(true);
|
|
196
|
-
expect(isProcessAlive(pid2)).toBe(true);
|
|
197
|
-
|
|
198
|
-
await registry.killAll();
|
|
199
|
-
|
|
200
|
-
await sleep(200);
|
|
201
|
-
expect(isProcessAlive(pid1)).toBe(false);
|
|
202
|
-
expect(isProcessAlive(pid2)).toBe(false);
|
|
203
|
-
}, 10000);
|
|
204
|
-
|
|
205
|
-
it("clears the registry after killAll", async () => {
|
|
206
|
-
registry.add(999999999); // Non-existent PID
|
|
207
|
-
await registry.killAll();
|
|
208
|
-
expect(registry.size).toBe(0);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("handles already-dead processes gracefully", async () => {
|
|
212
|
-
const pid = spawnSleepProcess();
|
|
213
|
-
pidsToCleanup.push(pid);
|
|
214
|
-
|
|
215
|
-
registry.add(pid);
|
|
216
|
-
|
|
217
|
-
// Kill the process before calling killAll
|
|
218
|
-
process.kill(pid, "SIGKILL");
|
|
219
|
-
await sleep(100);
|
|
220
|
-
|
|
221
|
-
// Should not throw
|
|
222
|
-
const killed = await registry.killAll();
|
|
223
|
-
expect(killed).toContain(pid);
|
|
224
|
-
expect(registry.size).toBe(0);
|
|
225
|
-
}, 10000);
|
|
226
|
-
|
|
227
|
-
it("waits for a SIGTERM-resistant process to exit after SIGKILL escalation", async () => {
|
|
228
|
-
const resistantProcess = await spawnSigtermIgnoringNodeProcess();
|
|
229
|
-
const { pid, tempDir } = resistantProcess;
|
|
230
|
-
pidsToCleanup.push(pid);
|
|
231
|
-
tempDirsToCleanup.push(tempDir);
|
|
232
|
-
registry.add(pid, {
|
|
233
|
-
command: process.execPath,
|
|
234
|
-
args: ["-e", SIGTERM_IGNORING_NODE_SCRIPT],
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
const startedAt = Date.now();
|
|
238
|
-
const killed = await registry.killAll();
|
|
239
|
-
const elapsedMs = Date.now() - startedAt;
|
|
240
|
-
|
|
241
|
-
expect(killed).toContain(pid);
|
|
242
|
-
expect(elapsedMs).toBeGreaterThanOrEqual(1900);
|
|
243
|
-
expect(isProcessAlive(pid)).toBe(false);
|
|
244
|
-
expect(registry.getProcess(pid)).toMatchObject({
|
|
245
|
-
pid,
|
|
246
|
-
status: "exited",
|
|
247
|
-
signal: "SIGKILL",
|
|
248
|
-
});
|
|
249
|
-
}, 15000);
|
|
250
|
-
|
|
251
|
-
it("preserves the leader signal when escalation only kills a trailing child", async () => {
|
|
252
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "aria-proc-leader-signal-"));
|
|
253
|
-
const childPidFile = path.join(tempDir, "child.pid");
|
|
254
|
-
tempDirsToCleanup.push(tempDir);
|
|
255
|
-
|
|
256
|
-
const proc = spawn(
|
|
257
|
-
process.execPath,
|
|
258
|
-
[
|
|
259
|
-
"-e",
|
|
260
|
-
[
|
|
261
|
-
"const { spawn } = require('node:child_process');",
|
|
262
|
-
"const fs = require('node:fs');",
|
|
263
|
-
"const child = spawn(process.execPath, ['-e', \"process.on('SIGTERM', () => {}); setInterval(() => {}, 1000);\"] , { stdio: 'ignore' });",
|
|
264
|
-
"fs.writeFileSync(process.env.CHILD_PID_FILE, String(child.pid));",
|
|
265
|
-
"setInterval(() => {}, 1000);",
|
|
266
|
-
].join(" "),
|
|
267
|
-
],
|
|
268
|
-
{
|
|
269
|
-
detached: true,
|
|
270
|
-
stdio: "ignore",
|
|
271
|
-
env: {
|
|
272
|
-
...process.env,
|
|
273
|
-
CHILD_PID_FILE: childPidFile,
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
);
|
|
277
|
-
proc.unref();
|
|
278
|
-
const pid = proc.pid!;
|
|
279
|
-
pidsToCleanup.push(pid);
|
|
280
|
-
await waitForFile(childPidFile);
|
|
281
|
-
|
|
282
|
-
registry.add(pid, {
|
|
283
|
-
command: process.execPath,
|
|
284
|
-
args: ["-e", "leader-dies-child-resists"],
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
await registry.killAll();
|
|
288
|
-
|
|
289
|
-
expect(registry.getProcess(pid)).toMatchObject({
|
|
290
|
-
pid,
|
|
291
|
-
status: "exited",
|
|
292
|
-
signal: "SIGTERM",
|
|
293
|
-
});
|
|
294
|
-
}, 15_000);
|
|
295
|
-
|
|
296
|
-
it("signals detached process groups so child processes do not survive teardown", async () => {
|
|
297
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "aria-proc-group-"));
|
|
298
|
-
const childPidFile = path.join(tempDir, "child.pid");
|
|
299
|
-
tempDirsToCleanup.push(tempDir);
|
|
300
|
-
|
|
301
|
-
const proc = spawn(
|
|
302
|
-
"sh",
|
|
303
|
-
["-c", 'sleep 60 & child=$!; echo "$child" > "$CHILD_PID_FILE"; wait "$child"'],
|
|
304
|
-
{
|
|
305
|
-
detached: true,
|
|
306
|
-
stdio: "ignore",
|
|
307
|
-
env: {
|
|
308
|
-
...process.env,
|
|
309
|
-
CHILD_PID_FILE: childPidFile,
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
);
|
|
313
|
-
proc.unref();
|
|
314
|
-
const pid = proc.pid!;
|
|
315
|
-
pidsToCleanup.push(pid);
|
|
316
|
-
await waitForFile(childPidFile);
|
|
317
|
-
const childPid = Number.parseInt(fs.readFileSync(childPidFile, "utf8").trim(), 10);
|
|
318
|
-
|
|
319
|
-
registry.add(pid, {
|
|
320
|
-
command: "sh",
|
|
321
|
-
args: ["-c", 'sleep 60 & child=$!; echo "$child" > "$CHILD_PID_FILE"; wait "$child"'],
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
expect(isProcessAlive(pid)).toBe(true);
|
|
325
|
-
expect(isProcessAlive(childPid)).toBe(true);
|
|
326
|
-
|
|
327
|
-
await registry.killAll();
|
|
328
|
-
await sleep(200);
|
|
329
|
-
|
|
330
|
-
expect(isProcessAlive(pid)).toBe(false);
|
|
331
|
-
expect(isProcessAlive(childPid)).toBe(false);
|
|
332
|
-
}, 15000);
|
|
333
|
-
|
|
334
|
-
it("auto-reaps tracked detached groups after the leader exits and the trailing child drains naturally", async () => {
|
|
335
|
-
const proc = spawn(
|
|
336
|
-
process.execPath,
|
|
337
|
-
[
|
|
338
|
-
"-e",
|
|
339
|
-
[
|
|
340
|
-
"const { spawn } = require('node:child_process');",
|
|
341
|
-
"const child = spawn('sleep', ['1'], { stdio: 'ignore' });",
|
|
342
|
-
"child.unref();",
|
|
343
|
-
"setTimeout(() => process.exit(0), 20);",
|
|
344
|
-
].join(" "),
|
|
345
|
-
],
|
|
346
|
-
{
|
|
347
|
-
detached: true,
|
|
348
|
-
stdio: "ignore",
|
|
349
|
-
},
|
|
350
|
-
);
|
|
351
|
-
proc.unref();
|
|
352
|
-
const pid = proc.pid!;
|
|
353
|
-
pidsToCleanup.push(pid);
|
|
354
|
-
|
|
355
|
-
registry.add(pid, {
|
|
356
|
-
command: process.execPath,
|
|
357
|
-
args: ["-e", "leader-exits-before-child-drains"],
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
expect(registry.has(pid)).toBe(true);
|
|
361
|
-
|
|
362
|
-
await sleep(1_500);
|
|
363
|
-
|
|
364
|
-
expect(registry.has(pid)).toBe(false);
|
|
365
|
-
expect(registry.getProcess(pid)).toMatchObject({
|
|
366
|
-
pid,
|
|
367
|
-
status: "exited",
|
|
368
|
-
});
|
|
369
|
-
}, 15_000);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
describe("normal exit removes PID", () => {
|
|
373
|
-
it("process that exits normally can be deregistered before killAll", async () => {
|
|
374
|
-
// Spawn a process that exits quickly
|
|
375
|
-
const proc = spawn("true", [], {
|
|
376
|
-
detached: true,
|
|
377
|
-
stdio: "ignore",
|
|
378
|
-
});
|
|
379
|
-
proc.unref();
|
|
380
|
-
const pid = proc.pid!;
|
|
381
|
-
pidsToCleanup.push(pid);
|
|
382
|
-
|
|
383
|
-
registry.add(pid);
|
|
384
|
-
|
|
385
|
-
// Wait for process to exit
|
|
386
|
-
await sleep(200);
|
|
387
|
-
|
|
388
|
-
// Simulate what the shell executor does: remove on exit
|
|
389
|
-
registry.remove(pid);
|
|
390
|
-
|
|
391
|
-
expect(registry.has(pid)).toBe(false);
|
|
392
|
-
expect(registry.size).toBe(0);
|
|
393
|
-
});
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
describe("waitForExit", () => {
|
|
397
|
-
it("returns not_found for unknown PID", async () => {
|
|
398
|
-
const result = await registry.waitForExit(424242, 100);
|
|
399
|
-
|
|
400
|
-
expect(result.status).toBe("not_found");
|
|
401
|
-
expect(result.pid).toBe(424242);
|
|
402
|
-
expect(result.timedOut).toBe(false);
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
it("returns immediate running timeout when timeout is zero", async () => {
|
|
406
|
-
registry.add(4343, { command: "sleep", args: ["60"] });
|
|
407
|
-
|
|
408
|
-
const result = await registry.waitForExit(4343, 0);
|
|
409
|
-
expect(result.status).toBe("running");
|
|
410
|
-
expect(result.pid).toBe(4343);
|
|
411
|
-
expect(result.timedOut).toBe(true);
|
|
412
|
-
expect(result.process).toMatchObject({
|
|
413
|
-
pid: 4343,
|
|
414
|
-
status: "running",
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
registry.remove(4343, { signal: "SIGKILL" });
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
it("resolves when tracked process exits", async () => {
|
|
421
|
-
registry.add(7777, { command: "sleep", args: ["1"] });
|
|
422
|
-
const waitPromise = registry.waitForExit(7777, 1000);
|
|
423
|
-
|
|
424
|
-
setTimeout(() => {
|
|
425
|
-
registry.remove(7777, { exitCode: 0 });
|
|
426
|
-
}, 50);
|
|
427
|
-
|
|
428
|
-
const result = await waitPromise;
|
|
429
|
-
expect(result.status).toBe("exited");
|
|
430
|
-
expect(result.pid).toBe(7777);
|
|
431
|
-
expect(result.timedOut).toBe(false);
|
|
432
|
-
expect(result.process).toMatchObject({
|
|
433
|
-
pid: 7777,
|
|
434
|
-
status: "exited",
|
|
435
|
-
exitCode: 0,
|
|
436
|
-
});
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
it("resolves quickly when process exits during waiter registration race window", async () => {
|
|
440
|
-
const pid = 7878;
|
|
441
|
-
registry.add(pid, { command: "sleep", args: ["1"] });
|
|
442
|
-
|
|
443
|
-
const internals = registry as unknown as { active: { get(targetPid: number): unknown } };
|
|
444
|
-
const activeMap = internals.active;
|
|
445
|
-
const originalGet = activeMap.get.bind(activeMap);
|
|
446
|
-
let injected = false;
|
|
447
|
-
|
|
448
|
-
activeMap.get = ((targetPid: number) => {
|
|
449
|
-
const record = originalGet(targetPid);
|
|
450
|
-
if (!injected && targetPid === pid) {
|
|
451
|
-
injected = true;
|
|
452
|
-
registry.remove(pid, { exitCode: 0 });
|
|
453
|
-
}
|
|
454
|
-
return record;
|
|
455
|
-
}) as typeof activeMap.get;
|
|
456
|
-
|
|
457
|
-
try {
|
|
458
|
-
const startedAt = Date.now();
|
|
459
|
-
const result = await registry.waitForExit(pid, 250);
|
|
460
|
-
const elapsedMs = Date.now() - startedAt;
|
|
461
|
-
|
|
462
|
-
expect(result.status).toBe("exited");
|
|
463
|
-
expect(result.timedOut).toBe(false);
|
|
464
|
-
expect(result.process).toMatchObject({
|
|
465
|
-
pid,
|
|
466
|
-
status: "exited",
|
|
467
|
-
exitCode: 0,
|
|
468
|
-
});
|
|
469
|
-
expect(elapsedMs).toBeLessThan(120);
|
|
470
|
-
} finally {
|
|
471
|
-
activeMap.get = originalGet;
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
it("returns timeout when process stays running", async () => {
|
|
476
|
-
registry.add(8888, { command: "sleep", args: ["60"] });
|
|
477
|
-
|
|
478
|
-
const result = await registry.waitForExit(8888, 50);
|
|
479
|
-
expect(result.status).toBe("running");
|
|
480
|
-
expect(result.pid).toBe(8888);
|
|
481
|
-
expect(result.timedOut).toBe(true);
|
|
482
|
-
expect(result.process).toMatchObject({
|
|
483
|
-
pid: 8888,
|
|
484
|
-
status: "running",
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
registry.remove(8888, { signal: "SIGKILL" });
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
it("returns cached exit info when process already exited", async () => {
|
|
491
|
-
registry.add(9999, { command: "true" });
|
|
492
|
-
registry.remove(9999, { exitCode: 0 });
|
|
493
|
-
|
|
494
|
-
const result = await registry.waitForExit(9999, 100);
|
|
495
|
-
expect(result.status).toBe("exited");
|
|
496
|
-
expect(result.pid).toBe(9999);
|
|
497
|
-
expect(result.timedOut).toBe(false);
|
|
498
|
-
expect(result.process).toMatchObject({
|
|
499
|
-
pid: 9999,
|
|
500
|
-
status: "exited",
|
|
501
|
-
exitCode: 0,
|
|
502
|
-
});
|
|
503
|
-
});
|
|
504
|
-
});
|
|
505
|
-
});
|