@wrongstack/tools 0.5.7 → 0.6.1
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/builtin.js +69 -62
- package/dist/builtin.js.map +1 -1
- package/dist/exec.js +9 -2
- package/dist/exec.js.map +1 -1
- package/dist/fetch.js.map +1 -1
- package/dist/grep.js +9 -4
- package/dist/grep.js.map +1 -1
- package/dist/index.js +69 -62
- package/dist/index.js.map +1 -1
- package/dist/logs.js +9 -4
- package/dist/logs.js.map +1 -1
- package/dist/outdated.js +2 -7
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +69 -62
- package/dist/pack.js.map +1 -1
- package/dist/replace.js +9 -4
- package/dist/replace.js.map +1 -1
- package/dist/scaffold.js +2 -1
- package/dist/scaffold.js.map +1 -1
- package/package.json +2 -2
package/dist/builtin.js
CHANGED
|
@@ -5,15 +5,11 @@ import { dirname } from 'path';
|
|
|
5
5
|
import * as os from 'os';
|
|
6
6
|
import { statSync } from 'fs';
|
|
7
7
|
import * as fs9 from 'fs/promises';
|
|
8
|
+
import { stat } from 'fs/promises';
|
|
8
9
|
import * as dns from 'dns/promises';
|
|
9
10
|
import * as net from 'net';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
13
|
-
}) : x)(function(x) {
|
|
14
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
15
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
16
|
-
});
|
|
12
|
+
// src/_spawn-stream.ts
|
|
17
13
|
async function* spawnStream(opts) {
|
|
18
14
|
const max = opts.maxBytes ?? 2e5;
|
|
19
15
|
const flushAt = opts.flushBytes ?? 4 * 1024;
|
|
@@ -173,14 +169,14 @@ var auditTool = {
|
|
|
173
169
|
}
|
|
174
170
|
};
|
|
175
171
|
async function detectManager(cwd) {
|
|
176
|
-
const { stat:
|
|
172
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
177
173
|
try {
|
|
178
|
-
await
|
|
174
|
+
await stat10(`${cwd}/pnpm-lock.yaml`);
|
|
179
175
|
return "pnpm";
|
|
180
176
|
} catch {
|
|
181
177
|
}
|
|
182
178
|
try {
|
|
183
|
-
await
|
|
179
|
+
await stat10(`${cwd}/yarn.lock`);
|
|
184
180
|
return "yarn";
|
|
185
181
|
} catch {
|
|
186
182
|
}
|
|
@@ -966,8 +962,8 @@ function findGitDir(cwd) {
|
|
|
966
962
|
let dir = cwd;
|
|
967
963
|
for (let i = 0; i < 20; i++) {
|
|
968
964
|
try {
|
|
969
|
-
const
|
|
970
|
-
if (
|
|
965
|
+
const stat10 = statSync(path.join(dir, ".git"));
|
|
966
|
+
if (stat10.isDirectory()) return dir;
|
|
971
967
|
} catch {
|
|
972
968
|
}
|
|
973
969
|
const parent = path.dirname(dir);
|
|
@@ -1006,8 +1002,8 @@ async function fileDiff(input, ctx, signal) {
|
|
|
1006
1002
|
const results = [];
|
|
1007
1003
|
for (const file of files) {
|
|
1008
1004
|
const absPath = safeResolve(file, ctx);
|
|
1009
|
-
const
|
|
1010
|
-
if (!
|
|
1005
|
+
const stat10 = await fs9.stat(absPath).catch(() => null);
|
|
1006
|
+
if (!stat10?.isFile()) continue;
|
|
1011
1007
|
const content = await fs9.readFile(absPath, "utf8");
|
|
1012
1008
|
const lines = content.split(/\r?\n/);
|
|
1013
1009
|
results.push(`--- ${file}
|
|
@@ -1105,8 +1101,8 @@ async function resolveFiles(filesInput, cwd) {
|
|
|
1105
1101
|
for (const f of files) {
|
|
1106
1102
|
const absPath = f.trim().startsWith("/") ? f.trim() : `${cwd}/${f.trim()}`;
|
|
1107
1103
|
try {
|
|
1108
|
-
const
|
|
1109
|
-
if (
|
|
1104
|
+
const stat10 = await fs9.stat(absPath);
|
|
1105
|
+
if (stat10.isFile()) resolved.push(absPath);
|
|
1110
1106
|
} catch {
|
|
1111
1107
|
}
|
|
1112
1108
|
}
|
|
@@ -1197,13 +1193,13 @@ var editTool = {
|
|
|
1197
1193
|
if (input.new_string === void 0) throw new Error("edit: new_string is required");
|
|
1198
1194
|
if (input.old_string === "") throw new Error("edit: old_string cannot be empty");
|
|
1199
1195
|
const absPath = safeResolve(input.path, ctx);
|
|
1200
|
-
const
|
|
1196
|
+
const stat10 = await fs9.stat(absPath).catch((err) => {
|
|
1201
1197
|
if (err.code === "ENOENT") {
|
|
1202
1198
|
throw new Error(`edit: file "${input.path}" does not exist. Use \`write\` instead.`);
|
|
1203
1199
|
}
|
|
1204
1200
|
throw err;
|
|
1205
1201
|
});
|
|
1206
|
-
if (!
|
|
1202
|
+
if (!stat10.isFile()) throw new Error(`edit: "${input.path}" is not a regular file`);
|
|
1207
1203
|
if (!ctx.hasRead(absPath)) {
|
|
1208
1204
|
throw new Error(`edit: file "${input.path}" was not read in this session. Read it first.`);
|
|
1209
1205
|
}
|
|
@@ -1347,14 +1343,21 @@ var BLOCKED_ARG_PATTERNS = {
|
|
|
1347
1343
|
// go run could execute arbitrary .go files; -ldflags could inject build-time code
|
|
1348
1344
|
go: [/^-ldflags$/],
|
|
1349
1345
|
// bun --preload is similar to node --require
|
|
1350
|
-
bun: [/^--preload$/],
|
|
1346
|
+
bun: [/^--preload$/, /^run$/, /^bunx$/, /^create$/, /^init$/],
|
|
1351
1347
|
// docker build/run can create containers with host access;
|
|
1352
1348
|
// only allow read-only commands (ps, images, version)
|
|
1353
1349
|
docker: [/^build$/, /^run$/, /^exec$/, /^push$/, /^pull$/],
|
|
1354
1350
|
// find -exec/-ok/-execdir execute arbitrary commands
|
|
1355
1351
|
find: [/^-exec$/, /^-exec;$/, /^-ok$/, /^-ok;$/, /^-execdir$/, /^-execdir;$/, /^-exec=/, /^-ok=/, /^-execdir=/],
|
|
1356
1352
|
// rm -rf / is catastrophic — block root and home targets
|
|
1357
|
-
rm: [/^\/$/, /^\/\*$/, /^~$/]
|
|
1353
|
+
rm: [/^\/$/, /^\/\*$/, /^~$/],
|
|
1354
|
+
// npm run/exec/create/pack/publish can execute arbitrary scripts or publish malware
|
|
1355
|
+
npm: [/^run$/, /^exec$/, /^create$/, /^init$/, /^pack$/, /^publish$/, /^deploy$/],
|
|
1356
|
+
// pnpm run/dlx/exec/create can execute arbitrary scripts
|
|
1357
|
+
pnpm: [/^run$/, /^dlx$/, /^exec$/, /^create$/, /^init$/, /^pack$/, /^publish$/, /^deploy$/],
|
|
1358
|
+
// npx should only be used for --version; any package name is a vector for
|
|
1359
|
+
// malicious package execution (typosquatting, dependency confusion)
|
|
1360
|
+
npx: [/^[^\s]+$/]
|
|
1358
1361
|
};
|
|
1359
1362
|
function validateArgs(cmd, args) {
|
|
1360
1363
|
const blocked = BLOCKED_ARG_PATTERNS[cmd];
|
|
@@ -1888,13 +1891,13 @@ var formatTool = {
|
|
|
1888
1891
|
}
|
|
1889
1892
|
};
|
|
1890
1893
|
async function detectFixer(cwd) {
|
|
1891
|
-
const { stat:
|
|
1894
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
1892
1895
|
try {
|
|
1893
|
-
await
|
|
1896
|
+
await stat10(`${cwd}/biome.json`);
|
|
1894
1897
|
return "biome";
|
|
1895
1898
|
} catch {
|
|
1896
1899
|
try {
|
|
1897
|
-
await
|
|
1900
|
+
await stat10(`${cwd}/.prettierrc`);
|
|
1898
1901
|
return "prettier";
|
|
1899
1902
|
} catch {
|
|
1900
1903
|
return "biome";
|
|
@@ -1971,8 +1974,8 @@ function findGitDir2(cwd, projectRoot) {
|
|
|
1971
1974
|
let dir = cwd;
|
|
1972
1975
|
for (let i = 0; i < 20; i++) {
|
|
1973
1976
|
try {
|
|
1974
|
-
const
|
|
1975
|
-
if (
|
|
1977
|
+
const stat10 = statSync(`${dir}/.git`);
|
|
1978
|
+
if (stat10.isDirectory()) return dir;
|
|
1976
1979
|
} catch {
|
|
1977
1980
|
}
|
|
1978
1981
|
if (dir === root) break;
|
|
@@ -2145,12 +2148,17 @@ async function readGitignore(dir) {
|
|
|
2145
2148
|
}
|
|
2146
2149
|
|
|
2147
2150
|
// src/_regex.ts
|
|
2148
|
-
var MAX_PATTERN_LEN =
|
|
2151
|
+
var MAX_PATTERN_LEN = 256;
|
|
2149
2152
|
var DANGEROUS_PATTERNS = [
|
|
2150
|
-
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
2151
2153
|
// (a+)+, (.*)+, etc — nested quantifier on a group with internal quantifier
|
|
2152
|
-
/(\(
|
|
2153
|
-
|
|
2154
|
+
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
2155
|
+
/(\(\?:[^)]*[+*][^)]*\))[+*]/,
|
|
2156
|
+
// Adjacent quantifiers: a++ a*+
|
|
2157
|
+
/[+*]{2,}/,
|
|
2158
|
+
// Quantifier on alternation with length 2+
|
|
2159
|
+
/\([^|)]+\|[^)]+\)[+*][+*]/,
|
|
2160
|
+
// Greedy quantifier inside lookahead/lookbehind — (?!.*a+)
|
|
2161
|
+
/[\(\[][^)\]]*[+*][^)\]]*[\)\]][^)]*\?\??/
|
|
2154
2162
|
];
|
|
2155
2163
|
function compileUserRegex(pattern, flags) {
|
|
2156
2164
|
if (typeof pattern !== "string") {
|
|
@@ -2406,8 +2414,8 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
2406
2414
|
if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;
|
|
2407
2415
|
if (globRe) globRe.lastIndex = 0;
|
|
2408
2416
|
try {
|
|
2409
|
-
const
|
|
2410
|
-
if (
|
|
2417
|
+
const stat10 = await fs9.stat(full);
|
|
2418
|
+
if (stat10.size > 1e6) continue;
|
|
2411
2419
|
const head = await fs9.readFile(full);
|
|
2412
2420
|
if (isBinaryBuffer(head)) continue;
|
|
2413
2421
|
const text = head.toString("utf8");
|
|
@@ -2546,13 +2554,13 @@ var installTool = {
|
|
|
2546
2554
|
}
|
|
2547
2555
|
};
|
|
2548
2556
|
async function detectPackageManager(cwd) {
|
|
2549
|
-
const { stat:
|
|
2557
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
2550
2558
|
try {
|
|
2551
|
-
await
|
|
2559
|
+
await stat10(`${cwd}/pnpm-lock.yaml`);
|
|
2552
2560
|
return "pnpm";
|
|
2553
2561
|
} catch {
|
|
2554
2562
|
try {
|
|
2555
|
-
await
|
|
2563
|
+
await stat10(`${cwd}/yarn.lock`);
|
|
2556
2564
|
return "yarn";
|
|
2557
2565
|
} catch {
|
|
2558
2566
|
return "npm";
|
|
@@ -2759,11 +2767,11 @@ var lintTool = {
|
|
|
2759
2767
|
}
|
|
2760
2768
|
};
|
|
2761
2769
|
async function detectLinter(cwd) {
|
|
2762
|
-
const { stat:
|
|
2770
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
2763
2771
|
const checks = ["biome.json", ".eslintrc.json", "tslint.json", ".eslintrc.js", "tsconfig.json"];
|
|
2764
2772
|
for (const f of checks) {
|
|
2765
2773
|
try {
|
|
2766
|
-
await
|
|
2774
|
+
await stat10(`${cwd}/${f}`);
|
|
2767
2775
|
if (f.includes("biome")) return "biome";
|
|
2768
2776
|
if (f.includes("eslint")) return "eslint";
|
|
2769
2777
|
if (f.includes("tslint")) return "tslint";
|
|
@@ -2996,14 +3004,13 @@ var outdatedTool = {
|
|
|
2996
3004
|
}
|
|
2997
3005
|
};
|
|
2998
3006
|
async function detectManager2(cwd) {
|
|
2999
|
-
const { stat: stat9 } = __require("fs/promises");
|
|
3000
3007
|
try {
|
|
3001
|
-
await
|
|
3008
|
+
await stat(`${cwd}/pnpm-lock.yaml`);
|
|
3002
3009
|
return "pnpm";
|
|
3003
3010
|
} catch {
|
|
3004
3011
|
}
|
|
3005
3012
|
try {
|
|
3006
|
-
await
|
|
3013
|
+
await stat(`${cwd}/yarn.lock`);
|
|
3007
3014
|
return "yarn";
|
|
3008
3015
|
} catch {
|
|
3009
3016
|
}
|
|
@@ -3339,17 +3346,17 @@ var readTool = {
|
|
|
3339
3346
|
async execute(input, ctx) {
|
|
3340
3347
|
if (!input?.path) throw new Error("read: path is required");
|
|
3341
3348
|
const absPath = safeResolve(input.path, ctx);
|
|
3342
|
-
let
|
|
3349
|
+
let stat10;
|
|
3343
3350
|
try {
|
|
3344
|
-
|
|
3351
|
+
stat10 = await fs9.stat(absPath);
|
|
3345
3352
|
} catch (err) {
|
|
3346
3353
|
const code = err.code;
|
|
3347
3354
|
if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
|
|
3348
3355
|
throw new Error(`read: failed to stat "${input.path}": ${err instanceof Error ? err.message : String(err)}`);
|
|
3349
3356
|
}
|
|
3350
|
-
if (!
|
|
3351
|
-
if (
|
|
3352
|
-
throw new Error(`read: file too large (${
|
|
3357
|
+
if (!stat10.isFile()) throw new Error(`read: "${input.path}" is not a regular file`);
|
|
3358
|
+
if (stat10.size > MAX_BYTES2) {
|
|
3359
|
+
throw new Error(`read: file too large (${stat10.size} bytes, limit ${MAX_BYTES2})`);
|
|
3353
3360
|
}
|
|
3354
3361
|
const buf = await fs9.readFile(absPath);
|
|
3355
3362
|
if (isBinaryBuffer(buf)) {
|
|
@@ -3361,14 +3368,14 @@ var readTool = {
|
|
|
3361
3368
|
const offset = Math.max(1, input.offset ?? 1);
|
|
3362
3369
|
const limit = Math.max(0, Math.min(input.limit ?? 2e3, 5e3));
|
|
3363
3370
|
if (limit === 0) {
|
|
3364
|
-
ctx.recordRead(absPath,
|
|
3371
|
+
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
3365
3372
|
return { text: "", total_lines: total, encoding: "utf8", truncated: total > 0 };
|
|
3366
3373
|
}
|
|
3367
3374
|
const slice = allLines.slice(offset - 1, offset - 1 + limit);
|
|
3368
3375
|
const truncated = offset - 1 + slice.length < total;
|
|
3369
3376
|
const width = String(offset + slice.length - 1).length;
|
|
3370
3377
|
const numbered = slice.map((line, i) => `${String(offset + i).padStart(width, " ")}\u2192${line}`).join("\n");
|
|
3371
|
-
ctx.recordRead(absPath,
|
|
3378
|
+
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
3372
3379
|
return {
|
|
3373
3380
|
text: numbered,
|
|
3374
3381
|
total_lines: total,
|
|
@@ -3435,8 +3442,8 @@ var replaceTool = {
|
|
|
3435
3442
|
}
|
|
3436
3443
|
const rel = path.relative(ctx.projectRoot, realPath);
|
|
3437
3444
|
if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
|
|
3438
|
-
const
|
|
3439
|
-
if (!
|
|
3445
|
+
const stat10 = await fs9.stat(realPath).catch(() => null);
|
|
3446
|
+
if (!stat10 || !stat10.isFile()) continue;
|
|
3440
3447
|
let content;
|
|
3441
3448
|
try {
|
|
3442
3449
|
const buf = await fs9.readFile(realPath);
|
|
@@ -3461,7 +3468,7 @@ var replaceTool = {
|
|
|
3461
3468
|
totalReplacements += count;
|
|
3462
3469
|
if (!dryRun) {
|
|
3463
3470
|
const newContent = toStyle(newContentLf, style);
|
|
3464
|
-
await atomicWrite(realPath, newContent, { mode:
|
|
3471
|
+
await atomicWrite(realPath, newContent, { mode: stat10.mode & 511 });
|
|
3465
3472
|
}
|
|
3466
3473
|
const diff = dryRun || matches.length > 0 ? unifiedDiff(content, toStyle(newContentLf, style), {
|
|
3467
3474
|
fromFile: absPath,
|
|
@@ -3491,8 +3498,8 @@ async function resolveFiles2(filesInput, ctx, extraGlob) {
|
|
|
3491
3498
|
const resolved = [];
|
|
3492
3499
|
for (const p of parts) {
|
|
3493
3500
|
const absPath = safeResolve(p, ctx);
|
|
3494
|
-
const
|
|
3495
|
-
if (
|
|
3501
|
+
const stat10 = await fs9.stat(absPath).catch(() => null);
|
|
3502
|
+
if (stat10?.isFile()) {
|
|
3496
3503
|
resolved.push(absPath);
|
|
3497
3504
|
}
|
|
3498
3505
|
}
|
|
@@ -3551,8 +3558,8 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
3551
3558
|
if (DEFAULT_IGNORE3.includes(e.name)) continue;
|
|
3552
3559
|
const full = path.join(dir, e.name);
|
|
3553
3560
|
try {
|
|
3554
|
-
const
|
|
3555
|
-
if (
|
|
3561
|
+
const stat10 = await fs9.lstat(full);
|
|
3562
|
+
if (stat10.isSymbolicLink()) continue;
|
|
3556
3563
|
} catch {
|
|
3557
3564
|
continue;
|
|
3558
3565
|
}
|
|
@@ -3728,7 +3735,7 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
|
|
|
3728
3735
|
const fullPath = target;
|
|
3729
3736
|
if (!dryRun) {
|
|
3730
3737
|
await fs9.mkdir(path.dirname(fullPath), { recursive: true });
|
|
3731
|
-
await
|
|
3738
|
+
await atomicWrite(fullPath, substituteVars(content, name, vars));
|
|
3732
3739
|
}
|
|
3733
3740
|
files.push(resolvedPath);
|
|
3734
3741
|
filesCreated++;
|
|
@@ -4038,11 +4045,11 @@ var testTool = {
|
|
|
4038
4045
|
}
|
|
4039
4046
|
};
|
|
4040
4047
|
async function detectRunner(cwd) {
|
|
4041
|
-
const { stat:
|
|
4048
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
4042
4049
|
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
4043
4050
|
for (const f of candidates) {
|
|
4044
4051
|
try {
|
|
4045
|
-
await
|
|
4052
|
+
await stat10(path.join(cwd, f));
|
|
4046
4053
|
if (f.includes("vitest")) return "vitest";
|
|
4047
4054
|
if (f.includes("jest")) return "jest";
|
|
4048
4055
|
if (f.includes("mocha")) return "mocha";
|
|
@@ -4661,11 +4668,11 @@ var typecheckTool = {
|
|
|
4661
4668
|
}
|
|
4662
4669
|
};
|
|
4663
4670
|
async function findTsConfig(cwd) {
|
|
4664
|
-
const { stat:
|
|
4671
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
4665
4672
|
const candidates = ["tsconfig.json", "tsconfig.base.json"];
|
|
4666
4673
|
for (const f of candidates) {
|
|
4667
4674
|
try {
|
|
4668
|
-
const s = await
|
|
4675
|
+
const s = await stat10(path.join(cwd, f));
|
|
4669
4676
|
if (s.isFile()) return path.join(cwd, f);
|
|
4670
4677
|
} catch {
|
|
4671
4678
|
}
|
|
@@ -4695,12 +4702,12 @@ var writeTool = {
|
|
|
4695
4702
|
let existed = false;
|
|
4696
4703
|
let prev = "";
|
|
4697
4704
|
try {
|
|
4698
|
-
const
|
|
4699
|
-
existed =
|
|
4705
|
+
const stat11 = await fs9.stat(absPath);
|
|
4706
|
+
existed = stat11.isFile();
|
|
4700
4707
|
if (existed) {
|
|
4701
4708
|
if (!ctx.hasRead(absPath)) {
|
|
4702
4709
|
prev = await fs9.readFile(absPath, "utf8");
|
|
4703
|
-
ctx.recordRead(absPath,
|
|
4710
|
+
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
4704
4711
|
} else {
|
|
4705
4712
|
prev = await fs9.readFile(absPath, "utf8");
|
|
4706
4713
|
}
|
|
@@ -4713,8 +4720,8 @@ var writeTool = {
|
|
|
4713
4720
|
await atomicWrite(absPath, input.content);
|
|
4714
4721
|
const diff = existed ? unifiedDiff(prev, input.content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
|
|
4715
4722
|
+ (new file, ${input.content.split("\n").length} lines)`;
|
|
4716
|
-
const
|
|
4717
|
-
ctx.recordRead(absPath,
|
|
4723
|
+
const stat10 = await fs9.stat(absPath);
|
|
4724
|
+
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
4718
4725
|
ctx.session.recordFileChange({
|
|
4719
4726
|
path: absPath,
|
|
4720
4727
|
action: existed ? "modified" : "created",
|