@cortexkit/aft 0.64.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +280 -52
- package/dist/lib/sanitize.d.ts +12 -6
- package/dist/lib/sanitize.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @cortexkit/aft
|
|
2
2
|
|
|
3
|
-
Unified CLI for [Agent
|
|
3
|
+
Unified CLI for [Agent Foundational Toolkit (AFT)](https://github.com/cortexkit/aft) — setup, doctor, and diagnostics across supported agent harnesses.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* @cortexkit/aft — unified CLI for Agent
|
|
3
|
+
* @cortexkit/aft — unified CLI for Agent Foundational Toolkit.
|
|
4
4
|
*
|
|
5
5
|
* Entry point parses argv and dispatches to commands. Harness selection
|
|
6
6
|
* (OpenCode, Pi) is auto-detected from installed config paths; explicit
|
package/dist/index.js
CHANGED
|
@@ -263,10 +263,87 @@ function relativePathEscapesRoot(rel, platform = process.platform) {
|
|
|
263
263
|
}
|
|
264
264
|
var init_path_display = () => {};
|
|
265
265
|
|
|
266
|
+
// ../aft-bridge/dist/wait-clock.js
|
|
267
|
+
class WaitClock {
|
|
268
|
+
wallNow;
|
|
269
|
+
awakeNow;
|
|
270
|
+
wall;
|
|
271
|
+
awake;
|
|
272
|
+
skew = 0;
|
|
273
|
+
elapsed;
|
|
274
|
+
suspendCount = 0;
|
|
275
|
+
lastSuspendEnd = Number.NEGATIVE_INFINITY;
|
|
276
|
+
constructor(wallNow = Date.now, awakeNow = () => performance.now()) {
|
|
277
|
+
this.wallNow = wallNow;
|
|
278
|
+
this.awakeNow = awakeNow;
|
|
279
|
+
this.wall = wallNow();
|
|
280
|
+
this.awake = awakeNow();
|
|
281
|
+
this.elapsed = this.awake;
|
|
282
|
+
}
|
|
283
|
+
now() {
|
|
284
|
+
const wall = this.wallNow();
|
|
285
|
+
const awake = this.awakeNow();
|
|
286
|
+
const active = Math.max(0, awake - this.awake);
|
|
287
|
+
this.skew = Math.max(0, this.skew + wall - this.wall - active);
|
|
288
|
+
this.elapsed += active;
|
|
289
|
+
if (this.skew >= SUSPEND_SKEW_MS) {
|
|
290
|
+
this.elapsed += this.skew;
|
|
291
|
+
this.lastSuspendEnd = this.elapsed;
|
|
292
|
+
this.skew = 0;
|
|
293
|
+
this.suspendCount++;
|
|
294
|
+
}
|
|
295
|
+
this.wall = wall;
|
|
296
|
+
this.awake = awake;
|
|
297
|
+
return this.elapsed;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
class RequestDeadline {
|
|
302
|
+
clock;
|
|
303
|
+
allowResume;
|
|
304
|
+
deadline;
|
|
305
|
+
suspendCount;
|
|
306
|
+
resumed = false;
|
|
307
|
+
constructor(clock, timeoutMs, allowResume = true) {
|
|
308
|
+
this.clock = clock;
|
|
309
|
+
this.allowResume = allowResume;
|
|
310
|
+
this.deadline = clock.now() + timeoutMs;
|
|
311
|
+
this.suspendCount = clock.suspendCount;
|
|
312
|
+
}
|
|
313
|
+
remaining() {
|
|
314
|
+
const now = this.clock.now();
|
|
315
|
+
if (now >= this.deadline && this.allowResume && !this.resumed && this.clock.suspendCount > this.suspendCount && this.clock.lastSuspendEnd >= this.deadline) {
|
|
316
|
+
this.resumed = true;
|
|
317
|
+
this.deadline = now + RESUME_ALLOWANCE_MS;
|
|
318
|
+
}
|
|
319
|
+
return Math.max(0, this.deadline - now);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
var SUSPEND_SKEW_MS = 1000, RESUME_ALLOWANCE_MS = 1e4, DEADLINE_POLL_MS = 250;
|
|
323
|
+
|
|
266
324
|
// ../aft-bridge/dist/bash-hints.js
|
|
325
|
+
function abortableSleep(ms, signal) {
|
|
326
|
+
if (signal?.aborted)
|
|
327
|
+
return Promise.resolve();
|
|
328
|
+
return new Promise((resolve) => {
|
|
329
|
+
const done = () => {
|
|
330
|
+
clearTimeout(timer);
|
|
331
|
+
signal?.removeEventListener("abort", done);
|
|
332
|
+
resolve();
|
|
333
|
+
};
|
|
334
|
+
const timer = setTimeout(done, ms);
|
|
335
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
var watchClock;
|
|
267
339
|
var init_bash_hints = __esm(() => {
|
|
268
340
|
init_bash_format();
|
|
269
341
|
init_path_display();
|
|
342
|
+
watchClock = {
|
|
343
|
+
source: new WaitClock,
|
|
344
|
+
now: () => watchClock.source.now(),
|
|
345
|
+
sleep: abortableSleep
|
|
346
|
+
};
|
|
270
347
|
});
|
|
271
348
|
|
|
272
349
|
// ../aft-bridge/dist/test-child-environment.js
|
|
@@ -882,6 +959,7 @@ var init_bridge = __esm(() => {
|
|
|
882
959
|
binaryFingerprintAsyncReader = binaryContentHash;
|
|
883
960
|
SYSTEM_CLOCK = {
|
|
884
961
|
now: () => Date.now(),
|
|
962
|
+
awakeNow: () => performance.now(),
|
|
885
963
|
setTimeout: (callback, ms) => setTimeout(callback, ms),
|
|
886
964
|
clearTimeout: (timer) => clearTimeout(timer)
|
|
887
965
|
};
|
|
@@ -961,6 +1039,7 @@ var init_bridge = __esm(() => {
|
|
|
961
1039
|
hangWindowMinMs;
|
|
962
1040
|
hangProbeTimeoutMs;
|
|
963
1041
|
clock;
|
|
1042
|
+
waitClock;
|
|
964
1043
|
lateShellReplyGraceMs;
|
|
965
1044
|
maxRestarts;
|
|
966
1045
|
configured = false;
|
|
@@ -997,6 +1076,7 @@ var init_bridge = __esm(() => {
|
|
|
997
1076
|
this.hangWindowMinMs = options?.hangWindowMinMs ?? DEFAULT_MIN_HANG_WINDOW_MS;
|
|
998
1077
|
this.hangProbeTimeoutMs = options?.hangProbeTimeoutMs ?? DEFAULT_HANG_PROBE_TIMEOUT_MS;
|
|
999
1078
|
this.clock = options?.clock ?? SYSTEM_CLOCK;
|
|
1079
|
+
this.waitClock = new WaitClock(() => this.clock.now(), () => this.clock.awakeNow?.() ?? this.clock.now());
|
|
1000
1080
|
this.lateShellReplyGraceMs = options?.lateShellReplyGraceMs ?? DEFAULT_LATE_SHELL_REPLY_GRACE_MS;
|
|
1001
1081
|
this.maxRestarts = options?.maxRestarts ?? 3;
|
|
1002
1082
|
this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
|
|
@@ -1363,11 +1443,17 @@ var init_bridge = __esm(() => {
|
|
|
1363
1443
|
const keepBridgeOnTimeout = passive || command === "bash_drain_completions" || options?.keepBridgeOnTimeout === true;
|
|
1364
1444
|
const child = this.process;
|
|
1365
1445
|
const response = await new Promise((resolve, reject) => {
|
|
1366
|
-
const
|
|
1446
|
+
const deadline = new RequestDeadline(this.waitClock, effectiveTimeoutMs);
|
|
1447
|
+
const expire = () => {
|
|
1367
1448
|
const entry = this.pending.get(id);
|
|
1368
1449
|
if (!entry)
|
|
1369
1450
|
return;
|
|
1370
|
-
|
|
1451
|
+
const remaining = deadline.remaining();
|
|
1452
|
+
if (remaining > 0) {
|
|
1453
|
+
entry.timer = this.clock.setTimeout(expire, Math.min(remaining, DEADLINE_POLL_MS));
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
if (keepBridgeOnTimeout && !deadline.resumed && this.holdForLateShellReply(id, entry, effectiveTimeoutMs, requestSessionId)) {
|
|
1371
1457
|
return;
|
|
1372
1458
|
}
|
|
1373
1459
|
this.pending.delete(id);
|
|
@@ -1380,11 +1466,12 @@ var init_bridge = __esm(() => {
|
|
|
1380
1466
|
this.warnVia(timeoutMsg);
|
|
1381
1467
|
}
|
|
1382
1468
|
if (keepBridgeOnTimeout) {
|
|
1383
|
-
entry.reject(new BridgeTransportTimeoutError(command, effectiveTimeoutMs, `${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms
|
|
1469
|
+
entry.reject(new BridgeTransportTimeoutError(command, effectiveTimeoutMs, `${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`, deadline.resumed && SHELL_TASK_COMMANDS.has(command) ? { kind: "unknown" } : undefined));
|
|
1384
1470
|
return;
|
|
1385
1471
|
}
|
|
1386
1472
|
entry.reject(new Error(`${this.errorPrefix} request "${command}" timed out after ${effectiveTimeoutMs}ms (bridge busy/under load); bridge kept warm — retry. A timed-out mutation may still complete; inspect before repeating it.`));
|
|
1387
|
-
}
|
|
1473
|
+
};
|
|
1474
|
+
const timer = this.clock.setTimeout(expire, Math.min(effectiveTimeoutMs, DEADLINE_POLL_MS));
|
|
1388
1475
|
const entry = {
|
|
1389
1476
|
resolve,
|
|
1390
1477
|
reject,
|
|
@@ -1940,14 +2027,21 @@ var init_bridge = __esm(() => {
|
|
|
1940
2027
|
};
|
|
1941
2028
|
entry.resolve = (response) => settle(shellOutcomeFromLateReply(response));
|
|
1942
2029
|
this.clock.clearTimeout(entry.timer);
|
|
1943
|
-
|
|
2030
|
+
const deadline = new RequestDeadline(this.waitClock, graceMs, false);
|
|
2031
|
+
const expire = () => {
|
|
1944
2032
|
if (this.pending.get(id) !== entry)
|
|
1945
2033
|
return;
|
|
2034
|
+
const remaining = deadline.remaining();
|
|
2035
|
+
if (remaining > 0) {
|
|
2036
|
+
entry.timer = this.clock.setTimeout(expire, Math.min(remaining, DEADLINE_POLL_MS));
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
1946
2039
|
this.pending.delete(id);
|
|
1947
2040
|
entry.onSettled?.();
|
|
1948
2041
|
this.warnVia(`Request "${entry.command}" (id=${id}) got no reply within ${graceMs}ms after its deadline`);
|
|
1949
2042
|
settle({ kind: "unknown" });
|
|
1950
|
-
}
|
|
2043
|
+
};
|
|
2044
|
+
entry.timer = this.clock.setTimeout(expire, Math.min(graceMs, DEADLINE_POLL_MS));
|
|
1951
2045
|
return true;
|
|
1952
2046
|
}
|
|
1953
2047
|
accountForBashTaskResponse(command, response) {
|
|
@@ -2840,10 +2934,11 @@ var init_durable_log = __esm(() => {
|
|
|
2840
2934
|
init_storage_paths();
|
|
2841
2935
|
DEFAULT_LOG_BYTES = 32 * 1024 * 1024;
|
|
2842
2936
|
});
|
|
2843
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
2844
|
-
var MAX_FRAME_BODY_LEN, FrameType, FRAME_TYPE_MAX, Priority, AdmissionClass;
|
|
2937
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/envelope.js
|
|
2938
|
+
var MAX_FRAME_BODY_LEN, MAX_DELIVERED_FRAME_LEN, FrameType, FRAME_TYPE_MAX, Priority, AdmissionClass;
|
|
2845
2939
|
var init_envelope = __esm(() => {
|
|
2846
2940
|
MAX_FRAME_BODY_LEN = 64 * 1024 * 1024;
|
|
2941
|
+
MAX_DELIVERED_FRAME_LEN = 4 * 1024 * 1024;
|
|
2847
2942
|
(function(FrameType) {
|
|
2848
2943
|
FrameType[FrameType["Request"] = 0] = "Request";
|
|
2849
2944
|
FrameType[FrameType["Response"] = 1] = "Response";
|
|
@@ -2871,7 +2966,7 @@ var init_envelope = __esm(() => {
|
|
|
2871
2966
|
})(AdmissionClass || (AdmissionClass = {}));
|
|
2872
2967
|
});
|
|
2873
2968
|
|
|
2874
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
2969
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/socket.js
|
|
2875
2970
|
var WRITE_BORROWED, WRITE_TRACKED_BORROWED;
|
|
2876
2971
|
var init_socket = __esm(() => {
|
|
2877
2972
|
init_envelope();
|
|
@@ -2879,17 +2974,17 @@ var init_socket = __esm(() => {
|
|
|
2879
2974
|
WRITE_TRACKED_BORROWED = Symbol("subc.socket.writeTrackedBorrowed");
|
|
2880
2975
|
});
|
|
2881
2976
|
|
|
2882
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
2977
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
2883
2978
|
var init_auth = __esm(() => {
|
|
2884
2979
|
init_socket();
|
|
2885
2980
|
});
|
|
2886
2981
|
|
|
2887
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
2982
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/connection-file.js
|
|
2888
2983
|
var init_connection_file = __esm(() => {
|
|
2889
2984
|
init_envelope();
|
|
2890
2985
|
});
|
|
2891
2986
|
|
|
2892
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
2987
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/launch-nonce.js
|
|
2893
2988
|
var PROCESS_CELL_KEY, U64_MAX, I32_MAX, RedactedLaunchNonce;
|
|
2894
2989
|
var init_launch_nonce = __esm(() => {
|
|
2895
2990
|
PROCESS_CELL_KEY = Symbol.for("@cortexkit/subc-client/launch-nonce/v1");
|
|
@@ -2914,15 +3009,14 @@ var init_launch_nonce = __esm(() => {
|
|
|
2914
3009
|
}
|
|
2915
3010
|
};
|
|
2916
3011
|
});
|
|
2917
|
-
|
|
2918
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/route-handle.js
|
|
3012
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/route-handle.js
|
|
2919
3013
|
var connectionToken, reverseRequests;
|
|
2920
3014
|
var init_route_handle = __esm(() => {
|
|
2921
3015
|
connectionToken = new WeakMap;
|
|
2922
3016
|
reverseRequests = new WeakMap;
|
|
2923
3017
|
});
|
|
2924
3018
|
|
|
2925
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
3019
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/client.js
|
|
2926
3020
|
import { debuglog } from "node:util";
|
|
2927
3021
|
var debug, EMPTY_BODY, errorFrameFlags, SCOPE_ROUTE_CLOSE_REASONS, ROUTE_OPEN_ADMISSION_PRESSURE_REASONS;
|
|
2928
3022
|
var init_client = __esm(() => {
|
|
@@ -2944,8 +3038,7 @@ var init_client = __esm(() => {
|
|
|
2944
3038
|
]);
|
|
2945
3039
|
ROUTE_OPEN_ADMISSION_PRESSURE_REASONS = new Set(["open_admission_full", "target_binds_full"]);
|
|
2946
3040
|
});
|
|
2947
|
-
|
|
2948
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/provider.js
|
|
3041
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/provider.js
|
|
2949
3042
|
var init_provider = __esm(() => {
|
|
2950
3043
|
init_auth();
|
|
2951
3044
|
init_client();
|
|
@@ -2956,7 +3049,7 @@ var init_provider = __esm(() => {
|
|
|
2956
3049
|
init_socket();
|
|
2957
3050
|
});
|
|
2958
3051
|
|
|
2959
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
3052
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/index.js
|
|
2960
3053
|
var init_dist = __esm(() => {
|
|
2961
3054
|
init_client();
|
|
2962
3055
|
init_route_handle();
|
|
@@ -3562,8 +3655,9 @@ function translateDataLayerPaths(map, prefix, out) {
|
|
|
3562
3655
|
}
|
|
3563
3656
|
}
|
|
3564
3657
|
for (const path of REMOVED_DATA_LAYER_PATHS) {
|
|
3565
|
-
|
|
3566
|
-
|
|
3658
|
+
const [container, leaf] = path.split(".");
|
|
3659
|
+
if (container && leaf && takeNested(map, container, leaf).found)
|
|
3660
|
+
recordRetired(out, prefix, path);
|
|
3567
3661
|
}
|
|
3568
3662
|
}
|
|
3569
3663
|
function dropRemovedDataLayerKeys(map) {
|
|
@@ -3815,7 +3909,6 @@ function translateConfigDocument(map, tier) {
|
|
|
3815
3909
|
warnings: [],
|
|
3816
3910
|
legacyInput: false,
|
|
3817
3911
|
retiredKeys: [],
|
|
3818
|
-
removedKeys: [],
|
|
3819
3912
|
retiredEnabledFalse: false
|
|
3820
3913
|
};
|
|
3821
3914
|
translateBlock(map, tier === "user", "base", out);
|
|
@@ -3826,7 +3919,6 @@ function translateConfigDocument(map, tier) {
|
|
|
3826
3919
|
}
|
|
3827
3920
|
}
|
|
3828
3921
|
out.retiredKeys = sortedUnique(out.retiredKeys);
|
|
3829
|
-
out.removedKeys = sortedUnique(out.removedKeys);
|
|
3830
3922
|
return out;
|
|
3831
3923
|
}
|
|
3832
3924
|
function canonicalJson(value) {
|
|
@@ -18498,8 +18590,22 @@ function safeRealpath(p) {
|
|
|
18498
18590
|
function looksLikeSecret(value) {
|
|
18499
18591
|
return value.length >= 16 && /^[A-Za-z0-9_\-.~+/=]+$/.test(value) && /[0-9]/.test(value) && /[A-Z]/.test(value) && /[a-z]/.test(value);
|
|
18500
18592
|
}
|
|
18593
|
+
function looksLikeAssignmentSecret(value) {
|
|
18594
|
+
return !["/", "~/", "./", "../"].some((prefix) => value.startsWith(prefix)) && !value.includes("://") && looksLikeSecret(value);
|
|
18595
|
+
}
|
|
18501
18596
|
function isSensitiveKeyName(keyName) {
|
|
18502
|
-
return SENSITIVE_KEY_WORD.test(keyName) || SEGMENTED_KEY_WORD.test(keyName) || CAMEL_CASE_KEY_WORD.test(keyName);
|
|
18597
|
+
return keyName !== "PWD" && (SENSITIVE_KEY_WORD.test(keyName) || SEGMENTED_KEY_WORD.test(keyName) || CAMEL_CASE_KEY_WORD.test(keyName) || SHORT_PASSWORD_NAME.test(keyName));
|
|
18598
|
+
}
|
|
18599
|
+
function shouldRedactAssignment(prefix, keyName, value, offset, content, commandContext) {
|
|
18600
|
+
if (keyName.toLowerCase() === "key" && /[?&]/.test(content[offset - 1] ?? "")) {
|
|
18601
|
+
return false;
|
|
18602
|
+
}
|
|
18603
|
+
return isSensitiveKeyName(keyName) || commandContext && prefix.trimEnd().endsWith("=") && looksLikeAssignmentSecret(value);
|
|
18604
|
+
}
|
|
18605
|
+
function redactAssignments(content, commandContext) {
|
|
18606
|
+
let sanitized = content.replace(unquotedSensitiveKeyValuePattern, (match, prefix, keyName, valueQuote, value, offset, input) => shouldRedactAssignment(prefix, keyName, value, offset, input, commandContext) ? `${prefix}${valueQuote}${SECRET_PLACEHOLDER}${valueQuote}` : match);
|
|
18607
|
+
sanitized = sanitized.replace(bareSensitiveKeyValuePattern, (match, prefix, keyName, value, offset, input) => shouldRedactAssignment(prefix, keyName, value, offset, input, commandContext) ? `${prefix}${SECRET_PLACEHOLDER}` : match);
|
|
18608
|
+
return sanitized;
|
|
18503
18609
|
}
|
|
18504
18610
|
function redactSecrets(content) {
|
|
18505
18611
|
let sanitized = content;
|
|
@@ -18512,64 +18618,179 @@ function redactSecrets(content) {
|
|
|
18512
18618
|
sanitized = sanitized.replace(CREDENTIAL_QUERY_PATTERN, `$1${SECRET_PLACEHOLDER}`);
|
|
18513
18619
|
sanitized = sanitized.replace(KEY_QUERY_PATTERN, (match, prefix, value) => looksLikeSecret(value) ? `${prefix}${SECRET_PLACEHOLDER}` : match);
|
|
18514
18620
|
sanitized = sanitized.replace(quotedSensitiveKeyValuePattern, (match, prefix, _keyQuote, keyName, valueQuote) => isSensitiveKeyName(keyName) ? `${prefix}${valueQuote}${SECRET_PLACEHOLDER}${valueQuote}` : match);
|
|
18515
|
-
sanitized = sanitized
|
|
18516
|
-
sanitized = sanitized.replace(bareSensitiveKeyValuePattern, (match, prefix, keyName) => isSensitiveKeyName(keyName) ? `${prefix}${SECRET_PLACEHOLDER}` : match);
|
|
18621
|
+
sanitized = redactAssignments(sanitized, false);
|
|
18517
18622
|
sanitized = sanitized.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^@\s/?#]+@/gi, `$1${URL_CREDENTIAL_PLACEHOLDER}@`);
|
|
18518
18623
|
sanitized = sanitized.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "<EMAIL>");
|
|
18624
|
+
sanitized = sanitized.replace(/^([^\r\n]*\bbash spawned task=[^\r\n]*? cmd=)([^\r\n]*)/gm, (_match, prefix, command) => `${prefix}${redactAssignments(command, true)}`);
|
|
18519
18625
|
return sanitized;
|
|
18520
18626
|
}
|
|
18521
|
-
function sanitizeContent(content) {
|
|
18522
|
-
const username = userInfo().username;
|
|
18523
|
-
const home = homedir17();
|
|
18627
|
+
function sanitizeContent(content, context = {}) {
|
|
18628
|
+
const username = context.username ?? userInfo().username;
|
|
18629
|
+
const home = context.home ?? homedir17();
|
|
18524
18630
|
let sanitized = redactSecrets(content);
|
|
18525
18631
|
const cwd = process.cwd();
|
|
18526
|
-
|
|
18527
|
-
|
|
18528
|
-
|
|
18632
|
+
const roots = new Set([
|
|
18633
|
+
cwd,
|
|
18634
|
+
safeRealpath(cwd),
|
|
18635
|
+
...(context.projectRoots ?? []).flatMap((root) => root.startsWith("~/") ? [root, home + root.slice(1)] : [root]),
|
|
18636
|
+
...reportProjectRoots(decodedView(content).text)
|
|
18637
|
+
]);
|
|
18638
|
+
const candidates = [...roots].filter((root) => Boolean(root));
|
|
18639
|
+
for (const root of candidates) {
|
|
18640
|
+
const real = safeRealpath(root);
|
|
18641
|
+
if (real)
|
|
18642
|
+
roots.add(real);
|
|
18643
|
+
}
|
|
18644
|
+
for (const root of [...roots].filter((root) => Boolean(root)).sort((a, b) => b.length - a.length)) {
|
|
18645
|
+
if (/^(?:\/|[A-Za-z]:[/\\]|~\/)/.test(root) && root !== home && !/^(?:[/\\]|[A-Za-z]:[/\\])$/.test(root)) {
|
|
18646
|
+
sanitized = replacePath(sanitized, root, "<PROJECT>");
|
|
18647
|
+
}
|
|
18648
|
+
}
|
|
18649
|
+
if (home && home !== "/")
|
|
18650
|
+
sanitized = replacePath(sanitized, home, "~");
|
|
18651
|
+
sanitized = replaceDecoded(sanitized, /(?:(?<![\p{L}\p{N}_/.-])|(?<=file:\/\/)|(?<=file:\/\/\/))(?:\/(?:home|Users)\/|[A-Za-z]:\/Users\/)([^/\s"'<>`,;:?#&()[\]{}=]+)/gu, (match) => ({ offset: match[0].length - match[1].length, value: "<USER>" }));
|
|
18652
|
+
return redactUsername(sanitized, username);
|
|
18653
|
+
}
|
|
18654
|
+
function decodedView(content) {
|
|
18655
|
+
let units = Array.from({ length: content.length }, (_, i) => ({
|
|
18656
|
+
char: content[i],
|
|
18657
|
+
start: i,
|
|
18658
|
+
end: i + 1
|
|
18659
|
+
}));
|
|
18660
|
+
const decoded = [];
|
|
18661
|
+
for (let i = 0;i < units.length; ) {
|
|
18662
|
+
const byte = content.slice(i, i + 3);
|
|
18663
|
+
const first = /^%[\da-f]{2}$/i.test(byte) ? Number.parseInt(byte.slice(1), 16) : -1;
|
|
18664
|
+
const width = first < 128 ? 1 : first >= 194 && first <= 223 ? 2 : first <= 239 ? 3 : 4;
|
|
18665
|
+
const spelling = content.slice(i, i + width * 3);
|
|
18666
|
+
if (first >= 0 && /^(?:%[\da-f]{2})+$/i.test(spelling)) {
|
|
18667
|
+
try {
|
|
18668
|
+
const value = decodeURIComponent(spelling);
|
|
18669
|
+
for (const char of value.split(""))
|
|
18670
|
+
decoded.push({ char, start: i, end: i + spelling.length });
|
|
18671
|
+
i += spelling.length;
|
|
18672
|
+
continue;
|
|
18673
|
+
} catch {}
|
|
18529
18674
|
}
|
|
18675
|
+
decoded.push(units[i++]);
|
|
18530
18676
|
}
|
|
18531
|
-
|
|
18532
|
-
|
|
18677
|
+
units = [];
|
|
18678
|
+
for (let i = 0;i < decoded.length; i++) {
|
|
18679
|
+
const unit = decoded[i];
|
|
18680
|
+
const next = decoded[i + 1];
|
|
18681
|
+
if (unit.char === "\\" && next && (next.char === "/" || next.char === "\\")) {
|
|
18682
|
+
units.push({ char: "/", start: unit.start, end: next.end });
|
|
18683
|
+
i++;
|
|
18684
|
+
} else
|
|
18685
|
+
units.push({ ...unit, char: unit.char === "\\" ? "/" : unit.char });
|
|
18533
18686
|
}
|
|
18534
|
-
|
|
18535
|
-
|
|
18536
|
-
|
|
18537
|
-
|
|
18538
|
-
|
|
18539
|
-
|
|
18540
|
-
|
|
18687
|
+
return { text: units.map((unit) => unit.char).join(""), units };
|
|
18688
|
+
}
|
|
18689
|
+
function replaceDecoded(content, pattern, replacement) {
|
|
18690
|
+
const view = decodedView(content);
|
|
18691
|
+
let result = "";
|
|
18692
|
+
let cursor = 0;
|
|
18693
|
+
for (const match of view.text.matchAll(pattern)) {
|
|
18694
|
+
const change = replacement(match);
|
|
18695
|
+
if (!change)
|
|
18696
|
+
continue;
|
|
18697
|
+
const { offset, value } = change;
|
|
18698
|
+
const start = view.units[match.index + offset].start;
|
|
18699
|
+
const end = view.units[match.index + match[0].length - 1].end;
|
|
18700
|
+
result += content.slice(cursor, start) + value;
|
|
18701
|
+
cursor = end;
|
|
18541
18702
|
}
|
|
18542
|
-
return
|
|
18703
|
+
return result + content.slice(cursor);
|
|
18543
18704
|
}
|
|
18544
|
-
function
|
|
18705
|
+
function replacePath(content, path, value) {
|
|
18706
|
+
const normalized = path.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
18707
|
+
if (!normalized)
|
|
18708
|
+
return content;
|
|
18709
|
+
const pattern = new RegExp(`(?:(?<![\\p{L}\\p{N}_/.-])|(?<=file://)|(?<=file:///))${escapeRegex(normalized)}(?=$|[/\\s"'<>\`,;:)\\]?#&])`, /^[A-Za-z]:/.test(path) ? "giu" : "gu");
|
|
18710
|
+
return replaceDecoded(content, pattern, () => ({ offset: 0, value }));
|
|
18711
|
+
}
|
|
18712
|
+
function reportProjectRoots(content) {
|
|
18713
|
+
return [
|
|
18714
|
+
...content.matchAll(/(?:project root set: |watcher started: )([^\r\n]+)/g),
|
|
18715
|
+
...content.matchAll(/(?:\broot=|"(?:projectRoot|project_root|canonical_root)"\s*:\s*)("[^"]+"|'[^']+'|[^\s"'<>`,;]+)/g)
|
|
18716
|
+
].map((match) => match[1].trim().replace(/^["']|["']$/g, "")).filter((root) => /^(?:\/|[A-Za-z]:\/)/.test(root));
|
|
18717
|
+
}
|
|
18718
|
+
function sanitizeValue(value, context = {}) {
|
|
18719
|
+
const scoped = {
|
|
18720
|
+
...context,
|
|
18721
|
+
projectRoots: [...context.projectRoots ?? [], ...projectRootsFromValue(value)]
|
|
18722
|
+
};
|
|
18723
|
+
return sanitizeNestedValue(value, scoped);
|
|
18724
|
+
}
|
|
18725
|
+
function sanitizeNestedValue(value, context) {
|
|
18545
18726
|
if (typeof value === "string") {
|
|
18546
|
-
return sanitizeContent(value);
|
|
18727
|
+
return sanitizeContent(value, context);
|
|
18547
18728
|
}
|
|
18548
18729
|
if (Array.isArray(value)) {
|
|
18549
|
-
return value.map((entry) =>
|
|
18730
|
+
return value.map((entry) => sanitizeNestedValue(entry, context));
|
|
18550
18731
|
}
|
|
18551
18732
|
if (value && typeof value === "object") {
|
|
18552
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key,
|
|
18733
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, sanitizeNestedValue(entry, context)]));
|
|
18553
18734
|
}
|
|
18554
18735
|
return value;
|
|
18555
18736
|
}
|
|
18737
|
+
function projectRootsFromValue(value) {
|
|
18738
|
+
if (!value || typeof value !== "object")
|
|
18739
|
+
return [];
|
|
18740
|
+
if (Array.isArray(value))
|
|
18741
|
+
return value.flatMap(projectRootsFromValue);
|
|
18742
|
+
const object = value;
|
|
18743
|
+
const roots = [object.projectRoot, object.project_root, object.canonical_root].filter((root) => typeof root === "string");
|
|
18744
|
+
const index = object.index;
|
|
18745
|
+
if (index && Array.isArray(index.roots)) {
|
|
18746
|
+
for (const root of index.roots)
|
|
18747
|
+
if (root && typeof root.path === "string")
|
|
18748
|
+
roots.push(root.path);
|
|
18749
|
+
}
|
|
18750
|
+
return [...roots, ...Object.values(object).flatMap(projectRootsFromValue)];
|
|
18751
|
+
}
|
|
18556
18752
|
function redactUsername(content, username) {
|
|
18557
18753
|
if (!username)
|
|
18558
18754
|
return content;
|
|
18559
|
-
|
|
18755
|
+
if (!COMMON_ACCOUNT_NAMES.has(username.toLowerCase())) {
|
|
18756
|
+
return replaceDecoded(content, new RegExp(`(?<![\\p{L}\\p{N}_])${escapeRegex(username)}(?![\\p{L}\\p{N}_])`, "gu"), () => ({ offset: 0, value: "<USER>" }));
|
|
18757
|
+
}
|
|
18758
|
+
return replaceDecoded(content, new RegExp(`(?<![\\p{L}\\p{N}_/.-])~${escapeRegex(username)}(?![\\p{L}\\p{N}_])|(?<![\\p{L}\\p{N}_/.-])${escapeRegex(username)}(?=@[\\p{L}\\p{N}])`, "gu"), (match) => ({ offset: match[0].startsWith("~") ? 1 : 0, value: "<USER>" }));
|
|
18560
18759
|
}
|
|
18561
|
-
var SECRET_PLACEHOLDER = "<REDACTED_SECRET>", URL_CREDENTIAL_PLACEHOLDER = "***", KEY_NAME = "[A-Za-z_][A-Za-z0-9_.-]*", SENSITIVE_KEY_WORD, SEGMENTED_KEY_WORD, CAMEL_CASE_KEY_WORD, JWT_PATTERN, AWS_ACCESS_KEY_ID_PATTERN, CREDENTIAL_QUERY_PATTERN, KEY_QUERY_PATTERN, quotedSensitiveKeyValuePattern, unquotedSensitiveKeyValuePattern, bareSensitiveKeyValuePattern;
|
|
18760
|
+
var SECRET_PLACEHOLDER = "<REDACTED_SECRET>", URL_CREDENTIAL_PLACEHOLDER = "***", KEY_NAME = "[A-Za-z_][A-Za-z0-9_.-]*", SENSITIVE_KEY_WORD, SEGMENTED_KEY_WORD, CAMEL_CASE_KEY_WORD, SHORT_PASSWORD_NAME, JWT_PATTERN, AWS_ACCESS_KEY_ID_PATTERN, CREDENTIAL_QUERY_PATTERN, KEY_QUERY_PATTERN, quotedSensitiveKeyValuePattern, unquotedSensitiveKeyValuePattern, bareSensitiveKeyValuePattern, COMMON_ACCOUNT_NAMES;
|
|
18562
18761
|
var init_sanitize = __esm(() => {
|
|
18563
18762
|
SENSITIVE_KEY_WORD = /(?:token|password|secret|api[_-]?key|passwd|pwd|credential)/i;
|
|
18564
18763
|
SEGMENTED_KEY_WORD = /(?:^|[_.-])key(?:$|[_.-])/i;
|
|
18565
18764
|
CAMEL_CASE_KEY_WORD = /[a-z0-9]Key(?:$|[A-Z_.-])/;
|
|
18765
|
+
SHORT_PASSWORD_NAME = /(?:^|_)(?:pw|pass)$/i;
|
|
18566
18766
|
JWT_PATTERN = /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g;
|
|
18567
18767
|
AWS_ACCESS_KEY_ID_PATTERN = /\b(?:AKIA|ASIA|AGPA|AIDA|AROA)[A-Z0-9]{16}\b/g;
|
|
18568
18768
|
CREDENTIAL_QUERY_PATTERN = /([?&](?:[a-z0-9_-]*token|api[_-]?key|apikey|[a-z0-9_-]*secret|password|passwd|pwd)=)[^&\s#"'<>]+/gi;
|
|
18569
18769
|
KEY_QUERY_PATTERN = /([?&]key=)([^&\s#"'<>]+)/gi;
|
|
18570
18770
|
quotedSensitiveKeyValuePattern = new RegExp(String.raw`((['"])(${KEY_NAME})\2[^\S\r\n]*:[^\S\r\n]*)(['"])([^'"\r\n]+)\4`, "gi");
|
|
18571
18771
|
unquotedSensitiveKeyValuePattern = new RegExp(String.raw`\b((${KEY_NAME})[^\S\r\n]*[=:][^\S\r\n]*)(['"])([^'"\r\n]+)\3`, "gi");
|
|
18572
|
-
bareSensitiveKeyValuePattern = new RegExp(String.raw`\b((${KEY_NAME})[^\S\r\n]*[=:][^\S\r\n]*)([^\s
|
|
18772
|
+
bareSensitiveKeyValuePattern = new RegExp(String.raw`\b((${KEY_NAME})[^\S\r\n]*[=:][^\S\r\n]*)([^\s,;&|<>()'"]+)`, "gi");
|
|
18773
|
+
COMMON_ACCOUNT_NAMES = new Set([
|
|
18774
|
+
"root",
|
|
18775
|
+
"admin",
|
|
18776
|
+
"administrator",
|
|
18777
|
+
"user",
|
|
18778
|
+
"dev",
|
|
18779
|
+
"developer",
|
|
18780
|
+
"test",
|
|
18781
|
+
"guest",
|
|
18782
|
+
"ubuntu",
|
|
18783
|
+
"debian",
|
|
18784
|
+
"pi",
|
|
18785
|
+
"runner",
|
|
18786
|
+
"vagrant",
|
|
18787
|
+
"ec2-user",
|
|
18788
|
+
"centos",
|
|
18789
|
+
"git",
|
|
18790
|
+
"docker",
|
|
18791
|
+
"build",
|
|
18792
|
+
"ci"
|
|
18793
|
+
]);
|
|
18573
18794
|
});
|
|
18574
18795
|
|
|
18575
18796
|
// src/lib/bridge-tool-failures.ts
|
|
@@ -18871,7 +19092,7 @@ function previewConfigMigration(targets) {
|
|
|
18871
19092
|
continue;
|
|
18872
19093
|
const translated = structuredClone(raw);
|
|
18873
19094
|
const translation = translateConfigDocument(translated, target.tier);
|
|
18874
|
-
if (!translation.legacyInput
|
|
19095
|
+
if (!translation.legacyInput)
|
|
18875
19096
|
continue;
|
|
18876
19097
|
dropRemovedDataLayerKeys(translated);
|
|
18877
19098
|
const changes = diffConfig(raw, translated);
|
|
@@ -21838,6 +22059,13 @@ async function runIssueFlow(argv) {
|
|
|
21838
22059
|
const selectedSession = await promptForIssueSession(adapters[0]);
|
|
21839
22060
|
const selectedBareSessionId = selectedSession?.id.replace(/^ses_/, "") ?? null;
|
|
21840
22061
|
const report = await collectDiagnostics(adapters);
|
|
22062
|
+
const sanitizeContext = {
|
|
22063
|
+
projectRoots: [
|
|
22064
|
+
...adapters.flatMap((adapter) => projectRootsFromValue(readJsoncFile(adapter.detectConfigPaths().aftConfig).value)),
|
|
22065
|
+
...adapters.flatMap((adapter) => projectRootsFromValue(listRecentSessions(adapter))),
|
|
22066
|
+
...projectRootsFromValue(report)
|
|
22067
|
+
]
|
|
22068
|
+
};
|
|
21841
22069
|
const logResults = adapters.flatMap((adapter) => {
|
|
21842
22070
|
const path = adapter.getLogFile();
|
|
21843
22071
|
const { logText, errors, notice } = collectIssueLog(path, selectedSession);
|
|
@@ -21867,7 +22095,7 @@ ${engine.logText || "<no log output>"}
|
|
|
21867
22095
|
});
|
|
21868
22096
|
const logSections = logResults.map((result) => result.section).join(`
|
|
21869
22097
|
`);
|
|
21870
|
-
const recentErrorLines = extractRecentErrors(logResults.map((result) => sanitizeContent(result.errors)).join(`
|
|
22098
|
+
const recentErrorLines = extractRecentErrors(logResults.map((result) => sanitizeContent(result.errors, sanitizeContext)).join(`
|
|
21871
22099
|
`), 20);
|
|
21872
22100
|
const recentErrorsSection = recentErrorLines.length === 0 ? "_No error-shaped log lines found in recent history._" : ["```", recentErrorLines.join(`
|
|
21873
22101
|
`), "```"].join(`
|
|
@@ -21897,7 +22125,7 @@ ${engine.logText || "<no log output>"}
|
|
|
21897
22125
|
"_Usernames and home paths have been stripped from this report._"
|
|
21898
22126
|
].join(`
|
|
21899
22127
|
`);
|
|
21900
|
-
const body = capBodyToGithubLimit(sanitizeContent(rawBody));
|
|
22128
|
+
const body = capBodyToGithubLimit(sanitizeContent(rawBody, sanitizeContext));
|
|
21901
22129
|
const reviewFile = writeIssueReviewFile(body);
|
|
21902
22130
|
if (!reviewFile) {
|
|
21903
22131
|
outro("Done — could not write the issue report.");
|
|
@@ -21922,7 +22150,7 @@ https://github.com/cortexkit/aft/issues/new and paste the contents of ${outPath}
|
|
|
21922
22150
|
outro("Done.");
|
|
21923
22151
|
return 1;
|
|
21924
22152
|
}
|
|
21925
|
-
const finalBody = capBodyToGithubLimit(sanitizeContent(reviewedBody));
|
|
22153
|
+
const finalBody = capBodyToGithubLimit(sanitizeContent(reviewedBody, sanitizeContext));
|
|
21926
22154
|
const finalTitle = deriveIssueTitleFromBody(finalBody);
|
|
21927
22155
|
if (isGhInstalled()) {
|
|
21928
22156
|
log2.info("Opening GitHub issue via `gh`…");
|
package/dist/lib/sanitize.d.ts
CHANGED
|
@@ -6,16 +6,22 @@ export declare function escapeRegex(value: string): string;
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function redactSecrets(content: string): string;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* so reports never leak usernames or home-directory paths.
|
|
9
|
+
* Redact known project/home paths and identifying usernames in logs,
|
|
10
|
+
* diagnostic JSON blocks and the final issue body.
|
|
12
11
|
*/
|
|
13
|
-
export declare function sanitizeContent(content: string): string;
|
|
12
|
+
export declare function sanitizeContent(content: string, context?: SanitizeContext): string;
|
|
13
|
+
export interface SanitizeContext {
|
|
14
|
+
projectRoots?: readonly string[];
|
|
15
|
+
home?: string;
|
|
16
|
+
username?: string;
|
|
17
|
+
}
|
|
14
18
|
/**
|
|
15
19
|
* Recursively sanitize any value by deep-walking objects/arrays. Strings are
|
|
16
20
|
* passed through `sanitizeContent`; other primitives are preserved.
|
|
17
21
|
*/
|
|
18
|
-
export declare function sanitizeValue(value: unknown): unknown;
|
|
19
|
-
/**
|
|
22
|
+
export declare function sanitizeValue(value: unknown, context?: SanitizeContext): unknown;
|
|
23
|
+
/** Collect only declared root fields, never arbitrary file/config/cache paths. */
|
|
24
|
+
export declare function projectRootsFromValue(value: unknown): string[];
|
|
25
|
+
/** Mask distinctive usernames as whole words; common names only in identity positions. */
|
|
20
26
|
export declare function redactUsername(content: string, username: string): string;
|
|
21
27
|
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/lib/sanitize.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/lib/sanitize.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAiID;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA4CrD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CAwCtF;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkFD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAMpF;AAiBD,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAa9D;AA0BD,0FAA0F;AAC1F,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiBxE"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/aft",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.65.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Unified CLI for Agent
|
|
5
|
+
"description": "Unified CLI for Agent Foundational Toolkit (AFT) — setup, one-shot index snapshots, doctor, and diagnostics across supported agent harnesses (OpenCode, Pi)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@clack/core": "^1.4.2",
|
|
27
27
|
"@clack/prompts": "^1.6.0",
|
|
28
|
-
"@cortexkit/aft-bridge": "0.
|
|
28
|
+
"@cortexkit/aft-bridge": "0.65.0",
|
|
29
29
|
"comment-json": "^4.6.2"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|