engineering-memory 1.11.9 → 1.11.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dispatcher/sections.mjs +7 -2
  2. package/install/codex-approval.mjs +457 -0
  3. package/install/files.mjs +5 -1
  4. package/install/installer.mjs +24 -1
  5. package/package.json +1 -1
  6. package/runtime/build.json +1 -1
  7. package/runtime/dist/src/config.js +2 -0
  8. package/runtime/dist/src/git/git-inspector.js +114 -4
  9. package/runtime/dist/src/git/verification-gate.js +30 -17
  10. package/runtime/dist/src/mcp/onboarding-tools.js +110 -16
  11. package/runtime/dist/src/mcp/questionnaire-tools.js +99 -15
  12. package/runtime/dist/src/mcp/tool-annotations.js +112 -0
  13. package/runtime/dist/src/mcp/tool-definitions.js +150 -29
  14. package/runtime/dist/src/mcp/worktree-tools.js +395 -236
  15. package/runtime/dist/src/project/repository.js +26 -8
  16. package/runtime/dist/src/runtime/active-context-store.js +3 -0
  17. package/runtime/dist/src/runtime/api-client.js +1 -0
  18. package/runtime/dist/src/runtime/branch-preferences.js +0 -19
  19. package/runtime/dist/src/runtime/bridge-service.js +1320 -246
  20. package/runtime/dist/src/runtime/offline-outbox.js +33 -158
  21. package/runtime/dist/src/runtime/phase-timer.js +26 -0
  22. package/runtime/dist/src/runtime/privacy-detector.js +263 -0
  23. package/runtime/dist/src/runtime/questionnaire-store.js +210 -49
  24. package/runtime/dist/src/runtime/recovery-error.js +3 -1
  25. package/runtime/dist/src/runtime/runtime-entry.js +8 -0
  26. package/runtime/dist/src/runtime/runtime-host.js +1 -3
  27. package/runtime/dist/src/runtime/task-branch-store.js +17 -1
  28. package/runtime/dist/src/runtime/task-start.js +281 -0
  29. package/runtime/dist/src/runtime/worktree-pool.js +374 -26
  30. package/runtime/dist/src/utilities/process.js +1 -0
  31. package/skill/SKILL.md +3 -3
  32. package/skill/references/lifecycle.md +59 -19
  33. package/skill/references/memory-updates.md +9 -3
  34. package/skill/references/project-onboarding.md +19 -2
  35. package/skill/references/questionnaires.md +86 -15
@@ -3,140 +3,9 @@ import { readdir } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { assertManagedPath, ensureManagedDirectory, readJson, removeFile, writeJson, } from '../utilities/files.js';
5
5
  import { sha256, stableStringify } from '../utilities/hash.js';
6
- const sensitiveKeyPattern = /(authorization|password|passcode|secret|token|cookie|email|phone|mobile|customer|account|user.?id|user.?name)/i;
7
- const emailSource = '(?:^|[^\\p{L}\\p{N}._%+-])[\\p{L}\\p{N}._%+-]{1,64}@([\\p{L}\\p{N}.-]+\\.[\\p{L}]{2,63})(?=$|[^\\p{L}\\p{N}.-])';
8
- const emailPattern = new RegExp(emailSource, 'iu');
9
- const emailMatchPattern = new RegExp(emailSource, 'giu');
10
- const phonePattern = /\+\d[\d ()-]{7,}\d/;
11
- const repositoryPathFields = new Set([
12
- 'path',
13
- 'paths',
14
- 'originalPath',
15
- 'changedPaths',
16
- 'pathPatterns',
17
- 'evidencePaths',
18
- 'screenPathPatterns',
19
- 'componentPathPatterns',
20
- 'pathChanges',
21
- 'templatePath',
22
- ]);
23
- const topLevelDomains = new Set([
24
- 'com',
25
- 'net',
26
- 'org',
27
- 'edu',
28
- 'gov',
29
- 'mil',
30
- 'int',
31
- 'info',
32
- 'biz',
33
- 'name',
34
- 'pro',
35
- 'mobi',
36
- 'tel',
37
- 'asia',
38
- 'jobs',
39
- 'coop',
40
- 'aero',
41
- 'museum',
42
- 'travel',
43
- 'app',
44
- 'dev',
45
- 'cloud',
46
- 'online',
47
- 'site',
48
- 'store',
49
- 'tech',
50
- 'space',
51
- 'website',
52
- 'digital',
53
- 'agency',
54
- 'systems',
55
- 'solutions',
56
- 'email',
57
- 'xyz',
58
- 'top',
59
- 'club',
60
- 'live',
61
- 'life',
62
- 'world',
63
- 'today',
64
- 'network',
65
- 'group',
66
- 'company',
67
- 'center',
68
- 'media',
69
- 'studio',
70
- 'design',
71
- 'software',
72
- 'tools',
73
- 'works',
74
- 'team',
75
- 'zone',
76
- 'one',
77
- 'ltd',
78
- 'inc',
79
- 'llc',
80
- 'gmbh',
81
- 'shop',
82
- 'page',
83
- 'blog',
84
- 'wiki',
85
- 'news',
86
- 'art',
87
- 'link',
88
- 'host',
89
- 'press',
90
- 'fyi',
91
- 'test',
92
- 'example',
93
- 'invalid',
94
- 'local',
95
- 'localhost',
96
- 'internal',
97
- ]);
98
- const countryCodeTopLevelDomains = new Set('ac ad ae af ag ai al am ao aq ar as at au aw ax az ba bb bd be bf bg bh bi bj bm bn bo br bs bt bw by bz ca cc cd cf cg ch ci ck cl cm cn co cr cu cv cw cx cy cz de dj dk dm do dz ec ee eg er es et eu fi fj fk fm fo fr ga gd ge gf gg gh gi gl gm gn gp gq gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in io iq ir is it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc li lk lr ls lt lu lv ly ma mc md me mg mh mk ml mm mn mo mp mq mr ms mt mu mv mw mx my mz na nc ne nf ng ni nl no np nr nu nz om pa pe pf pg ph pk pl pm pn pr ps pt pw py qa re ro rs ru rw sa sb sc sd se sg sh si sk sl sm sn so sr ss st su sv sx sy sz tc td tf tg th tj tk tl tm tn to tr tt tv tw tz ua ug uk us uy uz va vc ve vg vi vn vu wf ws ye yt za zm zw'.split(' '));
99
- function isTopLevelDomain(label) {
100
- const value = label.toLowerCase();
101
- return countryCodeTopLevelDomains.has(value) || topLevelDomains.has(value);
102
- }
103
- const extensionTopLevelDomains = new Set([
104
- 'ai',
105
- 'app',
106
- 'as',
107
- 'cc',
108
- 'md',
109
- 'ml',
110
- 'mm',
111
- 'pl',
112
- 'py',
113
- 'rs',
114
- 'sh',
115
- 'so',
116
- ]);
117
- function hasEmailAddress(value, repositoryPath) {
118
- if (!repositoryPath)
119
- return emailPattern.test(value);
120
- for (const match of value.matchAll(emailMatchPattern)) {
121
- const labels = (match[1] ?? '').split('.');
122
- const last = (labels[labels.length - 1] ?? '').toLowerCase();
123
- if (isTopLevelDomain(last) && !extensionTopLevelDomains.has(last))
124
- return true;
125
- const beforeExtension = (labels[labels.length - 2] ?? '').toLowerCase();
126
- if (labels.length >= 3 &&
127
- topLevelDomains.has(beforeExtension) &&
128
- !extensionTopLevelDomains.has(beforeExtension))
129
- return true;
130
- }
131
- return false;
132
- }
133
- const knownCredentialPattern = /\b(?:A(?:KI|SI)A[0-9A-Z]{16}|gh[pousr]_[A-Z0-9]{30,}|github_pat_[A-Z0-9_]{30,}|sk_live_[A-Z0-9]{16,}|rk_live_[A-Z0-9]{16,}|xox[baprs]-[A-Z0-9-]{12,}|AIza[A-Z0-9_-]{35}|sk-(?:proj-)?[A-Z0-9_-]{20,})\b/i;
134
- const bearerPattern = /\bbearer\s+[a-z0-9._~-]+/i;
135
- const jwtPattern = /\beyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/;
136
- const privateKeyPattern = /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/;
137
- const credentialUrlPattern = /\b[a-z][a-z0-9+.-]*:\/\/[^\s/@:]+:[^\s/@]+@/i;
138
- const ipv4Pattern = /\b(?:\d{1,3}\.){3}\d{1,3}\b/;
139
- const personalHomePathPattern = /(?:[A-Z]:[\\/](?:Users|Documents and Settings)[\\/][^\\/\s"'<>:|?*]+|(?:^|[\s"'(<=])\/(?:Users|home)\/[^/\s"'<>*]+)(?:[\\/]|$)/i;
6
+ import { restrictedFieldKind, restrictedNumberKind, restrictedValueKind, } from './privacy-detector.js';
7
+ const controlFields = new Set(['claimtoken', 'cookie', 'setcookie']);
8
+ const personalCacheFieldPattern = /(email|phone|mobile|user.?id|user.?name)/i;
140
9
  export class OfflineOutbox {
141
10
  root;
142
11
  queues = new Map();
@@ -282,33 +151,39 @@ export class OfflineOutbox {
282
151
  }
283
152
  }
284
153
  }
285
- export function assertSafeToPersist(value, key = '') {
286
- if (key && sensitiveKeyPattern.test(key)) {
287
- throw new Error(`Sensitive field cannot be persisted: ${key}`);
288
- }
289
- if (typeof value === 'string') {
290
- const repositoryPath = repositoryPathFields.has(key);
291
- if (hasEmailAddress(value, repositoryPath) ||
292
- phonePattern.test(value) ||
293
- bearerPattern.test(value) ||
294
- jwtPattern.test(value) ||
295
- privateKeyPattern.test(value) ||
296
- knownCredentialPattern.test(value) ||
297
- credentialUrlPattern.test(value) ||
298
- personalHomePathPattern.test(value) ||
299
- (!repositoryPath && ipv4Pattern.test(value))) {
300
- throw new Error(`PII or credentials cannot be persisted: ${key}`);
154
+ export function assertSafeToPersist(value, key = '', path = key) {
155
+ if (typeof value === 'string' || typeof value === 'number') {
156
+ const detector = typeof value === 'string'
157
+ ? restrictedValueKind(value, key)
158
+ : restrictedNumberKind(value, key);
159
+ if (detector) {
160
+ throw new Error(`PII or credentials cannot be persisted: ${path || '(value)'} (${detector})`);
301
161
  }
302
162
  return;
303
163
  }
304
164
  if (Array.isArray(value)) {
305
- value.forEach((item) => assertSafeToPersist(item, key));
165
+ value.forEach((item, index) => assertSafeToPersist(item, key, `${path}[${index}]`));
306
166
  return;
307
167
  }
308
168
  if (value !== null && typeof value === 'object') {
309
- Object.entries(value).forEach(([childKey, item]) => assertSafeToPersist(item, childKey));
169
+ Object.entries(value).forEach(([childKey, item]) => {
170
+ const field = sensitiveFieldKind(childKey, item);
171
+ if (field) {
172
+ throw new Error(`Sensitive field cannot be persisted: ${childPath(path, childKey)} (${field})`);
173
+ }
174
+ assertSafeToPersist(item, childKey, childPath(path, childKey));
175
+ });
310
176
  }
311
177
  }
178
+ function sensitiveFieldKind(key, value) {
179
+ if (controlFields.has(key.replace(/[^a-z0-9]/gi, '').toLowerCase()))
180
+ return 'bridge control field';
181
+ return (restrictedFieldKind(key, value) ??
182
+ (typeof value === 'number' ? restrictedNumberKind(value, key) : null));
183
+ }
184
+ function childPath(path, key) {
185
+ return path ? `${path}.${key}` : key;
186
+ }
312
187
  export function normalizeRepositoryPaths(value, repoRoot) {
313
188
  const root = trimTrailingSeparators(repoRoot);
314
189
  if (!root) {
@@ -333,21 +208,21 @@ export function redactForCache(value) {
333
208
  return null;
334
209
  }
335
210
  }
336
- function redactValue(value, key = '') {
211
+ function redactValue(value, key = '', path = key) {
337
212
  if (typeof value === 'string') {
338
- assertSafeToPersist(value, key);
213
+ assertSafeToPersist(value, key, path);
339
214
  return value;
340
215
  }
341
216
  if (Array.isArray(value)) {
342
- return value.map((entry) => redactValue(entry, key));
217
+ return value.map((entry, index) => redactValue(entry, key, `${path}[${index}]`));
343
218
  }
344
219
  if (value !== null && typeof value === 'object') {
345
220
  const result = {};
346
- for (const [key, entry] of Object.entries(value)) {
347
- if (sensitiveKeyPattern.test(key)) {
221
+ for (const [childKey, entry] of Object.entries(value)) {
222
+ if (personalCacheFieldPattern.test(childKey) || sensitiveFieldKind(childKey, entry)) {
348
223
  continue;
349
224
  }
350
- result[key] = redactValue(entry, key);
225
+ result[childKey] = redactValue(entry, childKey, childPath(path, childKey));
351
226
  }
352
227
  return result;
353
228
  }
@@ -0,0 +1,26 @@
1
+ const enabled = Boolean(process.env.ENGINEERING_MEMORY_BRIDGE_DEBUG);
2
+ function write(line) {
3
+ process.stderr.write(`${line}\n`);
4
+ }
5
+ export function startPhaseTimer(operation) {
6
+ if (!enabled) {
7
+ return { mark: () => { }, finish: () => { } };
8
+ }
9
+ const start = process.hrtime.bigint();
10
+ let last = start;
11
+ const phases = [];
12
+ return {
13
+ mark(phase) {
14
+ const now = process.hrtime.bigint();
15
+ phases.push({ phase, ms: Number(now - last) / 1e6 });
16
+ last = now;
17
+ },
18
+ finish() {
19
+ const now = process.hrtime.bigint();
20
+ const totalMs = Number(now - start) / 1e6;
21
+ const breakdown = phases.map((entry) => `${entry.phase}=${entry.ms.toFixed(1)}ms`).join(' ');
22
+ write(`[engineering-memory bridge] ${operation} total=${totalMs.toFixed(1)}ms ${breakdown}`);
23
+ },
24
+ };
25
+ }
26
+ //# sourceMappingURL=phase-timer.js.map
@@ -0,0 +1,263 @@
1
+ const emailSource = '(?:^|[^\\p{L}\\p{N}._%+-])[\\p{L}\\p{N}._%+-]{1,64}@([\\p{L}\\p{N}.-]+\\.[\\p{L}]{2,63})(?=$|[^\\p{L}\\p{N}.-])';
2
+ const emailPattern = new RegExp(emailSource, 'iu');
3
+ const emailMatchPattern = new RegExp(emailSource, 'giu');
4
+ const repositoryPathFields = new Set([
5
+ 'path',
6
+ 'paths',
7
+ 'originalPath',
8
+ 'changedPaths',
9
+ 'pathPatterns',
10
+ 'evidencePaths',
11
+ 'screenPathPatterns',
12
+ 'componentPathPatterns',
13
+ 'pathChanges',
14
+ 'templatePath',
15
+ ]);
16
+ const topLevelDomains = new Set([
17
+ 'com',
18
+ 'net',
19
+ 'org',
20
+ 'edu',
21
+ 'gov',
22
+ 'mil',
23
+ 'int',
24
+ 'info',
25
+ 'biz',
26
+ 'name',
27
+ 'pro',
28
+ 'mobi',
29
+ 'tel',
30
+ 'asia',
31
+ 'jobs',
32
+ 'coop',
33
+ 'aero',
34
+ 'museum',
35
+ 'travel',
36
+ 'app',
37
+ 'dev',
38
+ 'cloud',
39
+ 'online',
40
+ 'site',
41
+ 'store',
42
+ 'tech',
43
+ 'space',
44
+ 'website',
45
+ 'digital',
46
+ 'agency',
47
+ 'systems',
48
+ 'solutions',
49
+ 'email',
50
+ 'xyz',
51
+ 'top',
52
+ 'club',
53
+ 'live',
54
+ 'life',
55
+ 'world',
56
+ 'today',
57
+ 'network',
58
+ 'group',
59
+ 'company',
60
+ 'center',
61
+ 'media',
62
+ 'studio',
63
+ 'design',
64
+ 'software',
65
+ 'tools',
66
+ 'works',
67
+ 'team',
68
+ 'zone',
69
+ 'one',
70
+ 'ltd',
71
+ 'inc',
72
+ 'llc',
73
+ 'gmbh',
74
+ 'shop',
75
+ 'page',
76
+ 'blog',
77
+ 'wiki',
78
+ 'news',
79
+ 'art',
80
+ 'link',
81
+ 'host',
82
+ 'press',
83
+ 'fyi',
84
+ 'test',
85
+ 'example',
86
+ 'invalid',
87
+ 'local',
88
+ 'localhost',
89
+ 'internal',
90
+ ]);
91
+ const countryCodeTopLevelDomains = new Set('ac ad ae af ag ai al am ao aq ar as at au aw ax az ba bb bd be bf bg bh bi bj bm bn bo br bs bt bw by bz ca cc cd cf cg ch ci ck cl cm cn co cr cu cv cw cx cy cz de dj dk dm do dz ec ee eg er es et eu fi fj fk fm fo fr ga gd ge gf gg gh gi gl gm gn gp gq gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in io iq ir is it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc li lk lr ls lt lu lv ly ma mc md me mg mh mk ml mm mn mo mp mq mr ms mt mu mv mw mx my mz na nc ne nf ng ni nl no np nr nu nz om pa pe pf pg ph pk pl pm pn pr ps pt pw py qa re ro rs ru rw sa sb sc sd se sg sh si sk sl sm sn so sr ss st su sv sx sy sz tc td tf tg th tj tk tl tm tn to tr tt tv tw tz ua ug uk us uy uz va vc ve vg vi vn vu wf ws ye yt za zm zw'.split(' '));
92
+ function isTopLevelDomain(label) {
93
+ const value = label.toLowerCase();
94
+ return countryCodeTopLevelDomains.has(value) || topLevelDomains.has(value);
95
+ }
96
+ const extensionTopLevelDomains = new Set([
97
+ 'ai',
98
+ 'app',
99
+ 'as',
100
+ 'cc',
101
+ 'md',
102
+ 'ml',
103
+ 'mm',
104
+ 'pl',
105
+ 'py',
106
+ 'rs',
107
+ 'sh',
108
+ 'so',
109
+ ]);
110
+ function hasEmailAddress(value, repositoryPath) {
111
+ if (!repositoryPath)
112
+ return emailPattern.test(value);
113
+ for (const match of value.matchAll(emailMatchPattern)) {
114
+ const labels = (match[1] ?? '').split('.');
115
+ const last = (labels[labels.length - 1] ?? '').toLowerCase();
116
+ if (isTopLevelDomain(last) && !extensionTopLevelDomains.has(last))
117
+ return true;
118
+ const beforeExtension = (labels[labels.length - 2] ?? '').toLowerCase();
119
+ if (labels.length >= 3 &&
120
+ topLevelDomains.has(beforeExtension) &&
121
+ !extensionTopLevelDomains.has(beforeExtension))
122
+ return true;
123
+ }
124
+ return false;
125
+ }
126
+ const bearerPattern = /\bbearer\s+([A-Z0-9._~+/=-]{12,})/i;
127
+ const jwtPattern = /(?:^|[^A-Z0-9_-])eyJ[A-Z0-9_-]{5,}\.[A-Z0-9_-]{8,}\.[A-Z0-9_-]{8,}(?=$|[^A-Z0-9_-])/i;
128
+ const privateKeyPattern = /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/i;
129
+ const knownCredentialPattern = /\b(?:A(?:KI|SI)A[0-9A-Z]{16}|gh[pousr]_[A-Z0-9]{30,}|github_pat_[A-Z0-9_]{30,}|sk_live_[A-Z0-9]{16,}|rk_live_[A-Z0-9]{16,}|xox[baprs]-[A-Z0-9-]{12,}|AIza[A-Z0-9_-]{35}|sk-(?:proj-)?[A-Z0-9_-]{20,})\b/i;
130
+ const credentialUrlPattern = /\b[A-Z][A-Z0-9+.-]{1,15}:\/\/[^/\s:@]+:[^/\s@]{4,}@/i;
131
+ const secretAssignmentPattern = /(?:^|[\s{[(,;])["']?(password|passwd|pwd|secret|credential|client[_-]?secret|api[_-]?key|access[_-]?key|private[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|authorization)["']?\s*(=>|:=|=|:)\s*(?:"([^"\r\n]*)"|'([^'\r\n]*)'|([^\s,;}\]\r\n]+))/im;
132
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
133
+ const secretFieldPattern = /^(?:password|passwd|pwd|secret|credential|clientsecret|apikey|accesskey|privatekey|token|accesstoken|refreshtoken|idtoken|authorization)$/;
134
+ const phoneFieldPattern = /^(?:phone|tel|telephone|mobile|gsm|msisdn|cell|fax|whatsapp)$/;
135
+ const placeholderPattern = /^(?:null|undefined|true|false|string|number|boolean|required|optional|redacted|masked|example|sample|placeholder|password|secret|token|none)$/i;
136
+ const templatePattern = /^(?:<[^>]+>|\{\{[^}]+\}\}|\$\{[^}]+\}|\*+)$/;
137
+ const referencePattern = /^(?:process\.env|deno\.env|env\.|config\.|settings\.|request\.|dto\.|input\.|controller\.|model\.|user\.|secretref|vaultref)/i;
138
+ const bearerPlaceholderPattern = /^(?:your[_-]|example[_-]|sample[_-]|placeholder|redacted|masked|synthetic|x{8,}|\*+)/i;
139
+ const personalHomePathPattern = /(?:[A-Z]:[\\/](?:Users|Documents and Settings)[\\/][^\\/\s"'<>:|?*]+|(?:^|[\s"'(<=])\/(?:Users|home)\/[^/\s"'<>*]+)(?:[\\/]|$)/i;
140
+ const mountedHomePathPattern = /(?:\/mnt\/[a-z]\/Users\/|\/cygdrive\/[a-z]\/Users\/|(?:^|[\s"'(])\/[a-z]\/Users\/|\/System\/Volumes\/Data\/(?:Users|home)\/|\/Volumes\/[^/\s]+\/(?:Users|home)\/|\\\\[^\\\s]+\\Users\\|\/export\/home\/)[^/\\\s"'<>*]+/i;
141
+ function hasPersonalHomePath(value) {
142
+ return personalHomePathPattern.test(value) || mountedHomePathPattern.test(value);
143
+ }
144
+ export function restrictedFieldKind(key, value) {
145
+ return isSecretField(key) && isSecretValue(value) ? 'secret field with a credential value' : null;
146
+ }
147
+ export function restrictedNumberKind(value, key) {
148
+ return phoneFieldPattern.test(normalizedKey(key)) &&
149
+ Number.isSafeInteger(value) &&
150
+ hasPhoneNumber(String(value))
151
+ ? 'phone number'
152
+ : null;
153
+ }
154
+ export function restrictedValueKind(value, key) {
155
+ const repositoryPath = repositoryPathFields.has(key);
156
+ if (hasEmailAddress(value, repositoryPath))
157
+ return 'email address';
158
+ if (privateKeyPattern.test(value))
159
+ return 'private key';
160
+ if (jwtPattern.test(value))
161
+ return 'signed token';
162
+ if (knownCredentialPattern.test(value))
163
+ return 'credential';
164
+ if (credentialUrlPattern.test(value))
165
+ return 'credential in a URL';
166
+ if (hasPersonalHomePath(value))
167
+ return 'personal home directory';
168
+ if (hasBearerCredential(value))
169
+ return 'bearer credential';
170
+ if (hasPhoneNumber(value))
171
+ return 'phone number';
172
+ if (!repositoryPath && hasIpv4Address(value))
173
+ return 'network address';
174
+ if (hasSecretAssignment(value))
175
+ return 'assigned secret';
176
+ return null;
177
+ }
178
+ function hasSecretAssignment(value) {
179
+ const matches = value.matchAll(new RegExp(secretAssignmentPattern.source, 'gim'));
180
+ for (const match of matches) {
181
+ const key = match[1] ?? '';
182
+ const delimiter = match[2] ?? '';
183
+ const doubleQuoted = match[3];
184
+ const singleQuoted = match[4];
185
+ const raw = match[5];
186
+ const candidate = doubleQuoted ?? singleQuoted ?? raw ?? '';
187
+ if (isSecretString(candidate, doubleQuoted !== undefined || singleQuoted !== undefined, delimiter, key)) {
188
+ return true;
189
+ }
190
+ }
191
+ return false;
192
+ }
193
+ function hasBearerCredential(value) {
194
+ const candidate = bearerPattern.exec(value)?.[1];
195
+ return Boolean(candidate && !bearerPlaceholderPattern.test(candidate));
196
+ }
197
+ function normalizedKey(key) {
198
+ return key.replace(/[^a-z0-9]/gi, '').toLowerCase();
199
+ }
200
+ function isSecretField(key) {
201
+ return secretFieldPattern.test(normalizedKey(key));
202
+ }
203
+ function isSecretValue(value) {
204
+ if (typeof value === 'string')
205
+ return isSecretString(value, true, ':', '');
206
+ if (typeof value === 'number')
207
+ return String(Math.abs(value)).length >= 8;
208
+ return false;
209
+ }
210
+ function isSecretString(value, quoted, delimiter, key) {
211
+ const candidate = value.trim();
212
+ if (candidate.length < 8)
213
+ return false;
214
+ if (placeholderPattern.test(candidate) ||
215
+ templatePattern.test(candidate) ||
216
+ referencePattern.test(candidate) ||
217
+ candidate.startsWith('${') ||
218
+ candidate.startsWith('{') ||
219
+ candidate.startsWith('[')) {
220
+ return false;
221
+ }
222
+ if (!quoted && /^[A-Z_$][A-Z0-9_$?.<>[\].]*$/i.test(candidate)) {
223
+ return delimiter === '=' && key === key.toUpperCase();
224
+ }
225
+ return true;
226
+ }
227
+ function hasIpv4Address(value) {
228
+ const matches = value.matchAll(/(?:^|[^\d.])((?:\d{1,3}\.){3}\d{1,3})(?![\d.])/g);
229
+ for (const match of matches) {
230
+ const candidate = match[1];
231
+ if (candidate && candidate.split('.').every((part) => Number(part) <= 255)) {
232
+ return true;
233
+ }
234
+ }
235
+ return false;
236
+ }
237
+ function hasPhoneNumber(value) {
238
+ const trimmed = value.trim();
239
+ if (uuidPattern.test(trimmed))
240
+ return false;
241
+ if (/^\d{10,11}$/.test(trimmed))
242
+ return true;
243
+ if (/(?:^|[^A-Z0-9])0?5\d{2}[\s.-]?\d{3}[\s.-]?\d{2}[\s.-]?\d{2}(?![A-Z0-9])/i.test(value)) {
244
+ return true;
245
+ }
246
+ if (/\b(?:call|mobile|phone|tel|telephone|telefon|ara)\b[^\d+]{0,20}\d{10,11}\b/i.test(value)) {
247
+ return true;
248
+ }
249
+ if (/(?:^|\D)\(\d{3}\)\s*\d{3}[-\s]\d{4}(?:$|\D)/.test(value)) {
250
+ return true;
251
+ }
252
+ if (/(?:^|\D)\d{3}[-.\s]\d{3}[-.\s]\d{4}(?:$|\D)/.test(value)) {
253
+ return true;
254
+ }
255
+ const internationalMatches = value.matchAll(/(?:^|[^A-Z0-9])\+(\d(?:[\d ().-]{7,}\d))(?![A-Z0-9])/gi);
256
+ for (const match of internationalMatches) {
257
+ const digitCount = (match[1] ?? '').replace(/\D/g, '').length;
258
+ if (digitCount >= 9 && digitCount <= 14)
259
+ return true;
260
+ }
261
+ return false;
262
+ }
263
+ //# sourceMappingURL=privacy-detector.js.map