@wrongstack/core 0.310.1 → 0.313.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/chronicle/index.js +84 -6391
- package/dist/chronicle/project-server.js +84 -4868
- package/dist/chunk-2BUUIMQB.js +225 -0
- package/dist/chunk-2FK4WTFF.js +207 -0
- package/dist/chunk-2FQL4PDC.js +292 -0
- package/dist/chunk-2KKODIQU.js +2256 -0
- package/dist/chunk-2YU4WVBW.js +134 -0
- package/dist/chunk-3E244UDA.js +356 -0
- package/dist/chunk-3XPVB7BA.js +135 -0
- package/dist/chunk-4LJJYBAU.js +388 -0
- package/dist/chunk-4VWO3ZXL.js +342 -0
- package/dist/chunk-4XV6IR37.js +187 -0
- package/dist/chunk-4ZHTKJHM.js +183 -0
- package/dist/chunk-52RTVH4M.js +361 -0
- package/dist/chunk-57GEWK5X.js +26 -0
- package/dist/chunk-5DSMEVA4.js +931 -0
- package/dist/chunk-5RP3QTRL.js +1223 -0
- package/dist/chunk-5S437PRC.js +50 -0
- package/dist/chunk-5XDUGTKN.js +511 -0
- package/dist/chunk-62V2AFVG.js +422 -0
- package/dist/chunk-65RDIOR2.js +2470 -0
- package/dist/chunk-6AKOTGLM.js +597 -0
- package/dist/chunk-7ZHZE5EO.js +28 -0
- package/dist/chunk-A455ATZB.js +140 -0
- package/dist/chunk-AD7ZFZCS.js +46 -0
- package/dist/chunk-AI4LLQ2U.js +856 -0
- package/dist/chunk-ASX6AW36.js +789 -0
- package/dist/chunk-AT4V4XGI.js +3522 -0
- package/dist/chunk-AWNQ6ED6.js +311 -0
- package/dist/chunk-B4AMLZPB.js +9 -0
- package/dist/chunk-BAKVJLEV.js +417 -0
- package/dist/chunk-BJHVULHO.js +55 -0
- package/dist/chunk-CB2HFKTP.js +9 -0
- package/dist/chunk-CE4ZOCOI.js +1640 -0
- package/dist/chunk-CQQL4ZOQ.js +151 -0
- package/dist/chunk-DEIQMBD2.js +65 -0
- package/dist/chunk-E6I5RXVK.js +58 -0
- package/dist/chunk-EFEHDQGY.js +1589 -0
- package/dist/chunk-EODDVJBM.js +82 -0
- package/dist/chunk-EPC3GZD4.js +308 -0
- package/dist/chunk-FAYRX2AD.js +13 -0
- package/dist/chunk-FFSX2QDQ.js +362 -0
- package/dist/chunk-FHAMQS7Q.js +5856 -0
- package/dist/chunk-FIV4IJL7.js +2263 -0
- package/dist/chunk-FNXVT7HF.js +52 -0
- package/dist/chunk-FSWCWSTS.js +395 -0
- package/dist/chunk-FW5LDDMJ.js +16464 -0
- package/dist/chunk-FXQLIX3K.js +211 -0
- package/dist/chunk-G5AAKEEZ.js +228 -0
- package/dist/chunk-G5S3S5ZN.js +38 -0
- package/dist/chunk-GAZEWQEZ.js +231 -0
- package/dist/chunk-GFJOMSHG.js +125 -0
- package/dist/chunk-GFXZTHNI.js +158 -0
- package/dist/chunk-GM54GJBU.js +960 -0
- package/dist/chunk-GPQ3NJPR.js +900 -0
- package/dist/chunk-GTCU6DZ3.js +1 -0
- package/dist/chunk-H5E77KUJ.js +15 -0
- package/dist/chunk-H72H4XDU.js +1023 -0
- package/dist/chunk-HA4ZYA6T.js +178 -0
- package/dist/chunk-I45KAW2N.js +5020 -0
- package/dist/chunk-IQ35HMQL.js +1396 -0
- package/dist/chunk-IYFBFX34.js +86 -0
- package/dist/chunk-JJPMWPB6.js +109 -0
- package/dist/chunk-JTHKOYWU.js +663 -0
- package/dist/chunk-JVGPR3TS.js +365 -0
- package/dist/chunk-JVKB2TBL.js +48 -0
- package/dist/chunk-JX7VBZDK.js +31 -0
- package/dist/chunk-K3G6JBUC.js +5232 -0
- package/dist/chunk-K4YUNKU7.js +589 -0
- package/dist/chunk-KBZGE6LN.js +710 -0
- package/dist/chunk-KDQJCT2J.js +45 -0
- package/dist/chunk-KI4L2DUL.js +560 -0
- package/dist/chunk-KJ2ZTQSL.js +47 -0
- package/dist/chunk-KSJ5UTP7.js +154 -0
- package/dist/chunk-LHOA5DWA.js +28 -0
- package/dist/chunk-LX5QWLBL.js +27 -0
- package/dist/chunk-LZDDLNCZ.js +279 -0
- package/dist/chunk-M2WF6AXG.js +115 -0
- package/dist/chunk-M7BS2K6U.js +1 -0
- package/dist/chunk-NDBIPFNY.js +102 -0
- package/dist/chunk-NEMIWEXC.js +1199 -0
- package/dist/chunk-NFAYHPRH.js +323 -0
- package/dist/chunk-O37BQ65S.js +21 -0
- package/dist/chunk-O74MJHXU.js +187 -0
- package/dist/chunk-OJZEX73F.js +112 -0
- package/dist/chunk-PAAWLLZX.js +74 -0
- package/dist/chunk-PFXFQQRT.js +339 -0
- package/dist/chunk-PFZO3C6H.js +14 -0
- package/dist/chunk-PHH7LD45.js +113 -0
- package/dist/chunk-QRTG4XTI.js +188 -0
- package/dist/chunk-QVOVMXJR.js +1351 -0
- package/dist/chunk-RCQYT2I4.js +757 -0
- package/dist/chunk-RD6DEE36.js +1178 -0
- package/dist/chunk-RJIZEJ42.js +30 -0
- package/dist/chunk-RPOA7IAE.js +150 -0
- package/dist/chunk-SN2DQRL6.js +1172 -0
- package/dist/chunk-SQJVFEBV.js +41 -0
- package/dist/chunk-SQP64RCS.js +5391 -0
- package/dist/chunk-SVT4RNTR.js +49 -0
- package/dist/chunk-SWUFN7XM.js +43 -0
- package/dist/chunk-SXMVYC5H.js +174 -0
- package/dist/chunk-SZ6AYUIZ.js +23 -0
- package/dist/chunk-T3KGZBU5.js +3171 -0
- package/dist/chunk-TBRL2RCC.js +9 -0
- package/dist/chunk-TRT2HRWZ.js +18 -0
- package/dist/chunk-TYW2GI5I.js +1077 -0
- package/dist/chunk-UIWONWVG.js +4959 -0
- package/dist/chunk-VB3H5WWB.js +151 -0
- package/dist/chunk-W4A6QA3D.js +258 -0
- package/dist/chunk-WBPVKEEE.js +227 -0
- package/dist/chunk-WEBMSQP2.js +123 -0
- package/dist/chunk-WOEZV3Q4.js +333 -0
- package/dist/chunk-WQQON3SV.js +473 -0
- package/dist/chunk-WQTOVMGV.js +119 -0
- package/dist/chunk-WZO23AWO.js +2779 -0
- package/dist/chunk-XBDP4QPW.js +7 -0
- package/dist/chunk-XEBHSJLT.js +103 -0
- package/dist/chunk-XQ2ECNK3.js +1 -0
- package/dist/chunk-XQ2XGIBF.js +2086 -0
- package/dist/chunk-Y2GE5TSI.js +355 -0
- package/dist/chunk-YBUJCZ57.js +351 -0
- package/dist/chunk-YNCVSFGF.js +149 -0
- package/dist/chunk-YUPMXOGP.js +282 -0
- package/dist/chunk-YY6APYUM.js +49 -0
- package/dist/chunk-Z2IJUO45.js +691 -0
- package/dist/chunk-ZEHUCJHY.js +129 -0
- package/dist/chunk-ZYU2YOJ6.js +1496 -0
- package/dist/chunk-ZZ4V3WIZ.js +163 -0
- package/dist/coordination/agents/index.js +145 -6260
- package/dist/coordination/index.js +365 -34323
- package/dist/coordination/mailbox-project-server.js +48 -3220
- package/dist/coordination/mailbox-types.d.ts +13 -1
- package/dist/coordination/provider-status-tracker.d.ts +4 -0
- package/dist/coordination/sqlite-mailbox.d.ts +2 -1
- package/dist/core/agent.d.ts +14 -1
- package/dist/core/index.js +267 -12479
- package/dist/core/model-ref.js +5 -23
- package/dist/core/provider-runner.d.ts +36 -1
- package/dist/design/index.js +50 -1087
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +1599 -21887
- package/dist/execution/prompt-enhancer.js +16 -535
- package/dist/execution/retry-policy.d.ts +31 -1
- package/dist/extension/index.js +7 -311
- package/dist/goal/index.js +33 -3435
- package/dist/hooks/index.js +14 -1138
- package/dist/hq/index.js +112 -5139
- package/dist/hq/protocol.js +21 -668
- package/dist/index.js +1410 -92655
- package/dist/infrastructure/index.js +64 -4811
- package/dist/kernel/events/provider-events.d.ts +15 -0
- package/dist/kernel/events/wrongtrace-events.d.ts +26 -0
- package/dist/kernel/events.d.ts +16 -7
- package/dist/kernel/index.js +14 -992
- package/dist/models/index.js +40 -1117
- package/dist/notifications/index.js +3 -108
- package/dist/observability/index.js +17 -279
- package/dist/plugin/index.js +584 -14835
- package/dist/prompts/index.js +14 -948
- package/dist/registry/index.js +9 -822
- package/dist/registry-R23UBE7H.js +14 -0
- package/dist/replay/index.js +6 -84
- package/dist/review-finding-store-DKAV3AS6.js +21 -0
- package/dist/review-report-store-OWY3EC3N.js +19 -0
- package/dist/security/index.js +114 -2202
- package/dist/session-catalog/index.js +36 -2103
- package/dist/session-catalog/project-server.js +42 -1435
- package/dist/skills/index.js +35 -1578
- package/dist/storage/index.js +182 -15600
- package/dist/tasking/index.js +10 -676
- package/dist/tools/index.js +52 -9589
- package/dist/types/config/autonomy.d.ts +5 -0
- package/dist/types/config/tools.d.ts +2 -2
- package/dist/types/config/ui.d.ts +1 -1
- package/dist/types/errors.d.ts +18 -0
- package/dist/types/index.js +121 -1341
- package/dist/types/provider-runner.d.ts +8 -0
- package/dist/types/session-markers.js +8 -144
- package/dist/utils/error.js +3 -4
- package/dist/utils/expect-defined.js +3 -9
- package/dist/utils/heap-watchdog.js +6 -706
- package/dist/utils/index.js +269 -5685
- package/dist/utils/sage-output-block.js +5 -42
- package/dist/wiring/proxy-rewrite.d.ts +92 -4
- package/dist/wiring/proxy-rewrite.js +20 -61
- package/dist/worktree/index.js +6 -887
- package/package.json +4 -4
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// src/chronicle/prompt-manifest.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
function createChroniclePromptManifest(request) {
|
|
4
|
+
const roleCounts = {}, blockCounts = {};
|
|
5
|
+
const messages = request.messages.map((message, index) => messageSummary(message, index, roleCounts, blockCounts));
|
|
6
|
+
const systemText = (request.system ?? []).map((block) => block.text).join("\n");
|
|
7
|
+
const toolRecords = (request.tools ?? []).map((tool) => ({
|
|
8
|
+
name: tool.name,
|
|
9
|
+
schemaHash: hash(stable(tool.inputSchema)),
|
|
10
|
+
permission: tool.permission,
|
|
11
|
+
mutating: tool.mutating,
|
|
12
|
+
riskTier: tool.riskTier,
|
|
13
|
+
capabilities: tool.capabilities,
|
|
14
|
+
estimatedTokens: tool._estDefTokens ?? 0
|
|
15
|
+
}));
|
|
16
|
+
const core = {
|
|
17
|
+
systemHash: hash(systemText),
|
|
18
|
+
messages: messages.map(({ hash: contentHash, ...rest }) => ({ ...rest, contentHash })),
|
|
19
|
+
tools: toolRecords,
|
|
20
|
+
model: request.model,
|
|
21
|
+
maxTokens: request.maxTokens,
|
|
22
|
+
temperature: request.temperature,
|
|
23
|
+
topP: request.topP,
|
|
24
|
+
topK: request.topK,
|
|
25
|
+
seed: request.seed,
|
|
26
|
+
toolChoice: request.toolChoice,
|
|
27
|
+
reasoning: request.reasoning,
|
|
28
|
+
cache: request.cache,
|
|
29
|
+
responseFormat: request.responseFormat?.type
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
manifestId: `prompt_${hash(stable(core))}`,
|
|
33
|
+
messageCount: messages.length,
|
|
34
|
+
estimatedMessageTokens: messages.reduce((sum, message) => sum + (message.estimatedTokens ?? 0), 0),
|
|
35
|
+
contentBytes: messages.reduce((sum, message) => sum + message.bytes, 0) + Buffer.byteLength(systemText),
|
|
36
|
+
roleCounts,
|
|
37
|
+
blockCounts,
|
|
38
|
+
system: { blockCount: request.system?.length ?? 0, bytes: Buffer.byteLength(systemText), hash: hash(systemText) },
|
|
39
|
+
messages,
|
|
40
|
+
tools: {
|
|
41
|
+
count: toolRecords.length,
|
|
42
|
+
estimatedDefinitionTokens: toolRecords.reduce((sum, tool) => sum + tool.estimatedTokens, 0),
|
|
43
|
+
manifestHash: hash(stable(toolRecords)),
|
|
44
|
+
names: toolRecords.map((tool) => tool.name),
|
|
45
|
+
mutating: toolRecords.filter((tool) => tool.mutating).length,
|
|
46
|
+
destructive: toolRecords.filter((tool) => tool.riskTier === "destructive").length
|
|
47
|
+
},
|
|
48
|
+
request: {
|
|
49
|
+
maxTokens: request.maxTokens,
|
|
50
|
+
temperature: request.temperature,
|
|
51
|
+
topP: request.topP,
|
|
52
|
+
topK: request.topK,
|
|
53
|
+
frequencyPenalty: request.frequencyPenalty,
|
|
54
|
+
presencePenalty: request.presencePenalty,
|
|
55
|
+
seed: request.seed,
|
|
56
|
+
candidateCount: request.candidateCount,
|
|
57
|
+
logprobs: request.logprobs,
|
|
58
|
+
topLogprobs: request.topLogprobs,
|
|
59
|
+
stopSequenceCount: request.stopSequences?.length ?? 0,
|
|
60
|
+
toolChoice: request.toolChoice,
|
|
61
|
+
reasoning: request.reasoning,
|
|
62
|
+
cache: request.cache,
|
|
63
|
+
responseFormat: request.responseFormat?.type,
|
|
64
|
+
safetySettingCount: request.safetySettings?.length ?? 0,
|
|
65
|
+
userHash: request.user ? hash(request.user) : void 0
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function messageSummary(message, index, roles, totals) {
|
|
70
|
+
roles[message.role] = (roles[message.role] ?? 0) + 1;
|
|
71
|
+
const blocks = typeof message.content === "string" ? { text: 1 } : countBlocks(message.content);
|
|
72
|
+
for (const [type, count] of Object.entries(blocks)) totals[type] = (totals[type] ?? 0) + count;
|
|
73
|
+
const content = contentIdentity(message.content);
|
|
74
|
+
return {
|
|
75
|
+
index,
|
|
76
|
+
role: message.role,
|
|
77
|
+
bytes: content.bytes,
|
|
78
|
+
...message._estTokens !== void 0 ? { estimatedTokens: message._estTokens } : {},
|
|
79
|
+
hash: content.hash,
|
|
80
|
+
blocks
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function countBlocks(blocks) {
|
|
84
|
+
const counts = {};
|
|
85
|
+
for (const block of blocks) counts[block.type] = (counts[block.type] ?? 0) + 1;
|
|
86
|
+
return counts;
|
|
87
|
+
}
|
|
88
|
+
function hash(value) {
|
|
89
|
+
return createHash("sha256").update(value).digest("hex");
|
|
90
|
+
}
|
|
91
|
+
function contentIdentity(content) {
|
|
92
|
+
if (typeof content === "string") return { hash: hash(content), bytes: Buffer.byteLength(content) };
|
|
93
|
+
const digest = createHash("sha256");
|
|
94
|
+
let bytes = 0;
|
|
95
|
+
const add = (value) => {
|
|
96
|
+
if (!value) return;
|
|
97
|
+
digest.update(value);
|
|
98
|
+
bytes += Buffer.byteLength(value);
|
|
99
|
+
};
|
|
100
|
+
for (const block of content) {
|
|
101
|
+
add(block.type);
|
|
102
|
+
if (block.type === "text") add(block.text);
|
|
103
|
+
else if (block.type === "thinking") {
|
|
104
|
+
add(block.thinking);
|
|
105
|
+
add(block.signature);
|
|
106
|
+
} else if (block.type === "tool_use") {
|
|
107
|
+
add(block.id);
|
|
108
|
+
add(block.name);
|
|
109
|
+
add(stable(block.input));
|
|
110
|
+
} else if (block.type === "tool_result") {
|
|
111
|
+
add(block.tool_use_id);
|
|
112
|
+
add(block.name);
|
|
113
|
+
add(block.content);
|
|
114
|
+
add(String(block.is_error ?? false));
|
|
115
|
+
} else if (block.type === "image") {
|
|
116
|
+
add(block.source.type);
|
|
117
|
+
add(block.source.media_type);
|
|
118
|
+
add(block.source.url);
|
|
119
|
+
add(block.source.data);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { hash: digest.digest("hex"), bytes };
|
|
123
|
+
}
|
|
124
|
+
function stable(value) {
|
|
125
|
+
if (value === void 0) return "undefined";
|
|
126
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
127
|
+
if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
|
|
128
|
+
return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, child]) => `${JSON.stringify(key)}:${stable(child)}`).join(",")}}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
createChroniclePromptManifest
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=chunk-2YU4WVBW.js.map
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
// src/security/secret-scrubber.ts
|
|
2
|
+
var JSON_CREDENTIAL_KEY_ANCHORS = [
|
|
3
|
+
'Key"',
|
|
4
|
+
'key"',
|
|
5
|
+
'KEY"',
|
|
6
|
+
'token"',
|
|
7
|
+
'Token"',
|
|
8
|
+
'TOKEN"',
|
|
9
|
+
'secret"',
|
|
10
|
+
'Secret"',
|
|
11
|
+
'SECRET"',
|
|
12
|
+
'password"',
|
|
13
|
+
'Password"',
|
|
14
|
+
'PASSWORD"',
|
|
15
|
+
'authorization"',
|
|
16
|
+
'Authorization"',
|
|
17
|
+
'bearer"',
|
|
18
|
+
'Bearer"'
|
|
19
|
+
];
|
|
20
|
+
var PATTERNS = [
|
|
21
|
+
// Anchored at the start where possible so partial matches inside larger
|
|
22
|
+
// strings don't trigger false positives.
|
|
23
|
+
{
|
|
24
|
+
type: "anthropic_key",
|
|
25
|
+
regex: /(?<![A-Za-z0-9])sk-ant-api\d+-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g,
|
|
26
|
+
anchor: "sk-ant-"
|
|
27
|
+
},
|
|
28
|
+
{ type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g, anchor: "sk-" },
|
|
29
|
+
{
|
|
30
|
+
// `xai` is a first-class provider in this codebase, but its key shape was
|
|
31
|
+
// absent here — so the one credential format WrongStack itself hands users
|
|
32
|
+
// was the one the scrubber could not recognize (audit 2026-08-20).
|
|
33
|
+
type: "xai_key",
|
|
34
|
+
regex: /(?<![A-Za-z0-9])xai-[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
|
|
35
|
+
anchor: "xai-"
|
|
36
|
+
},
|
|
37
|
+
{ type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g, anchor: "ghp_" },
|
|
38
|
+
{ type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g, anchor: "github_pat_" },
|
|
39
|
+
{ type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g, anchor: "AKIA" },
|
|
40
|
+
{ type: "gcp_key", regex: /(?<![A-Za-z0-9])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9])/g, anchor: "AIza" },
|
|
41
|
+
{ type: "slack_token", regex: /(?<![A-Za-z0-9-])xox[abpos]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g, anchor: "xox" },
|
|
42
|
+
{
|
|
43
|
+
type: "stripe_key",
|
|
44
|
+
regex: /(?<![A-Za-z0-9])sk_(?:live|test)_[A-Za-z0-9]{24,}(?![A-Za-z0-9])/g,
|
|
45
|
+
anchor: "sk_"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "twilio_sid",
|
|
49
|
+
regex: /(?<![A-Za-z0-9])AC[a-f0-9]{32}(?![A-Za-z0-9])/g,
|
|
50
|
+
anchor: "AC"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: "telegram_bot_token",
|
|
54
|
+
// Telegram tokens are of the form bot<digits>:<alphanum> in URL paths
|
|
55
|
+
regex: /\/bot\d+:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
56
|
+
anchor: "/bot"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: "jwt",
|
|
60
|
+
// Anchored: look for literal "eyJ" which is unambiguous for JWT header
|
|
61
|
+
regex: /(?<![A-Za-z0-9/+=])eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}(?![A-Za-z0-9/+=])/g,
|
|
62
|
+
anchor: "eyJ"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
type: "private_key",
|
|
66
|
+
// Anchored: start must be BEGIN, end must be END with no extra dashes after END
|
|
67
|
+
regex: /(?:^|\n)-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----[\s\S]*?-----END[^-]*-----(?:\n|$)/g,
|
|
68
|
+
anchor: "-----BEGIN"
|
|
69
|
+
},
|
|
70
|
+
{ type: "mongodb_uri", regex: /mongodb(?:\+srv)?:\/\/[^\s"'`]+/g, anchor: "mongodb" },
|
|
71
|
+
{ type: "postgres_uri", regex: /postgres(?:ql)?:\/\/[^\s"'`]+/g, anchor: "postgres" },
|
|
72
|
+
{ type: "mysql_uri", regex: /mysql:\/\/[^\s"'`]+/g, anchor: "mysql://" },
|
|
73
|
+
{ type: "redis_uri", regex: /redis:\/\/[^\s"'`]+/g, anchor: "redis://" },
|
|
74
|
+
// AI/ML provider keys — modern LLM services with well-known prefixes
|
|
75
|
+
{
|
|
76
|
+
type: "huggingface_token",
|
|
77
|
+
// HuggingFace tokens: hf_ followed by 34 alphanumeric chars
|
|
78
|
+
regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g,
|
|
79
|
+
anchor: "hf_"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: "replicate_token",
|
|
83
|
+
// Replicate tokens: r8_ followed by 40+ alphanumeric chars
|
|
84
|
+
regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
85
|
+
anchor: "r8_"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: "perplexity_key",
|
|
89
|
+
// Perplexity API keys: pplx- followed by 40+ alphanumeric chars
|
|
90
|
+
regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
91
|
+
anchor: "pplx-"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: "groq_key",
|
|
95
|
+
// Groq API keys: gsk_ followed by 40+ alphanumeric chars
|
|
96
|
+
regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
97
|
+
anchor: "gsk_"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: "bearer_token",
|
|
101
|
+
// Anchored with alternation instead of negative lookahead — avoids V8
|
|
102
|
+
// backtracking risk on adversarial input. Bounded at 512 chars.
|
|
103
|
+
// Min 12 chars: some OAuth providers issue shorter-lived tokens (< 20
|
|
104
|
+
// chars). A 12-char base64 string has ~71 bits of entropy — above the
|
|
105
|
+
// threshold where random strings are unlikely to produce false matches.
|
|
106
|
+
// The trailing boundary is a NON-consuming lookahead: two adjacent bearer
|
|
107
|
+
// tokens sharing a single delimiter (`Bearer a… Bearer b…`) must both be
|
|
108
|
+
// redacted. A consuming trailing delimiter would eat the separator the
|
|
109
|
+
// next match needs for its leading anchor, leaking the second token.
|
|
110
|
+
regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?=$|[^A-Za-z0-9_.~+/-])/g,
|
|
111
|
+
anchor: "Bearer"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: "high_entropy_env",
|
|
115
|
+
// Anchored with alternation instead of lookbehind to avoid backtracking.
|
|
116
|
+
// Value bounded at 512 chars.
|
|
117
|
+
// The trailing boundary is a NON-consuming lookahead so two secrets
|
|
118
|
+
// separated by a single delimiter (one space OR one newline, e.g.
|
|
119
|
+
// `printenv` / `.env` dumps: `API_KEY=… \n SESSION_TOKEN=…`) are BOTH
|
|
120
|
+
// redacted. A consuming trailing `\s` would swallow the separator the
|
|
121
|
+
// next match needs for its leading anchor, so every other secret would
|
|
122
|
+
// leak in plaintext.
|
|
123
|
+
// The leading delimiter is CAPTURED (group 1) and re-emitted by the
|
|
124
|
+
// replacement so the separator between adjacent secrets is preserved
|
|
125
|
+
// rather than collapsed. Capture groups are therefore: 1=leading
|
|
126
|
+
// delimiter, 2=key name, 3=value.
|
|
127
|
+
regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD|PASSPHRASE))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
|
|
128
|
+
anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD", "PASSPHRASE"]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
type: "json_credential_key",
|
|
132
|
+
// The JSON counterpart to `high_entropy_env`, and the pattern that the
|
|
133
|
+
// now-deleted `JSON_KEY_ANCHORS` list was written for. Without it those
|
|
134
|
+
// anchors only widened the cheap pre-scan — `hasCredentialAnchors` said
|
|
135
|
+
// "this text may hold a secret", every pattern then declined to match, and
|
|
136
|
+
// the value went out verbatim. `high_entropy_env` cannot cover these: it
|
|
137
|
+
// requires an UPPERCASE unquoted key (`API_KEY=…`), so `{"apiKey":"…"}`
|
|
138
|
+
// never matched.
|
|
139
|
+
//
|
|
140
|
+
// Tool results are routinely serialised as JSON, and a credential with no
|
|
141
|
+
// recognisable prefix (Azure, self-hosted gateways, Anthropic/Codex OAuth)
|
|
142
|
+
// has no other pattern that can catch it — this is the only thing standing
|
|
143
|
+
// between such a value and the session JSONL, chronicle, HQ broadcast and
|
|
144
|
+
// the model's own context.
|
|
145
|
+
//
|
|
146
|
+
// The key may carry a prefix (`"anthropicApiKey"`), but the credential word
|
|
147
|
+
// must END the key: `"tokenCount"` and `"maxTokens"` do not match, because
|
|
148
|
+
// the closing quote has to follow the word immediately.
|
|
149
|
+
// Value floor of 8 chars keeps enum-ish values (`"authorization":"none"`)
|
|
150
|
+
// out. Capture groups: 1=key + punctuation, 2=value, 3=closing quote.
|
|
151
|
+
regex: /("[A-Za-z0-9_]*(?:apiKey|api_key|token|secret|password|authorization|bearer|private_key|access_token|refresh_token|client_secret)"\s*:\s*")([^"\\]{8,512})(")/gi,
|
|
152
|
+
anchor: JSON_CREDENTIAL_KEY_ANCHORS
|
|
153
|
+
},
|
|
154
|
+
// ── Ported from packages/plugins credential-patterns.ts (WS-034) ─────────
|
|
155
|
+
// The plugin runtime carried 37 patterns while this scrubber — the one that
|
|
156
|
+
// guards session JSONL, chronicle, HQ broadcast, WebUI events and the auth
|
|
157
|
+
// audit — carried 22. The plugin side already had a parity test; it just did
|
|
158
|
+
// not cover core. Most consequential: WrongStack mints `gho_` tokens itself
|
|
159
|
+
// in the Copilot OAuth flow, and `gh[ousr]_` was absent here.
|
|
160
|
+
{
|
|
161
|
+
type: "github_oauth_token",
|
|
162
|
+
regex: /(?<![A-Za-z0-9])gh[ousr]_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g,
|
|
163
|
+
anchor: ["gho_", "ghu_", "ghs_", "ghr_"]
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
type: "gitlab_pat",
|
|
167
|
+
regex: /(?<![A-Za-z0-9])glpat-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
168
|
+
anchor: "glpat-"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
type: "gitlab_runner_token",
|
|
172
|
+
regex: /(?<![A-Za-z0-9])glrt-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
173
|
+
anchor: "glrt-"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: "npm_token",
|
|
177
|
+
regex: /(?<![A-Za-z0-9])npm_[A-Za-z0-9]{36}(?![A-Za-z0-9])/g,
|
|
178
|
+
anchor: "npm_"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: "slack_app_token",
|
|
182
|
+
regex: /(?<![A-Za-z0-9-])xapp-\d-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
|
|
183
|
+
anchor: "xapp-"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: "slack_webhook",
|
|
187
|
+
regex: /https:\/\/hooks\.slack\.com\/services\/T[A-Za-z0-9_-]+\/B[A-Za-z0-9_-]+\/[A-Za-z0-9]{16,}/g,
|
|
188
|
+
anchor: "hooks.slack.com"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
type: "sendgrid_key",
|
|
192
|
+
regex: /(?<![A-Za-z0-9])SG\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
193
|
+
anchor: "SG."
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: "digitalocean_token",
|
|
197
|
+
regex: /(?<![A-Za-z0-9])dop_v1_[a-f0-9]{64}(?![A-Za-z0-9])/g,
|
|
198
|
+
anchor: "dop_v1_"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
type: "doppler_token",
|
|
202
|
+
regex: /(?<![A-Za-z0-9])dp\.(?:pt|st|sa|scim|audit)\.[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
203
|
+
anchor: "dp."
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
type: "shopify_token",
|
|
207
|
+
regex: /(?<![A-Za-z0-9])shp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}(?![A-Za-z0-9])/g,
|
|
208
|
+
anchor: "shp"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
type: "docker_pat",
|
|
212
|
+
regex: /(?<![A-Za-z0-9])dckr_pat_[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
213
|
+
anchor: "dckr_pat_"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
type: "linear_key",
|
|
217
|
+
regex: /(?<![A-Za-z0-9])lin_api_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
218
|
+
anchor: "lin_api_"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: "atlassian_token",
|
|
222
|
+
regex: /(?<![A-Za-z0-9])ATATT3[A-Za-z0-9_\-=]{40,}(?![A-Za-z0-9_\-=])/g,
|
|
223
|
+
anchor: "ATATT3"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
type: "square_token",
|
|
227
|
+
regex: /(?<![A-Za-z0-9])(?:sq0(?:atp|csp)-|EAAA)[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
228
|
+
anchor: ["sq0atp-", "sq0csp-", "EAAA"]
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
type: "google_oauth_client_secret",
|
|
232
|
+
regex: /(?<![A-Za-z0-9_-])GOCSPX-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
233
|
+
anchor: "GOCSPX-"
|
|
234
|
+
}
|
|
235
|
+
];
|
|
236
|
+
var SIMPLE_PATTERNS = PATTERNS.filter(
|
|
237
|
+
(p) => p.type !== "high_entropy_env" && p.type !== "json_credential_key"
|
|
238
|
+
);
|
|
239
|
+
var COMBINED_REGEX = new RegExp(SIMPLE_PATTERNS.map((p) => `(${p.regex.source})`).join("|"), "g");
|
|
240
|
+
var HIGH_ENTROPY_REGEX = PATTERNS.find((p) => p.type === "high_entropy_env").regex;
|
|
241
|
+
var JSON_CREDENTIAL_REGEX = PATTERNS.find((p) => p.type === "json_credential_key").regex;
|
|
242
|
+
var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
|
|
243
|
+
var SCRUB_CHUNK_BYTES = 64 * 1024;
|
|
244
|
+
var SCRUB_OVERLAP_BYTES = 1024;
|
|
245
|
+
var PEM_PRIVATE_KEY_BEGIN_RE = /-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----/;
|
|
246
|
+
var PEM_END_MARKER = "-----END";
|
|
247
|
+
var MAX_PEM_BLOCK_BYTES = 64 * 1024;
|
|
248
|
+
var PEM_END_LINE_TOLERANCE = 64;
|
|
249
|
+
function extendChunkBoundaryPastPem(text, chunkStart, proposedEnd) {
|
|
250
|
+
const head = text.slice(chunkStart, proposedEnd);
|
|
251
|
+
const lastBegin = head.lastIndexOf("-----BEGIN ");
|
|
252
|
+
if (lastBegin === -1) return proposedEnd;
|
|
253
|
+
const fromBegin = text.slice(chunkStart + lastBegin);
|
|
254
|
+
const marker = PEM_PRIVATE_KEY_BEGIN_RE.exec(fromBegin);
|
|
255
|
+
if (!marker || marker.index !== 0) return proposedEnd;
|
|
256
|
+
const bodyStart = marker[0].length;
|
|
257
|
+
const cap = Math.min(text.length, chunkStart + lastBegin + MAX_PEM_BLOCK_BYTES);
|
|
258
|
+
const closeIdx = fromBegin.indexOf(PEM_END_MARKER, bodyStart);
|
|
259
|
+
if (closeIdx === -1 || chunkStart + lastBegin + closeIdx >= cap + PEM_END_LINE_TOLERANCE) {
|
|
260
|
+
return proposedEnd;
|
|
261
|
+
}
|
|
262
|
+
const lineEnd = fromBegin.indexOf("\n", closeIdx);
|
|
263
|
+
const end = lineEnd === -1 ? text.length : chunkStart + lastBegin + lineEnd + 1;
|
|
264
|
+
return Math.max(proposedEnd, end);
|
|
265
|
+
}
|
|
266
|
+
var PATTERN_ANCHORS = [
|
|
267
|
+
...new Set(
|
|
268
|
+
PATTERNS.flatMap(
|
|
269
|
+
(pattern) => typeof pattern.anchor === "string" ? [pattern.anchor] : [...pattern.anchor]
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
];
|
|
273
|
+
var ALL_ANCHORS = PATTERN_ANCHORS;
|
|
274
|
+
function hasCredentialAnchors(text) {
|
|
275
|
+
for (const anchor of ALL_ANCHORS) {
|
|
276
|
+
if (text.includes(anchor)) return true;
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
var DefaultSecretScrubber = class {
|
|
281
|
+
scrub(text) {
|
|
282
|
+
if (!text) return text;
|
|
283
|
+
if (!hasCredentialAnchors(text)) return text;
|
|
284
|
+
if (text.length <= SCRUB_CHUNK_BYTES) {
|
|
285
|
+
return this.scrubOne(text);
|
|
286
|
+
}
|
|
287
|
+
const out = [];
|
|
288
|
+
let i = 0;
|
|
289
|
+
while (i < text.length) {
|
|
290
|
+
let end = Math.min(i + SCRUB_CHUNK_BYTES, text.length);
|
|
291
|
+
if (end < text.length) {
|
|
292
|
+
const limit = Math.min(end + SCRUB_OVERLAP_BYTES, text.length);
|
|
293
|
+
let safe = -1;
|
|
294
|
+
for (let j = end; j < limit; j++) {
|
|
295
|
+
const ch = text.charCodeAt(j);
|
|
296
|
+
if (ch === 32 || ch === 9 || ch === 10 || ch === 13) {
|
|
297
|
+
safe = j;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
end = safe === -1 ? end : safe + 1;
|
|
302
|
+
end = extendChunkBoundaryPastPem(text, i, end);
|
|
303
|
+
}
|
|
304
|
+
out.push(this.scrubOne(text.slice(i, end)));
|
|
305
|
+
i = end;
|
|
306
|
+
}
|
|
307
|
+
return out.join("");
|
|
308
|
+
}
|
|
309
|
+
scrubOne(text) {
|
|
310
|
+
if (!hasCredentialAnchors(text)) return text;
|
|
311
|
+
let out = text.replace(
|
|
312
|
+
COMBINED_REGEX,
|
|
313
|
+
(match, ...groups) => {
|
|
314
|
+
const idx = groups.findIndex((g) => g !== void 0);
|
|
315
|
+
if (idx < 0) return match;
|
|
316
|
+
const replacement = COMBINED_REPLACEMENTS[idx];
|
|
317
|
+
return replacement !== void 0 ? replacement : match;
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
out = out.replace(HIGH_ENTROPY_REGEX, (_match, lead, key, _value) => {
|
|
321
|
+
return `${lead}${key}=[REDACTED:high_entropy_env]`;
|
|
322
|
+
});
|
|
323
|
+
out = out.replace(JSON_CREDENTIAL_REGEX, (_match, keyPrefix, _value, closingQuote) => {
|
|
324
|
+
return `${keyPrefix}[REDACTED:json_credential_key]${closingQuote}`;
|
|
325
|
+
});
|
|
326
|
+
return out;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Recursively scrub every string value in an object/array graph. Secrets can
|
|
330
|
+
* appear under any key — a URL query param, an `authorization` header, an
|
|
331
|
+
* arbitrarily-named nested field — so we don't gate recursion on key names.
|
|
332
|
+
* The per-string `scrub()` fast-path (anchor pre-scan) keeps this cheap: any
|
|
333
|
+
* value without a credential anchor returns immediately without regex work.
|
|
334
|
+
*/
|
|
335
|
+
scrubObject(obj) {
|
|
336
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
337
|
+
const visit = (v) => {
|
|
338
|
+
if (typeof v === "string") return this.scrub(v);
|
|
339
|
+
if (v === null || typeof v !== "object") return v;
|
|
340
|
+
if (seen.has(v)) return v;
|
|
341
|
+
seen.add(v);
|
|
342
|
+
if (Array.isArray(v)) return v.map(visit);
|
|
343
|
+
const out = {};
|
|
344
|
+
for (const [k, val] of Object.entries(v)) {
|
|
345
|
+
out[k] = visit(val);
|
|
346
|
+
}
|
|
347
|
+
return out;
|
|
348
|
+
};
|
|
349
|
+
return visit(obj);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
export {
|
|
354
|
+
DefaultSecretScrubber
|
|
355
|
+
};
|
|
356
|
+
//# sourceMappingURL=chunk-3E244UDA.js.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// src/plugin/config.ts
|
|
2
|
+
function resolvePluginConfig(input) {
|
|
3
|
+
const names = [.../* @__PURE__ */ new Set([...input.aliases ?? [], input.name])];
|
|
4
|
+
const options = { ...input.defaults ?? {} };
|
|
5
|
+
const sources = [];
|
|
6
|
+
let configured = false;
|
|
7
|
+
const merge = (value, source) => {
|
|
8
|
+
if (!isRecord(value)) return;
|
|
9
|
+
Object.assign(options, value);
|
|
10
|
+
configured = true;
|
|
11
|
+
if (!sources.includes(source)) sources.push(source);
|
|
12
|
+
};
|
|
13
|
+
const plugins = input.config?.plugins;
|
|
14
|
+
if (isRecord(plugins)) {
|
|
15
|
+
for (const name of names) merge(plugins[name], "legacy-plugin-map");
|
|
16
|
+
} else if (Array.isArray(plugins)) {
|
|
17
|
+
for (const candidate of plugins) {
|
|
18
|
+
if (!isPluginEntry(candidate) || !names.includes(candidate.name)) continue;
|
|
19
|
+
merge(candidate.options, "plugin-entry");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const extensions = input.config?.extensions;
|
|
23
|
+
for (const name of names) merge(extensions?.[name], "extension");
|
|
24
|
+
merge(input.explicitOptions, "explicit-options");
|
|
25
|
+
return { options, configured, sources };
|
|
26
|
+
}
|
|
27
|
+
function pluginEntryMatchesName(configuredName, name, aliases = []) {
|
|
28
|
+
for (const candidate of [name, ...aliases]) {
|
|
29
|
+
if (configuredName === candidate) return true;
|
|
30
|
+
if (configuredName === `@wrongstack/plugins/${candidate}`) return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
function resolvePluginEnablement(input) {
|
|
35
|
+
if (input.config?.features?.plugins === false) {
|
|
36
|
+
return { enabled: false, source: "feature-flag" };
|
|
37
|
+
}
|
|
38
|
+
const names = [.../* @__PURE__ */ new Set([input.name, ...input.aliases ?? []])];
|
|
39
|
+
const matches = input.matches ?? ((configuredName) => pluginEntryMatchesName(configuredName, input.name, input.aliases ?? []));
|
|
40
|
+
const plugins = input.config?.plugins;
|
|
41
|
+
if (Array.isArray(plugins)) {
|
|
42
|
+
for (const candidate of plugins) {
|
|
43
|
+
if (typeof candidate === "string") {
|
|
44
|
+
if (matches(candidate)) return { enabled: true, source: "plugin-entry" };
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (!isPluginEntry(candidate) || !matches(candidate.name)) continue;
|
|
48
|
+
return { enabled: candidate.enabled !== false, source: "plugin-entry" };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const name of names) {
|
|
52
|
+
const enabled = input.config?.extensions?.[name]?.["enabled"];
|
|
53
|
+
if (typeof enabled === "boolean") return { enabled, source: "extension" };
|
|
54
|
+
}
|
|
55
|
+
return { enabled: input.defaultState === "active", source: "default" };
|
|
56
|
+
}
|
|
57
|
+
function resolvePluginManifestConfig(plugin, config, explicitOptions) {
|
|
58
|
+
return resolvePluginConfig({
|
|
59
|
+
name: plugin.name,
|
|
60
|
+
aliases: plugin.configAliases,
|
|
61
|
+
defaults: plugin.defaultConfig,
|
|
62
|
+
config,
|
|
63
|
+
explicitOptions
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function diffPluginConfig(previous, next, fields) {
|
|
67
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)]);
|
|
68
|
+
const changes = [];
|
|
69
|
+
for (const key of keys) {
|
|
70
|
+
if (valuesEqual(previous[key], next[key])) continue;
|
|
71
|
+
const metadata = fields[key];
|
|
72
|
+
changes.push({
|
|
73
|
+
key,
|
|
74
|
+
lifecycle: metadata?.lifecycle ?? "immutable",
|
|
75
|
+
secret: metadata?.secret === true,
|
|
76
|
+
previous: metadata?.secret === true ? "[REDACTED]" : previous[key],
|
|
77
|
+
next: metadata?.secret === true ? "[REDACTED]" : next[key]
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return changes;
|
|
81
|
+
}
|
|
82
|
+
function redactPluginConfig(options, fields) {
|
|
83
|
+
const redacted = { ...options };
|
|
84
|
+
for (const [key, metadata] of Object.entries(fields)) {
|
|
85
|
+
if (metadata.secret === true && key in redacted) redacted[key] = "[REDACTED]";
|
|
86
|
+
}
|
|
87
|
+
return redacted;
|
|
88
|
+
}
|
|
89
|
+
function validatePluginConfigMetadata(plugin) {
|
|
90
|
+
if (!plugin.configFields) return [];
|
|
91
|
+
const declared = plugin.configFields;
|
|
92
|
+
const expected = /* @__PURE__ */ new Set([
|
|
93
|
+
...Object.keys(plugin.configSchema?.properties ?? {}),
|
|
94
|
+
...Object.keys(plugin.defaultConfig ?? {})
|
|
95
|
+
]);
|
|
96
|
+
const issues = [];
|
|
97
|
+
for (const key of expected) {
|
|
98
|
+
if (!declared[key]) issues.push(`missing configFields metadata for "${key}"`);
|
|
99
|
+
}
|
|
100
|
+
for (const [key, metadata] of Object.entries(declared)) {
|
|
101
|
+
if (!["hot", "restart", "immutable"].includes(metadata.lifecycle)) {
|
|
102
|
+
issues.push(`invalid lifecycle for configFields."${key}"`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return issues;
|
|
106
|
+
}
|
|
107
|
+
function isPluginEntry(value) {
|
|
108
|
+
return isRecord(value) && typeof value["name"] === "string";
|
|
109
|
+
}
|
|
110
|
+
function isRecord(value) {
|
|
111
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
112
|
+
}
|
|
113
|
+
function valuesEqual(left, right) {
|
|
114
|
+
if (Object.is(left, right)) return true;
|
|
115
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
116
|
+
return left.length === right.length && left.every((value, index) => valuesEqual(value, right[index]));
|
|
117
|
+
}
|
|
118
|
+
if (isRecord(left) && isRecord(right)) {
|
|
119
|
+
const leftKeys = Object.keys(left);
|
|
120
|
+
const rightKeys = Object.keys(right);
|
|
121
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key) => key in right && valuesEqual(left[key], right[key]));
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export {
|
|
127
|
+
resolvePluginConfig,
|
|
128
|
+
pluginEntryMatchesName,
|
|
129
|
+
resolvePluginEnablement,
|
|
130
|
+
resolvePluginManifestConfig,
|
|
131
|
+
diffPluginConfig,
|
|
132
|
+
redactPluginConfig,
|
|
133
|
+
validatePluginConfigMetadata
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=chunk-3XPVB7BA.js.map
|