@wrongstack/plugins 0.1.0 → 0.9.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/auto-doc.js +0 -1
- package/dist/cost-tracker.js +4 -1
- package/dist/cron.js +2 -2
- package/dist/file-watcher.js +50 -3
- package/dist/git-autocommit.js +2 -2
- package/dist/index.js +169 -42
- package/dist/json-path.js +3 -3
- package/dist/semver-bump.js +4 -4
- package/dist/shell-check.js +10 -4
- package/dist/template-engine.js +8 -0
- package/dist/web-search.d.ts +0 -8
- package/dist/web-search.js +76 -5
- package/package.json +2 -2
package/dist/auto-doc.js
CHANGED
package/dist/cost-tracker.js
CHANGED
|
@@ -68,7 +68,10 @@ var plugin = {
|
|
|
68
68
|
sessionCost.totalCompletionTokens += completionTokens;
|
|
69
69
|
sessionCost.totalTokens += totalTokens;
|
|
70
70
|
sessionCost.totalCostUsd += costUsd;
|
|
71
|
-
|
|
71
|
+
if (sessionCost.byModel[model] === void 0) {
|
|
72
|
+
sessionCost.byModel[model] = { tokens: 0, costUsd: 0, requests: 0 };
|
|
73
|
+
}
|
|
74
|
+
const slot = sessionCost.byModel[model];
|
|
72
75
|
slot.tokens += totalTokens;
|
|
73
76
|
slot.costUsd += costUsd;
|
|
74
77
|
slot.requests += 1;
|
package/dist/cron.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/cron/index.ts
|
|
2
2
|
var API_VERSION = "^0.1.10";
|
|
3
3
|
function formatNextRun(intervalMs) {
|
|
4
|
-
const ms = isNaN(intervalMs) || intervalMs <= 0 ? 6e4 : intervalMs;
|
|
4
|
+
const ms = Number.isNaN(intervalMs) || intervalMs <= 0 ? 6e4 : intervalMs;
|
|
5
5
|
return new Date(Date.now() + ms).toISOString();
|
|
6
6
|
}
|
|
7
7
|
var plugin = {
|
|
@@ -124,7 +124,7 @@ var plugin = {
|
|
|
124
124
|
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
125
125
|
return { ok: false, error: "name is required and must be a non-empty string" };
|
|
126
126
|
}
|
|
127
|
-
if (isNaN(intervalMs)) {
|
|
127
|
+
if (Number.isNaN(intervalMs)) {
|
|
128
128
|
return { ok: false, error: "intervalMs must be a number >= 1000" };
|
|
129
129
|
}
|
|
130
130
|
if (state.jobs.has(name)) {
|
package/dist/file-watcher.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { watch } from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
2
3
|
|
|
3
4
|
// src/file-watcher/index.ts
|
|
4
5
|
var API_VERSION = "^0.1.10";
|
|
@@ -15,19 +16,31 @@ var plugin = {
|
|
|
15
16
|
defaultConfig: {
|
|
16
17
|
debounceMs: 500,
|
|
17
18
|
watchOnStartup: [],
|
|
18
|
-
autoUnwatchOnExit: true
|
|
19
|
+
autoUnwatchOnExit: true,
|
|
20
|
+
autoIndex: false,
|
|
21
|
+
indexProjectRoot: ""
|
|
19
22
|
},
|
|
20
23
|
configSchema: {
|
|
21
24
|
type: "object",
|
|
22
25
|
properties: {
|
|
23
26
|
debounceMs: { type: "number", default: 500 },
|
|
24
27
|
watchOnStartup: { type: "array", items: { type: "string" }, default: [] },
|
|
25
|
-
autoUnwatchOnExit: { type: "boolean", default: true }
|
|
28
|
+
autoUnwatchOnExit: { type: "boolean", default: true },
|
|
29
|
+
autoIndex: {
|
|
30
|
+
type: "boolean",
|
|
31
|
+
default: false,
|
|
32
|
+
description: "When true, automatically reindex changed .ts/.tsx/.js/.jsx files via codebase-index (incremental)"
|
|
33
|
+
},
|
|
34
|
+
indexProjectRoot: {
|
|
35
|
+
type: "string",
|
|
36
|
+
default: "",
|
|
37
|
+
description: "Project root directory for the indexer. Defaults to cwd when empty."
|
|
38
|
+
}
|
|
26
39
|
}
|
|
27
40
|
},
|
|
28
41
|
setup(api) {
|
|
29
42
|
const watches = /* @__PURE__ */ new Map();
|
|
30
|
-
|
|
43
|
+
const debounceMs = api.config.extensions?.["file-watcher"]?.["debounceMs"] ?? 500;
|
|
31
44
|
const debounceTimers = /* @__PURE__ */ new Map();
|
|
32
45
|
function debounceEvent(key, fn, ms) {
|
|
33
46
|
const existing = debounceTimers.get(key);
|
|
@@ -37,6 +50,12 @@ var plugin = {
|
|
|
37
50
|
fn();
|
|
38
51
|
}, ms));
|
|
39
52
|
}
|
|
53
|
+
const autoIndex = api.config.extensions?.["file-watcher"]?.["autoIndex"] ?? false;
|
|
54
|
+
const indexProjectRoot = api.config.extensions?.["file-watcher"]?.["indexProjectRoot"] ?? "";
|
|
55
|
+
const INDEXABLE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
56
|
+
function isIndexableFile(filePath) {
|
|
57
|
+
return INDEXABLE_EXTENSIONS.has(path.extname(filePath));
|
|
58
|
+
}
|
|
40
59
|
function safeWatchDir(dirPath, recursive, handle) {
|
|
41
60
|
try {
|
|
42
61
|
const watcher = watch(dirPath, { recursive }, (eventType, filename) => {
|
|
@@ -53,6 +72,34 @@ var plugin = {
|
|
|
53
72
|
});
|
|
54
73
|
api.metrics.counter("file_change", 1, { event: eventType ?? "unknown" });
|
|
55
74
|
api.log.debug(`file-watcher: ${eventType} ${fullPath} (watch=${handle.id})`);
|
|
75
|
+
if (autoIndex && isIndexableFile(fullPath)) {
|
|
76
|
+
debounceEvent(`index:${fullPath}`, async () => {
|
|
77
|
+
try {
|
|
78
|
+
const { runIndexer } = await import('@wrongstack/tools/codebase-index/index.js');
|
|
79
|
+
const root = indexProjectRoot || dirPath;
|
|
80
|
+
const fakeAppend = async () => {
|
|
81
|
+
};
|
|
82
|
+
const fakeClose = async () => {
|
|
83
|
+
};
|
|
84
|
+
const fakeRecordFileChange = () => {
|
|
85
|
+
};
|
|
86
|
+
const ctx = {
|
|
87
|
+
projectRoot: root,
|
|
88
|
+
cwd: root,
|
|
89
|
+
messages: [],
|
|
90
|
+
todos: [],
|
|
91
|
+
readFiles: /* @__PURE__ */ new Set(),
|
|
92
|
+
fileMtimes: /* @__PURE__ */ new Map(),
|
|
93
|
+
session: { id: "fw", append: fakeAppend, close: fakeClose, recordFileChange: fakeRecordFileChange }
|
|
94
|
+
};
|
|
95
|
+
await runIndexer(ctx, { projectRoot: root, files: [fullPath] });
|
|
96
|
+
api.metrics.counter("index_file", 1);
|
|
97
|
+
api.log.debug(`file-watcher: auto-index triggered for ${fullPath}`);
|
|
98
|
+
} catch (err) {
|
|
99
|
+
api.log.warn(`file-watcher: auto-index failed for ${fullPath}: ${err}`);
|
|
100
|
+
}
|
|
101
|
+
}, debounceMs);
|
|
102
|
+
}
|
|
56
103
|
}, debounceMs);
|
|
57
104
|
});
|
|
58
105
|
watcher.on("error", (err) => {
|
package/dist/git-autocommit.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
3
|
|
|
4
4
|
// src/git-autocommit/index.ts
|
|
5
5
|
var API_VERSION = "^0.1.10";
|
|
6
6
|
function runGit(args, cwd) {
|
|
7
7
|
try {
|
|
8
|
-
return
|
|
8
|
+
return execFileSync("git", args, {
|
|
9
9
|
encoding: "utf-8",
|
|
10
10
|
cwd,
|
|
11
11
|
stdio: ["pipe", "pipe", "pipe"],
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
1
|
+
import { execFileSync, execSync } from 'child_process';
|
|
2
2
|
import { watch, existsSync, readdirSync, readFileSync } from 'fs';
|
|
3
|
-
import
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { isAbsolute, join } from 'path';
|
|
5
|
+
import { lookup } from 'dns/promises';
|
|
6
|
+
import { isIPv4, isIPv6 } from 'net';
|
|
4
7
|
|
|
5
8
|
// src/auto-doc/index.ts
|
|
6
9
|
var AUTO_DOC_API_VERSION = "^0.1.10";
|
|
@@ -49,7 +52,6 @@ function parseSource(content) {
|
|
|
49
52
|
m = line.match(reInterface);
|
|
50
53
|
if (m) {
|
|
51
54
|
entities.push({ kind: "interface", name: m[1], startLine: i + 1 });
|
|
52
|
-
continue;
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
return entities;
|
|
@@ -242,7 +244,7 @@ var auto_doc_default = plugin;
|
|
|
242
244
|
var API_VERSION = "^0.1.10";
|
|
243
245
|
function runGit(args, cwd) {
|
|
244
246
|
try {
|
|
245
|
-
return
|
|
247
|
+
return execFileSync("git", args, {
|
|
246
248
|
encoding: "utf-8",
|
|
247
249
|
cwd,
|
|
248
250
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -562,7 +564,7 @@ function runShellCheck(files, severity, cwd) {
|
|
|
562
564
|
];
|
|
563
565
|
let raw;
|
|
564
566
|
try {
|
|
565
|
-
raw =
|
|
567
|
+
raw = execFileSync("shellcheck", args, {
|
|
566
568
|
encoding: "utf-8",
|
|
567
569
|
cwd,
|
|
568
570
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -670,7 +672,10 @@ var plugin3 = {
|
|
|
670
672
|
}
|
|
671
673
|
const byFile = {};
|
|
672
674
|
for (const issue of issues) {
|
|
673
|
-
(byFile[issue.file]
|
|
675
|
+
if (byFile[issue.file] === void 0) {
|
|
676
|
+
byFile[issue.file] = [];
|
|
677
|
+
}
|
|
678
|
+
byFile[issue.file].push(issue);
|
|
674
679
|
}
|
|
675
680
|
const errorCount = issues.filter((i) => i.level === "error").length;
|
|
676
681
|
const warningCount = issues.filter((i) => i.level === "warning").length;
|
|
@@ -736,7 +741,10 @@ var plugin3 = {
|
|
|
736
741
|
}
|
|
737
742
|
const byFile = {};
|
|
738
743
|
for (const issue of issues) {
|
|
739
|
-
(byFile[issue.file]
|
|
744
|
+
if (byFile[issue.file] === void 0) {
|
|
745
|
+
byFile[issue.file] = [];
|
|
746
|
+
}
|
|
747
|
+
byFile[issue.file].push(issue);
|
|
740
748
|
}
|
|
741
749
|
return {
|
|
742
750
|
ok: true,
|
|
@@ -827,7 +835,10 @@ var plugin4 = {
|
|
|
827
835
|
sessionCost.totalCompletionTokens += completionTokens;
|
|
828
836
|
sessionCost.totalTokens += totalTokens;
|
|
829
837
|
sessionCost.totalCostUsd += costUsd;
|
|
830
|
-
|
|
838
|
+
if (sessionCost.byModel[model] === void 0) {
|
|
839
|
+
sessionCost.byModel[model] = { tokens: 0, costUsd: 0, requests: 0 };
|
|
840
|
+
}
|
|
841
|
+
const slot = sessionCost.byModel[model];
|
|
831
842
|
slot.tokens += totalTokens;
|
|
832
843
|
slot.costUsd += costUsd;
|
|
833
844
|
slot.requests += 1;
|
|
@@ -978,19 +989,31 @@ var plugin5 = {
|
|
|
978
989
|
defaultConfig: {
|
|
979
990
|
debounceMs: 500,
|
|
980
991
|
watchOnStartup: [],
|
|
981
|
-
autoUnwatchOnExit: true
|
|
992
|
+
autoUnwatchOnExit: true,
|
|
993
|
+
autoIndex: false,
|
|
994
|
+
indexProjectRoot: ""
|
|
982
995
|
},
|
|
983
996
|
configSchema: {
|
|
984
997
|
type: "object",
|
|
985
998
|
properties: {
|
|
986
999
|
debounceMs: { type: "number", default: 500 },
|
|
987
1000
|
watchOnStartup: { type: "array", items: { type: "string" }, default: [] },
|
|
988
|
-
autoUnwatchOnExit: { type: "boolean", default: true }
|
|
1001
|
+
autoUnwatchOnExit: { type: "boolean", default: true },
|
|
1002
|
+
autoIndex: {
|
|
1003
|
+
type: "boolean",
|
|
1004
|
+
default: false,
|
|
1005
|
+
description: "When true, automatically reindex changed .ts/.tsx/.js/.jsx files via codebase-index (incremental)"
|
|
1006
|
+
},
|
|
1007
|
+
indexProjectRoot: {
|
|
1008
|
+
type: "string",
|
|
1009
|
+
default: "",
|
|
1010
|
+
description: "Project root directory for the indexer. Defaults to cwd when empty."
|
|
1011
|
+
}
|
|
989
1012
|
}
|
|
990
1013
|
},
|
|
991
1014
|
setup(api) {
|
|
992
1015
|
const watches = /* @__PURE__ */ new Map();
|
|
993
|
-
|
|
1016
|
+
const debounceMs = api.config.extensions?.["file-watcher"]?.["debounceMs"] ?? 500;
|
|
994
1017
|
const debounceTimers = /* @__PURE__ */ new Map();
|
|
995
1018
|
function debounceEvent(key, fn, ms) {
|
|
996
1019
|
const existing = debounceTimers.get(key);
|
|
@@ -1000,6 +1023,12 @@ var plugin5 = {
|
|
|
1000
1023
|
fn();
|
|
1001
1024
|
}, ms));
|
|
1002
1025
|
}
|
|
1026
|
+
const autoIndex = api.config.extensions?.["file-watcher"]?.["autoIndex"] ?? false;
|
|
1027
|
+
const indexProjectRoot = api.config.extensions?.["file-watcher"]?.["indexProjectRoot"] ?? "";
|
|
1028
|
+
const INDEXABLE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
1029
|
+
function isIndexableFile(filePath) {
|
|
1030
|
+
return INDEXABLE_EXTENSIONS.has(path.extname(filePath));
|
|
1031
|
+
}
|
|
1003
1032
|
function safeWatchDir(dirPath, recursive, handle) {
|
|
1004
1033
|
try {
|
|
1005
1034
|
const watcher = watch(dirPath, { recursive }, (eventType, filename) => {
|
|
@@ -1016,6 +1045,34 @@ var plugin5 = {
|
|
|
1016
1045
|
});
|
|
1017
1046
|
api.metrics.counter("file_change", 1, { event: eventType ?? "unknown" });
|
|
1018
1047
|
api.log.debug(`file-watcher: ${eventType} ${fullPath} (watch=${handle.id})`);
|
|
1048
|
+
if (autoIndex && isIndexableFile(fullPath)) {
|
|
1049
|
+
debounceEvent(`index:${fullPath}`, async () => {
|
|
1050
|
+
try {
|
|
1051
|
+
const { runIndexer } = await import('@wrongstack/tools/codebase-index/index.js');
|
|
1052
|
+
const root = indexProjectRoot || dirPath;
|
|
1053
|
+
const fakeAppend = async () => {
|
|
1054
|
+
};
|
|
1055
|
+
const fakeClose = async () => {
|
|
1056
|
+
};
|
|
1057
|
+
const fakeRecordFileChange = () => {
|
|
1058
|
+
};
|
|
1059
|
+
const ctx = {
|
|
1060
|
+
projectRoot: root,
|
|
1061
|
+
cwd: root,
|
|
1062
|
+
messages: [],
|
|
1063
|
+
todos: [],
|
|
1064
|
+
readFiles: /* @__PURE__ */ new Set(),
|
|
1065
|
+
fileMtimes: /* @__PURE__ */ new Map(),
|
|
1066
|
+
session: { id: "fw", append: fakeAppend, close: fakeClose, recordFileChange: fakeRecordFileChange }
|
|
1067
|
+
};
|
|
1068
|
+
await runIndexer(ctx, { projectRoot: root, files: [fullPath] });
|
|
1069
|
+
api.metrics.counter("index_file", 1);
|
|
1070
|
+
api.log.debug(`file-watcher: auto-index triggered for ${fullPath}`);
|
|
1071
|
+
} catch (err) {
|
|
1072
|
+
api.log.warn(`file-watcher: auto-index failed for ${fullPath}: ${err}`);
|
|
1073
|
+
}
|
|
1074
|
+
}, debounceMs);
|
|
1075
|
+
}
|
|
1019
1076
|
}, debounceMs);
|
|
1020
1077
|
});
|
|
1021
1078
|
watcher.on("error", (err) => {
|
|
@@ -1151,8 +1208,6 @@ var plugin5 = {
|
|
|
1151
1208
|
}
|
|
1152
1209
|
};
|
|
1153
1210
|
var file_watcher_default = plugin5;
|
|
1154
|
-
|
|
1155
|
-
// src/web-search/index.ts
|
|
1156
1211
|
var API_VERSION5 = "^0.1.10";
|
|
1157
1212
|
async function duckduckgoSearch(query, numResults) {
|
|
1158
1213
|
const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}&kl=us-en`;
|
|
@@ -1182,13 +1237,81 @@ async function duckduckgoSearch(query, numResults) {
|
|
|
1182
1237
|
}
|
|
1183
1238
|
return results;
|
|
1184
1239
|
}
|
|
1240
|
+
function isPrivateIPv4(host) {
|
|
1241
|
+
const parts = host.split(".").map(Number);
|
|
1242
|
+
if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return true;
|
|
1243
|
+
const [a, b] = parts;
|
|
1244
|
+
return a === 0 || // 0.0.0.0/8 "this host"
|
|
1245
|
+
a === 10 || // private
|
|
1246
|
+
a === 127 || // loopback
|
|
1247
|
+
a === 100 && b >= 64 && b <= 127 || // CGNAT 100.64/10
|
|
1248
|
+
a === 169 && b === 254 || // link-local incl. 169.254.169.254 (cloud IMDS)
|
|
1249
|
+
a === 172 && b >= 16 && b <= 31 || // private
|
|
1250
|
+
a === 192 && b === 168 || // private
|
|
1251
|
+
a >= 224;
|
|
1252
|
+
}
|
|
1253
|
+
function isPrivateIPv6(raw) {
|
|
1254
|
+
const host = raw.toLowerCase();
|
|
1255
|
+
if (host === "::1" || host === "::") return true;
|
|
1256
|
+
const mapped = host.match(/(?:::ffff:)?(\d+\.\d+\.\d+\.\d+)$/);
|
|
1257
|
+
if (mapped?.[1]) return isPrivateIPv4(mapped[1]);
|
|
1258
|
+
if (host.startsWith("fc") || host.startsWith("fd")) return true;
|
|
1259
|
+
if (host.startsWith("fe8") || host.startsWith("fe9") || host.startsWith("fea") || host.startsWith("feb"))
|
|
1260
|
+
return true;
|
|
1261
|
+
return false;
|
|
1262
|
+
}
|
|
1263
|
+
function assertSafeIp(ip) {
|
|
1264
|
+
if (isIPv4(ip) && isPrivateIPv4(ip)) {
|
|
1265
|
+
throw new Error(`Blocked private/loopback address: ${ip}`);
|
|
1266
|
+
}
|
|
1267
|
+
if (isIPv6(ip) && isPrivateIPv6(ip)) {
|
|
1268
|
+
throw new Error(`Blocked private/loopback address: ${ip}`);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
async function assertSafeUrl(rawUrl) {
|
|
1272
|
+
const u = new URL(rawUrl);
|
|
1273
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") {
|
|
1274
|
+
throw new Error(`Unsupported protocol: ${u.protocol}`);
|
|
1275
|
+
}
|
|
1276
|
+
const host = u.hostname.startsWith("[") && u.hostname.endsWith("]") ? u.hostname.slice(1, -1) : u.hostname;
|
|
1277
|
+
if (host === "localhost" || host.endsWith(".localhost") || host === "" || host === "0.0.0.0") {
|
|
1278
|
+
throw new Error("Blocked localhost target");
|
|
1279
|
+
}
|
|
1280
|
+
if (isIPv4(host) || isIPv6(host)) {
|
|
1281
|
+
assertSafeIp(host);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
let addrs;
|
|
1285
|
+
try {
|
|
1286
|
+
addrs = await lookup(host, { all: true });
|
|
1287
|
+
} catch {
|
|
1288
|
+
throw new Error(`Could not resolve host: ${host}`);
|
|
1289
|
+
}
|
|
1290
|
+
for (const { address } of addrs) assertSafeIp(address);
|
|
1291
|
+
}
|
|
1185
1292
|
async function fetchUrl(url, format) {
|
|
1186
|
-
const
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1293
|
+
const MAX_REDIRECTS = 5;
|
|
1294
|
+
let currentUrl = url;
|
|
1295
|
+
let resp;
|
|
1296
|
+
for (let i = 0; i <= MAX_REDIRECTS; i++) {
|
|
1297
|
+
await assertSafeUrl(currentUrl);
|
|
1298
|
+
resp = await fetch(currentUrl, {
|
|
1299
|
+
redirect: "manual",
|
|
1300
|
+
headers: {
|
|
1301
|
+
"User-Agent": "Mozilla/5.0 (compatible; WrongStack/1.0; +https://wrongstack.com)",
|
|
1302
|
+
Accept: format === "text" ? "text/plain" : "text/html"
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
if (resp.status >= 300 && resp.status < 400) {
|
|
1306
|
+
const loc = resp.headers.get("location");
|
|
1307
|
+
if (!loc) break;
|
|
1308
|
+
currentUrl = new URL(loc, currentUrl).toString();
|
|
1309
|
+
if (i === MAX_REDIRECTS) throw new Error("Too many redirects");
|
|
1310
|
+
continue;
|
|
1190
1311
|
}
|
|
1191
|
-
|
|
1312
|
+
break;
|
|
1313
|
+
}
|
|
1314
|
+
if (!resp) throw new Error(`Failed to fetch ${url}`);
|
|
1192
1315
|
if (!resp.ok) throw new Error(`Failed to fetch ${url}: ${resp.status} ${resp.statusText}`);
|
|
1193
1316
|
if (format === "text") {
|
|
1194
1317
|
return resp.text();
|
|
@@ -1376,7 +1499,7 @@ function jmespathSearch(data, query) {
|
|
|
1376
1499
|
}
|
|
1377
1500
|
const arrMatch = query.match(/^\[(\d+)\](?:\.(.+))?$/);
|
|
1378
1501
|
if (arrMatch) {
|
|
1379
|
-
const idx = parseInt(arrMatch[1], 10);
|
|
1502
|
+
const idx = Number.parseInt(arrMatch[1], 10);
|
|
1380
1503
|
const rest = arrMatch[2];
|
|
1381
1504
|
const arr = data;
|
|
1382
1505
|
const val = arr?.[idx];
|
|
@@ -1411,9 +1534,9 @@ function jmespathSearch(data, query) {
|
|
|
1411
1534
|
const itemVal = item[field];
|
|
1412
1535
|
switch (op) {
|
|
1413
1536
|
case "==":
|
|
1414
|
-
return itemVal
|
|
1537
|
+
return itemVal === cmpVal;
|
|
1415
1538
|
case "!=":
|
|
1416
|
-
return itemVal
|
|
1539
|
+
return itemVal !== cmpVal;
|
|
1417
1540
|
case ">":
|
|
1418
1541
|
return Number(itemVal) > Number(cmpVal);
|
|
1419
1542
|
case "<":
|
|
@@ -1456,46 +1579,46 @@ function jmespathSearch(data, query) {
|
|
|
1456
1579
|
}
|
|
1457
1580
|
function validateJsonSchema(data, schema) {
|
|
1458
1581
|
const errors = [];
|
|
1459
|
-
function check(value, s,
|
|
1582
|
+
function check(value, s, path2) {
|
|
1460
1583
|
if (s["type"]) {
|
|
1461
1584
|
const expectedType = s["type"];
|
|
1462
1585
|
const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
|
|
1463
1586
|
if (expectedType === "integer") {
|
|
1464
|
-
if (!Number.isInteger(value)) errors.push(`${
|
|
1587
|
+
if (!Number.isInteger(value)) errors.push(`${path2}: expected integer, got ${actualType}`);
|
|
1465
1588
|
} else if (expectedType !== actualType) {
|
|
1466
|
-
errors.push(`${
|
|
1589
|
+
errors.push(`${path2}: expected ${expectedType}, got ${actualType}`);
|
|
1467
1590
|
}
|
|
1468
1591
|
}
|
|
1469
1592
|
if (typeof value === "string" && s["format"] === "uri" && value) {
|
|
1470
1593
|
try {
|
|
1471
1594
|
new URL(value);
|
|
1472
1595
|
} catch {
|
|
1473
|
-
errors.push(`${
|
|
1596
|
+
errors.push(`${path2}: not a valid URI`);
|
|
1474
1597
|
}
|
|
1475
1598
|
}
|
|
1476
1599
|
if (typeof value === "string" && s["pattern"]) {
|
|
1477
1600
|
const re = new RegExp(s["pattern"]);
|
|
1478
|
-
if (!re.test(value)) errors.push(`${
|
|
1601
|
+
if (!re.test(value)) errors.push(`${path2}: does not match pattern ${s["pattern"]}`);
|
|
1479
1602
|
}
|
|
1480
1603
|
if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
|
|
1481
|
-
errors.push(`${
|
|
1604
|
+
errors.push(`${path2}: string too short (min ${s["minLength"]})`);
|
|
1482
1605
|
}
|
|
1483
1606
|
if (typeof value === "string" && s["maxLength"] !== void 0 && value.length > s["maxLength"]) {
|
|
1484
|
-
errors.push(`${
|
|
1607
|
+
errors.push(`${path2}: string too long (max ${s["maxLength"]})`);
|
|
1485
1608
|
}
|
|
1486
1609
|
if (typeof value === "number" && s["minimum"] !== void 0 && value < s["minimum"]) {
|
|
1487
|
-
errors.push(`${
|
|
1610
|
+
errors.push(`${path2}: below minimum ${s["minimum"]}`);
|
|
1488
1611
|
}
|
|
1489
1612
|
if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
|
|
1490
|
-
errors.push(`${
|
|
1613
|
+
errors.push(`${path2}: above maximum ${s["maximum"]}`);
|
|
1491
1614
|
}
|
|
1492
1615
|
if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
|
|
1493
|
-
value.forEach((item, i) => check(item, s["items"], `${
|
|
1616
|
+
value.forEach((item, i) => check(item, s["items"], `${path2}[${i}]`));
|
|
1494
1617
|
}
|
|
1495
1618
|
if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
|
|
1496
1619
|
const props = s["properties"];
|
|
1497
1620
|
for (const [k, propSchema] of Object.entries(props)) {
|
|
1498
|
-
check(value[k], propSchema, `${
|
|
1621
|
+
check(value[k], propSchema, `${path2}.${k}`);
|
|
1499
1622
|
}
|
|
1500
1623
|
}
|
|
1501
1624
|
}
|
|
@@ -1674,7 +1797,7 @@ var json_path_default = plugin7;
|
|
|
1674
1797
|
// src/cron/index.ts
|
|
1675
1798
|
var API_VERSION7 = "^0.1.10";
|
|
1676
1799
|
function formatNextRun(intervalMs) {
|
|
1677
|
-
const ms = isNaN(intervalMs) || intervalMs <= 0 ? 6e4 : intervalMs;
|
|
1800
|
+
const ms = Number.isNaN(intervalMs) || intervalMs <= 0 ? 6e4 : intervalMs;
|
|
1678
1801
|
return new Date(Date.now() + ms).toISOString();
|
|
1679
1802
|
}
|
|
1680
1803
|
var plugin8 = {
|
|
@@ -1797,7 +1920,7 @@ var plugin8 = {
|
|
|
1797
1920
|
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
1798
1921
|
return { ok: false, error: "name is required and must be a non-empty string" };
|
|
1799
1922
|
}
|
|
1800
|
-
if (isNaN(intervalMs)) {
|
|
1923
|
+
if (Number.isNaN(intervalMs)) {
|
|
1801
1924
|
return { ok: false, error: "intervalMs must be a number >= 1000" };
|
|
1802
1925
|
}
|
|
1803
1926
|
if (state.jobs.has(name)) {
|
|
@@ -1892,8 +2015,6 @@ var plugin8 = {
|
|
|
1892
2015
|
}
|
|
1893
2016
|
};
|
|
1894
2017
|
var cron_default = plugin8;
|
|
1895
|
-
|
|
1896
|
-
// src/template-engine/index.ts
|
|
1897
2018
|
var API_VERSION8 = "^0.1.10";
|
|
1898
2019
|
function expandTemplate(template, variables) {
|
|
1899
2020
|
let result = template;
|
|
@@ -2002,6 +2123,9 @@ var plugin9 = {
|
|
|
2002
2123
|
return { ok: false, error: String(err) };
|
|
2003
2124
|
}
|
|
2004
2125
|
if (outputPath) {
|
|
2126
|
+
if (isAbsolute(outputPath) || outputPath.includes("..")) {
|
|
2127
|
+
return { ok: false, error: 'outputPath must be a relative path without ".." components' };
|
|
2128
|
+
}
|
|
2005
2129
|
const { writeFileSync } = await import('fs');
|
|
2006
2130
|
writeFileSync(outputPath, result, "utf-8");
|
|
2007
2131
|
return {
|
|
@@ -2063,6 +2187,9 @@ var plugin9 = {
|
|
|
2063
2187
|
return { ok: false, error: `Template rendering failed: ${err}` };
|
|
2064
2188
|
}
|
|
2065
2189
|
if (outputPath) {
|
|
2190
|
+
if (isAbsolute(outputPath) || outputPath.includes("..")) {
|
|
2191
|
+
return { ok: false, error: 'outputPath must be a relative path without ".." components' };
|
|
2192
|
+
}
|
|
2066
2193
|
const { writeFileSync } = await import('fs');
|
|
2067
2194
|
writeFileSync(outputPath, result, "utf-8");
|
|
2068
2195
|
return {
|
|
@@ -2161,7 +2288,7 @@ var template_engine_default = plugin9;
|
|
|
2161
2288
|
var API_VERSION9 = "^0.1.10";
|
|
2162
2289
|
function runGit2(args, cwd) {
|
|
2163
2290
|
try {
|
|
2164
|
-
return
|
|
2291
|
+
return execFileSync("git", args, {
|
|
2165
2292
|
encoding: "utf-8",
|
|
2166
2293
|
cwd,
|
|
2167
2294
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -2174,10 +2301,10 @@ function runGit2(args, cwd) {
|
|
|
2174
2301
|
}
|
|
2175
2302
|
}
|
|
2176
2303
|
function getPackageJson(cwd) {
|
|
2177
|
-
const
|
|
2178
|
-
if (!existsSync(
|
|
2304
|
+
const path2 = cwd ? `${cwd}/package.json` : "package.json";
|
|
2305
|
+
if (!existsSync(path2)) return null;
|
|
2179
2306
|
try {
|
|
2180
|
-
return JSON.parse(readFileSync(
|
|
2307
|
+
return JSON.parse(readFileSync(path2, "utf-8"));
|
|
2181
2308
|
} catch {
|
|
2182
2309
|
return null;
|
|
2183
2310
|
}
|
|
@@ -2185,7 +2312,7 @@ function getPackageJson(cwd) {
|
|
|
2185
2312
|
function parseVersion(v) {
|
|
2186
2313
|
const m = v.match(/^v?(\d+)\.(\d+)\.(\d+)/);
|
|
2187
2314
|
if (!m) return [0, 0, 0];
|
|
2188
|
-
return [parseInt(m[1]), parseInt(m[2]), parseInt(m[3])];
|
|
2315
|
+
return [Number.parseInt(m[1]), Number.parseInt(m[2]), Number.parseInt(m[3])];
|
|
2189
2316
|
}
|
|
2190
2317
|
function bumpVersion(version, part) {
|
|
2191
2318
|
let [major, minor, patch] = parseVersion(version);
|
|
@@ -2421,7 +2548,7 @@ var plugin10 = {
|
|
|
2421
2548
|
latestTag = tagsOutput || null;
|
|
2422
2549
|
if (latestTag) {
|
|
2423
2550
|
const countOutput = runGit2(["rev-list", "--count", `${latestTag}..HEAD`], cwd);
|
|
2424
|
-
commitsSinceTag = parseInt(countOutput) || 0;
|
|
2551
|
+
commitsSinceTag = Number.parseInt(countOutput) || 0;
|
|
2425
2552
|
}
|
|
2426
2553
|
} catch {
|
|
2427
2554
|
latestTag = null;
|
package/dist/json-path.js
CHANGED
|
@@ -13,7 +13,7 @@ function jmespathSearch(data, query) {
|
|
|
13
13
|
}
|
|
14
14
|
const arrMatch = query.match(/^\[(\d+)\](?:\.(.+))?$/);
|
|
15
15
|
if (arrMatch) {
|
|
16
|
-
const idx = parseInt(arrMatch[1], 10);
|
|
16
|
+
const idx = Number.parseInt(arrMatch[1], 10);
|
|
17
17
|
const rest = arrMatch[2];
|
|
18
18
|
const arr = data;
|
|
19
19
|
const val = arr?.[idx];
|
|
@@ -48,9 +48,9 @@ function jmespathSearch(data, query) {
|
|
|
48
48
|
const itemVal = item[field];
|
|
49
49
|
switch (op) {
|
|
50
50
|
case "==":
|
|
51
|
-
return itemVal
|
|
51
|
+
return itemVal === cmpVal;
|
|
52
52
|
case "!=":
|
|
53
|
-
return itemVal
|
|
53
|
+
return itemVal !== cmpVal;
|
|
54
54
|
case ">":
|
|
55
55
|
return Number(itemVal) > Number(cmpVal);
|
|
56
56
|
case "<":
|
package/dist/semver-bump.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
2
|
import { existsSync, readFileSync } from 'fs';
|
|
3
3
|
|
|
4
4
|
// src/semver-bump/index.ts
|
|
5
5
|
var API_VERSION = "^0.1.10";
|
|
6
6
|
function runGit(args, cwd) {
|
|
7
7
|
try {
|
|
8
|
-
return
|
|
8
|
+
return execFileSync("git", args, {
|
|
9
9
|
encoding: "utf-8",
|
|
10
10
|
cwd,
|
|
11
11
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -29,7 +29,7 @@ function getPackageJson(cwd) {
|
|
|
29
29
|
function parseVersion(v) {
|
|
30
30
|
const m = v.match(/^v?(\d+)\.(\d+)\.(\d+)/);
|
|
31
31
|
if (!m) return [0, 0, 0];
|
|
32
|
-
return [parseInt(m[1]), parseInt(m[2]), parseInt(m[3])];
|
|
32
|
+
return [Number.parseInt(m[1]), Number.parseInt(m[2]), Number.parseInt(m[3])];
|
|
33
33
|
}
|
|
34
34
|
function bumpVersion(version, part) {
|
|
35
35
|
let [major, minor, patch] = parseVersion(version);
|
|
@@ -265,7 +265,7 @@ var plugin = {
|
|
|
265
265
|
latestTag = tagsOutput || null;
|
|
266
266
|
if (latestTag) {
|
|
267
267
|
const countOutput = runGit(["rev-list", "--count", `${latestTag}..HEAD`], cwd);
|
|
268
|
-
commitsSinceTag = parseInt(countOutput) || 0;
|
|
268
|
+
commitsSinceTag = Number.parseInt(countOutput) || 0;
|
|
269
269
|
}
|
|
270
270
|
} catch {
|
|
271
271
|
latestTag = null;
|
package/dist/shell-check.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
1
|
+
import { execSync, execFileSync } from 'child_process';
|
|
2
2
|
import { existsSync, readdirSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
|
|
@@ -27,7 +27,7 @@ function runShellCheck(files, severity, cwd) {
|
|
|
27
27
|
];
|
|
28
28
|
let raw;
|
|
29
29
|
try {
|
|
30
|
-
raw =
|
|
30
|
+
raw = execFileSync("shellcheck", args, {
|
|
31
31
|
encoding: "utf-8",
|
|
32
32
|
cwd,
|
|
33
33
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -135,7 +135,10 @@ var plugin = {
|
|
|
135
135
|
}
|
|
136
136
|
const byFile = {};
|
|
137
137
|
for (const issue of issues) {
|
|
138
|
-
(byFile[issue.file]
|
|
138
|
+
if (byFile[issue.file] === void 0) {
|
|
139
|
+
byFile[issue.file] = [];
|
|
140
|
+
}
|
|
141
|
+
byFile[issue.file].push(issue);
|
|
139
142
|
}
|
|
140
143
|
const errorCount = issues.filter((i) => i.level === "error").length;
|
|
141
144
|
const warningCount = issues.filter((i) => i.level === "warning").length;
|
|
@@ -201,7 +204,10 @@ var plugin = {
|
|
|
201
204
|
}
|
|
202
205
|
const byFile = {};
|
|
203
206
|
for (const issue of issues) {
|
|
204
|
-
(byFile[issue.file]
|
|
207
|
+
if (byFile[issue.file] === void 0) {
|
|
208
|
+
byFile[issue.file] = [];
|
|
209
|
+
}
|
|
210
|
+
byFile[issue.file].push(issue);
|
|
205
211
|
}
|
|
206
212
|
return {
|
|
207
213
|
ok: true,
|
package/dist/template-engine.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isAbsolute } from 'path';
|
|
2
|
+
|
|
1
3
|
// src/template-engine/index.ts
|
|
2
4
|
var API_VERSION = "^0.1.10";
|
|
3
5
|
function expandTemplate(template, variables) {
|
|
@@ -107,6 +109,9 @@ var plugin = {
|
|
|
107
109
|
return { ok: false, error: String(err) };
|
|
108
110
|
}
|
|
109
111
|
if (outputPath) {
|
|
112
|
+
if (isAbsolute(outputPath) || outputPath.includes("..")) {
|
|
113
|
+
return { ok: false, error: 'outputPath must be a relative path without ".." components' };
|
|
114
|
+
}
|
|
110
115
|
const { writeFileSync } = await import('fs');
|
|
111
116
|
writeFileSync(outputPath, result, "utf-8");
|
|
112
117
|
return {
|
|
@@ -168,6 +173,9 @@ var plugin = {
|
|
|
168
173
|
return { ok: false, error: `Template rendering failed: ${err}` };
|
|
169
174
|
}
|
|
170
175
|
if (outputPath) {
|
|
176
|
+
if (isAbsolute(outputPath) || outputPath.includes("..")) {
|
|
177
|
+
return { ok: false, error: 'outputPath must be a relative path without ".." components' };
|
|
178
|
+
}
|
|
171
179
|
const { writeFileSync } = await import('fs');
|
|
172
180
|
writeFileSync(outputPath, result, "utf-8");
|
|
173
181
|
return {
|
package/dist/web-search.d.ts
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { Plugin } from '@wrongstack/core';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* web-search plugin — Cached web search with deduplication and ranking.
|
|
5
|
-
*
|
|
6
|
-
* Tools registered:
|
|
7
|
-
* - web_search: Search the web with caching and deduplication
|
|
8
|
-
* - web_fetch: Fetch a URL and return content as markdown
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
3
|
declare const plugin: Plugin;
|
|
12
4
|
|
|
13
5
|
export { plugin as default };
|
package/dist/web-search.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { lookup } from 'dns/promises';
|
|
2
|
+
import { isIPv4, isIPv6 } from 'net';
|
|
3
|
+
|
|
1
4
|
// src/web-search/index.ts
|
|
2
5
|
var API_VERSION = "^0.1.10";
|
|
3
6
|
async function duckduckgoSearch(query, numResults) {
|
|
@@ -28,13 +31,81 @@ async function duckduckgoSearch(query, numResults) {
|
|
|
28
31
|
}
|
|
29
32
|
return results;
|
|
30
33
|
}
|
|
34
|
+
function isPrivateIPv4(host) {
|
|
35
|
+
const parts = host.split(".").map(Number);
|
|
36
|
+
if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return true;
|
|
37
|
+
const [a, b] = parts;
|
|
38
|
+
return a === 0 || // 0.0.0.0/8 "this host"
|
|
39
|
+
a === 10 || // private
|
|
40
|
+
a === 127 || // loopback
|
|
41
|
+
a === 100 && b >= 64 && b <= 127 || // CGNAT 100.64/10
|
|
42
|
+
a === 169 && b === 254 || // link-local incl. 169.254.169.254 (cloud IMDS)
|
|
43
|
+
a === 172 && b >= 16 && b <= 31 || // private
|
|
44
|
+
a === 192 && b === 168 || // private
|
|
45
|
+
a >= 224;
|
|
46
|
+
}
|
|
47
|
+
function isPrivateIPv6(raw) {
|
|
48
|
+
const host = raw.toLowerCase();
|
|
49
|
+
if (host === "::1" || host === "::") return true;
|
|
50
|
+
const mapped = host.match(/(?:::ffff:)?(\d+\.\d+\.\d+\.\d+)$/);
|
|
51
|
+
if (mapped?.[1]) return isPrivateIPv4(mapped[1]);
|
|
52
|
+
if (host.startsWith("fc") || host.startsWith("fd")) return true;
|
|
53
|
+
if (host.startsWith("fe8") || host.startsWith("fe9") || host.startsWith("fea") || host.startsWith("feb"))
|
|
54
|
+
return true;
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
function assertSafeIp(ip) {
|
|
58
|
+
if (isIPv4(ip) && isPrivateIPv4(ip)) {
|
|
59
|
+
throw new Error(`Blocked private/loopback address: ${ip}`);
|
|
60
|
+
}
|
|
61
|
+
if (isIPv6(ip) && isPrivateIPv6(ip)) {
|
|
62
|
+
throw new Error(`Blocked private/loopback address: ${ip}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function assertSafeUrl(rawUrl) {
|
|
66
|
+
const u = new URL(rawUrl);
|
|
67
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") {
|
|
68
|
+
throw new Error(`Unsupported protocol: ${u.protocol}`);
|
|
69
|
+
}
|
|
70
|
+
const host = u.hostname.startsWith("[") && u.hostname.endsWith("]") ? u.hostname.slice(1, -1) : u.hostname;
|
|
71
|
+
if (host === "localhost" || host.endsWith(".localhost") || host === "" || host === "0.0.0.0") {
|
|
72
|
+
throw new Error("Blocked localhost target");
|
|
73
|
+
}
|
|
74
|
+
if (isIPv4(host) || isIPv6(host)) {
|
|
75
|
+
assertSafeIp(host);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
let addrs;
|
|
79
|
+
try {
|
|
80
|
+
addrs = await lookup(host, { all: true });
|
|
81
|
+
} catch {
|
|
82
|
+
throw new Error(`Could not resolve host: ${host}`);
|
|
83
|
+
}
|
|
84
|
+
for (const { address } of addrs) assertSafeIp(address);
|
|
85
|
+
}
|
|
31
86
|
async function fetchUrl(url, format) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
87
|
+
const MAX_REDIRECTS = 5;
|
|
88
|
+
let currentUrl = url;
|
|
89
|
+
let resp;
|
|
90
|
+
for (let i = 0; i <= MAX_REDIRECTS; i++) {
|
|
91
|
+
await assertSafeUrl(currentUrl);
|
|
92
|
+
resp = await fetch(currentUrl, {
|
|
93
|
+
redirect: "manual",
|
|
94
|
+
headers: {
|
|
95
|
+
"User-Agent": "Mozilla/5.0 (compatible; WrongStack/1.0; +https://wrongstack.com)",
|
|
96
|
+
Accept: format === "text" ? "text/plain" : "text/html"
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
if (resp.status >= 300 && resp.status < 400) {
|
|
100
|
+
const loc = resp.headers.get("location");
|
|
101
|
+
if (!loc) break;
|
|
102
|
+
currentUrl = new URL(loc, currentUrl).toString();
|
|
103
|
+
if (i === MAX_REDIRECTS) throw new Error("Too many redirects");
|
|
104
|
+
continue;
|
|
36
105
|
}
|
|
37
|
-
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
if (!resp) throw new Error(`Failed to fetch ${url}`);
|
|
38
109
|
if (!resp.ok) throw new Error(`Failed to fetch ${url}: ${resp.status} ${resp.statusText}`);
|
|
39
110
|
if (format === "text") {
|
|
40
111
|
return resp.text();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/plugins",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Official WrongStack plugin collection — auto-doc, git-autocommit, shell-check, cost-tracker, file-watcher, web-search, json-path, cron, template-engine, semver-bump",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ECOSTACK TECHNOLOGY OÜ",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"vitest": "^3.0.0"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@wrongstack/core": "0.
|
|
66
|
+
"@wrongstack/core": "0.9.0"
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"build": "tsup",
|