agent-inspect 6.31.3 → 6.31.4
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/CHANGELOG.md +8 -0
- package/README.md +1 -1
- package/SECURITY.md +2 -0
- package/docs/GETTING-STARTED.md +2 -0
- package/docs/SAFE-TRACE-SHARING.md +1 -0
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-SJ6FRNOE.mjs → chunk-LKEPABND.mjs} +86 -80
- package/packages/cli/dist/chunk-LKEPABND.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +399 -91
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +307 -14
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-YNEJOPRA.mjs → src-4WYF5RFI.mjs} +3 -3
- package/packages/cli/dist/{src-YNEJOPRA.mjs.map → src-4WYF5RFI.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +97 -30
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.mjs +13 -13
- package/packages/core/dist/checks.cjs +51 -49
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.mjs +5 -5
- package/packages/core/dist/{chunk-PBNBQRXV.mjs → chunk-2KJR6X4V.mjs} +9 -9
- package/packages/core/dist/chunk-2KJR6X4V.mjs.map +1 -0
- package/packages/core/dist/{chunk-26YJSETN.mjs → chunk-7DHSWDKB.mjs} +19 -6
- package/packages/core/dist/chunk-7DHSWDKB.mjs.map +1 -0
- package/packages/core/dist/{chunk-BGF5JDJN.mjs → chunk-INE5IWLN.mjs} +3 -3
- package/packages/core/dist/{chunk-BGF5JDJN.mjs.map → chunk-INE5IWLN.mjs.map} +1 -1
- package/packages/core/dist/{chunk-ZFJ2ICTC.mjs → chunk-MTMTXTJL.mjs} +5 -5
- package/packages/core/dist/{chunk-ZFJ2ICTC.mjs.map → chunk-MTMTXTJL.mjs.map} +1 -1
- package/packages/core/dist/{chunk-KWBO36PH.mjs → chunk-NY4ILMXK.mjs} +4 -4
- package/packages/core/dist/{chunk-KWBO36PH.mjs.map → chunk-NY4ILMXK.mjs.map} +1 -1
- package/packages/core/dist/{chunk-WN5PT4GV.mjs → chunk-OSGNGRHF.mjs} +3 -3
- package/packages/core/dist/{chunk-WN5PT4GV.mjs.map → chunk-OSGNGRHF.mjs.map} +1 -1
- package/packages/core/dist/{chunk-CBPWLHNQ.mjs → chunk-PKDBFBJ7.mjs} +5 -76
- package/packages/core/dist/chunk-PKDBFBJ7.mjs.map +1 -0
- package/packages/core/dist/{chunk-VU6O5QAH.mjs → chunk-VLVCLAPB.mjs} +83 -5
- package/packages/core/dist/chunk-VLVCLAPB.mjs.map +1 -0
- package/packages/core/dist/exporters.cjs +217 -6
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.mjs +118 -9
- package/packages/core/dist/exporters.mjs.map +1 -1
- package/packages/core/dist/index.cjs +97 -8
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.mjs +6 -6
- package/packages/core/dist/logs.cjs +80 -2
- package/packages/core/dist/logs.cjs.map +1 -1
- package/packages/core/dist/logs.d.cts +2 -0
- package/packages/core/dist/logs.d.ts +2 -0
- package/packages/core/dist/logs.mjs +2 -2
- package/packages/core/dist/readers.cjs +2 -0
- package/packages/core/dist/readers.cjs.map +1 -1
- package/packages/core/dist/readers.mjs +2 -2
- package/packages/core/dist/writers.cjs +96 -8
- package/packages/core/dist/writers.cjs.map +1 -1
- package/packages/core/dist/writers.mjs +7 -8
- package/packages/core/dist/writers.mjs.map +1 -1
- package/packages/cli/dist/chunk-SJ6FRNOE.mjs.map +0 -1
- package/packages/core/dist/chunk-26YJSETN.mjs.map +0 -1
- package/packages/core/dist/chunk-CBPWLHNQ.mjs.map +0 -1
- package/packages/core/dist/chunk-PBNBQRXV.mjs.map +0 -1
- package/packages/core/dist/chunk-VU6O5QAH.mjs.map +0 -1
|
@@ -13,6 +13,7 @@ var readline = require('readline');
|
|
|
13
13
|
var url = require('url');
|
|
14
14
|
var module$1 = require('module');
|
|
15
15
|
var commander = require('commander');
|
|
16
|
+
var string_decoder = require('string_decoder');
|
|
16
17
|
var child_process = require('child_process');
|
|
17
18
|
var util = require('util');
|
|
18
19
|
var http = require('http');
|
|
@@ -66,6 +67,83 @@ var init_correlation_metadata = __esm({
|
|
|
66
67
|
];
|
|
67
68
|
}
|
|
68
69
|
});
|
|
70
|
+
|
|
71
|
+
// packages/core/src/safety/sensitive-key.ts
|
|
72
|
+
function keyHasExplicitSeparator(value) {
|
|
73
|
+
return /[_\-.]/.test(value);
|
|
74
|
+
}
|
|
75
|
+
function normalizeSensitiveKey(value) {
|
|
76
|
+
return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
77
|
+
}
|
|
78
|
+
function isTokenCredentialKey(normalized) {
|
|
79
|
+
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
80
|
+
if (normalized === "token") return true;
|
|
81
|
+
if (normalized.endsWith("tokens")) return false;
|
|
82
|
+
return normalized.endsWith("token");
|
|
83
|
+
}
|
|
84
|
+
function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
|
|
85
|
+
if (!key) return false;
|
|
86
|
+
const normalized = normalizeSensitiveKey(key);
|
|
87
|
+
if (!normalized) return false;
|
|
88
|
+
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
89
|
+
const allowPrefixCompound = keyHasExplicitSeparator(key);
|
|
90
|
+
for (const sensitive of sensitiveKeys) {
|
|
91
|
+
const s = normalizeSensitiveKey(sensitive);
|
|
92
|
+
if (!s) continue;
|
|
93
|
+
if (s === "token") {
|
|
94
|
+
if (isTokenCredentialKey(normalized)) return true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (normalized === s) return true;
|
|
98
|
+
if (normalized.endsWith(`_${s}`)) return true;
|
|
99
|
+
if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
var NON_CREDENTIAL_TOKEN_CONFIG_KEYS, DEFAULT_CREDENTIAL_SENSITIVE_KEYS;
|
|
104
|
+
var init_sensitive_key = __esm({
|
|
105
|
+
"packages/core/src/safety/sensitive-key.ts"() {
|
|
106
|
+
NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
|
|
107
|
+
[
|
|
108
|
+
"tokens",
|
|
109
|
+
"max_tokens",
|
|
110
|
+
"min_tokens",
|
|
111
|
+
"ls_max_tokens",
|
|
112
|
+
"token_count",
|
|
113
|
+
"token_limit",
|
|
114
|
+
"token_budget",
|
|
115
|
+
"input_tokens",
|
|
116
|
+
"output_tokens",
|
|
117
|
+
"total_tokens",
|
|
118
|
+
"cached_tokens",
|
|
119
|
+
"prompt_tokens",
|
|
120
|
+
"completion_tokens"
|
|
121
|
+
].map(normalizeSensitiveKey)
|
|
122
|
+
);
|
|
123
|
+
DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
|
|
124
|
+
"authorization",
|
|
125
|
+
"cookie",
|
|
126
|
+
"token",
|
|
127
|
+
"access_token",
|
|
128
|
+
"accesstoken",
|
|
129
|
+
"auth_token",
|
|
130
|
+
"authtoken",
|
|
131
|
+
"refresh_token",
|
|
132
|
+
"refreshtoken",
|
|
133
|
+
"id_token",
|
|
134
|
+
"idtoken",
|
|
135
|
+
"bearer_token",
|
|
136
|
+
"bearertoken",
|
|
137
|
+
"api_token",
|
|
138
|
+
"apitoken",
|
|
139
|
+
"apikey",
|
|
140
|
+
"api_key",
|
|
141
|
+
"password",
|
|
142
|
+
"secret",
|
|
143
|
+
"email"
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
});
|
|
69
147
|
function isRecord(v) {
|
|
70
148
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
71
149
|
}
|
|
@@ -80,7 +158,7 @@ function compileRules(rules, extraKeys) {
|
|
|
80
158
|
const out = /* @__PURE__ */ new Map();
|
|
81
159
|
const set = (r) => {
|
|
82
160
|
const k = toKey(r.key);
|
|
83
|
-
out.set(k, { ...r, key: k });
|
|
161
|
+
out.set(k, { ...r, key: k, raw: r.raw ?? r.key });
|
|
84
162
|
};
|
|
85
163
|
for (const k of DEFAULT_REDACT_KEYS) {
|
|
86
164
|
set({ key: k, strategy: "full" });
|
|
@@ -107,6 +185,7 @@ function compileRules(rules, extraKeys) {
|
|
|
107
185
|
var DEFAULT_REDACT_KEYS, Redactor;
|
|
108
186
|
var init_redactor = __esm({
|
|
109
187
|
"packages/core/src/logs/redactor.ts"() {
|
|
188
|
+
init_sensitive_key();
|
|
110
189
|
DEFAULT_REDACT_KEYS = [
|
|
111
190
|
"authorization",
|
|
112
191
|
"cookie",
|
|
@@ -121,9 +200,13 @@ var init_redactor = __esm({
|
|
|
121
200
|
constructor(options) {
|
|
122
201
|
this.#rules = compileRules(options?.rules, options?.extraKeys);
|
|
123
202
|
}
|
|
203
|
+
/** Field names this redactor treats as sensitive, for value-level helpers. */
|
|
204
|
+
get sensitiveKeys() {
|
|
205
|
+
return this.#rules.map((rule) => rule.raw ?? rule.key);
|
|
206
|
+
}
|
|
124
207
|
redactValue(key, value) {
|
|
125
208
|
const k = toKey(key);
|
|
126
|
-
const rule = this.#rules.find((r) => r.key === k);
|
|
209
|
+
const rule = this.#rules.find((r) => r.key === k) ?? this.#rules.find((r) => isCredentialSensitiveKey(key, [r.raw ?? r.key]));
|
|
127
210
|
if (!rule) {
|
|
128
211
|
return this.#redactNested(value);
|
|
129
212
|
}
|
|
@@ -178,7 +261,7 @@ function stringContainsHighConfidenceCredential(value) {
|
|
|
178
261
|
var KEY_VALUE_SECRET_PATTERN, HIGH_CONFIDENCE_CREDENTIAL_PATTERNS;
|
|
179
262
|
var init_credential_value_patterns = __esm({
|
|
180
263
|
"packages/core/src/safety/credential-value-patterns.ts"() {
|
|
181
|
-
KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i;
|
|
264
|
+
KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i;
|
|
182
265
|
HIGH_CONFIDENCE_CREDENTIAL_PATTERNS = [
|
|
183
266
|
/^(?:basic|bearer|digest|apikey)\s+[a-z0-9._~+/=-]+$/i,
|
|
184
267
|
/\bbearer\s+[a-z0-9._~+/=-]{12,}\b/i,
|
|
@@ -829,6 +912,10 @@ var init_nanoid = __esm({
|
|
|
829
912
|
POOL_SIZE_MULTIPLIER = 128;
|
|
830
913
|
}
|
|
831
914
|
});
|
|
915
|
+
var init_trace_filesystem = __esm({
|
|
916
|
+
"packages/core/src/trace-filesystem.ts"() {
|
|
917
|
+
}
|
|
918
|
+
});
|
|
832
919
|
|
|
833
920
|
// packages/core/src/utils/duration.ts
|
|
834
921
|
function parseDuration(duration) {
|
|
@@ -979,6 +1066,7 @@ function warn(message, error) {
|
|
|
979
1066
|
var DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME, FALLBACK_TRACE_DIR, MAX_NAME_LENGTH;
|
|
980
1067
|
var init_utils = __esm({
|
|
981
1068
|
"packages/core/src/utils.ts"() {
|
|
1069
|
+
init_trace_filesystem();
|
|
982
1070
|
init_duration();
|
|
983
1071
|
DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
|
|
984
1072
|
RUNS_DIR_NAME = "runs";
|
|
@@ -1149,6 +1237,7 @@ var init_storage = __esm({
|
|
|
1149
1237
|
init_types();
|
|
1150
1238
|
init_trace_event_safety();
|
|
1151
1239
|
init_read_trace();
|
|
1240
|
+
init_trace_filesystem();
|
|
1152
1241
|
init_utils();
|
|
1153
1242
|
}
|
|
1154
1243
|
});
|
|
@@ -4662,83 +4751,6 @@ var init_programmatic = __esm({
|
|
|
4662
4751
|
}
|
|
4663
4752
|
});
|
|
4664
4753
|
|
|
4665
|
-
// packages/core/src/safety/sensitive-key.ts
|
|
4666
|
-
function keyHasExplicitSeparator(value) {
|
|
4667
|
-
return /[_\-.]/.test(value);
|
|
4668
|
-
}
|
|
4669
|
-
function normalizeSensitiveKey(value) {
|
|
4670
|
-
return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
4671
|
-
}
|
|
4672
|
-
function isTokenCredentialKey(normalized) {
|
|
4673
|
-
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
4674
|
-
if (normalized === "token") return true;
|
|
4675
|
-
if (normalized.endsWith("tokens")) return false;
|
|
4676
|
-
return normalized.endsWith("token");
|
|
4677
|
-
}
|
|
4678
|
-
function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
|
|
4679
|
-
if (!key) return false;
|
|
4680
|
-
const normalized = normalizeSensitiveKey(key);
|
|
4681
|
-
if (!normalized) return false;
|
|
4682
|
-
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
4683
|
-
const allowPrefixCompound = keyHasExplicitSeparator(key);
|
|
4684
|
-
for (const sensitive of sensitiveKeys) {
|
|
4685
|
-
const s = normalizeSensitiveKey(sensitive);
|
|
4686
|
-
if (!s) continue;
|
|
4687
|
-
if (s === "token") {
|
|
4688
|
-
if (isTokenCredentialKey(normalized)) return true;
|
|
4689
|
-
continue;
|
|
4690
|
-
}
|
|
4691
|
-
if (normalized === s) return true;
|
|
4692
|
-
if (normalized.endsWith(`_${s}`)) return true;
|
|
4693
|
-
if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
|
|
4694
|
-
}
|
|
4695
|
-
return false;
|
|
4696
|
-
}
|
|
4697
|
-
var NON_CREDENTIAL_TOKEN_CONFIG_KEYS, DEFAULT_CREDENTIAL_SENSITIVE_KEYS;
|
|
4698
|
-
var init_sensitive_key = __esm({
|
|
4699
|
-
"packages/core/src/safety/sensitive-key.ts"() {
|
|
4700
|
-
NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
|
|
4701
|
-
[
|
|
4702
|
-
"tokens",
|
|
4703
|
-
"max_tokens",
|
|
4704
|
-
"min_tokens",
|
|
4705
|
-
"ls_max_tokens",
|
|
4706
|
-
"token_count",
|
|
4707
|
-
"token_limit",
|
|
4708
|
-
"token_budget",
|
|
4709
|
-
"input_tokens",
|
|
4710
|
-
"output_tokens",
|
|
4711
|
-
"total_tokens",
|
|
4712
|
-
"cached_tokens",
|
|
4713
|
-
"prompt_tokens",
|
|
4714
|
-
"completion_tokens"
|
|
4715
|
-
].map(normalizeSensitiveKey)
|
|
4716
|
-
);
|
|
4717
|
-
DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
|
|
4718
|
-
"authorization",
|
|
4719
|
-
"cookie",
|
|
4720
|
-
"token",
|
|
4721
|
-
"access_token",
|
|
4722
|
-
"accesstoken",
|
|
4723
|
-
"auth_token",
|
|
4724
|
-
"authtoken",
|
|
4725
|
-
"refresh_token",
|
|
4726
|
-
"refreshtoken",
|
|
4727
|
-
"id_token",
|
|
4728
|
-
"idtoken",
|
|
4729
|
-
"bearer_token",
|
|
4730
|
-
"bearertoken",
|
|
4731
|
-
"api_token",
|
|
4732
|
-
"apitoken",
|
|
4733
|
-
"apikey",
|
|
4734
|
-
"api_key",
|
|
4735
|
-
"password",
|
|
4736
|
-
"secret",
|
|
4737
|
-
"email"
|
|
4738
|
-
];
|
|
4739
|
-
}
|
|
4740
|
-
});
|
|
4741
|
-
|
|
4742
4754
|
// packages/core/src/checks/logical-events.ts
|
|
4743
4755
|
function isRecord7(value) {
|
|
4744
4756
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -7350,7 +7362,7 @@ var init_checks2 = __esm({
|
|
|
7350
7362
|
// Keep in sync with packages/redact/src/key-value-secret.ts (KEY_VALUE_SECRET_PATTERN_SOURCE).
|
|
7351
7363
|
{
|
|
7352
7364
|
id: "key-value-secret",
|
|
7353
|
-
pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i
|
|
7365
|
+
pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i
|
|
7354
7366
|
}
|
|
7355
7367
|
];
|
|
7356
7368
|
}
|
|
@@ -16153,7 +16165,7 @@ var init_src = __esm({
|
|
|
16153
16165
|
});
|
|
16154
16166
|
|
|
16155
16167
|
// package.json
|
|
16156
|
-
var version = "6.31.
|
|
16168
|
+
var version = "6.31.4";
|
|
16157
16169
|
|
|
16158
16170
|
// packages/cli/src/list.ts
|
|
16159
16171
|
init_advanced();
|
|
@@ -17757,6 +17769,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
17757
17769
|
await readFileOnce(filePath, onLine);
|
|
17758
17770
|
return;
|
|
17759
17771
|
}
|
|
17772
|
+
let decoder = new string_decoder.StringDecoder("utf-8");
|
|
17760
17773
|
pos = st.size;
|
|
17761
17774
|
while (!shouldStop()) {
|
|
17762
17775
|
let next;
|
|
@@ -17770,6 +17783,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
17770
17783
|
pos = 0;
|
|
17771
17784
|
carry = "";
|
|
17772
17785
|
lineNumber = 0;
|
|
17786
|
+
decoder = new string_decoder.StringDecoder("utf-8");
|
|
17773
17787
|
}
|
|
17774
17788
|
if (next.size > pos) {
|
|
17775
17789
|
const fh = await promises.open(filePath, "r");
|
|
@@ -17778,7 +17792,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
17778
17792
|
const buf = Buffer.allocUnsafe(Number(len));
|
|
17779
17793
|
const { bytesRead } = await fh.read(buf, 0, buf.length, pos);
|
|
17780
17794
|
pos += bytesRead;
|
|
17781
|
-
const chunk = carry + buf.
|
|
17795
|
+
const chunk = carry + decoder.write(buf.subarray(0, bytesRead));
|
|
17782
17796
|
const parts = chunk.split(/\r?\n/);
|
|
17783
17797
|
carry = parts.pop() ?? "";
|
|
17784
17798
|
for (const p of parts) {
|
|
@@ -17943,6 +17957,103 @@ var EXPORT_PAYLOAD_VERSION = "0.1.2";
|
|
|
17943
17957
|
|
|
17944
17958
|
// packages/core/src/exporters/redact-export.ts
|
|
17945
17959
|
init_redactor();
|
|
17960
|
+
init_credential_value_patterns();
|
|
17961
|
+
|
|
17962
|
+
// packages/core/src/safety/url-redaction.ts
|
|
17963
|
+
init_sensitive_key();
|
|
17964
|
+
var HTTP_URL = /^https?:\/\/[^\s]+$/i;
|
|
17965
|
+
var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
17966
|
+
var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
|
|
17967
|
+
var NUMERIC_SEGMENT = /^\d{5,}$/;
|
|
17968
|
+
var OPAQUE_SEGMENT = /^[A-Za-z0-9_-]{20,}$/;
|
|
17969
|
+
function looksLikeHttpUrl(value) {
|
|
17970
|
+
return HTTP_URL.test(value.trim());
|
|
17971
|
+
}
|
|
17972
|
+
function parseHttpUrl(value) {
|
|
17973
|
+
try {
|
|
17974
|
+
const url = new URL(value.trim());
|
|
17975
|
+
return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
|
|
17976
|
+
} catch {
|
|
17977
|
+
return void 0;
|
|
17978
|
+
}
|
|
17979
|
+
}
|
|
17980
|
+
function isIdentifierSegment(segment) {
|
|
17981
|
+
if (UUID_SEGMENT.test(segment)) return true;
|
|
17982
|
+
if (HEX_SEGMENT.test(segment)) return true;
|
|
17983
|
+
if (NUMERIC_SEGMENT.test(segment)) return true;
|
|
17984
|
+
return OPAQUE_SEGMENT.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
|
|
17985
|
+
}
|
|
17986
|
+
function decodeParam(value) {
|
|
17987
|
+
try {
|
|
17988
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
17989
|
+
} catch {
|
|
17990
|
+
return value;
|
|
17991
|
+
}
|
|
17992
|
+
}
|
|
17993
|
+
function redactParams(raw, options, replacement) {
|
|
17994
|
+
let redacted = false;
|
|
17995
|
+
const parts = raw.split("&").map((part) => {
|
|
17996
|
+
if (part.length === 0) return part;
|
|
17997
|
+
const separator = part.indexOf("=");
|
|
17998
|
+
if (separator < 0) return part;
|
|
17999
|
+
const rawKey = part.slice(0, separator);
|
|
18000
|
+
const rawValue = part.slice(separator + 1);
|
|
18001
|
+
const key = decodeParam(rawKey);
|
|
18002
|
+
const value = decodeParam(rawValue);
|
|
18003
|
+
if (isCredentialSensitiveKey(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
|
|
18004
|
+
redacted = true;
|
|
18005
|
+
return `${rawKey}=${replacement}`;
|
|
18006
|
+
}
|
|
18007
|
+
return part;
|
|
18008
|
+
});
|
|
18009
|
+
return { value: parts.join("&"), redacted };
|
|
18010
|
+
}
|
|
18011
|
+
function redactUrlString(value, options) {
|
|
18012
|
+
const unchanged = {
|
|
18013
|
+
value,
|
|
18014
|
+
credentialRedacted: false,
|
|
18015
|
+
identifiersMasked: false
|
|
18016
|
+
};
|
|
18017
|
+
if (!looksLikeHttpUrl(value)) return unchanged;
|
|
18018
|
+
const url = parseHttpUrl(value);
|
|
18019
|
+
if (!url) return unchanged;
|
|
18020
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
18021
|
+
const idPlaceholder = options.idPlaceholder ?? "[id]";
|
|
18022
|
+
let credentialRedacted = url.username.length > 0 || url.password.length > 0;
|
|
18023
|
+
let identifiersMasked = false;
|
|
18024
|
+
let pathname = url.pathname;
|
|
18025
|
+
if (options.maskPathIds === true) {
|
|
18026
|
+
pathname = url.pathname.split("/").map((segment) => {
|
|
18027
|
+
if (segment.length === 0 || !isIdentifierSegment(decodeParam(segment))) {
|
|
18028
|
+
return segment;
|
|
18029
|
+
}
|
|
18030
|
+
identifiersMasked = true;
|
|
18031
|
+
return idPlaceholder;
|
|
18032
|
+
}).join("/");
|
|
18033
|
+
}
|
|
18034
|
+
let search = "";
|
|
18035
|
+
if (url.search.length > 1) {
|
|
18036
|
+
const params = redactParams(url.search.slice(1), options, replacement);
|
|
18037
|
+
if (params.redacted) credentialRedacted = true;
|
|
18038
|
+
search = `?${params.value}`;
|
|
18039
|
+
} else if (url.search.length === 1) {
|
|
18040
|
+
search = "?";
|
|
18041
|
+
}
|
|
18042
|
+
let hash = url.hash;
|
|
18043
|
+
if (url.hash.includes("=")) {
|
|
18044
|
+
const params = redactParams(url.hash.slice(1), options, replacement);
|
|
18045
|
+
if (params.redacted) credentialRedacted = true;
|
|
18046
|
+
hash = `#${params.value}`;
|
|
18047
|
+
}
|
|
18048
|
+
if (!credentialRedacted && !identifiersMasked) return unchanged;
|
|
18049
|
+
return {
|
|
18050
|
+
value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
|
|
18051
|
+
credentialRedacted,
|
|
18052
|
+
identifiersMasked
|
|
18053
|
+
};
|
|
18054
|
+
}
|
|
18055
|
+
|
|
18056
|
+
// packages/core/src/exporters/redact-export.ts
|
|
17946
18057
|
init_redaction_profiles();
|
|
17947
18058
|
function isRecord15(value) {
|
|
17948
18059
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -17960,21 +18071,34 @@ function deepClone(value) {
|
|
|
17960
18071
|
}
|
|
17961
18072
|
return out;
|
|
17962
18073
|
}
|
|
17963
|
-
function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
18074
|
+
function boundAttributeValues(record, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
17964
18075
|
if (depth > 32) {
|
|
17965
18076
|
return { truncated: true, reason: "maxDepth" };
|
|
17966
18077
|
}
|
|
17967
18078
|
const out = {};
|
|
17968
18079
|
for (const [key, value] of Object.entries(record)) {
|
|
17969
|
-
out[key] = boundValue(
|
|
18080
|
+
out[key] = boundValue(
|
|
18081
|
+
value,
|
|
18082
|
+
key,
|
|
18083
|
+
sensitiveKeys,
|
|
18084
|
+
maxMetadataValueLength,
|
|
18085
|
+
maxPreviewLength,
|
|
18086
|
+
seen,
|
|
18087
|
+
depth
|
|
18088
|
+
);
|
|
17970
18089
|
}
|
|
17971
18090
|
return out;
|
|
17972
18091
|
}
|
|
17973
|
-
function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
18092
|
+
function boundValue(value, key, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
17974
18093
|
if (value === null || typeof value !== "object") {
|
|
17975
18094
|
if (typeof value === "string") {
|
|
18095
|
+
const rewritten = redactUrlString(value, {
|
|
18096
|
+
sensitiveKeys,
|
|
18097
|
+
isSensitiveValue: stringContainsHighConfidenceCredential,
|
|
18098
|
+
maskPathIds: true
|
|
18099
|
+
}).value;
|
|
17976
18100
|
return truncateStringForProfile(
|
|
17977
|
-
|
|
18101
|
+
stringContainsHighConfidenceCredential(rewritten) ? "[REDACTED]" : rewritten,
|
|
17978
18102
|
key,
|
|
17979
18103
|
maxMetadataValueLength,
|
|
17980
18104
|
maxPreviewLength
|
|
@@ -17989,6 +18113,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
|
|
|
17989
18113
|
(item, index) => boundValue(
|
|
17990
18114
|
item,
|
|
17991
18115
|
String(index),
|
|
18116
|
+
sensitiveKeys,
|
|
17992
18117
|
maxMetadataValueLength,
|
|
17993
18118
|
maxPreviewLength,
|
|
17994
18119
|
seen,
|
|
@@ -17998,6 +18123,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
|
|
|
17998
18123
|
}
|
|
17999
18124
|
return boundAttributeValues(
|
|
18000
18125
|
value,
|
|
18126
|
+
sensitiveKeys,
|
|
18001
18127
|
maxMetadataValueLength,
|
|
18002
18128
|
maxPreviewLength,
|
|
18003
18129
|
seen,
|
|
@@ -18012,6 +18138,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
|
|
|
18012
18138
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
18013
18139
|
const bounded = boundAttributeValues(
|
|
18014
18140
|
redacted,
|
|
18141
|
+
[...DEFAULT_REDACT_KEYS, ...redactor.sensitiveKeys],
|
|
18015
18142
|
maxMetadataValueLength,
|
|
18016
18143
|
maxPreviewLength,
|
|
18017
18144
|
seen,
|
|
@@ -20150,7 +20277,7 @@ function isCredentialSensitiveKey2(key, sensitiveKeys) {
|
|
|
20150
20277
|
}
|
|
20151
20278
|
|
|
20152
20279
|
// packages/redact/src/key-value-secret.ts
|
|
20153
|
-
var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})`;
|
|
20280
|
+
var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})`;
|
|
20154
20281
|
var KEY_VALUE_SECRET_PATTERN2 = new RegExp(
|
|
20155
20282
|
KEY_VALUE_SECRET_PATTERN_SOURCE,
|
|
20156
20283
|
"i"
|
|
@@ -20160,6 +20287,99 @@ function valueContainsKeyValueSecret(value) {
|
|
|
20160
20287
|
return KEY_VALUE_SECRET_PATTERN2.test(value);
|
|
20161
20288
|
}
|
|
20162
20289
|
|
|
20290
|
+
// packages/redact/src/url-redaction.ts
|
|
20291
|
+
var HTTP_URL2 = /^https?:\/\/[^\s]+$/i;
|
|
20292
|
+
var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
20293
|
+
var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
|
|
20294
|
+
var NUMERIC_SEGMENT2 = /^\d{5,}$/;
|
|
20295
|
+
var OPAQUE_SEGMENT2 = /^[A-Za-z0-9_-]{20,}$/;
|
|
20296
|
+
function looksLikeHttpUrl2(value) {
|
|
20297
|
+
return HTTP_URL2.test(value.trim());
|
|
20298
|
+
}
|
|
20299
|
+
function parseHttpUrl2(value) {
|
|
20300
|
+
try {
|
|
20301
|
+
const url = new URL(value.trim());
|
|
20302
|
+
return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
|
|
20303
|
+
} catch {
|
|
20304
|
+
return void 0;
|
|
20305
|
+
}
|
|
20306
|
+
}
|
|
20307
|
+
function isIdentifierSegment2(segment) {
|
|
20308
|
+
if (UUID_SEGMENT2.test(segment)) return true;
|
|
20309
|
+
if (HEX_SEGMENT2.test(segment)) return true;
|
|
20310
|
+
if (NUMERIC_SEGMENT2.test(segment)) return true;
|
|
20311
|
+
return OPAQUE_SEGMENT2.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
|
|
20312
|
+
}
|
|
20313
|
+
function decodeParam2(value) {
|
|
20314
|
+
try {
|
|
20315
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
20316
|
+
} catch {
|
|
20317
|
+
return value;
|
|
20318
|
+
}
|
|
20319
|
+
}
|
|
20320
|
+
function redactParams2(raw, options, replacement) {
|
|
20321
|
+
let redacted = false;
|
|
20322
|
+
const parts = raw.split("&").map((part) => {
|
|
20323
|
+
if (part.length === 0) return part;
|
|
20324
|
+
const separator = part.indexOf("=");
|
|
20325
|
+
if (separator < 0) return part;
|
|
20326
|
+
const rawKey = part.slice(0, separator);
|
|
20327
|
+
const rawValue = part.slice(separator + 1);
|
|
20328
|
+
const key = decodeParam2(rawKey);
|
|
20329
|
+
const value = decodeParam2(rawValue);
|
|
20330
|
+
if (isCredentialSensitiveKey2(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
|
|
20331
|
+
redacted = true;
|
|
20332
|
+
return `${rawKey}=${replacement}`;
|
|
20333
|
+
}
|
|
20334
|
+
return part;
|
|
20335
|
+
});
|
|
20336
|
+
return { value: parts.join("&"), redacted };
|
|
20337
|
+
}
|
|
20338
|
+
function redactUrlString2(value, options) {
|
|
20339
|
+
const unchanged = {
|
|
20340
|
+
value,
|
|
20341
|
+
credentialRedacted: false,
|
|
20342
|
+
identifiersMasked: false
|
|
20343
|
+
};
|
|
20344
|
+
if (!looksLikeHttpUrl2(value)) return unchanged;
|
|
20345
|
+
const url = parseHttpUrl2(value);
|
|
20346
|
+
if (!url) return unchanged;
|
|
20347
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
20348
|
+
const idPlaceholder = options.idPlaceholder ?? "[id]";
|
|
20349
|
+
let credentialRedacted = url.username.length > 0 || url.password.length > 0;
|
|
20350
|
+
let identifiersMasked = false;
|
|
20351
|
+
let pathname = url.pathname;
|
|
20352
|
+
if (options.maskPathIds === true) {
|
|
20353
|
+
pathname = url.pathname.split("/").map((segment) => {
|
|
20354
|
+
if (segment.length === 0 || !isIdentifierSegment2(decodeParam2(segment))) {
|
|
20355
|
+
return segment;
|
|
20356
|
+
}
|
|
20357
|
+
identifiersMasked = true;
|
|
20358
|
+
return idPlaceholder;
|
|
20359
|
+
}).join("/");
|
|
20360
|
+
}
|
|
20361
|
+
let search = "";
|
|
20362
|
+
if (url.search.length > 1) {
|
|
20363
|
+
const params = redactParams2(url.search.slice(1), options, replacement);
|
|
20364
|
+
if (params.redacted) credentialRedacted = true;
|
|
20365
|
+
search = `?${params.value}`;
|
|
20366
|
+
} else if (url.search.length === 1) {
|
|
20367
|
+
search = "?";
|
|
20368
|
+
}
|
|
20369
|
+
let hash = url.hash;
|
|
20370
|
+
if (url.hash.includes("=")) {
|
|
20371
|
+
const params = redactParams2(url.hash.slice(1), options, replacement);
|
|
20372
|
+
if (params.redacted) credentialRedacted = true;
|
|
20373
|
+
hash = `#${params.value}`;
|
|
20374
|
+
}
|
|
20375
|
+
if (!credentialRedacted && !identifiersMasked) return unchanged;
|
|
20376
|
+
return {
|
|
20377
|
+
value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
|
|
20378
|
+
credentialRedacted,
|
|
20379
|
+
identifiersMasked
|
|
20380
|
+
};
|
|
20381
|
+
}
|
|
20382
|
+
|
|
20163
20383
|
// packages/redact/src/index.ts
|
|
20164
20384
|
var DEFAULT_REDACT_KEYS2 = [
|
|
20165
20385
|
"authorization",
|
|
@@ -20226,7 +20446,9 @@ function findCompiledKeyRule(key, rules) {
|
|
|
20226
20446
|
const exact = toKey2(key);
|
|
20227
20447
|
const direct = rules.find((candidate) => candidate.key === exact);
|
|
20228
20448
|
if (direct) return direct;
|
|
20229
|
-
return rules.find(
|
|
20449
|
+
return rules.find(
|
|
20450
|
+
(candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
|
|
20451
|
+
);
|
|
20230
20452
|
}
|
|
20231
20453
|
function stableHash2(value) {
|
|
20232
20454
|
const hash = crypto__default.default.createHash("sha256").update(value, "utf8").digest("hex");
|
|
@@ -20425,7 +20647,7 @@ function compileRules2(rules, extraKeys) {
|
|
|
20425
20647
|
const out = /* @__PURE__ */ new Map();
|
|
20426
20648
|
const set = (rule) => {
|
|
20427
20649
|
const key = toKey2(rule.key);
|
|
20428
|
-
out.set(key, { ...rule, key });
|
|
20650
|
+
out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
|
|
20429
20651
|
};
|
|
20430
20652
|
for (const key of DEFAULT_REDACT_KEYS2) {
|
|
20431
20653
|
set({ key, strategy: "full" });
|
|
@@ -20504,6 +20726,7 @@ function createRedactionProfile(profile = "local") {
|
|
|
20504
20726
|
}
|
|
20505
20727
|
var Redactor2 = class {
|
|
20506
20728
|
#rules;
|
|
20729
|
+
#sensitiveKeys;
|
|
20507
20730
|
#detectors;
|
|
20508
20731
|
#profile;
|
|
20509
20732
|
#replacement;
|
|
@@ -20516,6 +20739,7 @@ var Redactor2 = class {
|
|
|
20516
20739
|
...resolved.extraKeys,
|
|
20517
20740
|
...options?.extraKeys ?? []
|
|
20518
20741
|
]);
|
|
20742
|
+
this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
|
|
20519
20743
|
this.#detectors = [
|
|
20520
20744
|
...builtInDetectorsForProfile(this.#profile),
|
|
20521
20745
|
...options?.detectors ?? []
|
|
@@ -20549,6 +20773,14 @@ var Redactor2 = class {
|
|
|
20549
20773
|
#recordFinding(state, finding) {
|
|
20550
20774
|
if (this.#collectFindings) state.findings.push(finding);
|
|
20551
20775
|
}
|
|
20776
|
+
#detect(value, key, path44) {
|
|
20777
|
+
for (const detector of this.#detectors) {
|
|
20778
|
+
for (const detection of detector.detect({ path: path44, key, value })) {
|
|
20779
|
+
if ((detection.action ?? "replace") !== "keep") return { detector, detection };
|
|
20780
|
+
}
|
|
20781
|
+
}
|
|
20782
|
+
return void 0;
|
|
20783
|
+
}
|
|
20552
20784
|
#redactValue(value, key, path44, depth, state) {
|
|
20553
20785
|
if (depth > this.#maxDepth) {
|
|
20554
20786
|
this.#recordFinding(
|
|
@@ -20567,6 +20799,43 @@ var Redactor2 = class {
|
|
|
20567
20799
|
return applyRule(rule, value, this.#replacement);
|
|
20568
20800
|
}
|
|
20569
20801
|
}
|
|
20802
|
+
if (typeof value === "string" && looksLikeHttpUrl2(value)) {
|
|
20803
|
+
const url = redactUrlString2(value, {
|
|
20804
|
+
sensitiveKeys: this.#sensitiveKeys,
|
|
20805
|
+
isSensitiveValue: (candidate) => this.#detect(candidate, key, path44) !== void 0,
|
|
20806
|
+
replacement: this.#replacement,
|
|
20807
|
+
maskPathIds: this.#profile !== "local"
|
|
20808
|
+
});
|
|
20809
|
+
if (url.value !== value) {
|
|
20810
|
+
this.#recordFinding(
|
|
20811
|
+
state,
|
|
20812
|
+
makeFinding(
|
|
20813
|
+
path44,
|
|
20814
|
+
url.credentialRedacted ? "value.urlCredential" : "value.urlIdentifier",
|
|
20815
|
+
"replace",
|
|
20816
|
+
"value",
|
|
20817
|
+
url.credentialRedacted ? "error" : "warning"
|
|
20818
|
+
)
|
|
20819
|
+
);
|
|
20820
|
+
const residual = this.#detect(
|
|
20821
|
+
url.value.split(this.#replacement).join(""),
|
|
20822
|
+
key,
|
|
20823
|
+
path44
|
|
20824
|
+
);
|
|
20825
|
+
if (residual === void 0) return url.value;
|
|
20826
|
+
this.#recordFinding(
|
|
20827
|
+
state,
|
|
20828
|
+
makeFinding(
|
|
20829
|
+
path44,
|
|
20830
|
+
residual.detector.id,
|
|
20831
|
+
"replace",
|
|
20832
|
+
residual.detection.matchKind ?? residual.detector.matchKind ?? "custom",
|
|
20833
|
+
residual.detection.severity ?? residual.detector.severity ?? "warning"
|
|
20834
|
+
)
|
|
20835
|
+
);
|
|
20836
|
+
return this.#replacement;
|
|
20837
|
+
}
|
|
20838
|
+
}
|
|
20570
20839
|
for (const detector of this.#detectors) {
|
|
20571
20840
|
const detections = detector.detect({ path: path44, key, value });
|
|
20572
20841
|
for (const detection of detections) {
|
|
@@ -28582,7 +28851,7 @@ function envCheck(name, optional = true) {
|
|
|
28582
28851
|
async function traceDirWritable(traceDir) {
|
|
28583
28852
|
const resolved = path33__default.default.resolve(traceDir);
|
|
28584
28853
|
try {
|
|
28585
|
-
await promises.mkdir(resolved, { recursive: true });
|
|
28854
|
+
await promises.mkdir(resolved, { recursive: true, mode: 448 });
|
|
28586
28855
|
await promises.access(resolved, promises.constants.W_OK);
|
|
28587
28856
|
return {
|
|
28588
28857
|
id: "trace-dir-writable",
|
|
@@ -28600,6 +28869,44 @@ async function traceDirWritable(traceDir) {
|
|
|
28600
28869
|
};
|
|
28601
28870
|
}
|
|
28602
28871
|
}
|
|
28872
|
+
async function traceDirPermissions(traceDir) {
|
|
28873
|
+
const resolved = path33__default.default.resolve(traceDir);
|
|
28874
|
+
if (process3__default.default.platform === "win32") {
|
|
28875
|
+
return {
|
|
28876
|
+
id: "trace-dir-permissions",
|
|
28877
|
+
status: "skipped",
|
|
28878
|
+
message: "POSIX confidentiality mode check skipped on Windows.",
|
|
28879
|
+
evidence: resolved
|
|
28880
|
+
};
|
|
28881
|
+
}
|
|
28882
|
+
try {
|
|
28883
|
+
const st = await promises.stat(resolved);
|
|
28884
|
+
const mode = st.mode & 511;
|
|
28885
|
+
const octal = mode.toString(8).padStart(3, "0");
|
|
28886
|
+
if ((mode & 63) !== 0) {
|
|
28887
|
+
return {
|
|
28888
|
+
id: "trace-dir-permissions",
|
|
28889
|
+
status: "warn",
|
|
28890
|
+
message: `Trace directory allows group/other access (mode ${octal}): ${resolved}`,
|
|
28891
|
+
remediation: `Restrict with chmod 700 on the directory and review existing JSONL files with chmod 600. AgentInspect does not rewrite existing modes. See ${DOCS_BASE}/GETTING-STARTED.md`,
|
|
28892
|
+
evidence: `mode=${octal}`
|
|
28893
|
+
};
|
|
28894
|
+
}
|
|
28895
|
+
return {
|
|
28896
|
+
id: "trace-dir-permissions",
|
|
28897
|
+
status: "pass",
|
|
28898
|
+
message: `Trace directory mode is restrictive (${octal}): ${resolved}`,
|
|
28899
|
+
evidence: `mode=${octal}`
|
|
28900
|
+
};
|
|
28901
|
+
} catch (error) {
|
|
28902
|
+
return {
|
|
28903
|
+
id: "trace-dir-permissions",
|
|
28904
|
+
status: "skipped",
|
|
28905
|
+
message: `Could not inspect trace directory permissions: ${resolved}`,
|
|
28906
|
+
evidence: error instanceof Error ? error.message : String(error)
|
|
28907
|
+
};
|
|
28908
|
+
}
|
|
28909
|
+
}
|
|
28603
28910
|
function readPackageVersionNearEntry(entryPath, packageName) {
|
|
28604
28911
|
let dir = path33__default.default.dirname(path33__default.default.resolve(entryPath));
|
|
28605
28912
|
const { root } = path33__default.default.parse(dir);
|
|
@@ -28731,6 +29038,7 @@ async function runDoctorChecks(options = {}) {
|
|
|
28731
29038
|
evidence: version
|
|
28732
29039
|
},
|
|
28733
29040
|
await traceDirWritable(traceDir),
|
|
29041
|
+
await traceDirPermissions(traceDir),
|
|
28734
29042
|
envCheck("AGENT_INSPECT"),
|
|
28735
29043
|
envCheck("AGENT_INSPECT_TRACE_DIR"),
|
|
28736
29044
|
versionMismatchCheck(cwd)
|