@wrongstack/tools 0.299.0 → 0.301.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/audit.js +6 -2
- package/dist/bash.js +6 -2
- package/dist/batch-tool-use.js +3 -1
- package/dist/browser/index.js +1 -1
- package/dist/builtin.d.ts +3 -2
- package/dist/builtin.js +1781 -376
- package/dist/codebase-index/bm25.d.ts +7 -1
- package/dist/codebase-index/import-extractor.d.ts +39 -0
- package/dist/codebase-index/index.js +1418 -267
- package/dist/codebase-index/languages.d.ts +24 -0
- package/dist/codebase-index/module-resolver.d.ts +78 -0
- package/dist/codebase-index/module-roots.d.ts +81 -0
- package/dist/codebase-index/parser-output.d.ts +29 -0
- package/dist/codebase-index/project-server.js +1402 -249
- package/dist/codebase-index/rs-parser.d.ts +22 -0
- package/dist/codebase-index/schema.d.ts +24 -1
- package/dist/codebase-index/worker.js +1401 -250
- package/dist/codebase-index/writer-graph-helpers.d.ts +17 -5
- package/dist/codebase-index/writer-ref-mapper.d.ts +3 -0
- package/dist/codebase-index/writer-schema.d.ts +15 -3
- package/dist/codebase-index/writer.d.ts +76 -3
- package/dist/exec.js +35 -2
- package/dist/format.js +6 -2
- package/dist/git.js +2 -5
- package/dist/glob.js +2 -2
- package/dist/grep.js +118 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1925 -415
- package/dist/install.js +6 -2
- package/dist/json.js +132 -2
- package/dist/languages/index.js +6 -2
- package/dist/lint.js +6 -2
- package/dist/logs.js +81 -0
- package/dist/next-steps-tool.d.ts +26 -0
- package/dist/outdated.js +6 -2
- package/dist/pack.js +1781 -376
- package/dist/patch.js +206 -45
- package/dist/process-registry.d.ts +6 -0
- package/dist/process-registry.js +6 -2
- package/dist/ps-slash.js +6 -2
- package/dist/read.js +1410 -257
- package/dist/replace.js +81 -0
- package/dist/skill.js +51 -2
- package/dist/test.js +6 -2
- package/dist/tool-help.js +2 -2
- package/dist/tool-search.js +1 -1
- package/dist/tool-tier.d.ts +1 -1
- package/dist/tool-tier.js +1786 -397
- package/dist/tool-use.js +1 -1
- package/dist/tree.js +13 -3
- package/dist/typecheck.js +6 -2
- package/package.json +3 -3
- package/dist/codebase-index/refs-extractor.d.ts +0 -11
package/dist/patch.js
CHANGED
|
@@ -3,10 +3,11 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import * as path2 from "node:path";
|
|
6
|
-
import { buildChildEnv } from "@wrongstack/core/utils";
|
|
6
|
+
import { buildChildEnv, toErrorMessage } from "@wrongstack/core/utils";
|
|
7
7
|
|
|
8
8
|
// src/_util.ts
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
|
+
import * as fsp from "node:fs/promises";
|
|
10
11
|
import * as path from "node:path";
|
|
11
12
|
import * as Core from "@wrongstack/core/utils";
|
|
12
13
|
function sha256hex(content) {
|
|
@@ -33,13 +34,46 @@ function ensureInsideRoot(absPath, ctx) {
|
|
|
33
34
|
function safeResolve(input, ctx) {
|
|
34
35
|
return ensureInsideRoot(resolvePath(input, ctx), ctx);
|
|
35
36
|
}
|
|
37
|
+
async function resolveRealInsideRoot(absPath, ctx) {
|
|
38
|
+
if (ctx.allowOutsideProjectRoot) return absPath;
|
|
39
|
+
const realRoots = await Promise.all(
|
|
40
|
+
allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r)))
|
|
41
|
+
);
|
|
42
|
+
let probe = absPath;
|
|
43
|
+
const pendingTail = [];
|
|
44
|
+
for (; ; ) {
|
|
45
|
+
let real;
|
|
46
|
+
try {
|
|
47
|
+
real = await fsp.realpath(probe);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (err.code === "ENOENT") {
|
|
50
|
+
const parent = path.dirname(probe);
|
|
51
|
+
if (parent === probe) return absPath;
|
|
52
|
+
pendingTail.unshift(path.basename(probe));
|
|
53
|
+
probe = parent;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
if (isInsideAny(real, realRoots)) {
|
|
59
|
+
return pendingTail.length > 0 ? path.join(real, ...pendingTail) : real;
|
|
60
|
+
}
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Path "${absPath}" resolves through a symlink outside project root "${realRoots[0]}"`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function safeResolveReal(input, ctx) {
|
|
67
|
+
const abs = safeResolve(input, ctx);
|
|
68
|
+
return await resolveRealInsideRoot(abs, ctx);
|
|
69
|
+
}
|
|
36
70
|
|
|
37
71
|
// src/patch.ts
|
|
38
72
|
var patchTool = {
|
|
39
73
|
name: "patch",
|
|
40
74
|
category: "Filesystem",
|
|
41
75
|
description: "Apply a unified diff (patch) to the project. This is the correct tool when you have a diff that needs to be applied precisely, including handling of rejects.",
|
|
42
|
-
usageHint: "Best used when you already have a diff (from generation, external source, or previous step).\n- Use `dry_run: true` to see what would happen without modifying files.\n-
|
|
76
|
+
usageHint: "Best used when you already have a diff (from generation, external source, or previous step).\n- Use `dry_run: true` to see what would happen without modifying files.\n- Applied with `--merge`: a conflicting hunk writes git-style conflict\n markers (<<<<<<< / ======= / >>>>>>>) INTO the file and reports failure.\n It does NOT create .rej/.orig files. `files` lists what changed on disk\n even when the patch failed, so read those back before retrying.\nOften cleaner than many small `edit` operations for larger changes.",
|
|
43
77
|
selection: {
|
|
44
78
|
doNotUseWhen: "you do not already have a unified diff or only need one precise replacement.",
|
|
45
79
|
useInstead: ["edit"]
|
|
@@ -65,31 +99,50 @@ var patchTool = {
|
|
|
65
99
|
},
|
|
66
100
|
async execute(input, ctx, opts) {
|
|
67
101
|
if (!input?.patch) throw new Error("patch: patch content is required");
|
|
68
|
-
const dir = input.directory ? safeResolve(input.directory, ctx) : ctx.cwd;
|
|
69
102
|
const strip = Math.max(1, input.strip ?? 1);
|
|
70
103
|
const dryRun = input.dry_run ?? false;
|
|
104
|
+
const refuse = (message) => ({
|
|
105
|
+
applied: 0,
|
|
106
|
+
rejected: 1,
|
|
107
|
+
files: [],
|
|
108
|
+
dry_run: dryRun,
|
|
109
|
+
message
|
|
110
|
+
});
|
|
111
|
+
let dir;
|
|
112
|
+
try {
|
|
113
|
+
dir = input.directory ? await safeResolveReal(input.directory, ctx) : ctx.cwd;
|
|
114
|
+
} catch (err) {
|
|
115
|
+
return refuse(`patch refused: ${toErrorMessage(err)}`);
|
|
116
|
+
}
|
|
117
|
+
const realRoot = await fs.realpath(ctx.projectRoot).catch(() => path2.resolve(ctx.projectRoot));
|
|
71
118
|
const targets = extractDiffTargets(input.patch);
|
|
72
119
|
const resolvedTargets = [];
|
|
73
120
|
for (const t of targets) {
|
|
74
|
-
const stripped = stripPathComponents(t, strip);
|
|
121
|
+
const stripped = stripPathComponents(t.raw, strip);
|
|
75
122
|
if (!stripped) continue;
|
|
123
|
+
if (path2.isAbsolute(stripped)) {
|
|
124
|
+
return refuse(`patch refused: target "${t.raw}" strips to absolute path`);
|
|
125
|
+
}
|
|
76
126
|
const candidate = path2.resolve(dir, stripped);
|
|
77
|
-
|
|
127
|
+
let real;
|
|
128
|
+
try {
|
|
129
|
+
real = await resolveRealInsideRoot(candidate, ctx);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
return refuse(`patch refused: target "${t.raw}" ${toErrorMessage(err)}`);
|
|
132
|
+
}
|
|
133
|
+
const rel = path2.relative(realRoot, real);
|
|
78
134
|
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
79
|
-
return {
|
|
80
|
-
applied: 0,
|
|
81
|
-
rejected: 1,
|
|
82
|
-
files: [],
|
|
83
|
-
dry_run: dryRun,
|
|
84
|
-
message: `patch refused: target "${t}" resolves outside project root`
|
|
85
|
-
};
|
|
135
|
+
return refuse(`patch refused: target "${t.raw}" resolves outside project root`);
|
|
86
136
|
}
|
|
87
|
-
resolvedTargets.push(
|
|
137
|
+
resolvedTargets.push({ raw: t.raw, deleted: t.deleted, abs: real });
|
|
88
138
|
}
|
|
89
139
|
const beforeContents = /* @__PURE__ */ new Map();
|
|
140
|
+
const beforeExisted = /* @__PURE__ */ new Set();
|
|
90
141
|
if (!dryRun) {
|
|
91
142
|
for (const target of resolvedTargets) {
|
|
92
|
-
|
|
143
|
+
const existed = (await fs.stat(target.abs).catch(() => null))?.isFile() ?? false;
|
|
144
|
+
if (existed) beforeExisted.add(target.abs);
|
|
145
|
+
beforeContents.set(target.abs, await readTextForTracking(target.abs));
|
|
93
146
|
}
|
|
94
147
|
}
|
|
95
148
|
const tmpDir = await fs.mkdtemp(path2.join(os.tmpdir(), ".wstack_patch_"));
|
|
@@ -99,32 +152,70 @@ var patchTool = {
|
|
|
99
152
|
const patchFile = path2.join(tmpDir, "in.diff");
|
|
100
153
|
await fs.writeFile(patchFile, input.patch, { mode: 384 });
|
|
101
154
|
const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
|
|
102
|
-
const result = await runPatch(args, dir, opts.signal
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
dry_run: dryRun,
|
|
109
|
-
message: `patch failed: ${result.stderr || result.stdout}`
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
const patched = extractPatchedFiles(result.stdout);
|
|
155
|
+
const result = await runPatch(args, dir, opts.signal, {
|
|
156
|
+
patchFile,
|
|
157
|
+
strip,
|
|
158
|
+
dryRun
|
|
159
|
+
});
|
|
160
|
+
const touched = [];
|
|
113
161
|
if (!dryRun) {
|
|
114
162
|
for (const target of resolvedTargets) {
|
|
115
|
-
const
|
|
116
|
-
const
|
|
163
|
+
const abs = target.abs;
|
|
164
|
+
const before = beforeContents.get(abs) ?? null;
|
|
165
|
+
const stat2 = await fs.stat(abs).catch(() => null);
|
|
166
|
+
if (!stat2?.isFile()) {
|
|
167
|
+
if (beforeExisted.has(abs)) {
|
|
168
|
+
touched.push(abs);
|
|
169
|
+
ctx.session?.recordFileChange?.({
|
|
170
|
+
path: abs,
|
|
171
|
+
action: "deleted",
|
|
172
|
+
before,
|
|
173
|
+
after: null
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const after = await readTextForTracking(abs);
|
|
117
179
|
if (after === null || after === before) continue;
|
|
118
|
-
|
|
119
|
-
|
|
180
|
+
touched.push(abs);
|
|
181
|
+
ctx.recordRead?.(abs, stat2.mtimeMs, "write", sha256hex(after));
|
|
120
182
|
ctx.session?.recordFileChange?.({
|
|
121
|
-
path:
|
|
183
|
+
path: abs,
|
|
122
184
|
action: before === null ? "created" : "modified",
|
|
123
185
|
before,
|
|
124
186
|
after
|
|
125
187
|
});
|
|
126
188
|
}
|
|
127
189
|
}
|
|
190
|
+
if (result.exitCode !== 0) {
|
|
191
|
+
if (!dryRun) {
|
|
192
|
+
const partial = touched.length > 0 ? ` ${touched.length} file(s) were still modified on disk and have been recorded for rewind: ${touched.map((p) => path2.relative(realRoot, p) || p).join(", ")}.` : "";
|
|
193
|
+
return {
|
|
194
|
+
applied: touched.length,
|
|
195
|
+
rejected: 1,
|
|
196
|
+
// Normalize to relative-to-realRoot for API consistency with the
|
|
197
|
+
// success path (which returns GNU patch's dir-relative names).
|
|
198
|
+
// `touched` entries are realpaths from resolveRealInsideRoot, and
|
|
199
|
+
// realRoot is also a realpath, so path.relative is like-for-like.
|
|
200
|
+
files: touched.map((p) => path2.relative(realRoot, p) || p),
|
|
201
|
+
dry_run: dryRun,
|
|
202
|
+
message: `patch failed: ${result.stderr || result.stdout}${partial}`
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const wouldPatch = extractPatchedFiles(result.stdout);
|
|
206
|
+
return {
|
|
207
|
+
applied: wouldPatch.length,
|
|
208
|
+
rejected: 1,
|
|
209
|
+
files: wouldPatch,
|
|
210
|
+
dry_run: dryRun,
|
|
211
|
+
message: `patch preview: would conflict \u2014 ${result.stderr || result.stdout}`
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const patched = result.engine === "git" ? [
|
|
215
|
+
...new Set(
|
|
216
|
+
resolvedTargets.map((target) => path2.relative(dir, target.abs) || target.abs)
|
|
217
|
+
)
|
|
218
|
+
] : extractPatchedFiles(result.stdout);
|
|
128
219
|
return {
|
|
129
220
|
applied: patched.length,
|
|
130
221
|
rejected: 0,
|
|
@@ -152,27 +243,86 @@ async function readTextForTracking(absPath) {
|
|
|
152
243
|
}
|
|
153
244
|
function extractDiffTargets(patch) {
|
|
154
245
|
const out = [];
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
246
|
+
const clean = (raw) => {
|
|
247
|
+
if (!raw) return "";
|
|
248
|
+
return (raw.length > 4096 ? raw.slice(0, 4096) : raw).trim();
|
|
249
|
+
};
|
|
250
|
+
let lastOld;
|
|
251
|
+
let inHunk = false;
|
|
252
|
+
let oldLinesLeft = 0;
|
|
253
|
+
let newLinesLeft = 0;
|
|
254
|
+
for (const line of patch.split(/\r?\n/)) {
|
|
255
|
+
const hunkMatch = /^@@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? @@/.exec(line);
|
|
256
|
+
if (hunkMatch) {
|
|
257
|
+
inHunk = true;
|
|
258
|
+
oldLinesLeft = hunkMatch[1] ? Number(hunkMatch[1]) : 1;
|
|
259
|
+
newLinesLeft = hunkMatch[2] ? Number(hunkMatch[2]) : 1;
|
|
260
|
+
lastOld = void 0;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (inHunk) {
|
|
264
|
+
const ch = line[0];
|
|
265
|
+
if (ch === "-") oldLinesLeft--;
|
|
266
|
+
else if (ch === "+") newLinesLeft--;
|
|
267
|
+
else if (ch === " " || ch === void 0) {
|
|
268
|
+
oldLinesLeft--;
|
|
269
|
+
newLinesLeft--;
|
|
270
|
+
}
|
|
271
|
+
if (oldLinesLeft <= 0 && newLinesLeft <= 0) inHunk = false;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const oldMatch = /^---\s+([^\t\r\n]+)/.exec(line);
|
|
275
|
+
if (oldMatch) {
|
|
276
|
+
lastOld = clean(oldMatch[1]);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const newMatch = /^\+\+\+\s+([^\t\r\n]+)/.exec(line);
|
|
280
|
+
if (!newMatch) continue;
|
|
281
|
+
const newTarget = clean(newMatch[1]);
|
|
282
|
+
if (newTarget && newTarget !== "/dev/null") {
|
|
283
|
+
out.push({ raw: newTarget, deleted: false });
|
|
284
|
+
} else if (lastOld && lastOld !== "/dev/null") {
|
|
285
|
+
out.push({ raw: lastOld, deleted: true });
|
|
286
|
+
}
|
|
287
|
+
lastOld = void 0;
|
|
162
288
|
}
|
|
163
289
|
return out;
|
|
164
290
|
}
|
|
165
291
|
function stripPathComponents(p, strip) {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
292
|
+
const s = p.replace(/\\/g, "/");
|
|
293
|
+
let idx = 0;
|
|
294
|
+
for (let i = 0; i < strip; i++) {
|
|
295
|
+
while (idx < s.length && s[idx] !== "/") idx++;
|
|
296
|
+
let hadSlash = false;
|
|
297
|
+
while (idx < s.length && s[idx] === "/") {
|
|
298
|
+
idx++;
|
|
299
|
+
hadSlash = true;
|
|
300
|
+
}
|
|
301
|
+
if (!hadSlash) return void 0;
|
|
302
|
+
}
|
|
303
|
+
return s.slice(idx) || void 0;
|
|
304
|
+
}
|
|
305
|
+
function runPatch(args, cwd, signal, fallback) {
|
|
306
|
+
return runPatchProcess("patch", args, cwd, signal).then(async (result) => {
|
|
307
|
+
if (!result.unavailable) return { ...result, engine: "patch" };
|
|
308
|
+
const gitArgs = [
|
|
309
|
+
"apply",
|
|
310
|
+
"--unsafe-paths",
|
|
311
|
+
`-p${fallback.strip}`,
|
|
312
|
+
"--verbose",
|
|
313
|
+
...fallback.dryRun ? ["--check"] : [],
|
|
314
|
+
fallback.patchFile
|
|
315
|
+
];
|
|
316
|
+
const gitResult = await runPatchProcess("git", gitArgs, cwd, signal);
|
|
317
|
+
return { ...gitResult, engine: "git" };
|
|
318
|
+
});
|
|
169
319
|
}
|
|
170
|
-
function
|
|
320
|
+
function runPatchProcess(command, args, cwd, signal) {
|
|
171
321
|
return new Promise((resolve3) => {
|
|
172
322
|
let stdout = "";
|
|
173
323
|
let stderr = "";
|
|
174
324
|
const env = { ...buildChildEnv(), LANG: "C", LC_ALL: "C" };
|
|
175
|
-
const child = spawn(
|
|
325
|
+
const child = spawn(command, args, {
|
|
176
326
|
cwd,
|
|
177
327
|
signal,
|
|
178
328
|
env,
|
|
@@ -185,13 +335,24 @@ function runPatch(args, cwd, signal) {
|
|
|
185
335
|
child.stderr?.on("data", (c) => {
|
|
186
336
|
stderr += c.toString();
|
|
187
337
|
});
|
|
188
|
-
child.on(
|
|
189
|
-
|
|
338
|
+
child.on(
|
|
339
|
+
"close",
|
|
340
|
+
(code) => resolve3({ exitCode: code ?? 1, stdout, stderr, unavailable: false })
|
|
341
|
+
);
|
|
342
|
+
child.on(
|
|
343
|
+
"error",
|
|
344
|
+
(e) => resolve3({
|
|
345
|
+
exitCode: 1,
|
|
346
|
+
stdout: "",
|
|
347
|
+
stderr: e.message,
|
|
348
|
+
unavailable: e.code === "ENOENT"
|
|
349
|
+
})
|
|
350
|
+
);
|
|
190
351
|
});
|
|
191
352
|
}
|
|
192
353
|
function extractPatchedFiles(output) {
|
|
193
354
|
const files = [];
|
|
194
|
-
const re = /patching file (.+)/gi;
|
|
355
|
+
const re = /(?:patching|checking) file (.+)/gi;
|
|
195
356
|
for (const m of output.matchAll(re)) {
|
|
196
357
|
if (m[1]) files.push(m[1]);
|
|
197
358
|
}
|
|
@@ -37,6 +37,12 @@ interface KillOpts {
|
|
|
37
37
|
graceMs?: number | undefined;
|
|
38
38
|
/** Leave explicitly backgrounded jobs alive. Default false. */
|
|
39
39
|
preserveBackground?: boolean | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Also kill processes marked `protected` (browser open, etc.).
|
|
42
|
+
* Default false. Host-process shutdown should pass true so protected
|
|
43
|
+
* children cannot strand the parent event loop (issue #322).
|
|
44
|
+
*/
|
|
45
|
+
includeProtected?: boolean | undefined;
|
|
40
46
|
}
|
|
41
47
|
/**
|
|
42
48
|
* Snapshot of the armed auto kill/reset countdown, or null when nothing is
|
package/dist/process-registry.js
CHANGED
|
@@ -503,7 +503,7 @@ var ProcessRegistryImpl = class {
|
|
|
503
503
|
const p = this.processes.get(pid);
|
|
504
504
|
if (!p) return false;
|
|
505
505
|
if (p.killed) return true;
|
|
506
|
-
if (p.protected) return false;
|
|
506
|
+
if (p.protected && opts.includeProtected !== true) return false;
|
|
507
507
|
if (opts.preserveBackground && p.background) return false;
|
|
508
508
|
const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
|
|
509
509
|
const isWin = os.platform() === "win32";
|
|
@@ -554,9 +554,13 @@ var ProcessRegistryImpl = class {
|
|
|
554
554
|
killAll(opts = {}) {
|
|
555
555
|
const pids = Array.from(this.processes.keys());
|
|
556
556
|
const killed = [];
|
|
557
|
+
const includeProtected = opts.includeProtected === true;
|
|
557
558
|
for (const pid of pids) {
|
|
558
559
|
const p = this.processes.get(pid);
|
|
559
|
-
if (
|
|
560
|
+
if (!p) continue;
|
|
561
|
+
if (p.protected && !includeProtected) continue;
|
|
562
|
+
if (opts.preserveBackground && p.background) continue;
|
|
563
|
+
if (this.kill(pid, opts)) killed.push(pid);
|
|
560
564
|
}
|
|
561
565
|
return killed;
|
|
562
566
|
}
|
package/dist/ps-slash.js
CHANGED
|
@@ -473,7 +473,7 @@ var ProcessRegistryImpl = class {
|
|
|
473
473
|
const p = this.processes.get(pid);
|
|
474
474
|
if (!p) return false;
|
|
475
475
|
if (p.killed) return true;
|
|
476
|
-
if (p.protected) return false;
|
|
476
|
+
if (p.protected && opts.includeProtected !== true) return false;
|
|
477
477
|
if (opts.preserveBackground && p.background) return false;
|
|
478
478
|
const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
|
|
479
479
|
const isWin = os.platform() === "win32";
|
|
@@ -524,9 +524,13 @@ var ProcessRegistryImpl = class {
|
|
|
524
524
|
killAll(opts = {}) {
|
|
525
525
|
const pids = Array.from(this.processes.keys());
|
|
526
526
|
const killed = [];
|
|
527
|
+
const includeProtected = opts.includeProtected === true;
|
|
527
528
|
for (const pid of pids) {
|
|
528
529
|
const p = this.processes.get(pid);
|
|
529
|
-
if (
|
|
530
|
+
if (!p) continue;
|
|
531
|
+
if (p.protected && !includeProtected) continue;
|
|
532
|
+
if (opts.preserveBackground && p.background) continue;
|
|
533
|
+
if (this.kill(pid, opts)) killed.push(pid);
|
|
530
534
|
}
|
|
531
535
|
return killed;
|
|
532
536
|
}
|