blun-king-cli 9.1.600 → 9.1.601
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 +9 -2
- package/LIESMICH.txt +1 -1
- package/README.md +1 -1
- package/blun.mjs +1502 -1166
- package/package.json +1 -1
- package/worker-host.mjs +3250 -2977
package/blun.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// BLUN_BUILD_INPUT_SHA256:
|
|
2
|
+
// BLUN_BUILD_INPUT_SHA256:b5b19f76ccc092b4eba456b9da34bbda10f5c0717713caa03a28337eab2cac02
|
|
3
3
|
import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
|
|
4
4
|
import { dirname as __cjsShimDirname } from 'node:path';
|
|
5
5
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
@@ -181000,7 +181000,10 @@ function applyCompletionBudgetWithDetails(args) {
|
|
|
181000
181000
|
});
|
|
181001
181001
|
if (args.retry !== void 0) cap = Math.max(args.retry.minimumCompletionTokens, Math.ceil(cap * args.retry.multiplier));
|
|
181002
181002
|
const maxContextTokens = args.capability?.max_context_tokens;
|
|
181003
|
-
if (args.usedContextTokens !== void 0 && maxContextTokens !== void 0 && maxContextTokens > 0)
|
|
181003
|
+
if (args.usedContextTokens !== void 0 && maxContextTokens !== void 0 && maxContextTokens > 0) {
|
|
181004
|
+
const headroom = Math.min(2048, Math.ceil(maxContextTokens * .01));
|
|
181005
|
+
cap = Math.max(MIN_FLOOR, Math.min(cap, maxContextTokens - args.usedContextTokens - headroom));
|
|
181006
|
+
}
|
|
181004
181007
|
return {
|
|
181005
181008
|
provider: args.provider.withMaxCompletionTokens(cap, {
|
|
181006
181009
|
usedContextTokens: args.usedContextTokens,
|
|
@@ -235215,6 +235218,9 @@ var init_engine = __esmMin((() => {
|
|
|
235215
235218
|
for (const [event, hooks] of this.byEvent.entries()) result[event] = hooks.length;
|
|
235216
235219
|
return result;
|
|
235217
235220
|
}
|
|
235221
|
+
hasMatchingHook(event, matcherValue) {
|
|
235222
|
+
return this.matchingHooks(event, matcherValue).length > 0;
|
|
235223
|
+
}
|
|
235218
235224
|
closeAdmission() {
|
|
235219
235225
|
this.admissionClosed = true;
|
|
235220
235226
|
}
|
|
@@ -264967,6 +264973,1381 @@ var init_tool_result_budget = __esmMin((() => {
|
|
|
264967
264973
|
TOOL_RESULT_PREVIEW_CHARS = 2e3;
|
|
264968
264974
|
}));
|
|
264969
264975
|
//#endregion
|
|
264976
|
+
//#region ../../packages/kaos/src/errors.ts
|
|
264977
|
+
var KaosError, KaosFileExistsError, KaosShellNotFoundError;
|
|
264978
|
+
var init_errors$1 = __esmMin((() => {
|
|
264979
|
+
KaosError = class extends Error {
|
|
264980
|
+
constructor(message) {
|
|
264981
|
+
super(message);
|
|
264982
|
+
this.name = "KaosError";
|
|
264983
|
+
}
|
|
264984
|
+
};
|
|
264985
|
+
KaosFileExistsError = class extends KaosError {
|
|
264986
|
+
constructor(message) {
|
|
264987
|
+
super(message);
|
|
264988
|
+
this.name = "KaosFileExistsError";
|
|
264989
|
+
}
|
|
264990
|
+
};
|
|
264991
|
+
KaosShellNotFoundError = class extends KaosError {
|
|
264992
|
+
constructor(message) {
|
|
264993
|
+
super(message);
|
|
264994
|
+
this.name = "KaosShellNotFoundError";
|
|
264995
|
+
}
|
|
264996
|
+
};
|
|
264997
|
+
}));
|
|
264998
|
+
//#endregion
|
|
264999
|
+
//#region ../../packages/kaos/src/environment.ts
|
|
265000
|
+
/**
|
|
265001
|
+
* Environment — cross-platform probe of OS / shell.
|
|
265002
|
+
*
|
|
265003
|
+
* Detection is a pure function of injected probes (`platform` / `arch` /
|
|
265004
|
+
* `release` / `env` / `isFile` / `execFileText`) so the same suite runs
|
|
265005
|
+
* identically on any host OS. `detectEnvironmentFromNode()` bundles the Node
|
|
265006
|
+
* defaults for production callers.
|
|
265007
|
+
*
|
|
265008
|
+
* On Windows the probe expects Git Bash (the canonical POSIX shell that
|
|
265009
|
+
* ships with Git for Windows). If it cannot be located the function
|
|
265010
|
+
* throws `KaosShellNotFoundError`; the SDK layer can wrap that into a
|
|
265011
|
+
* user-facing install hint. Set `BLUN_SHELL_PATH` to override.
|
|
265012
|
+
*/
|
|
265013
|
+
function resolveOsKind(platform) {
|
|
265014
|
+
switch (platform) {
|
|
265015
|
+
case "darwin": return "macOS";
|
|
265016
|
+
case "linux": return "Linux";
|
|
265017
|
+
case "win32": return "Windows";
|
|
265018
|
+
default: return platform;
|
|
265019
|
+
}
|
|
265020
|
+
}
|
|
265021
|
+
async function detectEnvironment(deps) {
|
|
265022
|
+
const osKind = resolveOsKind(deps.platform);
|
|
265023
|
+
const osArch = deps.arch;
|
|
265024
|
+
const osVersion = deps.release;
|
|
265025
|
+
if (deps.platform === "win32") return {
|
|
265026
|
+
osKind,
|
|
265027
|
+
osArch,
|
|
265028
|
+
osVersion,
|
|
265029
|
+
shellName: "bash",
|
|
265030
|
+
shellPath: await locateWindowsGitBash(deps)
|
|
265031
|
+
};
|
|
265032
|
+
const candidates = [
|
|
265033
|
+
"/bin/bash",
|
|
265034
|
+
"/usr/bin/bash",
|
|
265035
|
+
"/usr/local/bin/bash"
|
|
265036
|
+
];
|
|
265037
|
+
let found;
|
|
265038
|
+
for (const p of candidates) if (await deps.isFile(p)) {
|
|
265039
|
+
found = p;
|
|
265040
|
+
break;
|
|
265041
|
+
}
|
|
265042
|
+
if (found !== void 0) return {
|
|
265043
|
+
osKind,
|
|
265044
|
+
osArch,
|
|
265045
|
+
osVersion,
|
|
265046
|
+
shellName: "bash",
|
|
265047
|
+
shellPath: found
|
|
265048
|
+
};
|
|
265049
|
+
return {
|
|
265050
|
+
osKind,
|
|
265051
|
+
osArch,
|
|
265052
|
+
osVersion,
|
|
265053
|
+
shellName: "sh",
|
|
265054
|
+
shellPath: "/bin/sh"
|
|
265055
|
+
};
|
|
265056
|
+
}
|
|
265057
|
+
async function locateWindowsGitBash(deps) {
|
|
265058
|
+
const checked = [];
|
|
265059
|
+
const override = deps.env["BLUN_SHELL_PATH"]?.trim();
|
|
265060
|
+
if (override !== void 0 && override.length > 0) {
|
|
265061
|
+
checked.push(override);
|
|
265062
|
+
if (await deps.isFile(override)) return override;
|
|
265063
|
+
}
|
|
265064
|
+
const gitExecutables = await findExecutablesOnPath("git.exe", deps.env["PATH"], deps.platform, deps.isFile);
|
|
265065
|
+
for (const gitExe of gitExecutables) {
|
|
265066
|
+
const inferred = gitBashCandidatesFromGitExe(gitExe);
|
|
265067
|
+
if (inferred !== void 0) for (const candidate of inferred) {
|
|
265068
|
+
checked.push(candidate);
|
|
265069
|
+
if (await deps.isFile(candidate)) return candidate;
|
|
265070
|
+
}
|
|
265071
|
+
const gitExecPath = await readGitExecPath(deps, gitExe);
|
|
265072
|
+
if (gitExecPath === void 0) continue;
|
|
265073
|
+
for (const candidate of gitBashCandidatesFromGitExecPath(gitExecPath)) {
|
|
265074
|
+
checked.push(candidate);
|
|
265075
|
+
if (await deps.isFile(candidate)) return candidate;
|
|
265076
|
+
}
|
|
265077
|
+
}
|
|
265078
|
+
const candidates = [
|
|
265079
|
+
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
265080
|
+
"C:\\Program Files\\Git\\usr\\bin\\bash.exe",
|
|
265081
|
+
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
|
|
265082
|
+
"C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe"
|
|
265083
|
+
];
|
|
265084
|
+
const localAppData = deps.env["LOCALAPPDATA"]?.trim();
|
|
265085
|
+
if (localAppData !== void 0 && localAppData.length > 0) {
|
|
265086
|
+
candidates.push(`${localAppData}\\Programs\\Git\\bin\\bash.exe`);
|
|
265087
|
+
candidates.push(`${localAppData}\\Programs\\Git\\usr\\bin\\bash.exe`);
|
|
265088
|
+
}
|
|
265089
|
+
for (const candidate of candidates) {
|
|
265090
|
+
checked.push(candidate);
|
|
265091
|
+
if (await deps.isFile(candidate)) return candidate;
|
|
265092
|
+
}
|
|
265093
|
+
throw new KaosShellNotFoundError(`Git Bash was not found on this Windows host. Install Git for Windows from https://gitforwindows.org/ or set BLUN_SHELL_PATH to a bash.exe. Checked: ${checked.join(", ")}.`);
|
|
265094
|
+
}
|
|
265095
|
+
async function readGitExecPath(deps, gitExe) {
|
|
265096
|
+
if (deps.platform === "win32" && !isAbsoluteWindowsPath(gitExe)) return void 0;
|
|
265097
|
+
const stdout = await deps.execFileText(gitExe, ["--exec-path"], GIT_EXEC_PATH_TIMEOUT_MS);
|
|
265098
|
+
if (stdout === void 0) return void 0;
|
|
265099
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
265100
|
+
const execPath = line.trim();
|
|
265101
|
+
if (execPath.length > 0) return execPath;
|
|
265102
|
+
}
|
|
265103
|
+
}
|
|
265104
|
+
function gitBashCandidatesFromGitExe(gitExe) {
|
|
265105
|
+
const normalizedGitExe = path$18.win32.normalize(normalizeWindowsPath(gitExe));
|
|
265106
|
+
const gitDir = path$18.win32.dirname(normalizedGitExe);
|
|
265107
|
+
const gitDirName = path$18.win32.basename(gitDir).toLowerCase();
|
|
265108
|
+
if (gitDirName !== "cmd" && gitDirName !== "bin") return;
|
|
265109
|
+
return gitBashCandidatesFromGitRoot(path$18.win32.dirname(gitDir));
|
|
265110
|
+
}
|
|
265111
|
+
function gitBashCandidatesFromGitExecPath(execPath) {
|
|
265112
|
+
const normalized = path$18.win32.normalize(normalizeWindowsPath(execPath));
|
|
265113
|
+
const parts = normalized.split("\\");
|
|
265114
|
+
for (let i = parts.length - 1; i >= 0; i -= 1) {
|
|
265115
|
+
const segment = parts[i]?.toLowerCase();
|
|
265116
|
+
if (segment === "mingw32" || segment === "mingw64") {
|
|
265117
|
+
const root = parts.slice(0, i).join("\\");
|
|
265118
|
+
if (root.length > 0) return gitBashCandidatesFromGitRoot(root);
|
|
265119
|
+
}
|
|
265120
|
+
}
|
|
265121
|
+
return gitBashCandidatesFromGitRoot(path$18.win32.join(normalized, "..", ".."));
|
|
265122
|
+
}
|
|
265123
|
+
function gitBashCandidatesFromGitRoot(root) {
|
|
265124
|
+
return [path$18.win32.normalize(path$18.win32.join(root, "bin", "bash.exe")), path$18.win32.normalize(path$18.win32.join(root, "usr", "bin", "bash.exe"))];
|
|
265125
|
+
}
|
|
265126
|
+
function normalizeWindowsPath(path) {
|
|
265127
|
+
return path.replaceAll("/", "\\");
|
|
265128
|
+
}
|
|
265129
|
+
function isAbsoluteWindowsPath(path) {
|
|
265130
|
+
return path$18.win32.isAbsolute(normalizeWindowsPath(path));
|
|
265131
|
+
}
|
|
265132
|
+
function dedupeWindowsPaths(paths) {
|
|
265133
|
+
const deduped = [];
|
|
265134
|
+
const seen = /* @__PURE__ */ new Set();
|
|
265135
|
+
for (const path of paths) {
|
|
265136
|
+
const key = normalizeWindowsPath(path).toLowerCase();
|
|
265137
|
+
if (seen.has(key)) continue;
|
|
265138
|
+
seen.add(key);
|
|
265139
|
+
deduped.push(path);
|
|
265140
|
+
}
|
|
265141
|
+
return deduped;
|
|
265142
|
+
}
|
|
265143
|
+
function detectEnvironmentFromNode() {
|
|
265144
|
+
if (detectedEnvironment !== void 0) return detectedEnvironment;
|
|
265145
|
+
const platform = process.platform;
|
|
265146
|
+
const env = process.env;
|
|
265147
|
+
const isFile = async (path) => {
|
|
265148
|
+
try {
|
|
265149
|
+
await access(path, constants.F_OK);
|
|
265150
|
+
return true;
|
|
265151
|
+
} catch {
|
|
265152
|
+
return false;
|
|
265153
|
+
}
|
|
265154
|
+
};
|
|
265155
|
+
detectedEnvironment = detectEnvironment({
|
|
265156
|
+
platform,
|
|
265157
|
+
arch: process.arch,
|
|
265158
|
+
release: nodeOs.release(),
|
|
265159
|
+
env,
|
|
265160
|
+
isFile,
|
|
265161
|
+
execFileText: execFileText$1
|
|
265162
|
+
});
|
|
265163
|
+
return detectedEnvironment;
|
|
265164
|
+
}
|
|
265165
|
+
async function findExecutablesOnPath(name, pathEnv, platform, isFile) {
|
|
265166
|
+
if (pathEnv === void 0 || pathEnv.length === 0) return [];
|
|
265167
|
+
const listSep = platform === "win32" ? ";" : ":";
|
|
265168
|
+
const dirSep = platform === "win32" ? "\\" : "/";
|
|
265169
|
+
const paths = [];
|
|
265170
|
+
for (const rawDir of pathEnv.split(listSep)) {
|
|
265171
|
+
const dir = rawDir.trim();
|
|
265172
|
+
if (dir.length === 0) continue;
|
|
265173
|
+
if (platform === "win32" && !isAbsoluteWindowsPath(dir)) continue;
|
|
265174
|
+
const candidate = dir.endsWith(dirSep) ? `${dir}${name}` : `${dir}${dirSep}${name}`;
|
|
265175
|
+
if (await isFile(candidate)) paths.push(candidate);
|
|
265176
|
+
}
|
|
265177
|
+
return platform === "win32" ? dedupeWindowsPaths(paths) : paths;
|
|
265178
|
+
}
|
|
265179
|
+
async function execFileText$1(file, args, timeoutMs) {
|
|
265180
|
+
return new Promise((resolve) => {
|
|
265181
|
+
execFile(file, [...args], {
|
|
265182
|
+
encoding: "utf8",
|
|
265183
|
+
timeout: timeoutMs,
|
|
265184
|
+
windowsHide: true
|
|
265185
|
+
}, (error, stdout) => {
|
|
265186
|
+
if (error !== null) {
|
|
265187
|
+
resolve(void 0);
|
|
265188
|
+
return;
|
|
265189
|
+
}
|
|
265190
|
+
resolve(stdout);
|
|
265191
|
+
});
|
|
265192
|
+
});
|
|
265193
|
+
}
|
|
265194
|
+
var GIT_EXEC_PATH_TIMEOUT_MS, detectedEnvironment;
|
|
265195
|
+
var init_environment$1 = __esmMin((() => {
|
|
265196
|
+
init_errors$1();
|
|
265197
|
+
GIT_EXEC_PATH_TIMEOUT_MS = 5e3;
|
|
265198
|
+
}));
|
|
265199
|
+
//#endregion
|
|
265200
|
+
//#region ../../packages/kaos/src/internal.ts
|
|
265201
|
+
function isUtf8Continuation(byte) {
|
|
265202
|
+
return byte >= 128 && byte <= 191;
|
|
265203
|
+
}
|
|
265204
|
+
function decodeUtf8Ignore(data) {
|
|
265205
|
+
let output = "";
|
|
265206
|
+
let i = 0;
|
|
265207
|
+
while (i < data.length) {
|
|
265208
|
+
const b0 = data[i];
|
|
265209
|
+
if (b0 === void 0) break;
|
|
265210
|
+
if (b0 <= 127) {
|
|
265211
|
+
output += String.fromCodePoint(b0);
|
|
265212
|
+
i += 1;
|
|
265213
|
+
continue;
|
|
265214
|
+
}
|
|
265215
|
+
if (b0 >= 194 && b0 <= 223) {
|
|
265216
|
+
const b1 = data[i + 1];
|
|
265217
|
+
if (b1 !== void 0 && isUtf8Continuation(b1)) {
|
|
265218
|
+
output += String.fromCodePoint((b0 & 31) << 6 | b1 & 63);
|
|
265219
|
+
i += 2;
|
|
265220
|
+
continue;
|
|
265221
|
+
}
|
|
265222
|
+
i += 1;
|
|
265223
|
+
continue;
|
|
265224
|
+
}
|
|
265225
|
+
if (b0 >= 224 && b0 <= 239) {
|
|
265226
|
+
const b1 = data[i + 1];
|
|
265227
|
+
const b2 = data[i + 2];
|
|
265228
|
+
if (b1 !== void 0 && (b0 === 224 && b1 >= 160 && b1 <= 191 || b0 >= 225 && b0 <= 236 && isUtf8Continuation(b1) || b0 === 237 && b1 >= 128 && b1 <= 159 || b0 >= 238 && b0 <= 239 && isUtf8Continuation(b1)) && b2 !== void 0 && isUtf8Continuation(b2)) {
|
|
265229
|
+
output += String.fromCodePoint((b0 & 15) << 12 | (b1 & 63) << 6 | b2 & 63);
|
|
265230
|
+
i += 3;
|
|
265231
|
+
continue;
|
|
265232
|
+
}
|
|
265233
|
+
i += 1;
|
|
265234
|
+
continue;
|
|
265235
|
+
}
|
|
265236
|
+
if (b0 >= 240 && b0 <= 244) {
|
|
265237
|
+
const b1 = data[i + 1];
|
|
265238
|
+
const b2 = data[i + 2];
|
|
265239
|
+
const b3 = data[i + 3];
|
|
265240
|
+
if (b1 !== void 0 && (b0 === 240 && b1 >= 144 && b1 <= 191 || b0 >= 241 && b0 <= 243 && isUtf8Continuation(b1) || b0 === 244 && b1 >= 128 && b1 <= 143) && b2 !== void 0 && b3 !== void 0 && isUtf8Continuation(b2) && isUtf8Continuation(b3)) {
|
|
265241
|
+
output += String.fromCodePoint((b0 & 7) << 18 | (b1 & 63) << 12 | (b2 & 63) << 6 | b3 & 63);
|
|
265242
|
+
i += 4;
|
|
265243
|
+
continue;
|
|
265244
|
+
}
|
|
265245
|
+
i += 1;
|
|
265246
|
+
continue;
|
|
265247
|
+
}
|
|
265248
|
+
i += 1;
|
|
265249
|
+
}
|
|
265250
|
+
return output;
|
|
265251
|
+
}
|
|
265252
|
+
function decodeUtf16LeIgnore(data) {
|
|
265253
|
+
let output = "";
|
|
265254
|
+
let i = 0;
|
|
265255
|
+
while (i + 1 < data.length) {
|
|
265256
|
+
const first = data[i];
|
|
265257
|
+
const second = data[i + 1];
|
|
265258
|
+
if (first === void 0 || second === void 0) break;
|
|
265259
|
+
const codeUnit = first | second << 8;
|
|
265260
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
265261
|
+
const lowFirst = data[i + 2];
|
|
265262
|
+
const lowSecond = data[i + 3];
|
|
265263
|
+
if (lowFirst !== void 0 && lowSecond !== void 0) {
|
|
265264
|
+
const low = lowFirst | lowSecond << 8;
|
|
265265
|
+
if (low >= 56320 && low <= 57343) {
|
|
265266
|
+
const codePoint = 65536 + (codeUnit - 55296 << 10) + (low - 56320);
|
|
265267
|
+
output += String.fromCodePoint(codePoint);
|
|
265268
|
+
i += 4;
|
|
265269
|
+
continue;
|
|
265270
|
+
}
|
|
265271
|
+
}
|
|
265272
|
+
i += 2;
|
|
265273
|
+
continue;
|
|
265274
|
+
}
|
|
265275
|
+
if (codeUnit >= 56320 && codeUnit <= 57343) {
|
|
265276
|
+
i += 2;
|
|
265277
|
+
continue;
|
|
265278
|
+
}
|
|
265279
|
+
output += String.fromCodePoint(codeUnit);
|
|
265280
|
+
i += 2;
|
|
265281
|
+
}
|
|
265282
|
+
return output;
|
|
265283
|
+
}
|
|
265284
|
+
/**
|
|
265285
|
+
* Decode a Buffer into a string with Python-compatible `errors` handling.
|
|
265286
|
+
*
|
|
265287
|
+
* - `'strict'` (default): throw on invalid sequences (via TextDecoder `fatal: true`)
|
|
265288
|
+
* - `'replace'`: substitute each invalid sequence with U+FFFD (TextDecoder default)
|
|
265289
|
+
* - `'ignore'`: drop invalid input sequences while preserving valid U+FFFD characters
|
|
265290
|
+
*
|
|
265291
|
+
* Falls back to `Buffer.toString(encoding)` for encodings TextDecoder does not
|
|
265292
|
+
* support (e.g. `hex`, `base64`, `binary`, `latin1`) — those are lossless
|
|
265293
|
+
* byte-to-character mappings so `errors` has no effect.
|
|
265294
|
+
* @internal
|
|
265295
|
+
*/
|
|
265296
|
+
function decodeTextWithErrors(data, encoding, errors = "strict", ignoreBOM = false) {
|
|
265297
|
+
let webLabel;
|
|
265298
|
+
switch (encoding) {
|
|
265299
|
+
case "utf-8":
|
|
265300
|
+
case "utf8":
|
|
265301
|
+
webLabel = "utf-8";
|
|
265302
|
+
break;
|
|
265303
|
+
case "utf16le":
|
|
265304
|
+
case "ucs2":
|
|
265305
|
+
case "ucs-2":
|
|
265306
|
+
webLabel = "utf-16le";
|
|
265307
|
+
break;
|
|
265308
|
+
default: webLabel = void 0;
|
|
265309
|
+
}
|
|
265310
|
+
if (webLabel === void 0) return data.toString(encoding);
|
|
265311
|
+
if (errors === "strict") return new TextDecoder(webLabel, {
|
|
265312
|
+
fatal: true,
|
|
265313
|
+
ignoreBOM
|
|
265314
|
+
}).decode(data);
|
|
265315
|
+
if (errors === "ignore") return webLabel === "utf-8" ? decodeUtf8Ignore(data) : decodeUtf16LeIgnore(data);
|
|
265316
|
+
return new TextDecoder(webLabel, {
|
|
265317
|
+
fatal: false,
|
|
265318
|
+
ignoreBOM
|
|
265319
|
+
}).decode(data);
|
|
265320
|
+
}
|
|
265321
|
+
/**
|
|
265322
|
+
* Convert a glob pattern segment (e.g. "*.txt", "file?.log") into a RegExp.
|
|
265323
|
+
* Mirrors Python pathlib behavior: includes dotfiles, case-sensitive by default.
|
|
265324
|
+
* @internal
|
|
265325
|
+
*/
|
|
265326
|
+
function globPatternToRegex(pattern, caseSensitive) {
|
|
265327
|
+
let regex = "^";
|
|
265328
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
265329
|
+
const ch = pattern[i];
|
|
265330
|
+
if (ch === void 0) break;
|
|
265331
|
+
switch (ch) {
|
|
265332
|
+
case "*":
|
|
265333
|
+
regex += "[^/]*";
|
|
265334
|
+
break;
|
|
265335
|
+
case "?":
|
|
265336
|
+
regex += "[^/]";
|
|
265337
|
+
break;
|
|
265338
|
+
case "[": {
|
|
265339
|
+
const end = pattern.indexOf("]", i + 1);
|
|
265340
|
+
if (end === -1) regex += "\\[";
|
|
265341
|
+
else {
|
|
265342
|
+
let charClass = pattern.slice(i + 1, end);
|
|
265343
|
+
charClass = charClass.replace(/\\/g, "\\\\");
|
|
265344
|
+
if (charClass.startsWith("!")) charClass = "^" + charClass.slice(1);
|
|
265345
|
+
else if (charClass.startsWith("^")) charClass = "\\" + charClass;
|
|
265346
|
+
regex += "[" + charClass + "]";
|
|
265347
|
+
i = end;
|
|
265348
|
+
}
|
|
265349
|
+
break;
|
|
265350
|
+
}
|
|
265351
|
+
case "\\":
|
|
265352
|
+
if (i + 1 < pattern.length) {
|
|
265353
|
+
const next = pattern.charAt(i + 1);
|
|
265354
|
+
regex += next.replaceAll(/[{}()+.\\[\]^$|]/g, "\\$&");
|
|
265355
|
+
i++;
|
|
265356
|
+
} else regex += "\\\\";
|
|
265357
|
+
break;
|
|
265358
|
+
default: regex += ch.replaceAll(/[{}()+.\\[\]^$|]/g, "\\$&");
|
|
265359
|
+
}
|
|
265360
|
+
}
|
|
265361
|
+
regex += "$";
|
|
265362
|
+
return new RegExp(regex, caseSensitive ? "" : "i");
|
|
265363
|
+
}
|
|
265364
|
+
var BufferedReadable;
|
|
265365
|
+
var init_internal = __esmMin((() => {
|
|
265366
|
+
BufferedReadable = class extends Readable$1 {
|
|
265367
|
+
_source;
|
|
265368
|
+
_ended = false;
|
|
265369
|
+
constructor(source) {
|
|
265370
|
+
super({ highWaterMark: 128 * 1024 });
|
|
265371
|
+
this._source = source;
|
|
265372
|
+
this._source.on("data", this._onData);
|
|
265373
|
+
this._source.on("end", this._onEnd);
|
|
265374
|
+
this._source.on("close", this._onClose);
|
|
265375
|
+
this._source.on("error", this._onError);
|
|
265376
|
+
}
|
|
265377
|
+
_read() {
|
|
265378
|
+
if (!this._ended && !this.destroyed) this._source.resume();
|
|
265379
|
+
}
|
|
265380
|
+
_destroy(error, callback) {
|
|
265381
|
+
this._source.off("data", this._onData);
|
|
265382
|
+
this._source.off("end", this._onEnd);
|
|
265383
|
+
this._source.off("close", this._onClose);
|
|
265384
|
+
this._source.off("error", this._onError);
|
|
265385
|
+
this._source.destroy();
|
|
265386
|
+
callback(error);
|
|
265387
|
+
}
|
|
265388
|
+
_onData = (chunk) => {
|
|
265389
|
+
if (!this.push(chunk)) this._source.pause();
|
|
265390
|
+
};
|
|
265391
|
+
_onEnd = () => {
|
|
265392
|
+
this._ended = true;
|
|
265393
|
+
this.push(null);
|
|
265394
|
+
};
|
|
265395
|
+
_onClose = () => {
|
|
265396
|
+
if (!this._ended) {
|
|
265397
|
+
this._ended = true;
|
|
265398
|
+
this.push(null);
|
|
265399
|
+
}
|
|
265400
|
+
};
|
|
265401
|
+
_onError = (error) => {
|
|
265402
|
+
this.destroy(error);
|
|
265403
|
+
};
|
|
265404
|
+
};
|
|
265405
|
+
}));
|
|
265406
|
+
//#endregion
|
|
265407
|
+
//#region ../../packages/kaos/src/login-shell-path.ts
|
|
265408
|
+
/**
|
|
265409
|
+
* Login-shell PATH probe — enrich `process.env.PATH` with entries from the
|
|
265410
|
+
* user's login shell.
|
|
265411
|
+
*
|
|
265412
|
+
* When blun is launched from a context that skipped the user's shell
|
|
265413
|
+
* profile (GUI launchers, non-login parent shells), `process.env.PATH`
|
|
265414
|
+
* misses entries like `/opt/homebrew/bin`, so commands spawned by the Bash
|
|
265415
|
+
* tool can't find tools the user has in their interactive shell (e.g.
|
|
265416
|
+
* `gh`). We run the user's login shell once (`$SHELL -l -c /usr/bin/env`),
|
|
265417
|
+
* extract its PATH, and append the entries the current PATH lacks. Existing
|
|
265418
|
+
* entries keep their order and priority; failures (no resolvable shell,
|
|
265419
|
+
* hung or broken profile) silently leave PATH untouched.
|
|
265420
|
+
*
|
|
265421
|
+
* launchd/daemon launches can leave `$SHELL` unset or blank (see
|
|
265422
|
+
* `defaultShell()` in agent-core's terminalService for the same case), so
|
|
265423
|
+
* the probe falls back to the OS account's login shell from the user
|
|
265424
|
+
* database before giving up.
|
|
265425
|
+
*
|
|
265426
|
+
* Like `detectEnvironment`, the probe is a pure function of injected deps
|
|
265427
|
+
* so the suite runs identically on any host. Windows is skipped: the
|
|
265428
|
+
* problem is specific to POSIX login-shell profiles.
|
|
265429
|
+
*/
|
|
265430
|
+
/**
|
|
265431
|
+
* Run the user's login shell and return its PATH, or `undefined` when the
|
|
265432
|
+
* probe does not apply (Windows, no resolvable shell) or fails (spawn
|
|
265433
|
+
* error, timeout, no PATH in the output).
|
|
265434
|
+
*/
|
|
265435
|
+
async function probeLoginShellPath(deps) {
|
|
265436
|
+
if (deps.platform === "win32") return void 0;
|
|
265437
|
+
const envShell = deps.env["SHELL"]?.trim();
|
|
265438
|
+
const shell = envShell === void 0 || envShell.length === 0 ? deps.userShell() : envShell;
|
|
265439
|
+
if (shell === void 0 || shell.length === 0) return void 0;
|
|
265440
|
+
const stdout = await deps.execFileText(shell, [
|
|
265441
|
+
"-l",
|
|
265442
|
+
"-c",
|
|
265443
|
+
"/usr/bin/env"
|
|
265444
|
+
], LOGIN_SHELL_ENV_TIMEOUT_MS);
|
|
265445
|
+
if (stdout === void 0) return void 0;
|
|
265446
|
+
let path;
|
|
265447
|
+
for (const line of stdout.split("\n")) if (line.startsWith("PATH=")) path = line.slice(5).trim();
|
|
265448
|
+
if (path === void 0 || path.length === 0) return void 0;
|
|
265449
|
+
return path;
|
|
265450
|
+
}
|
|
265451
|
+
/**
|
|
265452
|
+
* Union of the current PATH and the login-shell PATH: the current PATH
|
|
265453
|
+
* string is kept verbatim — including empty components, which POSIX
|
|
265454
|
+
* command lookup treats as the current directory — and login-shell
|
|
265455
|
+
* entries the current PATH lacks are appended in their own order. When
|
|
265456
|
+
* nothing is missing the current string is returned unchanged. Only
|
|
265457
|
+
* absolute login-shell entries are imported: empty, `.`, and relative
|
|
265458
|
+
* components are all cwd-dependent lookup, and appending one the user
|
|
265459
|
+
* did not already have would widen their search path — LocalKaos runs
|
|
265460
|
+
* commands from arbitrary workspace directories.
|
|
265461
|
+
*/
|
|
265462
|
+
function mergeLoginShellPath(currentPath, loginShellPath) {
|
|
265463
|
+
const current = currentPath ?? "";
|
|
265464
|
+
const seen = new Set(current.split(":").filter((entry) => entry.length > 0));
|
|
265465
|
+
const additions = [];
|
|
265466
|
+
for (const entry of loginShellPath.split(":")) {
|
|
265467
|
+
if (!entry.startsWith("/") || seen.has(entry)) continue;
|
|
265468
|
+
seen.add(entry);
|
|
265469
|
+
additions.push(entry);
|
|
265470
|
+
}
|
|
265471
|
+
if (additions.length === 0) return current;
|
|
265472
|
+
if (currentPath === void 0) return additions.join(":");
|
|
265473
|
+
return `${current}:${additions.join(":")}`;
|
|
265474
|
+
}
|
|
265475
|
+
/** Probe the login shell and merge its PATH into `deps.env['PATH']`. */
|
|
265476
|
+
async function applyLoginShellPath(deps) {
|
|
265477
|
+
const loginShellPath = await probeLoginShellPath(deps);
|
|
265478
|
+
if (loginShellPath === void 0) return;
|
|
265479
|
+
const currentPath = deps.env["PATH"];
|
|
265480
|
+
const merged = mergeLoginShellPath(currentPath, loginShellPath);
|
|
265481
|
+
if (merged === (currentPath ?? "")) return;
|
|
265482
|
+
deps.env["PATH"] = merged;
|
|
265483
|
+
}
|
|
265484
|
+
/**
|
|
265485
|
+
* Production convenience — apply the probe to `process.env` once per
|
|
265486
|
+
* process. Memoised like `detectEnvironmentFromNode`: the login-shell PATH
|
|
265487
|
+
* does not change for the lifetime of the process, and repeated
|
|
265488
|
+
* `LocalKaos.create()` calls must not re-spawn the shell.
|
|
265489
|
+
*/
|
|
265490
|
+
/**
|
|
265491
|
+
* Login shell from the OS user database (`/etc/passwd` via getpwuid on
|
|
265492
|
+
* Linux, Directory Services on macOS). `userInfo()` throws when the uid
|
|
265493
|
+
* has no database entry (e.g. containers running an arbitrary uid), and
|
|
265494
|
+
* service accounts may carry `/usr/sbin/nologin` — the latter needs no
|
|
265495
|
+
* special casing here because probing it simply fails and degrades
|
|
265496
|
+
* silently.
|
|
265497
|
+
*/
|
|
265498
|
+
function userShellFromNode() {
|
|
265499
|
+
try {
|
|
265500
|
+
const shell = userInfo().shell;
|
|
265501
|
+
return shell === null || shell.length === 0 ? void 0 : shell;
|
|
265502
|
+
} catch {
|
|
265503
|
+
return;
|
|
265504
|
+
}
|
|
265505
|
+
}
|
|
265506
|
+
function applyLoginShellPathFromNode() {
|
|
265507
|
+
if (appliedLoginShellPath !== void 0) return appliedLoginShellPath;
|
|
265508
|
+
appliedLoginShellPath = applyLoginShellPath({
|
|
265509
|
+
platform: process.platform,
|
|
265510
|
+
env: process.env,
|
|
265511
|
+
userShell: userShellFromNode,
|
|
265512
|
+
execFileText: execFileText$1
|
|
265513
|
+
});
|
|
265514
|
+
return appliedLoginShellPath;
|
|
265515
|
+
}
|
|
265516
|
+
var LOGIN_SHELL_ENV_TIMEOUT_MS, appliedLoginShellPath;
|
|
265517
|
+
var init_login_shell_path = __esmMin((() => {
|
|
265518
|
+
init_environment$1();
|
|
265519
|
+
LOGIN_SHELL_ENV_TIMEOUT_MS = 5e3;
|
|
265520
|
+
}));
|
|
265521
|
+
//#endregion
|
|
265522
|
+
//#region ../../packages/kaos/src/local.ts
|
|
265523
|
+
/**
|
|
265524
|
+
* Build the `(dev, ino)` cycle-detection key used by `_globWalk`'s
|
|
265525
|
+
* visited set. Returns `null` when `ino` is 0, which Node returns on
|
|
265526
|
+
* filesystems that don't carry inodes (Windows FAT/exFAT, some SMB/NFS
|
|
265527
|
+
* mounts). A null key signals "no reliable identity for this dir" so
|
|
265528
|
+
* the caller skips visited tracking for that descent — cycle safety
|
|
265529
|
+
* is weakened on those filesystems, but normal walking works instead
|
|
265530
|
+
* of every directory colliding on the shared key `"<dev>:0"`.
|
|
265531
|
+
*/
|
|
265532
|
+
function cycleKey(s) {
|
|
265533
|
+
if (s.ino === 0) return null;
|
|
265534
|
+
return `${String(s.dev)}:${String(s.ino)}`;
|
|
265535
|
+
}
|
|
265536
|
+
function buildLocalSpawnOptions(_isWindows, cwd, env) {
|
|
265537
|
+
return {
|
|
265538
|
+
cwd,
|
|
265539
|
+
env,
|
|
265540
|
+
stdio: [
|
|
265541
|
+
"pipe",
|
|
265542
|
+
"pipe",
|
|
265543
|
+
"pipe"
|
|
265544
|
+
],
|
|
265545
|
+
detached: true,
|
|
265546
|
+
windowsHide: true
|
|
265547
|
+
};
|
|
265548
|
+
}
|
|
265549
|
+
function isUtf8Encoding(encoding) {
|
|
265550
|
+
return encoding === "utf-8" || encoding === "utf8";
|
|
265551
|
+
}
|
|
265552
|
+
function* splitLinesKeepingTerminator(text) {
|
|
265553
|
+
if (text.length === 0) return;
|
|
265554
|
+
let start = 0;
|
|
265555
|
+
for (let i = 0; i < text.length; i += 1) if (text.codePointAt(i) === 10) {
|
|
265556
|
+
yield text.slice(start, i + 1);
|
|
265557
|
+
start = i + 1;
|
|
265558
|
+
}
|
|
265559
|
+
if (start < text.length) yield text.slice(start);
|
|
265560
|
+
}
|
|
265561
|
+
function updateLineEndingFlagsFromBytes(flags, chunk, prevWasCr) {
|
|
265562
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
265563
|
+
const byte = chunk[i];
|
|
265564
|
+
if (byte === void 0) continue;
|
|
265565
|
+
if (byte === 13) {
|
|
265566
|
+
if (prevWasCr) flags.hasLoneCr = true;
|
|
265567
|
+
prevWasCr = true;
|
|
265568
|
+
} else if (byte === 10) {
|
|
265569
|
+
if (prevWasCr) flags.hasCrLf = true;
|
|
265570
|
+
else flags.hasLf = true;
|
|
265571
|
+
prevWasCr = false;
|
|
265572
|
+
} else {
|
|
265573
|
+
if (prevWasCr) flags.hasLoneCr = true;
|
|
265574
|
+
prevWasCr = false;
|
|
265575
|
+
}
|
|
265576
|
+
}
|
|
265577
|
+
return prevWasCr;
|
|
265578
|
+
}
|
|
265579
|
+
function createUtf8Validator() {
|
|
265580
|
+
let needed = 0;
|
|
265581
|
+
let lower = 128;
|
|
265582
|
+
let upper = 191;
|
|
265583
|
+
const fail = () => {
|
|
265584
|
+
throw new TypeError("Invalid UTF-8 data");
|
|
265585
|
+
};
|
|
265586
|
+
return {
|
|
265587
|
+
write(chunk) {
|
|
265588
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
265589
|
+
const byte = chunk[i];
|
|
265590
|
+
if (byte === void 0) continue;
|
|
265591
|
+
if (needed === 0) {
|
|
265592
|
+
if (byte <= 127) continue;
|
|
265593
|
+
if (byte >= 194 && byte <= 223) needed = 1;
|
|
265594
|
+
else if (byte === 224) {
|
|
265595
|
+
needed = 2;
|
|
265596
|
+
lower = 160;
|
|
265597
|
+
} else if (byte >= 225 && byte <= 236) needed = 2;
|
|
265598
|
+
else if (byte === 237) {
|
|
265599
|
+
needed = 2;
|
|
265600
|
+
upper = 159;
|
|
265601
|
+
} else if (byte >= 238 && byte <= 239) needed = 2;
|
|
265602
|
+
else if (byte === 240) {
|
|
265603
|
+
needed = 3;
|
|
265604
|
+
lower = 144;
|
|
265605
|
+
} else if (byte >= 241 && byte <= 243) needed = 3;
|
|
265606
|
+
else if (byte === 244) {
|
|
265607
|
+
needed = 3;
|
|
265608
|
+
upper = 143;
|
|
265609
|
+
} else fail();
|
|
265610
|
+
} else {
|
|
265611
|
+
if (byte < lower || byte > upper) fail();
|
|
265612
|
+
lower = 128;
|
|
265613
|
+
upper = 191;
|
|
265614
|
+
needed -= 1;
|
|
265615
|
+
}
|
|
265616
|
+
}
|
|
265617
|
+
},
|
|
265618
|
+
end() {
|
|
265619
|
+
if (needed !== 0) fail();
|
|
265620
|
+
}
|
|
265621
|
+
};
|
|
265622
|
+
}
|
|
265623
|
+
async function readRange(fh, start, length) {
|
|
265624
|
+
const data = Buffer.alloc(length);
|
|
265625
|
+
let offset = 0;
|
|
265626
|
+
while (offset < length) {
|
|
265627
|
+
const { bytesRead } = await fh.read(data, offset, length - offset, start + offset);
|
|
265628
|
+
if (bytesRead === 0) break;
|
|
265629
|
+
offset += bytesRead;
|
|
265630
|
+
}
|
|
265631
|
+
return offset === length ? data : data.subarray(0, offset);
|
|
265632
|
+
}
|
|
265633
|
+
function waitForSpawn(child) {
|
|
265634
|
+
return new Promise((resolve, reject) => {
|
|
265635
|
+
const onSpawn = () => {
|
|
265636
|
+
child.off("error", onError);
|
|
265637
|
+
resolve();
|
|
265638
|
+
};
|
|
265639
|
+
const onError = (err) => {
|
|
265640
|
+
child.off("spawn", onSpawn);
|
|
265641
|
+
reject(err);
|
|
265642
|
+
};
|
|
265643
|
+
child.once("spawn", onSpawn);
|
|
265644
|
+
child.once("error", onError);
|
|
265645
|
+
});
|
|
265646
|
+
}
|
|
265647
|
+
var isWindows$1, READ_CHUNK_SIZE, LocalProcess, LocalKaos;
|
|
265648
|
+
var init_local = __esmMin((() => {
|
|
265649
|
+
init_dist$6();
|
|
265650
|
+
init_environment$1();
|
|
265651
|
+
init_errors$1();
|
|
265652
|
+
init_internal();
|
|
265653
|
+
init_login_shell_path();
|
|
265654
|
+
isWindows$1 = process.platform === "win32";
|
|
265655
|
+
READ_CHUNK_SIZE = 64 * 1024;
|
|
265656
|
+
LocalProcess = class {
|
|
265657
|
+
stdin;
|
|
265658
|
+
stdout;
|
|
265659
|
+
stderr;
|
|
265660
|
+
pid;
|
|
265661
|
+
_child;
|
|
265662
|
+
_exitCode = null;
|
|
265663
|
+
_exitPromise;
|
|
265664
|
+
_disposed = false;
|
|
265665
|
+
constructor(child) {
|
|
265666
|
+
if (child.stdin === null || child.stdout === null || child.stderr === null) throw new Error("Process must be created with stdin/stdout/stderr pipes.");
|
|
265667
|
+
this._child = child;
|
|
265668
|
+
this.stdin = child.stdin;
|
|
265669
|
+
this.stdout = new BufferedReadable(child.stdout);
|
|
265670
|
+
this.stderr = new BufferedReadable(child.stderr);
|
|
265671
|
+
this.pid = child.pid ?? -1;
|
|
265672
|
+
this._exitPromise = new Promise((resolve, reject) => {
|
|
265673
|
+
child.on("exit", (code) => {
|
|
265674
|
+
this._exitCode = code ?? -1;
|
|
265675
|
+
resolve(this._exitCode);
|
|
265676
|
+
});
|
|
265677
|
+
child.on("error", (error) => {
|
|
265678
|
+
reject(error);
|
|
265679
|
+
});
|
|
265680
|
+
});
|
|
265681
|
+
}
|
|
265682
|
+
get exitCode() {
|
|
265683
|
+
return this._exitCode;
|
|
265684
|
+
}
|
|
265685
|
+
async wait() {
|
|
265686
|
+
return this._exitPromise;
|
|
265687
|
+
}
|
|
265688
|
+
kill(signal) {
|
|
265689
|
+
if (this.pid <= 0) return Promise.resolve();
|
|
265690
|
+
if (isWindows$1) {
|
|
265691
|
+
const taskkillArgs = [
|
|
265692
|
+
"/T",
|
|
265693
|
+
"/F",
|
|
265694
|
+
"/PID",
|
|
265695
|
+
String(this.pid)
|
|
265696
|
+
];
|
|
265697
|
+
return new Promise((resolve) => {
|
|
265698
|
+
const killer = spawn("taskkill", taskkillArgs, {
|
|
265699
|
+
stdio: "ignore",
|
|
265700
|
+
windowsHide: true
|
|
265701
|
+
});
|
|
265702
|
+
const done = () => {
|
|
265703
|
+
resolve();
|
|
265704
|
+
};
|
|
265705
|
+
killer.once("error", done);
|
|
265706
|
+
killer.once("close", done);
|
|
265707
|
+
});
|
|
265708
|
+
}
|
|
265709
|
+
try {
|
|
265710
|
+
process.kill(-this.pid, signal ?? "SIGTERM");
|
|
265711
|
+
} catch (error) {
|
|
265712
|
+
const err = error;
|
|
265713
|
+
if (err.code === "ESRCH") return Promise.resolve();
|
|
265714
|
+
if (err.code === "EPERM") {
|
|
265715
|
+
try {
|
|
265716
|
+
this._child.kill(signal ?? "SIGTERM");
|
|
265717
|
+
} catch {}
|
|
265718
|
+
return Promise.resolve();
|
|
265719
|
+
}
|
|
265720
|
+
throw error;
|
|
265721
|
+
}
|
|
265722
|
+
return Promise.resolve();
|
|
265723
|
+
}
|
|
265724
|
+
dispose() {
|
|
265725
|
+
if (this._disposed) return;
|
|
265726
|
+
this._disposed = true;
|
|
265727
|
+
this.stdin.destroy();
|
|
265728
|
+
this.stdout.destroy();
|
|
265729
|
+
this.stderr.destroy();
|
|
265730
|
+
}
|
|
265731
|
+
};
|
|
265732
|
+
LocalKaos = class LocalKaos {
|
|
265733
|
+
name = "local";
|
|
265734
|
+
osEnv;
|
|
265735
|
+
_cwd;
|
|
265736
|
+
_envLayers;
|
|
265737
|
+
constructor(osEnv, cwd, envLayers = []) {
|
|
265738
|
+
this._cwd = normalize$4(cwd ?? process.cwd());
|
|
265739
|
+
this.osEnv = osEnv;
|
|
265740
|
+
this._envLayers = envLayers;
|
|
265741
|
+
}
|
|
265742
|
+
/**
|
|
265743
|
+
* Construct a fresh `LocalKaos` after probing the host environment.
|
|
265744
|
+
*
|
|
265745
|
+
* Each call returns a new instance with its own `_cwd`; concurrent
|
|
265746
|
+
* callers can therefore operate on independent working directories
|
|
265747
|
+
* without polluting one another.
|
|
265748
|
+
*/
|
|
265749
|
+
static async create() {
|
|
265750
|
+
const [osEnv] = await Promise.all([detectEnvironmentFromNode(), applyLoginShellPathFromNode()]);
|
|
265751
|
+
return new LocalKaos(osEnv);
|
|
265752
|
+
}
|
|
265753
|
+
withCwd(cwd) {
|
|
265754
|
+
return new LocalKaos(this.osEnv, cwd, this._envLayers);
|
|
265755
|
+
}
|
|
265756
|
+
withEnv(env) {
|
|
265757
|
+
return new LocalKaos(this.osEnv, this._cwd, [...this._envLayers, env]);
|
|
265758
|
+
}
|
|
265759
|
+
_resolvePath(path) {
|
|
265760
|
+
if (isAbsolute$2(path)) return normalize$4(path);
|
|
265761
|
+
return join$4(this._cwd, path);
|
|
265762
|
+
}
|
|
265763
|
+
pathClass() {
|
|
265764
|
+
return isWindows$1 ? "win32" : "posix";
|
|
265765
|
+
}
|
|
265766
|
+
normpath(path) {
|
|
265767
|
+
return normalize$4(path);
|
|
265768
|
+
}
|
|
265769
|
+
gethome() {
|
|
265770
|
+
return normalize$4(homedir());
|
|
265771
|
+
}
|
|
265772
|
+
getcwd() {
|
|
265773
|
+
return this._cwd;
|
|
265774
|
+
}
|
|
265775
|
+
/**
|
|
265776
|
+
* Change the working directory of this LocalKaos instance.
|
|
265777
|
+
*
|
|
265778
|
+
* Unlike Python's `os.chdir`, this is instance-scoped and never touches
|
|
265779
|
+
* `process.cwd()`. Child processes spawned via {@link exec} inherit this
|
|
265780
|
+
* instance's `_cwd`; concurrent LocalKaos instances each carry their own
|
|
265781
|
+
* independent cwd. If you need Python-compatible process-global cwd,
|
|
265782
|
+
* call `process.chdir(x)` directly.
|
|
265783
|
+
*/
|
|
265784
|
+
async chdir(path) {
|
|
265785
|
+
const resolved = this._resolvePath(path);
|
|
265786
|
+
if (!(await stat(resolved)).isDirectory()) throw new Error(`Not a directory: ${resolved}`);
|
|
265787
|
+
this._cwd = resolved;
|
|
265788
|
+
}
|
|
265789
|
+
async stat(path, options) {
|
|
265790
|
+
const resolved = this._resolvePath(path);
|
|
265791
|
+
const s = options?.followSymlinks ?? true ? await stat(resolved) : await lstat(resolved);
|
|
265792
|
+
return {
|
|
265793
|
+
stMode: s.mode,
|
|
265794
|
+
stIno: s.ino,
|
|
265795
|
+
stDev: s.dev,
|
|
265796
|
+
stNlink: s.nlink,
|
|
265797
|
+
stUid: s.uid,
|
|
265798
|
+
stGid: s.gid,
|
|
265799
|
+
stSize: s.size,
|
|
265800
|
+
stAtime: s.atimeMs / 1e3,
|
|
265801
|
+
stMtime: s.mtimeMs / 1e3,
|
|
265802
|
+
stCtime: isWindows$1 ? s.birthtimeMs / 1e3 : s.ctimeMs / 1e3
|
|
265803
|
+
};
|
|
265804
|
+
}
|
|
265805
|
+
async *iterdir(path) {
|
|
265806
|
+
const resolved = this._resolvePath(path);
|
|
265807
|
+
const entries = await readdir(resolved);
|
|
265808
|
+
for (const entry of entries) yield join$4(resolved, entry);
|
|
265809
|
+
}
|
|
265810
|
+
async *glob(path, pattern, options) {
|
|
265811
|
+
const resolved = this._resolvePath(path);
|
|
265812
|
+
const caseSensitive = options?.caseSensitive ?? true;
|
|
265813
|
+
const patternParts = pattern.split("/");
|
|
265814
|
+
const initVisited = /* @__PURE__ */ new Set();
|
|
265815
|
+
try {
|
|
265816
|
+
const rootKey = cycleKey(await stat(resolved));
|
|
265817
|
+
if (rootKey !== null) initVisited.add(rootKey);
|
|
265818
|
+
} catch {}
|
|
265819
|
+
yield* this._globWalk(resolved, patternParts, caseSensitive, initVisited);
|
|
265820
|
+
}
|
|
265821
|
+
async *_globWalk(basePath, patternParts, caseSensitive, visited) {
|
|
265822
|
+
if (patternParts.length === 0) return;
|
|
265823
|
+
const [currentPattern, ...remainingParts] = patternParts;
|
|
265824
|
+
if (currentPattern === "**") {
|
|
265825
|
+
if (remainingParts.length > 0) yield* this._globWalk(basePath, remainingParts, caseSensitive, visited);
|
|
265826
|
+
else yield basePath;
|
|
265827
|
+
let entries;
|
|
265828
|
+
try {
|
|
265829
|
+
entries = await readdir(basePath);
|
|
265830
|
+
} catch {
|
|
265831
|
+
return;
|
|
265832
|
+
}
|
|
265833
|
+
for (const entry of entries) {
|
|
265834
|
+
const fullPath = join$4(basePath, entry);
|
|
265835
|
+
let entryStat;
|
|
265836
|
+
try {
|
|
265837
|
+
entryStat = await stat(fullPath);
|
|
265838
|
+
} catch {
|
|
265839
|
+
continue;
|
|
265840
|
+
}
|
|
265841
|
+
if (entryStat.isDirectory()) {
|
|
265842
|
+
const key = cycleKey(entryStat);
|
|
265843
|
+
if (key !== null && visited.has(key)) continue;
|
|
265844
|
+
yield* this._globWalk(fullPath, patternParts, caseSensitive, key !== null ? new Set([...visited, key]) : visited);
|
|
265845
|
+
} else if (remainingParts.length === 0) yield fullPath;
|
|
265846
|
+
}
|
|
265847
|
+
} else {
|
|
265848
|
+
const regex = globPatternToRegex(currentPattern ?? "", caseSensitive);
|
|
265849
|
+
let entries;
|
|
265850
|
+
try {
|
|
265851
|
+
entries = await readdir(basePath);
|
|
265852
|
+
} catch {
|
|
265853
|
+
return;
|
|
265854
|
+
}
|
|
265855
|
+
for (const entry of entries) {
|
|
265856
|
+
if (!regex.test(entry)) continue;
|
|
265857
|
+
const fullPath = join$4(basePath, entry);
|
|
265858
|
+
if (remainingParts.length === 0) yield fullPath;
|
|
265859
|
+
else {
|
|
265860
|
+
let entryStat;
|
|
265861
|
+
try {
|
|
265862
|
+
entryStat = await stat(fullPath);
|
|
265863
|
+
} catch {
|
|
265864
|
+
continue;
|
|
265865
|
+
}
|
|
265866
|
+
if (entryStat.isDirectory()) {
|
|
265867
|
+
const key = cycleKey(entryStat);
|
|
265868
|
+
if (key !== null && visited.has(key)) continue;
|
|
265869
|
+
yield* this._globWalk(fullPath, remainingParts, caseSensitive, key !== null ? new Set([...visited, key]) : visited);
|
|
265870
|
+
}
|
|
265871
|
+
}
|
|
265872
|
+
}
|
|
265873
|
+
}
|
|
265874
|
+
}
|
|
265875
|
+
async readBytes(path, n) {
|
|
265876
|
+
const resolved = this._resolvePath(path);
|
|
265877
|
+
if (n === void 0) return Buffer.from(await readFile(resolved));
|
|
265878
|
+
const fh = await open(resolved, "r");
|
|
265879
|
+
try {
|
|
265880
|
+
const buf = Buffer.alloc(n);
|
|
265881
|
+
const { bytesRead } = await fh.read(buf, 0, n, 0);
|
|
265882
|
+
return buf.subarray(0, bytesRead);
|
|
265883
|
+
} finally {
|
|
265884
|
+
await fh.close();
|
|
265885
|
+
}
|
|
265886
|
+
}
|
|
265887
|
+
async readText(path, options) {
|
|
265888
|
+
const resolved = this._resolvePath(path);
|
|
265889
|
+
const encoding = options?.encoding ?? "utf-8";
|
|
265890
|
+
const errors = options?.errors ?? "strict";
|
|
265891
|
+
return decodeTextWithErrors(await readFile(resolved), encoding, errors);
|
|
265892
|
+
}
|
|
265893
|
+
async *readLines(path, options) {
|
|
265894
|
+
const resolved = this._resolvePath(path);
|
|
265895
|
+
const encoding = options?.encoding ?? "utf-8";
|
|
265896
|
+
const errors = options?.errors ?? "strict";
|
|
265897
|
+
if (!isUtf8Encoding(encoding)) {
|
|
265898
|
+
yield* splitLinesKeepingTerminator(decodeTextWithErrors(await readFile(resolved), encoding, errors));
|
|
265899
|
+
return;
|
|
265900
|
+
}
|
|
265901
|
+
yield* this._readUtf8Lines(resolved, errors);
|
|
265902
|
+
}
|
|
265903
|
+
async scanTextFile(path) {
|
|
265904
|
+
const fh = await open(this._resolvePath(path), "r");
|
|
265905
|
+
try {
|
|
265906
|
+
const buf = Buffer.alloc(READ_CHUNK_SIZE);
|
|
265907
|
+
const flags = {
|
|
265908
|
+
hasCrLf: false,
|
|
265909
|
+
hasLf: false,
|
|
265910
|
+
hasLoneCr: false
|
|
265911
|
+
};
|
|
265912
|
+
const validator = createUtf8Validator();
|
|
265913
|
+
let totalLines = 0;
|
|
265914
|
+
let totalBytes = 0;
|
|
265915
|
+
let endsWithNewline = false;
|
|
265916
|
+
let hasNul = false;
|
|
265917
|
+
let prevWasCr = false;
|
|
265918
|
+
while (true) {
|
|
265919
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, null);
|
|
265920
|
+
if (bytesRead === 0) break;
|
|
265921
|
+
const chunk = buf.subarray(0, bytesRead);
|
|
265922
|
+
validator.write(chunk);
|
|
265923
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
265924
|
+
const byte = chunk[i];
|
|
265925
|
+
if (byte === void 0) continue;
|
|
265926
|
+
if (byte === 0) hasNul = true;
|
|
265927
|
+
if (byte === 10) totalLines += 1;
|
|
265928
|
+
}
|
|
265929
|
+
prevWasCr = updateLineEndingFlagsFromBytes(flags, chunk, prevWasCr);
|
|
265930
|
+
totalBytes += bytesRead;
|
|
265931
|
+
endsWithNewline = chunk[bytesRead - 1] === 10;
|
|
265932
|
+
}
|
|
265933
|
+
if (prevWasCr) flags.hasLoneCr = true;
|
|
265934
|
+
validator.end();
|
|
265935
|
+
if (totalBytes > 0 && !endsWithNewline) totalLines += 1;
|
|
265936
|
+
return {
|
|
265937
|
+
totalLines,
|
|
265938
|
+
endsWithNewline,
|
|
265939
|
+
hasNul,
|
|
265940
|
+
lineEndingFlags: flags
|
|
265941
|
+
};
|
|
265942
|
+
} finally {
|
|
265943
|
+
await fh.close();
|
|
265944
|
+
}
|
|
265945
|
+
}
|
|
265946
|
+
async *readLineRange(path, options) {
|
|
265947
|
+
const resolved = this._resolvePath(path);
|
|
265948
|
+
const errors = options.errors ?? "strict";
|
|
265949
|
+
yield* this._readUtf8Lines(resolved, errors, {
|
|
265950
|
+
startLine: options.startLine,
|
|
265951
|
+
maxLines: options.maxLines
|
|
265952
|
+
});
|
|
265953
|
+
}
|
|
265954
|
+
async *readTailLines(path, options) {
|
|
265955
|
+
if (options.tailCount <= 0) return;
|
|
265956
|
+
const resolved = this._resolvePath(path);
|
|
265957
|
+
const errors = options.errors ?? "strict";
|
|
265958
|
+
const fh = await open(resolved, "r");
|
|
265959
|
+
try {
|
|
265960
|
+
const s = await fh.stat();
|
|
265961
|
+
if (s.size === 0) return;
|
|
265962
|
+
let pos = s.size;
|
|
265963
|
+
let foundLf = 0;
|
|
265964
|
+
let startOffset = 0;
|
|
265965
|
+
let needLf = options.tailCount;
|
|
265966
|
+
let sawTailBlock = false;
|
|
265967
|
+
while (pos > 0 && foundLf < needLf) {
|
|
265968
|
+
const readSize = Math.min(READ_CHUNK_SIZE, pos);
|
|
265969
|
+
pos -= readSize;
|
|
265970
|
+
const buf = Buffer.alloc(readSize);
|
|
265971
|
+
await fh.read(buf, 0, readSize, pos);
|
|
265972
|
+
if (!sawTailBlock) {
|
|
265973
|
+
sawTailBlock = true;
|
|
265974
|
+
needLf = buf[readSize - 1] === 10 ? options.tailCount + 1 : options.tailCount;
|
|
265975
|
+
}
|
|
265976
|
+
for (let i = readSize - 1; i >= 0; i -= 1) {
|
|
265977
|
+
if (buf[i] !== 10) continue;
|
|
265978
|
+
foundLf += 1;
|
|
265979
|
+
if (foundLf === needLf) {
|
|
265980
|
+
startOffset = pos + i + 1;
|
|
265981
|
+
break;
|
|
265982
|
+
}
|
|
265983
|
+
}
|
|
265984
|
+
}
|
|
265985
|
+
if (foundLf < needLf) startOffset = 0;
|
|
265986
|
+
yield* splitLinesKeepingTerminator(decodeTextWithErrors(await readRange(fh, startOffset, s.size - startOffset), "utf-8", errors, startOffset !== 0));
|
|
265987
|
+
} finally {
|
|
265988
|
+
await fh.close();
|
|
265989
|
+
}
|
|
265990
|
+
}
|
|
265991
|
+
async *_readUtf8Lines(resolved, errors, range) {
|
|
265992
|
+
const startLine = range?.startLine ?? 1;
|
|
265993
|
+
const maxLines = range?.maxLines ?? Number.POSITIVE_INFINITY;
|
|
265994
|
+
const fh = await open(resolved, "r");
|
|
265995
|
+
try {
|
|
265996
|
+
const buf = Buffer.alloc(READ_CHUNK_SIZE);
|
|
265997
|
+
let pending = [];
|
|
265998
|
+
let pendingOffset = 0;
|
|
265999
|
+
let fileOffset = 0;
|
|
266000
|
+
let lineNo = 1;
|
|
266001
|
+
let yielded = 0;
|
|
266002
|
+
while (true) {
|
|
266003
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, null);
|
|
266004
|
+
if (bytesRead === 0) break;
|
|
266005
|
+
const chunk = buf.subarray(0, bytesRead);
|
|
266006
|
+
let lineStart = 0;
|
|
266007
|
+
for (let i = 0; i < chunk.length; i += 1) {
|
|
266008
|
+
if (chunk[i] !== 10) continue;
|
|
266009
|
+
const piece = chunk.subarray(lineStart, i + 1);
|
|
266010
|
+
const lineOffset = pending.length === 0 ? fileOffset + lineStart : pendingOffset;
|
|
266011
|
+
const line = pending.length === 0 ? piece : Buffer.concat([...pending, piece]);
|
|
266012
|
+
if (lineNo >= startLine) {
|
|
266013
|
+
yield decodeTextWithErrors(line, "utf-8", errors, lineOffset !== 0);
|
|
266014
|
+
yielded += 1;
|
|
266015
|
+
if (yielded >= maxLines) return;
|
|
266016
|
+
}
|
|
266017
|
+
pending = [];
|
|
266018
|
+
lineStart = i + 1;
|
|
266019
|
+
lineNo += 1;
|
|
266020
|
+
}
|
|
266021
|
+
if (lineStart < chunk.length) {
|
|
266022
|
+
const tail = Buffer.from(chunk.subarray(lineStart));
|
|
266023
|
+
if (pending.length === 0) pendingOffset = fileOffset + lineStart;
|
|
266024
|
+
pending.push(tail);
|
|
266025
|
+
}
|
|
266026
|
+
fileOffset += bytesRead;
|
|
266027
|
+
}
|
|
266028
|
+
if (pending.length > 0) {
|
|
266029
|
+
const line = Buffer.concat(pending);
|
|
266030
|
+
if (lineNo >= startLine) yield decodeTextWithErrors(line, "utf-8", errors, pendingOffset !== 0);
|
|
266031
|
+
}
|
|
266032
|
+
} finally {
|
|
266033
|
+
await fh.close();
|
|
266034
|
+
}
|
|
266035
|
+
}
|
|
266036
|
+
async writeBytes(path, data) {
|
|
266037
|
+
await writeFile(this._resolvePath(path), data);
|
|
266038
|
+
return data.length;
|
|
266039
|
+
}
|
|
266040
|
+
async writeText(path, data, options) {
|
|
266041
|
+
const resolved = this._resolvePath(path);
|
|
266042
|
+
const encoding = options?.encoding ?? "utf-8";
|
|
266043
|
+
if ((options?.mode ?? "w") === "a") await appendFile(resolved, data, encoding);
|
|
266044
|
+
else await writeFile(resolved, data, encoding);
|
|
266045
|
+
return data.length;
|
|
266046
|
+
}
|
|
266047
|
+
async mkdir(path, options) {
|
|
266048
|
+
const resolved = this._resolvePath(path);
|
|
266049
|
+
const parents = options?.parents ?? false;
|
|
266050
|
+
const existOk = options?.existOk ?? false;
|
|
266051
|
+
if (parents) {
|
|
266052
|
+
if (!existOk) try {
|
|
266053
|
+
if ((await stat(resolved)).isDirectory()) throw new KaosFileExistsError(`${resolved} already exists`);
|
|
266054
|
+
} catch (error) {
|
|
266055
|
+
if (error instanceof KaosFileExistsError) throw error;
|
|
266056
|
+
if (error.code !== "ENOENT") throw error;
|
|
266057
|
+
}
|
|
266058
|
+
await mkdir(resolved, { recursive: true });
|
|
266059
|
+
return;
|
|
266060
|
+
}
|
|
266061
|
+
try {
|
|
266062
|
+
await mkdir(resolved);
|
|
266063
|
+
} catch (error) {
|
|
266064
|
+
if (existOk && error instanceof Error && "code" in error && error.code === "EEXIST") {
|
|
266065
|
+
if (!(await stat(resolved)).isDirectory()) throw new KaosFileExistsError(`${resolved} already exists but is not a directory`);
|
|
266066
|
+
return;
|
|
266067
|
+
}
|
|
266068
|
+
throw error;
|
|
266069
|
+
}
|
|
266070
|
+
}
|
|
266071
|
+
async exec(...args) {
|
|
266072
|
+
const command = args[0];
|
|
266073
|
+
if (command === void 0) throw new Error("LocalKaos.exec(): at least one argument (the command to run) is required.");
|
|
266074
|
+
const child = spawn(command, args.slice(1), buildLocalSpawnOptions(isWindows$1, this._cwd, this._buildExecEnv()));
|
|
266075
|
+
await waitForSpawn(child);
|
|
266076
|
+
return new LocalProcess(child);
|
|
266077
|
+
}
|
|
266078
|
+
async execWithEnv(args, env) {
|
|
266079
|
+
const command = args[0];
|
|
266080
|
+
if (command === void 0) throw new Error("LocalKaos.execWithEnv(): at least one argument (the command to run) is required.");
|
|
266081
|
+
const child = spawn(command, args.slice(1), buildLocalSpawnOptions(isWindows$1, this._cwd, this._buildExecEnv(env)));
|
|
266082
|
+
await waitForSpawn(child);
|
|
266083
|
+
return new LocalProcess(child);
|
|
266084
|
+
}
|
|
266085
|
+
_buildExecEnv(invocationEnv) {
|
|
266086
|
+
if (this._envLayers.length === 0) return invocationEnv;
|
|
266087
|
+
const merged = {
|
|
266088
|
+
...process.env,
|
|
266089
|
+
...invocationEnv
|
|
266090
|
+
};
|
|
266091
|
+
for (const layer of this._envLayers) Object.assign(merged, layer);
|
|
266092
|
+
return merged;
|
|
266093
|
+
}
|
|
266094
|
+
};
|
|
266095
|
+
}));
|
|
266096
|
+
var init_current = __esmMin((() => {
|
|
266097
|
+
new AsyncLocalStorage();
|
|
266098
|
+
}));
|
|
266099
|
+
//#endregion
|
|
266100
|
+
//#region ../../packages/kaos/src/index.ts
|
|
266101
|
+
var init_src$1 = __esmMin((() => {
|
|
266102
|
+
init_environment$1();
|
|
266103
|
+
init_errors$1();
|
|
266104
|
+
init_local();
|
|
266105
|
+
init_current();
|
|
266106
|
+
}));
|
|
266107
|
+
//#endregion
|
|
266108
|
+
//#region ../../packages/agent-core/src/agent/turn/historical-tool-projection.ts
|
|
266109
|
+
function identity(stat) {
|
|
266110
|
+
return `${stat.dev}:${stat.ino}:${stat.birthtimeNs}`;
|
|
266111
|
+
}
|
|
266112
|
+
function revision(stat) {
|
|
266113
|
+
return `${identity(stat)}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}:${stat.mode}`;
|
|
266114
|
+
}
|
|
266115
|
+
function canonical(path) {
|
|
266116
|
+
const normalized = resolve(path);
|
|
266117
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
266118
|
+
}
|
|
266119
|
+
async function checkedDirectory(path) {
|
|
266120
|
+
const stat = await lstat(path, { bigint: true });
|
|
266121
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || canonical(await realpath(path)) !== canonical(path)) throw new Error("Unsafe recovery directory");
|
|
266122
|
+
return stat;
|
|
266123
|
+
}
|
|
266124
|
+
async function checkedAncestors(path) {
|
|
266125
|
+
for (let current = resolve(path); current !== parse(current).root; current = dirname(current)) await checkedDirectory(current);
|
|
266126
|
+
}
|
|
266127
|
+
function persistableText(message) {
|
|
266128
|
+
if (message.isError === true || message.content.length !== 1) return void 0;
|
|
266129
|
+
const part = message.content[0];
|
|
266130
|
+
if (part?.type !== "text") return void 0;
|
|
266131
|
+
const text = part.text;
|
|
266132
|
+
if (text.length < MIN_CHARS || text.includes("\0") || RECOVERY_REFERENCE.test(text) || Buffer.from(text, "utf8").toString("utf8") !== text || text.split("\n").some((line) => line.length > 2e3)) return void 0;
|
|
266133
|
+
return text;
|
|
266134
|
+
}
|
|
266135
|
+
var MIN_CHARS, MAX_REFERENCE_CHARS, RECOVERY_REFERENCE, HistoricalToolProjection;
|
|
266136
|
+
var init_historical_tool_projection = __esmMin((() => {
|
|
266137
|
+
init_src$1();
|
|
266138
|
+
init_src$3();
|
|
266139
|
+
init_read();
|
|
266140
|
+
init_file_type$1();
|
|
266141
|
+
init_fs$3();
|
|
266142
|
+
MIN_CHARS = 3e3;
|
|
266143
|
+
MAX_REFERENCE_CHARS = 2500;
|
|
266144
|
+
RECOVERY_REFERENCE = /\[Tool result offloaded\]|\[Repeated tool result omitted\]|^output_path\s*:|Tool output exceeded \d+ characters/m;
|
|
266145
|
+
HistoricalToolProjection = class {
|
|
266146
|
+
agent;
|
|
266147
|
+
files = /* @__PURE__ */ new Map();
|
|
266148
|
+
directories = /* @__PURE__ */ new Map();
|
|
266149
|
+
dirtyDirectories = /* @__PURE__ */ new Set();
|
|
266150
|
+
pending = Promise.resolve();
|
|
266151
|
+
constructor(agent) {
|
|
266152
|
+
this.agent = agent;
|
|
266153
|
+
}
|
|
266154
|
+
async project(history, turnAnchor, tools, context) {
|
|
266155
|
+
const unchanged = {
|
|
266156
|
+
history,
|
|
266157
|
+
changed: false
|
|
266158
|
+
};
|
|
266159
|
+
const boundary = turnAnchor === void 0 ? -1 : history.indexOf(turnAnchor);
|
|
266160
|
+
const read = tools.find((tool) => tool.name === "Read");
|
|
266161
|
+
if (boundary < 0 || !(read instanceof ReadTool) || !(this.agent.kaos instanceof LocalKaos) || this.agent.homedir === void 0 || this.agent.hooks?.hasMatchingHook("PreToolUse", "Read")) return unchanged;
|
|
266162
|
+
const calls = /* @__PURE__ */ new Map();
|
|
266163
|
+
const results = /* @__PURE__ */ new Map();
|
|
266164
|
+
const knownTools = new Set(this.agent.tools.loopTools.map((tool) => tool.name));
|
|
266165
|
+
history.forEach((message, index) => {
|
|
266166
|
+
for (const call of message.toolCalls) {
|
|
266167
|
+
const previous = calls.get(call.id);
|
|
266168
|
+
calls.set(call.id, {
|
|
266169
|
+
index,
|
|
266170
|
+
name: call.name,
|
|
266171
|
+
count: (previous?.count ?? 0) + 1
|
|
266172
|
+
});
|
|
266173
|
+
}
|
|
266174
|
+
if (message.role === "tool" && message.toolCallId !== void 0) {
|
|
266175
|
+
const previous = results.get(message.toolCallId);
|
|
266176
|
+
results.set(message.toolCallId, {
|
|
266177
|
+
index,
|
|
266178
|
+
count: (previous?.count ?? 0) + 1
|
|
266179
|
+
});
|
|
266180
|
+
}
|
|
266181
|
+
});
|
|
266182
|
+
const closedBatches = /* @__PURE__ */ new Set();
|
|
266183
|
+
for (let index = 0; index < boundary; index++) {
|
|
266184
|
+
const message = history[index];
|
|
266185
|
+
if (message.role === "assistant" && message.toolCalls.length > 0 && message.toolCalls.every((call) => {
|
|
266186
|
+
const result = results.get(call.id);
|
|
266187
|
+
return calls.get(call.id)?.count === 1 && result?.count === 1 && result.index > index && result.index < boundary;
|
|
266188
|
+
})) closedBatches.add(index);
|
|
266189
|
+
}
|
|
266190
|
+
const projected = [...history];
|
|
266191
|
+
let changed = false;
|
|
266192
|
+
for (let index = 0; index < boundary; index++) {
|
|
266193
|
+
context.signal.throwIfAborted();
|
|
266194
|
+
const message = history[index];
|
|
266195
|
+
if (message.role !== "tool" || !message.toolCallId) continue;
|
|
266196
|
+
const call = calls.get(message.toolCallId);
|
|
266197
|
+
if (call === void 0 || call.count !== 1 || !closedBatches.has(call.index) || !knownTools.has(call.name)) continue;
|
|
266198
|
+
const text = persistableText(message);
|
|
266199
|
+
if (text === void 0) continue;
|
|
266200
|
+
const digest = createHash("sha256").update(JSON.stringify([
|
|
266201
|
+
canonical(this.agent.homedir),
|
|
266202
|
+
call.name,
|
|
266203
|
+
message.toolCallId,
|
|
266204
|
+
text
|
|
266205
|
+
])).digest("hex");
|
|
266206
|
+
const outputPath = join(resolve(this.agent.homedir), "tool-results", "historical", `${digest}.txt`);
|
|
266207
|
+
const header = `[Tool result offloaded]\noutput_path: ${outputPath}\noutput_size_chars: ${text.length}\nsha256: ${createHash("sha256").update(text).digest("hex")}\nnext_step: Use Read with this path and line_offset/n_lines to recover the complete output.
|
|
266208
|
+
|
|
266209
|
+
[preview]
|
|
266210
|
+
`;
|
|
266211
|
+
if (header.length >= MAX_REFERENCE_CHARS) continue;
|
|
266212
|
+
try {
|
|
266213
|
+
if (!await this.canRead(read, outputPath, context)) continue;
|
|
266214
|
+
const save = this.pending.then(() => this.persist(outputPath, text));
|
|
266215
|
+
this.pending = save.catch(() => void 0);
|
|
266216
|
+
await save;
|
|
266217
|
+
const stat = await this.agent.kaos.stat(outputPath);
|
|
266218
|
+
if ((stat.stMode & 61440) !== 32768 || stat.stSize !== Buffer.byteLength(text)) continue;
|
|
266219
|
+
const bytes = await this.agent.kaos.readBytes(outputPath, Buffer.byteLength(text) + 1);
|
|
266220
|
+
if (!Buffer.from(bytes).equals(Buffer.from(text)) || detectFileType(outputPath, Buffer.from(bytes).subarray(0, 512)).kind !== "text") continue;
|
|
266221
|
+
await this.verifyFile(outputPath, text);
|
|
266222
|
+
context.signal.throwIfAborted();
|
|
266223
|
+
if (!await this.canRead(read, outputPath, context)) continue;
|
|
266224
|
+
let preview = text.slice(0, MAX_REFERENCE_CHARS - header.length);
|
|
266225
|
+
if (/[\uD800-\uDBFF]$/.test(preview)) preview = preview.slice(0, -1);
|
|
266226
|
+
projected[index] = {
|
|
266227
|
+
...message,
|
|
266228
|
+
content: [{
|
|
266229
|
+
type: "text",
|
|
266230
|
+
text: header + preview
|
|
266231
|
+
}]
|
|
266232
|
+
};
|
|
266233
|
+
changed = true;
|
|
266234
|
+
} catch {
|
|
266235
|
+
context.signal.throwIfAborted();
|
|
266236
|
+
}
|
|
266237
|
+
}
|
|
266238
|
+
try {
|
|
266239
|
+
for (const path of this.dirtyDirectories) await this.verifyDirectory(path);
|
|
266240
|
+
this.dirtyDirectories.clear();
|
|
266241
|
+
} catch {
|
|
266242
|
+
return unchanged;
|
|
266243
|
+
}
|
|
266244
|
+
return {
|
|
266245
|
+
history: changed ? projected : history,
|
|
266246
|
+
changed
|
|
266247
|
+
};
|
|
266248
|
+
}
|
|
266249
|
+
async canRead(read, path, context) {
|
|
266250
|
+
if (this.agent.tools.loopTools.find((tool) => tool.name === "Read") !== read || this.agent.hooks?.hasMatchingHook("PreToolUse", "Read")) return false;
|
|
266251
|
+
const args = {
|
|
266252
|
+
path,
|
|
266253
|
+
line_offset: 1,
|
|
266254
|
+
n_lines: 1e3
|
|
266255
|
+
};
|
|
266256
|
+
const execution = read.resolveExecution(args);
|
|
266257
|
+
if ("isError" in execution) return false;
|
|
266258
|
+
const toolCall = {
|
|
266259
|
+
type: "function",
|
|
266260
|
+
id: "historical-recovery-check",
|
|
266261
|
+
name: "Read",
|
|
266262
|
+
arguments: JSON.stringify(args)
|
|
266263
|
+
};
|
|
266264
|
+
for (const policy of this.agent.permission.policies) {
|
|
266265
|
+
const result = await policy.evaluate({
|
|
266266
|
+
...context,
|
|
266267
|
+
tool: read,
|
|
266268
|
+
toolCall,
|
|
266269
|
+
toolCalls: [toolCall],
|
|
266270
|
+
args,
|
|
266271
|
+
execution
|
|
266272
|
+
});
|
|
266273
|
+
if (result !== void 0) return result.kind === "approve" && result.executionMetadata === void 0;
|
|
266274
|
+
}
|
|
266275
|
+
return false;
|
|
266276
|
+
}
|
|
266277
|
+
async ensureDirectory(path) {
|
|
266278
|
+
let created = false;
|
|
266279
|
+
try {
|
|
266280
|
+
await mkdir(path, { mode: 448 });
|
|
266281
|
+
created = true;
|
|
266282
|
+
} catch (error) {
|
|
266283
|
+
if (error.code !== "EEXIST") throw error;
|
|
266284
|
+
}
|
|
266285
|
+
if (created && this.directories.has(dirname(path))) this.dirtyDirectories.add(dirname(path));
|
|
266286
|
+
const stat = await checkedDirectory(path);
|
|
266287
|
+
const previous = this.directories.get(path);
|
|
266288
|
+
if (previous !== void 0 && previous.identity !== identity(stat)) throw new Error("Recovery directory replaced");
|
|
266289
|
+
if (previous === void 0) {
|
|
266290
|
+
if (created) await hardenPrivatePath(path, "directory");
|
|
266291
|
+
await this.verifyDirectory(path);
|
|
266292
|
+
} else if (previous.revision !== revision(stat) && !this.dirtyDirectories.has(path)) await this.verifyDirectory(path);
|
|
266293
|
+
}
|
|
266294
|
+
async verifyDirectory(path) {
|
|
266295
|
+
const before = await checkedDirectory(path);
|
|
266296
|
+
const previous = this.directories.get(path);
|
|
266297
|
+
if (previous !== void 0 && previous.identity !== identity(before)) throw new Error("Recovery directory replaced");
|
|
266298
|
+
if (process.platform === "win32") new WindowsPrivateAcl().verify(path, "directory");
|
|
266299
|
+
else if ((before.mode & 63n) !== 0n) throw new Error("Recovery directory is not private");
|
|
266300
|
+
const after = await checkedDirectory(path);
|
|
266301
|
+
if (revision(before) !== revision(after)) throw new Error("Recovery directory changed during verification");
|
|
266302
|
+
this.directories.set(path, {
|
|
266303
|
+
identity: identity(after),
|
|
266304
|
+
revision: revision(after)
|
|
266305
|
+
});
|
|
266306
|
+
}
|
|
266307
|
+
async persist(path, text) {
|
|
266308
|
+
await checkedAncestors(dirname(dirname(dirname(path))));
|
|
266309
|
+
await this.ensureDirectory(dirname(dirname(path)));
|
|
266310
|
+
await this.ensureDirectory(dirname(path));
|
|
266311
|
+
if (this.files.has(path)) {
|
|
266312
|
+
await this.verifyFile(path, text);
|
|
266313
|
+
return;
|
|
266314
|
+
}
|
|
266315
|
+
try {
|
|
266316
|
+
await lstat(path);
|
|
266317
|
+
await this.verifyFile(path, text);
|
|
266318
|
+
if (process.platform === "win32") new WindowsPrivateAcl().verify(path, "file");
|
|
266319
|
+
this.files.set(path, revision(await lstat(path, { bigint: true })));
|
|
266320
|
+
return;
|
|
266321
|
+
} catch (error) {
|
|
266322
|
+
if (error.code !== "ENOENT") throw error;
|
|
266323
|
+
}
|
|
266324
|
+
const staging = join(dirname(path), `${randomUUID()}.staging`);
|
|
266325
|
+
this.dirtyDirectories.add(dirname(path));
|
|
266326
|
+
try {
|
|
266327
|
+
await atomicWritePrivate$1(staging, text);
|
|
266328
|
+
await checkedAncestors(dirname(path));
|
|
266329
|
+
await link(staging, path);
|
|
266330
|
+
} finally {
|
|
266331
|
+
await unlink(staging).catch(() => void 0);
|
|
266332
|
+
}
|
|
266333
|
+
await this.verifyFile(path, text);
|
|
266334
|
+
this.files.set(path, revision(await lstat(path, { bigint: true })));
|
|
266335
|
+
}
|
|
266336
|
+
async verifyFile(path, text) {
|
|
266337
|
+
const before = await lstat(path, { bigint: true });
|
|
266338
|
+
if (!before.isFile() || before.isSymbolicLink() || before.nlink !== 1n || before.size !== BigInt(Buffer.byteLength(text)) || process.platform !== "win32" && (before.mode & 63n) !== 0n || canonical(await realpath(path)) !== canonical(path)) throw new Error("Unsafe recovery artifact");
|
|
266339
|
+
const previous = this.files.get(path);
|
|
266340
|
+
if (previous !== void 0 && previous !== revision(before)) throw new Error("Recovery artifact changed");
|
|
266341
|
+
const handle = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
266342
|
+
try {
|
|
266343
|
+
if (revision(await handle.stat({ bigint: true })) !== revision(before) || !(await handle.readFile()).equals(Buffer.from(text)) || revision(await handle.stat({ bigint: true })) !== revision(before) || revision(await lstat(path, { bigint: true })) !== revision(before) || canonical(await realpath(path)) !== canonical(path)) throw new Error("Recovery artifact changed");
|
|
266344
|
+
} finally {
|
|
266345
|
+
await handle.close();
|
|
266346
|
+
}
|
|
266347
|
+
}
|
|
266348
|
+
};
|
|
266349
|
+
}));
|
|
266350
|
+
//#endregion
|
|
264970
266351
|
//#region ../../packages/agent-core/src/agent/response-review-decision.ts
|
|
264971
266352
|
/** Never carry provider/guard prose, signed receipts or arbitrary codes into logs. */
|
|
264972
266353
|
function rejectionFindings(report) {
|
|
@@ -265611,6 +266992,7 @@ var init_turn = __esmMin((() => {
|
|
|
265611
266992
|
init_canonical_args();
|
|
265612
266993
|
init_tool_dedup();
|
|
265613
266994
|
init_tool_result_budget();
|
|
266995
|
+
init_historical_tool_projection();
|
|
265614
266996
|
init_response_review();
|
|
265615
266997
|
import_turn_tool_performance_policy = require_turn_tool_performance_policy();
|
|
265616
266998
|
BLUN_LEAN_TOOL_NAMES = new Set([
|
|
@@ -265693,8 +267075,10 @@ var init_turn = __esmMin((() => {
|
|
|
265693
267075
|
stepFailureByTurn = /* @__PURE__ */ new Map();
|
|
265694
267076
|
providerRequestFailureByTurn = /* @__PURE__ */ new Map();
|
|
265695
267077
|
currentStep = 0;
|
|
267078
|
+
historicalToolProjection;
|
|
265696
267079
|
constructor(agent) {
|
|
265697
267080
|
this.agent = agent;
|
|
267081
|
+
this.historicalToolProjection = new HistoricalToolProjection(agent);
|
|
265698
267082
|
}
|
|
265699
267083
|
/** Explicit host shutdown only; ordinary cancellation remains resumable. */
|
|
265700
267084
|
closeAdmission() {
|
|
@@ -266100,7 +267484,10 @@ var init_turn = __esmMin((() => {
|
|
|
266100
267484
|
turnId,
|
|
266101
267485
|
origin
|
|
266102
267486
|
});
|
|
267487
|
+
const previousLastMessage = this.agent.context.history.at(-1);
|
|
266103
267488
|
this.agent.context.appendUserMessage(input, origin);
|
|
267489
|
+
const appendedMessage = this.agent.context.history.at(-1);
|
|
267490
|
+
const turnAnchor = appendedMessage !== previousLastMessage && appendedMessage?.role === "user" && appendedMessage.origin === origin ? appendedMessage : void 0;
|
|
266104
267491
|
const startedAt = Date.now();
|
|
266105
267492
|
let ended;
|
|
266106
267493
|
let blockedByUserPromptHook = false;
|
|
@@ -266127,7 +267514,7 @@ var init_turn = __esmMin((() => {
|
|
|
266127
267514
|
this.agent.log.warn("Turn-start observation failed");
|
|
266128
267515
|
}
|
|
266129
267516
|
signal.throwIfAborted();
|
|
266130
|
-
const stopReason = await this.runStepLoop(turnId, signal, input, origin, (response) => {
|
|
267517
|
+
const stopReason = await this.runStepLoop(turnId, signal, input, origin, turnAnchor, (response) => {
|
|
266131
267518
|
completedResponse = response;
|
|
266132
267519
|
});
|
|
266133
267520
|
completedStopReason = stopReason;
|
|
@@ -266292,7 +267679,7 @@ var init_turn = __esmMin((() => {
|
|
|
266292
267679
|
content: hookResult.message
|
|
266293
267680
|
});
|
|
266294
267681
|
}
|
|
266295
|
-
async runStepLoop(turnId, signal, input, origin, onFinalResponse) {
|
|
267682
|
+
async runStepLoop(turnId, signal, input, origin, turnAnchor, onFinalResponse) {
|
|
266296
267683
|
let stopHookContinuationUsed = false;
|
|
266297
267684
|
let goalOutcomeMessageContinuationUsed = false;
|
|
266298
267685
|
const deduper = new ToolCallDeduplicator({ telemetry: this.agent.telemetry });
|
|
@@ -266305,11 +267692,25 @@ var init_turn = __esmMin((() => {
|
|
|
266305
267692
|
let taskToolsNeeded = turnNeedsThinking || blunTurnHasAttachment(input);
|
|
266306
267693
|
const greetingAnchor = this.agent.context.history.findLast((message) => message.role === "user" && message.origin?.kind === "user");
|
|
266307
267694
|
let partialContext = false;
|
|
266308
|
-
const turnHistory = () => {
|
|
266309
|
-
const
|
|
267695
|
+
const turnHistory = async (tools) => {
|
|
267696
|
+
const raw = [...this.agent.context.history];
|
|
267697
|
+
const history = personalMemoryRecall === void 0 ? raw : [...raw, personalMemoryRecall];
|
|
266310
267698
|
const selected = taskToolsNeeded ? history : greetingHistory(history, greetingAnchor);
|
|
266311
|
-
|
|
266312
|
-
|
|
267699
|
+
const projected = await this.historicalToolProjection.project(selected, turnAnchor, tools, {
|
|
267700
|
+
turnId: String(turnId),
|
|
267701
|
+
stepNumber: this.currentStep,
|
|
267702
|
+
signal,
|
|
267703
|
+
llm: turnLLM
|
|
267704
|
+
});
|
|
267705
|
+
const current = this.agent.context.history;
|
|
267706
|
+
if (current.length !== raw.length || current.some((message, index) => message !== raw[index])) {
|
|
267707
|
+
const latest = personalMemoryRecall === void 0 ? current : [...current, personalMemoryRecall];
|
|
267708
|
+
const retained = taskToolsNeeded ? latest : greetingHistory(latest, greetingAnchor);
|
|
267709
|
+
partialContext = retained.length < latest.length;
|
|
267710
|
+
return retained;
|
|
267711
|
+
}
|
|
267712
|
+
partialContext = selected.length < history.length || projected.changed;
|
|
267713
|
+
return projected.history;
|
|
266313
267714
|
};
|
|
266314
267715
|
const turnLLM = this.agent.llmForTurn(turnNeedsThinking ? void 0 : "off");
|
|
266315
267716
|
const turnTools = this.agent.tools.captureTurnTools();
|
|
@@ -266345,8 +267746,8 @@ var init_turn = __esmMin((() => {
|
|
|
266345
267746
|
}, completion);
|
|
266346
267747
|
},
|
|
266347
267748
|
llm: turnLLM,
|
|
266348
|
-
buildMessages: () => this.agent.context.project(turnHistory(), { dropOrphanResults: true }),
|
|
266349
|
-
buildMessagesStrict: () => this.agent.context.project(turnHistory(), {
|
|
267749
|
+
buildMessages: async () => this.agent.context.project(await turnHistory(selectedTools), { dropOrphanResults: true }),
|
|
267750
|
+
buildMessagesStrict: async () => this.agent.context.project(await turnHistory(selectedTools), {
|
|
266350
267751
|
synthesizeMissing: true,
|
|
266351
267752
|
dropOrphanResults: true,
|
|
266352
267753
|
dedupeDuplicateToolCalls: true,
|
|
@@ -269581,6 +270982,7 @@ async function reviewNativeResponse(agent, language, input) {
|
|
|
269581
270982
|
});
|
|
269582
270983
|
};
|
|
269583
270984
|
const valueType = (value) => value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
270985
|
+
let formatRetry = false;
|
|
269584
270986
|
for (let attempt = 1; attempt <= 2; attempt += 1) {
|
|
269585
270987
|
let receivedBytes = 0;
|
|
269586
270988
|
let outputLimitExceeded = false;
|
|
@@ -269597,7 +270999,7 @@ async function reviewNativeResponse(agent, language, input) {
|
|
|
269597
270999
|
attempt: `${attempt}/2`
|
|
269598
271000
|
}
|
|
269599
271001
|
};
|
|
269600
|
-
const prompt =
|
|
271002
|
+
const prompt = !formatRetry ? NATIVE_REVIEW_PROMPT : `${NATIVE_REVIEW_PROMPT}\nThe previous review did not satisfy the required JSON format. Return exactly one object with the two required boolean keys nativeness and orthography, no wrapper or additional keys. Use true or false, not strings or null. Set a check to false if it fails or you are uncertain.`;
|
|
269601
271003
|
try {
|
|
269602
271004
|
signal.throwIfAborted();
|
|
269603
271005
|
const requested = agent.generate(provider, prompt, [], [createUserMessage(candidateMessage)], { onMessagePart: (part) => {
|
|
@@ -269622,7 +271024,10 @@ async function reviewNativeResponse(agent, language, input) {
|
|
|
269622
271024
|
parsed = JSON.parse(text);
|
|
269623
271025
|
} catch {
|
|
269624
271026
|
record("invalid_json", attempt);
|
|
269625
|
-
if (attempt === 1)
|
|
271027
|
+
if (attempt === 1) {
|
|
271028
|
+
formatRetry = true;
|
|
271029
|
+
continue;
|
|
271030
|
+
}
|
|
269626
271031
|
return;
|
|
269627
271032
|
}
|
|
269628
271033
|
if (!isPlainRecord(parsed) || Object.keys(parsed).length !== 2 || typeof parsed["nativeness"] !== "boolean" || typeof parsed["orthography"] !== "boolean") {
|
|
@@ -269633,7 +271038,10 @@ async function reviewNativeResponse(agent, language, input) {
|
|
|
269633
271038
|
nativenessType: fields ? valueType(fields["nativeness"]) : "missing",
|
|
269634
271039
|
orthographyType: fields ? valueType(fields["orthography"]) : "missing"
|
|
269635
271040
|
});
|
|
269636
|
-
if (attempt === 1 && !(fields && (fields["nativeness"] === false || fields["orthography"] === false)))
|
|
271041
|
+
if (attempt === 1 && !(fields && (fields["nativeness"] === false || fields["orthography"] === false))) {
|
|
271042
|
+
formatRetry = true;
|
|
271043
|
+
continue;
|
|
271044
|
+
}
|
|
269637
271045
|
return;
|
|
269638
271046
|
}
|
|
269639
271047
|
record(parsed["nativeness"] && parsed["orthography"] ? "approved" : "native_rejected", attempt, {
|
|
@@ -269644,7 +271052,12 @@ async function reviewNativeResponse(agent, language, input) {
|
|
|
269644
271052
|
nativeness: parsed["nativeness"],
|
|
269645
271053
|
orthography: parsed["orthography"]
|
|
269646
271054
|
};
|
|
269647
|
-
} catch {
|
|
271055
|
+
} catch (error) {
|
|
271056
|
+
if (!signal.aborted && !outputLimitExceeded && attempt === 1 && error instanceof APIProviderRateLimitError) {
|
|
271057
|
+
record("rate_limited", attempt);
|
|
271058
|
+
if (error.retryAfterMs === null) await agent.providerRetryWait.defer(providerRetryScope(modelAlias, provider), 1e3);
|
|
271059
|
+
continue;
|
|
271060
|
+
}
|
|
269648
271061
|
return invalid(signal.aborted ? "cancelled" : outputLimitExceeded ? "output_limit" : "generation_failed");
|
|
269649
271062
|
}
|
|
269650
271063
|
}
|
|
@@ -269699,6 +271112,7 @@ var init_response_review_host = __esmMin((() => {
|
|
|
269699
271112
|
init_src$4();
|
|
269700
271113
|
init_abort();
|
|
269701
271114
|
init_response_review_mcp();
|
|
271115
|
+
init_provider_retry_wait();
|
|
269702
271116
|
init_canonical_args();
|
|
269703
271117
|
NATIVE_REVIEW_PROMPT = [
|
|
269704
271118
|
"You are an independent language reviewer, not the author of the candidate.",
|
|
@@ -334226,1138 +335640,6 @@ var init_blun_media = __esmMin((() => {
|
|
|
334226
335640
|
};
|
|
334227
335641
|
}));
|
|
334228
335642
|
//#endregion
|
|
334229
|
-
//#region ../../packages/kaos/src/errors.ts
|
|
334230
|
-
var KaosError, KaosFileExistsError, KaosShellNotFoundError;
|
|
334231
|
-
var init_errors$1 = __esmMin((() => {
|
|
334232
|
-
KaosError = class extends Error {
|
|
334233
|
-
constructor(message) {
|
|
334234
|
-
super(message);
|
|
334235
|
-
this.name = "KaosError";
|
|
334236
|
-
}
|
|
334237
|
-
};
|
|
334238
|
-
KaosFileExistsError = class extends KaosError {
|
|
334239
|
-
constructor(message) {
|
|
334240
|
-
super(message);
|
|
334241
|
-
this.name = "KaosFileExistsError";
|
|
334242
|
-
}
|
|
334243
|
-
};
|
|
334244
|
-
KaosShellNotFoundError = class extends KaosError {
|
|
334245
|
-
constructor(message) {
|
|
334246
|
-
super(message);
|
|
334247
|
-
this.name = "KaosShellNotFoundError";
|
|
334248
|
-
}
|
|
334249
|
-
};
|
|
334250
|
-
}));
|
|
334251
|
-
//#endregion
|
|
334252
|
-
//#region ../../packages/kaos/src/environment.ts
|
|
334253
|
-
/**
|
|
334254
|
-
* Environment — cross-platform probe of OS / shell.
|
|
334255
|
-
*
|
|
334256
|
-
* Detection is a pure function of injected probes (`platform` / `arch` /
|
|
334257
|
-
* `release` / `env` / `isFile` / `execFileText`) so the same suite runs
|
|
334258
|
-
* identically on any host OS. `detectEnvironmentFromNode()` bundles the Node
|
|
334259
|
-
* defaults for production callers.
|
|
334260
|
-
*
|
|
334261
|
-
* On Windows the probe expects Git Bash (the canonical POSIX shell that
|
|
334262
|
-
* ships with Git for Windows). If it cannot be located the function
|
|
334263
|
-
* throws `KaosShellNotFoundError`; the SDK layer can wrap that into a
|
|
334264
|
-
* user-facing install hint. Set `BLUN_SHELL_PATH` to override.
|
|
334265
|
-
*/
|
|
334266
|
-
function resolveOsKind(platform) {
|
|
334267
|
-
switch (platform) {
|
|
334268
|
-
case "darwin": return "macOS";
|
|
334269
|
-
case "linux": return "Linux";
|
|
334270
|
-
case "win32": return "Windows";
|
|
334271
|
-
default: return platform;
|
|
334272
|
-
}
|
|
334273
|
-
}
|
|
334274
|
-
async function detectEnvironment(deps) {
|
|
334275
|
-
const osKind = resolveOsKind(deps.platform);
|
|
334276
|
-
const osArch = deps.arch;
|
|
334277
|
-
const osVersion = deps.release;
|
|
334278
|
-
if (deps.platform === "win32") return {
|
|
334279
|
-
osKind,
|
|
334280
|
-
osArch,
|
|
334281
|
-
osVersion,
|
|
334282
|
-
shellName: "bash",
|
|
334283
|
-
shellPath: await locateWindowsGitBash(deps)
|
|
334284
|
-
};
|
|
334285
|
-
const candidates = [
|
|
334286
|
-
"/bin/bash",
|
|
334287
|
-
"/usr/bin/bash",
|
|
334288
|
-
"/usr/local/bin/bash"
|
|
334289
|
-
];
|
|
334290
|
-
let found;
|
|
334291
|
-
for (const p of candidates) if (await deps.isFile(p)) {
|
|
334292
|
-
found = p;
|
|
334293
|
-
break;
|
|
334294
|
-
}
|
|
334295
|
-
if (found !== void 0) return {
|
|
334296
|
-
osKind,
|
|
334297
|
-
osArch,
|
|
334298
|
-
osVersion,
|
|
334299
|
-
shellName: "bash",
|
|
334300
|
-
shellPath: found
|
|
334301
|
-
};
|
|
334302
|
-
return {
|
|
334303
|
-
osKind,
|
|
334304
|
-
osArch,
|
|
334305
|
-
osVersion,
|
|
334306
|
-
shellName: "sh",
|
|
334307
|
-
shellPath: "/bin/sh"
|
|
334308
|
-
};
|
|
334309
|
-
}
|
|
334310
|
-
async function locateWindowsGitBash(deps) {
|
|
334311
|
-
const checked = [];
|
|
334312
|
-
const override = deps.env["BLUN_SHELL_PATH"]?.trim();
|
|
334313
|
-
if (override !== void 0 && override.length > 0) {
|
|
334314
|
-
checked.push(override);
|
|
334315
|
-
if (await deps.isFile(override)) return override;
|
|
334316
|
-
}
|
|
334317
|
-
const gitExecutables = await findExecutablesOnPath("git.exe", deps.env["PATH"], deps.platform, deps.isFile);
|
|
334318
|
-
for (const gitExe of gitExecutables) {
|
|
334319
|
-
const inferred = gitBashCandidatesFromGitExe(gitExe);
|
|
334320
|
-
if (inferred !== void 0) for (const candidate of inferred) {
|
|
334321
|
-
checked.push(candidate);
|
|
334322
|
-
if (await deps.isFile(candidate)) return candidate;
|
|
334323
|
-
}
|
|
334324
|
-
const gitExecPath = await readGitExecPath(deps, gitExe);
|
|
334325
|
-
if (gitExecPath === void 0) continue;
|
|
334326
|
-
for (const candidate of gitBashCandidatesFromGitExecPath(gitExecPath)) {
|
|
334327
|
-
checked.push(candidate);
|
|
334328
|
-
if (await deps.isFile(candidate)) return candidate;
|
|
334329
|
-
}
|
|
334330
|
-
}
|
|
334331
|
-
const candidates = [
|
|
334332
|
-
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
334333
|
-
"C:\\Program Files\\Git\\usr\\bin\\bash.exe",
|
|
334334
|
-
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
|
|
334335
|
-
"C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe"
|
|
334336
|
-
];
|
|
334337
|
-
const localAppData = deps.env["LOCALAPPDATA"]?.trim();
|
|
334338
|
-
if (localAppData !== void 0 && localAppData.length > 0) {
|
|
334339
|
-
candidates.push(`${localAppData}\\Programs\\Git\\bin\\bash.exe`);
|
|
334340
|
-
candidates.push(`${localAppData}\\Programs\\Git\\usr\\bin\\bash.exe`);
|
|
334341
|
-
}
|
|
334342
|
-
for (const candidate of candidates) {
|
|
334343
|
-
checked.push(candidate);
|
|
334344
|
-
if (await deps.isFile(candidate)) return candidate;
|
|
334345
|
-
}
|
|
334346
|
-
throw new KaosShellNotFoundError(`Git Bash was not found on this Windows host. Install Git for Windows from https://gitforwindows.org/ or set BLUN_SHELL_PATH to a bash.exe. Checked: ${checked.join(", ")}.`);
|
|
334347
|
-
}
|
|
334348
|
-
async function readGitExecPath(deps, gitExe) {
|
|
334349
|
-
if (deps.platform === "win32" && !isAbsoluteWindowsPath(gitExe)) return void 0;
|
|
334350
|
-
const stdout = await deps.execFileText(gitExe, ["--exec-path"], GIT_EXEC_PATH_TIMEOUT_MS);
|
|
334351
|
-
if (stdout === void 0) return void 0;
|
|
334352
|
-
for (const line of stdout.split(/\r?\n/)) {
|
|
334353
|
-
const execPath = line.trim();
|
|
334354
|
-
if (execPath.length > 0) return execPath;
|
|
334355
|
-
}
|
|
334356
|
-
}
|
|
334357
|
-
function gitBashCandidatesFromGitExe(gitExe) {
|
|
334358
|
-
const normalizedGitExe = path$18.win32.normalize(normalizeWindowsPath(gitExe));
|
|
334359
|
-
const gitDir = path$18.win32.dirname(normalizedGitExe);
|
|
334360
|
-
const gitDirName = path$18.win32.basename(gitDir).toLowerCase();
|
|
334361
|
-
if (gitDirName !== "cmd" && gitDirName !== "bin") return;
|
|
334362
|
-
return gitBashCandidatesFromGitRoot(path$18.win32.dirname(gitDir));
|
|
334363
|
-
}
|
|
334364
|
-
function gitBashCandidatesFromGitExecPath(execPath) {
|
|
334365
|
-
const normalized = path$18.win32.normalize(normalizeWindowsPath(execPath));
|
|
334366
|
-
const parts = normalized.split("\\");
|
|
334367
|
-
for (let i = parts.length - 1; i >= 0; i -= 1) {
|
|
334368
|
-
const segment = parts[i]?.toLowerCase();
|
|
334369
|
-
if (segment === "mingw32" || segment === "mingw64") {
|
|
334370
|
-
const root = parts.slice(0, i).join("\\");
|
|
334371
|
-
if (root.length > 0) return gitBashCandidatesFromGitRoot(root);
|
|
334372
|
-
}
|
|
334373
|
-
}
|
|
334374
|
-
return gitBashCandidatesFromGitRoot(path$18.win32.join(normalized, "..", ".."));
|
|
334375
|
-
}
|
|
334376
|
-
function gitBashCandidatesFromGitRoot(root) {
|
|
334377
|
-
return [path$18.win32.normalize(path$18.win32.join(root, "bin", "bash.exe")), path$18.win32.normalize(path$18.win32.join(root, "usr", "bin", "bash.exe"))];
|
|
334378
|
-
}
|
|
334379
|
-
function normalizeWindowsPath(path) {
|
|
334380
|
-
return path.replaceAll("/", "\\");
|
|
334381
|
-
}
|
|
334382
|
-
function isAbsoluteWindowsPath(path) {
|
|
334383
|
-
return path$18.win32.isAbsolute(normalizeWindowsPath(path));
|
|
334384
|
-
}
|
|
334385
|
-
function dedupeWindowsPaths(paths) {
|
|
334386
|
-
const deduped = [];
|
|
334387
|
-
const seen = /* @__PURE__ */ new Set();
|
|
334388
|
-
for (const path of paths) {
|
|
334389
|
-
const key = normalizeWindowsPath(path).toLowerCase();
|
|
334390
|
-
if (seen.has(key)) continue;
|
|
334391
|
-
seen.add(key);
|
|
334392
|
-
deduped.push(path);
|
|
334393
|
-
}
|
|
334394
|
-
return deduped;
|
|
334395
|
-
}
|
|
334396
|
-
function detectEnvironmentFromNode() {
|
|
334397
|
-
if (detectedEnvironment !== void 0) return detectedEnvironment;
|
|
334398
|
-
const platform = process.platform;
|
|
334399
|
-
const env = process.env;
|
|
334400
|
-
const isFile = async (path) => {
|
|
334401
|
-
try {
|
|
334402
|
-
await access(path, constants.F_OK);
|
|
334403
|
-
return true;
|
|
334404
|
-
} catch {
|
|
334405
|
-
return false;
|
|
334406
|
-
}
|
|
334407
|
-
};
|
|
334408
|
-
detectedEnvironment = detectEnvironment({
|
|
334409
|
-
platform,
|
|
334410
|
-
arch: process.arch,
|
|
334411
|
-
release: nodeOs.release(),
|
|
334412
|
-
env,
|
|
334413
|
-
isFile,
|
|
334414
|
-
execFileText: execFileText$1
|
|
334415
|
-
});
|
|
334416
|
-
return detectedEnvironment;
|
|
334417
|
-
}
|
|
334418
|
-
async function findExecutablesOnPath(name, pathEnv, platform, isFile) {
|
|
334419
|
-
if (pathEnv === void 0 || pathEnv.length === 0) return [];
|
|
334420
|
-
const listSep = platform === "win32" ? ";" : ":";
|
|
334421
|
-
const dirSep = platform === "win32" ? "\\" : "/";
|
|
334422
|
-
const paths = [];
|
|
334423
|
-
for (const rawDir of pathEnv.split(listSep)) {
|
|
334424
|
-
const dir = rawDir.trim();
|
|
334425
|
-
if (dir.length === 0) continue;
|
|
334426
|
-
if (platform === "win32" && !isAbsoluteWindowsPath(dir)) continue;
|
|
334427
|
-
const candidate = dir.endsWith(dirSep) ? `${dir}${name}` : `${dir}${dirSep}${name}`;
|
|
334428
|
-
if (await isFile(candidate)) paths.push(candidate);
|
|
334429
|
-
}
|
|
334430
|
-
return platform === "win32" ? dedupeWindowsPaths(paths) : paths;
|
|
334431
|
-
}
|
|
334432
|
-
async function execFileText$1(file, args, timeoutMs) {
|
|
334433
|
-
return new Promise((resolve) => {
|
|
334434
|
-
execFile(file, [...args], {
|
|
334435
|
-
encoding: "utf8",
|
|
334436
|
-
timeout: timeoutMs,
|
|
334437
|
-
windowsHide: true
|
|
334438
|
-
}, (error, stdout) => {
|
|
334439
|
-
if (error !== null) {
|
|
334440
|
-
resolve(void 0);
|
|
334441
|
-
return;
|
|
334442
|
-
}
|
|
334443
|
-
resolve(stdout);
|
|
334444
|
-
});
|
|
334445
|
-
});
|
|
334446
|
-
}
|
|
334447
|
-
var GIT_EXEC_PATH_TIMEOUT_MS, detectedEnvironment;
|
|
334448
|
-
var init_environment$1 = __esmMin((() => {
|
|
334449
|
-
init_errors$1();
|
|
334450
|
-
GIT_EXEC_PATH_TIMEOUT_MS = 5e3;
|
|
334451
|
-
}));
|
|
334452
|
-
//#endregion
|
|
334453
|
-
//#region ../../packages/kaos/src/internal.ts
|
|
334454
|
-
function isUtf8Continuation(byte) {
|
|
334455
|
-
return byte >= 128 && byte <= 191;
|
|
334456
|
-
}
|
|
334457
|
-
function decodeUtf8Ignore(data) {
|
|
334458
|
-
let output = "";
|
|
334459
|
-
let i = 0;
|
|
334460
|
-
while (i < data.length) {
|
|
334461
|
-
const b0 = data[i];
|
|
334462
|
-
if (b0 === void 0) break;
|
|
334463
|
-
if (b0 <= 127) {
|
|
334464
|
-
output += String.fromCodePoint(b0);
|
|
334465
|
-
i += 1;
|
|
334466
|
-
continue;
|
|
334467
|
-
}
|
|
334468
|
-
if (b0 >= 194 && b0 <= 223) {
|
|
334469
|
-
const b1 = data[i + 1];
|
|
334470
|
-
if (b1 !== void 0 && isUtf8Continuation(b1)) {
|
|
334471
|
-
output += String.fromCodePoint((b0 & 31) << 6 | b1 & 63);
|
|
334472
|
-
i += 2;
|
|
334473
|
-
continue;
|
|
334474
|
-
}
|
|
334475
|
-
i += 1;
|
|
334476
|
-
continue;
|
|
334477
|
-
}
|
|
334478
|
-
if (b0 >= 224 && b0 <= 239) {
|
|
334479
|
-
const b1 = data[i + 1];
|
|
334480
|
-
const b2 = data[i + 2];
|
|
334481
|
-
if (b1 !== void 0 && (b0 === 224 && b1 >= 160 && b1 <= 191 || b0 >= 225 && b0 <= 236 && isUtf8Continuation(b1) || b0 === 237 && b1 >= 128 && b1 <= 159 || b0 >= 238 && b0 <= 239 && isUtf8Continuation(b1)) && b2 !== void 0 && isUtf8Continuation(b2)) {
|
|
334482
|
-
output += String.fromCodePoint((b0 & 15) << 12 | (b1 & 63) << 6 | b2 & 63);
|
|
334483
|
-
i += 3;
|
|
334484
|
-
continue;
|
|
334485
|
-
}
|
|
334486
|
-
i += 1;
|
|
334487
|
-
continue;
|
|
334488
|
-
}
|
|
334489
|
-
if (b0 >= 240 && b0 <= 244) {
|
|
334490
|
-
const b1 = data[i + 1];
|
|
334491
|
-
const b2 = data[i + 2];
|
|
334492
|
-
const b3 = data[i + 3];
|
|
334493
|
-
if (b1 !== void 0 && (b0 === 240 && b1 >= 144 && b1 <= 191 || b0 >= 241 && b0 <= 243 && isUtf8Continuation(b1) || b0 === 244 && b1 >= 128 && b1 <= 143) && b2 !== void 0 && b3 !== void 0 && isUtf8Continuation(b2) && isUtf8Continuation(b3)) {
|
|
334494
|
-
output += String.fromCodePoint((b0 & 7) << 18 | (b1 & 63) << 12 | (b2 & 63) << 6 | b3 & 63);
|
|
334495
|
-
i += 4;
|
|
334496
|
-
continue;
|
|
334497
|
-
}
|
|
334498
|
-
i += 1;
|
|
334499
|
-
continue;
|
|
334500
|
-
}
|
|
334501
|
-
i += 1;
|
|
334502
|
-
}
|
|
334503
|
-
return output;
|
|
334504
|
-
}
|
|
334505
|
-
function decodeUtf16LeIgnore(data) {
|
|
334506
|
-
let output = "";
|
|
334507
|
-
let i = 0;
|
|
334508
|
-
while (i + 1 < data.length) {
|
|
334509
|
-
const first = data[i];
|
|
334510
|
-
const second = data[i + 1];
|
|
334511
|
-
if (first === void 0 || second === void 0) break;
|
|
334512
|
-
const codeUnit = first | second << 8;
|
|
334513
|
-
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
334514
|
-
const lowFirst = data[i + 2];
|
|
334515
|
-
const lowSecond = data[i + 3];
|
|
334516
|
-
if (lowFirst !== void 0 && lowSecond !== void 0) {
|
|
334517
|
-
const low = lowFirst | lowSecond << 8;
|
|
334518
|
-
if (low >= 56320 && low <= 57343) {
|
|
334519
|
-
const codePoint = 65536 + (codeUnit - 55296 << 10) + (low - 56320);
|
|
334520
|
-
output += String.fromCodePoint(codePoint);
|
|
334521
|
-
i += 4;
|
|
334522
|
-
continue;
|
|
334523
|
-
}
|
|
334524
|
-
}
|
|
334525
|
-
i += 2;
|
|
334526
|
-
continue;
|
|
334527
|
-
}
|
|
334528
|
-
if (codeUnit >= 56320 && codeUnit <= 57343) {
|
|
334529
|
-
i += 2;
|
|
334530
|
-
continue;
|
|
334531
|
-
}
|
|
334532
|
-
output += String.fromCodePoint(codeUnit);
|
|
334533
|
-
i += 2;
|
|
334534
|
-
}
|
|
334535
|
-
return output;
|
|
334536
|
-
}
|
|
334537
|
-
/**
|
|
334538
|
-
* Decode a Buffer into a string with Python-compatible `errors` handling.
|
|
334539
|
-
*
|
|
334540
|
-
* - `'strict'` (default): throw on invalid sequences (via TextDecoder `fatal: true`)
|
|
334541
|
-
* - `'replace'`: substitute each invalid sequence with U+FFFD (TextDecoder default)
|
|
334542
|
-
* - `'ignore'`: drop invalid input sequences while preserving valid U+FFFD characters
|
|
334543
|
-
*
|
|
334544
|
-
* Falls back to `Buffer.toString(encoding)` for encodings TextDecoder does not
|
|
334545
|
-
* support (e.g. `hex`, `base64`, `binary`, `latin1`) — those are lossless
|
|
334546
|
-
* byte-to-character mappings so `errors` has no effect.
|
|
334547
|
-
* @internal
|
|
334548
|
-
*/
|
|
334549
|
-
function decodeTextWithErrors(data, encoding, errors = "strict", ignoreBOM = false) {
|
|
334550
|
-
let webLabel;
|
|
334551
|
-
switch (encoding) {
|
|
334552
|
-
case "utf-8":
|
|
334553
|
-
case "utf8":
|
|
334554
|
-
webLabel = "utf-8";
|
|
334555
|
-
break;
|
|
334556
|
-
case "utf16le":
|
|
334557
|
-
case "ucs2":
|
|
334558
|
-
case "ucs-2":
|
|
334559
|
-
webLabel = "utf-16le";
|
|
334560
|
-
break;
|
|
334561
|
-
default: webLabel = void 0;
|
|
334562
|
-
}
|
|
334563
|
-
if (webLabel === void 0) return data.toString(encoding);
|
|
334564
|
-
if (errors === "strict") return new TextDecoder(webLabel, {
|
|
334565
|
-
fatal: true,
|
|
334566
|
-
ignoreBOM
|
|
334567
|
-
}).decode(data);
|
|
334568
|
-
if (errors === "ignore") return webLabel === "utf-8" ? decodeUtf8Ignore(data) : decodeUtf16LeIgnore(data);
|
|
334569
|
-
return new TextDecoder(webLabel, {
|
|
334570
|
-
fatal: false,
|
|
334571
|
-
ignoreBOM
|
|
334572
|
-
}).decode(data);
|
|
334573
|
-
}
|
|
334574
|
-
/**
|
|
334575
|
-
* Convert a glob pattern segment (e.g. "*.txt", "file?.log") into a RegExp.
|
|
334576
|
-
* Mirrors Python pathlib behavior: includes dotfiles, case-sensitive by default.
|
|
334577
|
-
* @internal
|
|
334578
|
-
*/
|
|
334579
|
-
function globPatternToRegex(pattern, caseSensitive) {
|
|
334580
|
-
let regex = "^";
|
|
334581
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
334582
|
-
const ch = pattern[i];
|
|
334583
|
-
if (ch === void 0) break;
|
|
334584
|
-
switch (ch) {
|
|
334585
|
-
case "*":
|
|
334586
|
-
regex += "[^/]*";
|
|
334587
|
-
break;
|
|
334588
|
-
case "?":
|
|
334589
|
-
regex += "[^/]";
|
|
334590
|
-
break;
|
|
334591
|
-
case "[": {
|
|
334592
|
-
const end = pattern.indexOf("]", i + 1);
|
|
334593
|
-
if (end === -1) regex += "\\[";
|
|
334594
|
-
else {
|
|
334595
|
-
let charClass = pattern.slice(i + 1, end);
|
|
334596
|
-
charClass = charClass.replace(/\\/g, "\\\\");
|
|
334597
|
-
if (charClass.startsWith("!")) charClass = "^" + charClass.slice(1);
|
|
334598
|
-
else if (charClass.startsWith("^")) charClass = "\\" + charClass;
|
|
334599
|
-
regex += "[" + charClass + "]";
|
|
334600
|
-
i = end;
|
|
334601
|
-
}
|
|
334602
|
-
break;
|
|
334603
|
-
}
|
|
334604
|
-
case "\\":
|
|
334605
|
-
if (i + 1 < pattern.length) {
|
|
334606
|
-
const next = pattern.charAt(i + 1);
|
|
334607
|
-
regex += next.replaceAll(/[{}()+.\\[\]^$|]/g, "\\$&");
|
|
334608
|
-
i++;
|
|
334609
|
-
} else regex += "\\\\";
|
|
334610
|
-
break;
|
|
334611
|
-
default: regex += ch.replaceAll(/[{}()+.\\[\]^$|]/g, "\\$&");
|
|
334612
|
-
}
|
|
334613
|
-
}
|
|
334614
|
-
regex += "$";
|
|
334615
|
-
return new RegExp(regex, caseSensitive ? "" : "i");
|
|
334616
|
-
}
|
|
334617
|
-
var BufferedReadable;
|
|
334618
|
-
var init_internal = __esmMin((() => {
|
|
334619
|
-
BufferedReadable = class extends Readable$1 {
|
|
334620
|
-
_source;
|
|
334621
|
-
_ended = false;
|
|
334622
|
-
constructor(source) {
|
|
334623
|
-
super({ highWaterMark: 128 * 1024 });
|
|
334624
|
-
this._source = source;
|
|
334625
|
-
this._source.on("data", this._onData);
|
|
334626
|
-
this._source.on("end", this._onEnd);
|
|
334627
|
-
this._source.on("close", this._onClose);
|
|
334628
|
-
this._source.on("error", this._onError);
|
|
334629
|
-
}
|
|
334630
|
-
_read() {
|
|
334631
|
-
if (!this._ended && !this.destroyed) this._source.resume();
|
|
334632
|
-
}
|
|
334633
|
-
_destroy(error, callback) {
|
|
334634
|
-
this._source.off("data", this._onData);
|
|
334635
|
-
this._source.off("end", this._onEnd);
|
|
334636
|
-
this._source.off("close", this._onClose);
|
|
334637
|
-
this._source.off("error", this._onError);
|
|
334638
|
-
this._source.destroy();
|
|
334639
|
-
callback(error);
|
|
334640
|
-
}
|
|
334641
|
-
_onData = (chunk) => {
|
|
334642
|
-
if (!this.push(chunk)) this._source.pause();
|
|
334643
|
-
};
|
|
334644
|
-
_onEnd = () => {
|
|
334645
|
-
this._ended = true;
|
|
334646
|
-
this.push(null);
|
|
334647
|
-
};
|
|
334648
|
-
_onClose = () => {
|
|
334649
|
-
if (!this._ended) {
|
|
334650
|
-
this._ended = true;
|
|
334651
|
-
this.push(null);
|
|
334652
|
-
}
|
|
334653
|
-
};
|
|
334654
|
-
_onError = (error) => {
|
|
334655
|
-
this.destroy(error);
|
|
334656
|
-
};
|
|
334657
|
-
};
|
|
334658
|
-
}));
|
|
334659
|
-
//#endregion
|
|
334660
|
-
//#region ../../packages/kaos/src/login-shell-path.ts
|
|
334661
|
-
/**
|
|
334662
|
-
* Login-shell PATH probe — enrich `process.env.PATH` with entries from the
|
|
334663
|
-
* user's login shell.
|
|
334664
|
-
*
|
|
334665
|
-
* When blun is launched from a context that skipped the user's shell
|
|
334666
|
-
* profile (GUI launchers, non-login parent shells), `process.env.PATH`
|
|
334667
|
-
* misses entries like `/opt/homebrew/bin`, so commands spawned by the Bash
|
|
334668
|
-
* tool can't find tools the user has in their interactive shell (e.g.
|
|
334669
|
-
* `gh`). We run the user's login shell once (`$SHELL -l -c /usr/bin/env`),
|
|
334670
|
-
* extract its PATH, and append the entries the current PATH lacks. Existing
|
|
334671
|
-
* entries keep their order and priority; failures (no resolvable shell,
|
|
334672
|
-
* hung or broken profile) silently leave PATH untouched.
|
|
334673
|
-
*
|
|
334674
|
-
* launchd/daemon launches can leave `$SHELL` unset or blank (see
|
|
334675
|
-
* `defaultShell()` in agent-core's terminalService for the same case), so
|
|
334676
|
-
* the probe falls back to the OS account's login shell from the user
|
|
334677
|
-
* database before giving up.
|
|
334678
|
-
*
|
|
334679
|
-
* Like `detectEnvironment`, the probe is a pure function of injected deps
|
|
334680
|
-
* so the suite runs identically on any host. Windows is skipped: the
|
|
334681
|
-
* problem is specific to POSIX login-shell profiles.
|
|
334682
|
-
*/
|
|
334683
|
-
/**
|
|
334684
|
-
* Run the user's login shell and return its PATH, or `undefined` when the
|
|
334685
|
-
* probe does not apply (Windows, no resolvable shell) or fails (spawn
|
|
334686
|
-
* error, timeout, no PATH in the output).
|
|
334687
|
-
*/
|
|
334688
|
-
async function probeLoginShellPath(deps) {
|
|
334689
|
-
if (deps.platform === "win32") return void 0;
|
|
334690
|
-
const envShell = deps.env["SHELL"]?.trim();
|
|
334691
|
-
const shell = envShell === void 0 || envShell.length === 0 ? deps.userShell() : envShell;
|
|
334692
|
-
if (shell === void 0 || shell.length === 0) return void 0;
|
|
334693
|
-
const stdout = await deps.execFileText(shell, [
|
|
334694
|
-
"-l",
|
|
334695
|
-
"-c",
|
|
334696
|
-
"/usr/bin/env"
|
|
334697
|
-
], LOGIN_SHELL_ENV_TIMEOUT_MS);
|
|
334698
|
-
if (stdout === void 0) return void 0;
|
|
334699
|
-
let path;
|
|
334700
|
-
for (const line of stdout.split("\n")) if (line.startsWith("PATH=")) path = line.slice(5).trim();
|
|
334701
|
-
if (path === void 0 || path.length === 0) return void 0;
|
|
334702
|
-
return path;
|
|
334703
|
-
}
|
|
334704
|
-
/**
|
|
334705
|
-
* Union of the current PATH and the login-shell PATH: the current PATH
|
|
334706
|
-
* string is kept verbatim — including empty components, which POSIX
|
|
334707
|
-
* command lookup treats as the current directory — and login-shell
|
|
334708
|
-
* entries the current PATH lacks are appended in their own order. When
|
|
334709
|
-
* nothing is missing the current string is returned unchanged. Only
|
|
334710
|
-
* absolute login-shell entries are imported: empty, `.`, and relative
|
|
334711
|
-
* components are all cwd-dependent lookup, and appending one the user
|
|
334712
|
-
* did not already have would widen their search path — LocalKaos runs
|
|
334713
|
-
* commands from arbitrary workspace directories.
|
|
334714
|
-
*/
|
|
334715
|
-
function mergeLoginShellPath(currentPath, loginShellPath) {
|
|
334716
|
-
const current = currentPath ?? "";
|
|
334717
|
-
const seen = new Set(current.split(":").filter((entry) => entry.length > 0));
|
|
334718
|
-
const additions = [];
|
|
334719
|
-
for (const entry of loginShellPath.split(":")) {
|
|
334720
|
-
if (!entry.startsWith("/") || seen.has(entry)) continue;
|
|
334721
|
-
seen.add(entry);
|
|
334722
|
-
additions.push(entry);
|
|
334723
|
-
}
|
|
334724
|
-
if (additions.length === 0) return current;
|
|
334725
|
-
if (currentPath === void 0) return additions.join(":");
|
|
334726
|
-
return `${current}:${additions.join(":")}`;
|
|
334727
|
-
}
|
|
334728
|
-
/** Probe the login shell and merge its PATH into `deps.env['PATH']`. */
|
|
334729
|
-
async function applyLoginShellPath(deps) {
|
|
334730
|
-
const loginShellPath = await probeLoginShellPath(deps);
|
|
334731
|
-
if (loginShellPath === void 0) return;
|
|
334732
|
-
const currentPath = deps.env["PATH"];
|
|
334733
|
-
const merged = mergeLoginShellPath(currentPath, loginShellPath);
|
|
334734
|
-
if (merged === (currentPath ?? "")) return;
|
|
334735
|
-
deps.env["PATH"] = merged;
|
|
334736
|
-
}
|
|
334737
|
-
/**
|
|
334738
|
-
* Production convenience — apply the probe to `process.env` once per
|
|
334739
|
-
* process. Memoised like `detectEnvironmentFromNode`: the login-shell PATH
|
|
334740
|
-
* does not change for the lifetime of the process, and repeated
|
|
334741
|
-
* `LocalKaos.create()` calls must not re-spawn the shell.
|
|
334742
|
-
*/
|
|
334743
|
-
/**
|
|
334744
|
-
* Login shell from the OS user database (`/etc/passwd` via getpwuid on
|
|
334745
|
-
* Linux, Directory Services on macOS). `userInfo()` throws when the uid
|
|
334746
|
-
* has no database entry (e.g. containers running an arbitrary uid), and
|
|
334747
|
-
* service accounts may carry `/usr/sbin/nologin` — the latter needs no
|
|
334748
|
-
* special casing here because probing it simply fails and degrades
|
|
334749
|
-
* silently.
|
|
334750
|
-
*/
|
|
334751
|
-
function userShellFromNode() {
|
|
334752
|
-
try {
|
|
334753
|
-
const shell = userInfo().shell;
|
|
334754
|
-
return shell === null || shell.length === 0 ? void 0 : shell;
|
|
334755
|
-
} catch {
|
|
334756
|
-
return;
|
|
334757
|
-
}
|
|
334758
|
-
}
|
|
334759
|
-
function applyLoginShellPathFromNode() {
|
|
334760
|
-
if (appliedLoginShellPath !== void 0) return appliedLoginShellPath;
|
|
334761
|
-
appliedLoginShellPath = applyLoginShellPath({
|
|
334762
|
-
platform: process.platform,
|
|
334763
|
-
env: process.env,
|
|
334764
|
-
userShell: userShellFromNode,
|
|
334765
|
-
execFileText: execFileText$1
|
|
334766
|
-
});
|
|
334767
|
-
return appliedLoginShellPath;
|
|
334768
|
-
}
|
|
334769
|
-
var LOGIN_SHELL_ENV_TIMEOUT_MS, appliedLoginShellPath;
|
|
334770
|
-
var init_login_shell_path = __esmMin((() => {
|
|
334771
|
-
init_environment$1();
|
|
334772
|
-
LOGIN_SHELL_ENV_TIMEOUT_MS = 5e3;
|
|
334773
|
-
}));
|
|
334774
|
-
//#endregion
|
|
334775
|
-
//#region ../../packages/kaos/src/local.ts
|
|
334776
|
-
/**
|
|
334777
|
-
* Build the `(dev, ino)` cycle-detection key used by `_globWalk`'s
|
|
334778
|
-
* visited set. Returns `null` when `ino` is 0, which Node returns on
|
|
334779
|
-
* filesystems that don't carry inodes (Windows FAT/exFAT, some SMB/NFS
|
|
334780
|
-
* mounts). A null key signals "no reliable identity for this dir" so
|
|
334781
|
-
* the caller skips visited tracking for that descent — cycle safety
|
|
334782
|
-
* is weakened on those filesystems, but normal walking works instead
|
|
334783
|
-
* of every directory colliding on the shared key `"<dev>:0"`.
|
|
334784
|
-
*/
|
|
334785
|
-
function cycleKey(s) {
|
|
334786
|
-
if (s.ino === 0) return null;
|
|
334787
|
-
return `${String(s.dev)}:${String(s.ino)}`;
|
|
334788
|
-
}
|
|
334789
|
-
function buildLocalSpawnOptions(_isWindows, cwd, env) {
|
|
334790
|
-
return {
|
|
334791
|
-
cwd,
|
|
334792
|
-
env,
|
|
334793
|
-
stdio: [
|
|
334794
|
-
"pipe",
|
|
334795
|
-
"pipe",
|
|
334796
|
-
"pipe"
|
|
334797
|
-
],
|
|
334798
|
-
detached: true,
|
|
334799
|
-
windowsHide: true
|
|
334800
|
-
};
|
|
334801
|
-
}
|
|
334802
|
-
function isUtf8Encoding(encoding) {
|
|
334803
|
-
return encoding === "utf-8" || encoding === "utf8";
|
|
334804
|
-
}
|
|
334805
|
-
function* splitLinesKeepingTerminator(text) {
|
|
334806
|
-
if (text.length === 0) return;
|
|
334807
|
-
let start = 0;
|
|
334808
|
-
for (let i = 0; i < text.length; i += 1) if (text.codePointAt(i) === 10) {
|
|
334809
|
-
yield text.slice(start, i + 1);
|
|
334810
|
-
start = i + 1;
|
|
334811
|
-
}
|
|
334812
|
-
if (start < text.length) yield text.slice(start);
|
|
334813
|
-
}
|
|
334814
|
-
function updateLineEndingFlagsFromBytes(flags, chunk, prevWasCr) {
|
|
334815
|
-
for (let i = 0; i < chunk.length; i += 1) {
|
|
334816
|
-
const byte = chunk[i];
|
|
334817
|
-
if (byte === void 0) continue;
|
|
334818
|
-
if (byte === 13) {
|
|
334819
|
-
if (prevWasCr) flags.hasLoneCr = true;
|
|
334820
|
-
prevWasCr = true;
|
|
334821
|
-
} else if (byte === 10) {
|
|
334822
|
-
if (prevWasCr) flags.hasCrLf = true;
|
|
334823
|
-
else flags.hasLf = true;
|
|
334824
|
-
prevWasCr = false;
|
|
334825
|
-
} else {
|
|
334826
|
-
if (prevWasCr) flags.hasLoneCr = true;
|
|
334827
|
-
prevWasCr = false;
|
|
334828
|
-
}
|
|
334829
|
-
}
|
|
334830
|
-
return prevWasCr;
|
|
334831
|
-
}
|
|
334832
|
-
function createUtf8Validator() {
|
|
334833
|
-
let needed = 0;
|
|
334834
|
-
let lower = 128;
|
|
334835
|
-
let upper = 191;
|
|
334836
|
-
const fail = () => {
|
|
334837
|
-
throw new TypeError("Invalid UTF-8 data");
|
|
334838
|
-
};
|
|
334839
|
-
return {
|
|
334840
|
-
write(chunk) {
|
|
334841
|
-
for (let i = 0; i < chunk.length; i += 1) {
|
|
334842
|
-
const byte = chunk[i];
|
|
334843
|
-
if (byte === void 0) continue;
|
|
334844
|
-
if (needed === 0) {
|
|
334845
|
-
if (byte <= 127) continue;
|
|
334846
|
-
if (byte >= 194 && byte <= 223) needed = 1;
|
|
334847
|
-
else if (byte === 224) {
|
|
334848
|
-
needed = 2;
|
|
334849
|
-
lower = 160;
|
|
334850
|
-
} else if (byte >= 225 && byte <= 236) needed = 2;
|
|
334851
|
-
else if (byte === 237) {
|
|
334852
|
-
needed = 2;
|
|
334853
|
-
upper = 159;
|
|
334854
|
-
} else if (byte >= 238 && byte <= 239) needed = 2;
|
|
334855
|
-
else if (byte === 240) {
|
|
334856
|
-
needed = 3;
|
|
334857
|
-
lower = 144;
|
|
334858
|
-
} else if (byte >= 241 && byte <= 243) needed = 3;
|
|
334859
|
-
else if (byte === 244) {
|
|
334860
|
-
needed = 3;
|
|
334861
|
-
upper = 143;
|
|
334862
|
-
} else fail();
|
|
334863
|
-
} else {
|
|
334864
|
-
if (byte < lower || byte > upper) fail();
|
|
334865
|
-
lower = 128;
|
|
334866
|
-
upper = 191;
|
|
334867
|
-
needed -= 1;
|
|
334868
|
-
}
|
|
334869
|
-
}
|
|
334870
|
-
},
|
|
334871
|
-
end() {
|
|
334872
|
-
if (needed !== 0) fail();
|
|
334873
|
-
}
|
|
334874
|
-
};
|
|
334875
|
-
}
|
|
334876
|
-
async function readRange(fh, start, length) {
|
|
334877
|
-
const data = Buffer.alloc(length);
|
|
334878
|
-
let offset = 0;
|
|
334879
|
-
while (offset < length) {
|
|
334880
|
-
const { bytesRead } = await fh.read(data, offset, length - offset, start + offset);
|
|
334881
|
-
if (bytesRead === 0) break;
|
|
334882
|
-
offset += bytesRead;
|
|
334883
|
-
}
|
|
334884
|
-
return offset === length ? data : data.subarray(0, offset);
|
|
334885
|
-
}
|
|
334886
|
-
function waitForSpawn(child) {
|
|
334887
|
-
return new Promise((resolve, reject) => {
|
|
334888
|
-
const onSpawn = () => {
|
|
334889
|
-
child.off("error", onError);
|
|
334890
|
-
resolve();
|
|
334891
|
-
};
|
|
334892
|
-
const onError = (err) => {
|
|
334893
|
-
child.off("spawn", onSpawn);
|
|
334894
|
-
reject(err);
|
|
334895
|
-
};
|
|
334896
|
-
child.once("spawn", onSpawn);
|
|
334897
|
-
child.once("error", onError);
|
|
334898
|
-
});
|
|
334899
|
-
}
|
|
334900
|
-
var isWindows$1, READ_CHUNK_SIZE, LocalProcess, LocalKaos;
|
|
334901
|
-
var init_local = __esmMin((() => {
|
|
334902
|
-
init_dist$6();
|
|
334903
|
-
init_environment$1();
|
|
334904
|
-
init_errors$1();
|
|
334905
|
-
init_internal();
|
|
334906
|
-
init_login_shell_path();
|
|
334907
|
-
isWindows$1 = process.platform === "win32";
|
|
334908
|
-
READ_CHUNK_SIZE = 64 * 1024;
|
|
334909
|
-
LocalProcess = class {
|
|
334910
|
-
stdin;
|
|
334911
|
-
stdout;
|
|
334912
|
-
stderr;
|
|
334913
|
-
pid;
|
|
334914
|
-
_child;
|
|
334915
|
-
_exitCode = null;
|
|
334916
|
-
_exitPromise;
|
|
334917
|
-
_disposed = false;
|
|
334918
|
-
constructor(child) {
|
|
334919
|
-
if (child.stdin === null || child.stdout === null || child.stderr === null) throw new Error("Process must be created with stdin/stdout/stderr pipes.");
|
|
334920
|
-
this._child = child;
|
|
334921
|
-
this.stdin = child.stdin;
|
|
334922
|
-
this.stdout = new BufferedReadable(child.stdout);
|
|
334923
|
-
this.stderr = new BufferedReadable(child.stderr);
|
|
334924
|
-
this.pid = child.pid ?? -1;
|
|
334925
|
-
this._exitPromise = new Promise((resolve, reject) => {
|
|
334926
|
-
child.on("exit", (code) => {
|
|
334927
|
-
this._exitCode = code ?? -1;
|
|
334928
|
-
resolve(this._exitCode);
|
|
334929
|
-
});
|
|
334930
|
-
child.on("error", (error) => {
|
|
334931
|
-
reject(error);
|
|
334932
|
-
});
|
|
334933
|
-
});
|
|
334934
|
-
}
|
|
334935
|
-
get exitCode() {
|
|
334936
|
-
return this._exitCode;
|
|
334937
|
-
}
|
|
334938
|
-
async wait() {
|
|
334939
|
-
return this._exitPromise;
|
|
334940
|
-
}
|
|
334941
|
-
kill(signal) {
|
|
334942
|
-
if (this.pid <= 0) return Promise.resolve();
|
|
334943
|
-
if (isWindows$1) {
|
|
334944
|
-
const taskkillArgs = [
|
|
334945
|
-
"/T",
|
|
334946
|
-
"/F",
|
|
334947
|
-
"/PID",
|
|
334948
|
-
String(this.pid)
|
|
334949
|
-
];
|
|
334950
|
-
return new Promise((resolve) => {
|
|
334951
|
-
const killer = spawn("taskkill", taskkillArgs, {
|
|
334952
|
-
stdio: "ignore",
|
|
334953
|
-
windowsHide: true
|
|
334954
|
-
});
|
|
334955
|
-
const done = () => {
|
|
334956
|
-
resolve();
|
|
334957
|
-
};
|
|
334958
|
-
killer.once("error", done);
|
|
334959
|
-
killer.once("close", done);
|
|
334960
|
-
});
|
|
334961
|
-
}
|
|
334962
|
-
try {
|
|
334963
|
-
process.kill(-this.pid, signal ?? "SIGTERM");
|
|
334964
|
-
} catch (error) {
|
|
334965
|
-
const err = error;
|
|
334966
|
-
if (err.code === "ESRCH") return Promise.resolve();
|
|
334967
|
-
if (err.code === "EPERM") {
|
|
334968
|
-
try {
|
|
334969
|
-
this._child.kill(signal ?? "SIGTERM");
|
|
334970
|
-
} catch {}
|
|
334971
|
-
return Promise.resolve();
|
|
334972
|
-
}
|
|
334973
|
-
throw error;
|
|
334974
|
-
}
|
|
334975
|
-
return Promise.resolve();
|
|
334976
|
-
}
|
|
334977
|
-
dispose() {
|
|
334978
|
-
if (this._disposed) return;
|
|
334979
|
-
this._disposed = true;
|
|
334980
|
-
this.stdin.destroy();
|
|
334981
|
-
this.stdout.destroy();
|
|
334982
|
-
this.stderr.destroy();
|
|
334983
|
-
}
|
|
334984
|
-
};
|
|
334985
|
-
LocalKaos = class LocalKaos {
|
|
334986
|
-
name = "local";
|
|
334987
|
-
osEnv;
|
|
334988
|
-
_cwd;
|
|
334989
|
-
_envLayers;
|
|
334990
|
-
constructor(osEnv, cwd, envLayers = []) {
|
|
334991
|
-
this._cwd = normalize$4(cwd ?? process.cwd());
|
|
334992
|
-
this.osEnv = osEnv;
|
|
334993
|
-
this._envLayers = envLayers;
|
|
334994
|
-
}
|
|
334995
|
-
/**
|
|
334996
|
-
* Construct a fresh `LocalKaos` after probing the host environment.
|
|
334997
|
-
*
|
|
334998
|
-
* Each call returns a new instance with its own `_cwd`; concurrent
|
|
334999
|
-
* callers can therefore operate on independent working directories
|
|
335000
|
-
* without polluting one another.
|
|
335001
|
-
*/
|
|
335002
|
-
static async create() {
|
|
335003
|
-
const [osEnv] = await Promise.all([detectEnvironmentFromNode(), applyLoginShellPathFromNode()]);
|
|
335004
|
-
return new LocalKaos(osEnv);
|
|
335005
|
-
}
|
|
335006
|
-
withCwd(cwd) {
|
|
335007
|
-
return new LocalKaos(this.osEnv, cwd, this._envLayers);
|
|
335008
|
-
}
|
|
335009
|
-
withEnv(env) {
|
|
335010
|
-
return new LocalKaos(this.osEnv, this._cwd, [...this._envLayers, env]);
|
|
335011
|
-
}
|
|
335012
|
-
_resolvePath(path) {
|
|
335013
|
-
if (isAbsolute$2(path)) return normalize$4(path);
|
|
335014
|
-
return join$4(this._cwd, path);
|
|
335015
|
-
}
|
|
335016
|
-
pathClass() {
|
|
335017
|
-
return isWindows$1 ? "win32" : "posix";
|
|
335018
|
-
}
|
|
335019
|
-
normpath(path) {
|
|
335020
|
-
return normalize$4(path);
|
|
335021
|
-
}
|
|
335022
|
-
gethome() {
|
|
335023
|
-
return normalize$4(homedir());
|
|
335024
|
-
}
|
|
335025
|
-
getcwd() {
|
|
335026
|
-
return this._cwd;
|
|
335027
|
-
}
|
|
335028
|
-
/**
|
|
335029
|
-
* Change the working directory of this LocalKaos instance.
|
|
335030
|
-
*
|
|
335031
|
-
* Unlike Python's `os.chdir`, this is instance-scoped and never touches
|
|
335032
|
-
* `process.cwd()`. Child processes spawned via {@link exec} inherit this
|
|
335033
|
-
* instance's `_cwd`; concurrent LocalKaos instances each carry their own
|
|
335034
|
-
* independent cwd. If you need Python-compatible process-global cwd,
|
|
335035
|
-
* call `process.chdir(x)` directly.
|
|
335036
|
-
*/
|
|
335037
|
-
async chdir(path) {
|
|
335038
|
-
const resolved = this._resolvePath(path);
|
|
335039
|
-
if (!(await stat(resolved)).isDirectory()) throw new Error(`Not a directory: ${resolved}`);
|
|
335040
|
-
this._cwd = resolved;
|
|
335041
|
-
}
|
|
335042
|
-
async stat(path, options) {
|
|
335043
|
-
const resolved = this._resolvePath(path);
|
|
335044
|
-
const s = options?.followSymlinks ?? true ? await stat(resolved) : await lstat(resolved);
|
|
335045
|
-
return {
|
|
335046
|
-
stMode: s.mode,
|
|
335047
|
-
stIno: s.ino,
|
|
335048
|
-
stDev: s.dev,
|
|
335049
|
-
stNlink: s.nlink,
|
|
335050
|
-
stUid: s.uid,
|
|
335051
|
-
stGid: s.gid,
|
|
335052
|
-
stSize: s.size,
|
|
335053
|
-
stAtime: s.atimeMs / 1e3,
|
|
335054
|
-
stMtime: s.mtimeMs / 1e3,
|
|
335055
|
-
stCtime: isWindows$1 ? s.birthtimeMs / 1e3 : s.ctimeMs / 1e3
|
|
335056
|
-
};
|
|
335057
|
-
}
|
|
335058
|
-
async *iterdir(path) {
|
|
335059
|
-
const resolved = this._resolvePath(path);
|
|
335060
|
-
const entries = await readdir(resolved);
|
|
335061
|
-
for (const entry of entries) yield join$4(resolved, entry);
|
|
335062
|
-
}
|
|
335063
|
-
async *glob(path, pattern, options) {
|
|
335064
|
-
const resolved = this._resolvePath(path);
|
|
335065
|
-
const caseSensitive = options?.caseSensitive ?? true;
|
|
335066
|
-
const patternParts = pattern.split("/");
|
|
335067
|
-
const initVisited = /* @__PURE__ */ new Set();
|
|
335068
|
-
try {
|
|
335069
|
-
const rootKey = cycleKey(await stat(resolved));
|
|
335070
|
-
if (rootKey !== null) initVisited.add(rootKey);
|
|
335071
|
-
} catch {}
|
|
335072
|
-
yield* this._globWalk(resolved, patternParts, caseSensitive, initVisited);
|
|
335073
|
-
}
|
|
335074
|
-
async *_globWalk(basePath, patternParts, caseSensitive, visited) {
|
|
335075
|
-
if (patternParts.length === 0) return;
|
|
335076
|
-
const [currentPattern, ...remainingParts] = patternParts;
|
|
335077
|
-
if (currentPattern === "**") {
|
|
335078
|
-
if (remainingParts.length > 0) yield* this._globWalk(basePath, remainingParts, caseSensitive, visited);
|
|
335079
|
-
else yield basePath;
|
|
335080
|
-
let entries;
|
|
335081
|
-
try {
|
|
335082
|
-
entries = await readdir(basePath);
|
|
335083
|
-
} catch {
|
|
335084
|
-
return;
|
|
335085
|
-
}
|
|
335086
|
-
for (const entry of entries) {
|
|
335087
|
-
const fullPath = join$4(basePath, entry);
|
|
335088
|
-
let entryStat;
|
|
335089
|
-
try {
|
|
335090
|
-
entryStat = await stat(fullPath);
|
|
335091
|
-
} catch {
|
|
335092
|
-
continue;
|
|
335093
|
-
}
|
|
335094
|
-
if (entryStat.isDirectory()) {
|
|
335095
|
-
const key = cycleKey(entryStat);
|
|
335096
|
-
if (key !== null && visited.has(key)) continue;
|
|
335097
|
-
yield* this._globWalk(fullPath, patternParts, caseSensitive, key !== null ? new Set([...visited, key]) : visited);
|
|
335098
|
-
} else if (remainingParts.length === 0) yield fullPath;
|
|
335099
|
-
}
|
|
335100
|
-
} else {
|
|
335101
|
-
const regex = globPatternToRegex(currentPattern ?? "", caseSensitive);
|
|
335102
|
-
let entries;
|
|
335103
|
-
try {
|
|
335104
|
-
entries = await readdir(basePath);
|
|
335105
|
-
} catch {
|
|
335106
|
-
return;
|
|
335107
|
-
}
|
|
335108
|
-
for (const entry of entries) {
|
|
335109
|
-
if (!regex.test(entry)) continue;
|
|
335110
|
-
const fullPath = join$4(basePath, entry);
|
|
335111
|
-
if (remainingParts.length === 0) yield fullPath;
|
|
335112
|
-
else {
|
|
335113
|
-
let entryStat;
|
|
335114
|
-
try {
|
|
335115
|
-
entryStat = await stat(fullPath);
|
|
335116
|
-
} catch {
|
|
335117
|
-
continue;
|
|
335118
|
-
}
|
|
335119
|
-
if (entryStat.isDirectory()) {
|
|
335120
|
-
const key = cycleKey(entryStat);
|
|
335121
|
-
if (key !== null && visited.has(key)) continue;
|
|
335122
|
-
yield* this._globWalk(fullPath, remainingParts, caseSensitive, key !== null ? new Set([...visited, key]) : visited);
|
|
335123
|
-
}
|
|
335124
|
-
}
|
|
335125
|
-
}
|
|
335126
|
-
}
|
|
335127
|
-
}
|
|
335128
|
-
async readBytes(path, n) {
|
|
335129
|
-
const resolved = this._resolvePath(path);
|
|
335130
|
-
if (n === void 0) return Buffer.from(await readFile(resolved));
|
|
335131
|
-
const fh = await open(resolved, "r");
|
|
335132
|
-
try {
|
|
335133
|
-
const buf = Buffer.alloc(n);
|
|
335134
|
-
const { bytesRead } = await fh.read(buf, 0, n, 0);
|
|
335135
|
-
return buf.subarray(0, bytesRead);
|
|
335136
|
-
} finally {
|
|
335137
|
-
await fh.close();
|
|
335138
|
-
}
|
|
335139
|
-
}
|
|
335140
|
-
async readText(path, options) {
|
|
335141
|
-
const resolved = this._resolvePath(path);
|
|
335142
|
-
const encoding = options?.encoding ?? "utf-8";
|
|
335143
|
-
const errors = options?.errors ?? "strict";
|
|
335144
|
-
return decodeTextWithErrors(await readFile(resolved), encoding, errors);
|
|
335145
|
-
}
|
|
335146
|
-
async *readLines(path, options) {
|
|
335147
|
-
const resolved = this._resolvePath(path);
|
|
335148
|
-
const encoding = options?.encoding ?? "utf-8";
|
|
335149
|
-
const errors = options?.errors ?? "strict";
|
|
335150
|
-
if (!isUtf8Encoding(encoding)) {
|
|
335151
|
-
yield* splitLinesKeepingTerminator(decodeTextWithErrors(await readFile(resolved), encoding, errors));
|
|
335152
|
-
return;
|
|
335153
|
-
}
|
|
335154
|
-
yield* this._readUtf8Lines(resolved, errors);
|
|
335155
|
-
}
|
|
335156
|
-
async scanTextFile(path) {
|
|
335157
|
-
const fh = await open(this._resolvePath(path), "r");
|
|
335158
|
-
try {
|
|
335159
|
-
const buf = Buffer.alloc(READ_CHUNK_SIZE);
|
|
335160
|
-
const flags = {
|
|
335161
|
-
hasCrLf: false,
|
|
335162
|
-
hasLf: false,
|
|
335163
|
-
hasLoneCr: false
|
|
335164
|
-
};
|
|
335165
|
-
const validator = createUtf8Validator();
|
|
335166
|
-
let totalLines = 0;
|
|
335167
|
-
let totalBytes = 0;
|
|
335168
|
-
let endsWithNewline = false;
|
|
335169
|
-
let hasNul = false;
|
|
335170
|
-
let prevWasCr = false;
|
|
335171
|
-
while (true) {
|
|
335172
|
-
const { bytesRead } = await fh.read(buf, 0, buf.length, null);
|
|
335173
|
-
if (bytesRead === 0) break;
|
|
335174
|
-
const chunk = buf.subarray(0, bytesRead);
|
|
335175
|
-
validator.write(chunk);
|
|
335176
|
-
for (let i = 0; i < chunk.length; i += 1) {
|
|
335177
|
-
const byte = chunk[i];
|
|
335178
|
-
if (byte === void 0) continue;
|
|
335179
|
-
if (byte === 0) hasNul = true;
|
|
335180
|
-
if (byte === 10) totalLines += 1;
|
|
335181
|
-
}
|
|
335182
|
-
prevWasCr = updateLineEndingFlagsFromBytes(flags, chunk, prevWasCr);
|
|
335183
|
-
totalBytes += bytesRead;
|
|
335184
|
-
endsWithNewline = chunk[bytesRead - 1] === 10;
|
|
335185
|
-
}
|
|
335186
|
-
if (prevWasCr) flags.hasLoneCr = true;
|
|
335187
|
-
validator.end();
|
|
335188
|
-
if (totalBytes > 0 && !endsWithNewline) totalLines += 1;
|
|
335189
|
-
return {
|
|
335190
|
-
totalLines,
|
|
335191
|
-
endsWithNewline,
|
|
335192
|
-
hasNul,
|
|
335193
|
-
lineEndingFlags: flags
|
|
335194
|
-
};
|
|
335195
|
-
} finally {
|
|
335196
|
-
await fh.close();
|
|
335197
|
-
}
|
|
335198
|
-
}
|
|
335199
|
-
async *readLineRange(path, options) {
|
|
335200
|
-
const resolved = this._resolvePath(path);
|
|
335201
|
-
const errors = options.errors ?? "strict";
|
|
335202
|
-
yield* this._readUtf8Lines(resolved, errors, {
|
|
335203
|
-
startLine: options.startLine,
|
|
335204
|
-
maxLines: options.maxLines
|
|
335205
|
-
});
|
|
335206
|
-
}
|
|
335207
|
-
async *readTailLines(path, options) {
|
|
335208
|
-
if (options.tailCount <= 0) return;
|
|
335209
|
-
const resolved = this._resolvePath(path);
|
|
335210
|
-
const errors = options.errors ?? "strict";
|
|
335211
|
-
const fh = await open(resolved, "r");
|
|
335212
|
-
try {
|
|
335213
|
-
const s = await fh.stat();
|
|
335214
|
-
if (s.size === 0) return;
|
|
335215
|
-
let pos = s.size;
|
|
335216
|
-
let foundLf = 0;
|
|
335217
|
-
let startOffset = 0;
|
|
335218
|
-
let needLf = options.tailCount;
|
|
335219
|
-
let sawTailBlock = false;
|
|
335220
|
-
while (pos > 0 && foundLf < needLf) {
|
|
335221
|
-
const readSize = Math.min(READ_CHUNK_SIZE, pos);
|
|
335222
|
-
pos -= readSize;
|
|
335223
|
-
const buf = Buffer.alloc(readSize);
|
|
335224
|
-
await fh.read(buf, 0, readSize, pos);
|
|
335225
|
-
if (!sawTailBlock) {
|
|
335226
|
-
sawTailBlock = true;
|
|
335227
|
-
needLf = buf[readSize - 1] === 10 ? options.tailCount + 1 : options.tailCount;
|
|
335228
|
-
}
|
|
335229
|
-
for (let i = readSize - 1; i >= 0; i -= 1) {
|
|
335230
|
-
if (buf[i] !== 10) continue;
|
|
335231
|
-
foundLf += 1;
|
|
335232
|
-
if (foundLf === needLf) {
|
|
335233
|
-
startOffset = pos + i + 1;
|
|
335234
|
-
break;
|
|
335235
|
-
}
|
|
335236
|
-
}
|
|
335237
|
-
}
|
|
335238
|
-
if (foundLf < needLf) startOffset = 0;
|
|
335239
|
-
yield* splitLinesKeepingTerminator(decodeTextWithErrors(await readRange(fh, startOffset, s.size - startOffset), "utf-8", errors, startOffset !== 0));
|
|
335240
|
-
} finally {
|
|
335241
|
-
await fh.close();
|
|
335242
|
-
}
|
|
335243
|
-
}
|
|
335244
|
-
async *_readUtf8Lines(resolved, errors, range) {
|
|
335245
|
-
const startLine = range?.startLine ?? 1;
|
|
335246
|
-
const maxLines = range?.maxLines ?? Number.POSITIVE_INFINITY;
|
|
335247
|
-
const fh = await open(resolved, "r");
|
|
335248
|
-
try {
|
|
335249
|
-
const buf = Buffer.alloc(READ_CHUNK_SIZE);
|
|
335250
|
-
let pending = [];
|
|
335251
|
-
let pendingOffset = 0;
|
|
335252
|
-
let fileOffset = 0;
|
|
335253
|
-
let lineNo = 1;
|
|
335254
|
-
let yielded = 0;
|
|
335255
|
-
while (true) {
|
|
335256
|
-
const { bytesRead } = await fh.read(buf, 0, buf.length, null);
|
|
335257
|
-
if (bytesRead === 0) break;
|
|
335258
|
-
const chunk = buf.subarray(0, bytesRead);
|
|
335259
|
-
let lineStart = 0;
|
|
335260
|
-
for (let i = 0; i < chunk.length; i += 1) {
|
|
335261
|
-
if (chunk[i] !== 10) continue;
|
|
335262
|
-
const piece = chunk.subarray(lineStart, i + 1);
|
|
335263
|
-
const lineOffset = pending.length === 0 ? fileOffset + lineStart : pendingOffset;
|
|
335264
|
-
const line = pending.length === 0 ? piece : Buffer.concat([...pending, piece]);
|
|
335265
|
-
if (lineNo >= startLine) {
|
|
335266
|
-
yield decodeTextWithErrors(line, "utf-8", errors, lineOffset !== 0);
|
|
335267
|
-
yielded += 1;
|
|
335268
|
-
if (yielded >= maxLines) return;
|
|
335269
|
-
}
|
|
335270
|
-
pending = [];
|
|
335271
|
-
lineStart = i + 1;
|
|
335272
|
-
lineNo += 1;
|
|
335273
|
-
}
|
|
335274
|
-
if (lineStart < chunk.length) {
|
|
335275
|
-
const tail = Buffer.from(chunk.subarray(lineStart));
|
|
335276
|
-
if (pending.length === 0) pendingOffset = fileOffset + lineStart;
|
|
335277
|
-
pending.push(tail);
|
|
335278
|
-
}
|
|
335279
|
-
fileOffset += bytesRead;
|
|
335280
|
-
}
|
|
335281
|
-
if (pending.length > 0) {
|
|
335282
|
-
const line = Buffer.concat(pending);
|
|
335283
|
-
if (lineNo >= startLine) yield decodeTextWithErrors(line, "utf-8", errors, pendingOffset !== 0);
|
|
335284
|
-
}
|
|
335285
|
-
} finally {
|
|
335286
|
-
await fh.close();
|
|
335287
|
-
}
|
|
335288
|
-
}
|
|
335289
|
-
async writeBytes(path, data) {
|
|
335290
|
-
await writeFile(this._resolvePath(path), data);
|
|
335291
|
-
return data.length;
|
|
335292
|
-
}
|
|
335293
|
-
async writeText(path, data, options) {
|
|
335294
|
-
const resolved = this._resolvePath(path);
|
|
335295
|
-
const encoding = options?.encoding ?? "utf-8";
|
|
335296
|
-
if ((options?.mode ?? "w") === "a") await appendFile(resolved, data, encoding);
|
|
335297
|
-
else await writeFile(resolved, data, encoding);
|
|
335298
|
-
return data.length;
|
|
335299
|
-
}
|
|
335300
|
-
async mkdir(path, options) {
|
|
335301
|
-
const resolved = this._resolvePath(path);
|
|
335302
|
-
const parents = options?.parents ?? false;
|
|
335303
|
-
const existOk = options?.existOk ?? false;
|
|
335304
|
-
if (parents) {
|
|
335305
|
-
if (!existOk) try {
|
|
335306
|
-
if ((await stat(resolved)).isDirectory()) throw new KaosFileExistsError(`${resolved} already exists`);
|
|
335307
|
-
} catch (error) {
|
|
335308
|
-
if (error instanceof KaosFileExistsError) throw error;
|
|
335309
|
-
if (error.code !== "ENOENT") throw error;
|
|
335310
|
-
}
|
|
335311
|
-
await mkdir(resolved, { recursive: true });
|
|
335312
|
-
return;
|
|
335313
|
-
}
|
|
335314
|
-
try {
|
|
335315
|
-
await mkdir(resolved);
|
|
335316
|
-
} catch (error) {
|
|
335317
|
-
if (existOk && error instanceof Error && "code" in error && error.code === "EEXIST") {
|
|
335318
|
-
if (!(await stat(resolved)).isDirectory()) throw new KaosFileExistsError(`${resolved} already exists but is not a directory`);
|
|
335319
|
-
return;
|
|
335320
|
-
}
|
|
335321
|
-
throw error;
|
|
335322
|
-
}
|
|
335323
|
-
}
|
|
335324
|
-
async exec(...args) {
|
|
335325
|
-
const command = args[0];
|
|
335326
|
-
if (command === void 0) throw new Error("LocalKaos.exec(): at least one argument (the command to run) is required.");
|
|
335327
|
-
const child = spawn(command, args.slice(1), buildLocalSpawnOptions(isWindows$1, this._cwd, this._buildExecEnv()));
|
|
335328
|
-
await waitForSpawn(child);
|
|
335329
|
-
return new LocalProcess(child);
|
|
335330
|
-
}
|
|
335331
|
-
async execWithEnv(args, env) {
|
|
335332
|
-
const command = args[0];
|
|
335333
|
-
if (command === void 0) throw new Error("LocalKaos.execWithEnv(): at least one argument (the command to run) is required.");
|
|
335334
|
-
const child = spawn(command, args.slice(1), buildLocalSpawnOptions(isWindows$1, this._cwd, this._buildExecEnv(env)));
|
|
335335
|
-
await waitForSpawn(child);
|
|
335336
|
-
return new LocalProcess(child);
|
|
335337
|
-
}
|
|
335338
|
-
_buildExecEnv(invocationEnv) {
|
|
335339
|
-
if (this._envLayers.length === 0) return invocationEnv;
|
|
335340
|
-
const merged = {
|
|
335341
|
-
...process.env,
|
|
335342
|
-
...invocationEnv
|
|
335343
|
-
};
|
|
335344
|
-
for (const layer of this._envLayers) Object.assign(merged, layer);
|
|
335345
|
-
return merged;
|
|
335346
|
-
}
|
|
335347
|
-
};
|
|
335348
|
-
}));
|
|
335349
|
-
var init_current = __esmMin((() => {
|
|
335350
|
-
new AsyncLocalStorage();
|
|
335351
|
-
}));
|
|
335352
|
-
//#endregion
|
|
335353
|
-
//#region ../../packages/kaos/src/index.ts
|
|
335354
|
-
var init_src$1 = __esmMin((() => {
|
|
335355
|
-
init_environment$1();
|
|
335356
|
-
init_errors$1();
|
|
335357
|
-
init_local();
|
|
335358
|
-
init_current();
|
|
335359
|
-
}));
|
|
335360
|
-
//#endregion
|
|
335361
335643
|
//#region ../../packages/agent-core/src/agent/replay/build.ts
|
|
335362
335644
|
async function buildReplay(persistence, range) {
|
|
335363
335645
|
const agent = new Agent$1({
|
|
@@ -531879,6 +532161,7 @@ var SessionEventHandler = class {
|
|
|
531879
532161
|
}
|
|
531880
532162
|
}
|
|
531881
532163
|
handleSessionError(event) {
|
|
532164
|
+
if (event.code === "turn.agent_busy" && this.host.isManualPromptAdmissionPending?.(event.sessionId, event.agentId)) return;
|
|
531882
532165
|
this.host.streamingUI.flushNow();
|
|
531883
532166
|
this.host.streamingUI.resetToolUi();
|
|
531884
532167
|
this.host.streamingUI.finalizeLiveTextBuffers("idle");
|
|
@@ -539188,17 +539471,22 @@ var BlunTUI = class {
|
|
|
539188
539471
|
editorReplacement;
|
|
539189
539472
|
editorReplacementGeneration = 0;
|
|
539190
539473
|
preserveQueueAcrossSessionReset = false;
|
|
539474
|
+
manualPromptInFlight;
|
|
539475
|
+
isManualPromptAdmissionPending(sessionId, agentId) {
|
|
539476
|
+
const pending = this.manualPromptInFlight;
|
|
539477
|
+
return pending !== void 0 && this.session === pending.session && pending.session.id === sessionId && pending.agentId === agentId;
|
|
539478
|
+
}
|
|
539191
539479
|
hasWaitingChannelMessage() {
|
|
539192
539480
|
return this.state.queuedMessages.some((item) => item.mode === "channel");
|
|
539193
539481
|
}
|
|
539194
539482
|
canInterruptForQueuedChannel() {
|
|
539195
539483
|
const item = this.state.queuedMessages[0];
|
|
539196
539484
|
if (/^[1-9]\d*$/u.test(this.pendingChannelReplyGuard?.chatId ?? "") && item?.channelChatId?.startsWith("-") === true) return false;
|
|
539197
|
-
return item?.mode === "channel" && item.channelContextOnly !== true && this.session !== void 0 && this.queueSteerInFlight === void 0 && !this.deferUserMessages && !this.state.appState.isCompacting && this.state.appState.streamingPhase !== "idle" && this.state.appState.streamingPhase !== "shell" && this.shellOutputStreams.size === 0 && !this.streamingUI.hasActiveToolCalls();
|
|
539485
|
+
return item?.mode === "channel" && item.channelContextOnly !== true && this.session !== void 0 && this.queueSteerInFlight === void 0 && this.manualPromptInFlight === void 0 && !this.deferUserMessages && !this.state.appState.isCompacting && this.state.appState.streamingPhase !== "idle" && this.state.appState.streamingPhase !== "shell" && this.shellOutputStreams.size === 0 && !this.streamingUI.hasActiveToolCalls();
|
|
539198
539486
|
}
|
|
539199
539487
|
canDeliverQueuedChannelHead() {
|
|
539200
539488
|
const item = this.state.queuedMessages[0];
|
|
539201
|
-
if (this.isShuttingDown || this.queueCommandRunning || this.queueSteerInFlight !== void 0 || this.editorReplacementActive || this.deferUserMessages || this.session === void 0 || this.state.appState.model.trim().length === 0 || item?.mode !== "channel") return false;
|
|
539489
|
+
if (this.isShuttingDown || this.queueCommandRunning || this.queueSteerInFlight !== void 0 || this.manualPromptInFlight !== void 0 || this.editorReplacementActive || this.deferUserMessages || this.session === void 0 || this.state.appState.model.trim().length === 0 || item?.mode !== "channel") return false;
|
|
539202
539490
|
const guard = this.pendingChannelReplyGuard;
|
|
539203
539491
|
return guard === void 0 || item.channelChatId !== void 0 && guard.chatId === item.channelChatId;
|
|
539204
539492
|
}
|
|
@@ -539322,7 +539610,7 @@ var BlunTUI = class {
|
|
|
539322
539610
|
canDrainQueue() {
|
|
539323
539611
|
const head = this.state.queuedMessages[0];
|
|
539324
539612
|
const remoteControl = head?.mode === "command" && head.channelCommandEnvelope !== void 0 && channelCommandCanInterrupt(head.text);
|
|
539325
|
-
return !this.isShuttingDown && !this.queueCommandRunning && this.queueSteerInFlight === void 0 && !this.editorReplacementActive && !this.deferUserMessages && (remoteControl || this.state.appState.streamingPhase === "idle" && !this.state.appState.isCompacting);
|
|
539613
|
+
return !this.isShuttingDown && !this.queueCommandRunning && this.queueSteerInFlight === void 0 && this.manualPromptInFlight === void 0 && !this.editorReplacementActive && !this.deferUserMessages && (remoteControl || this.state.appState.streamingPhase === "idle" && !this.state.appState.isCompacting);
|
|
539326
539614
|
}
|
|
539327
539615
|
scheduleQueueDrain() {
|
|
539328
539616
|
if (this.queueDrainTimer !== void 0 || !this.canDrainQueue()) return;
|
|
@@ -539350,11 +539638,12 @@ var BlunTUI = class {
|
|
|
539350
539638
|
}
|
|
539351
539639
|
const inFlightCount = this.queueSteerInFlight?.items.length ?? 0;
|
|
539352
539640
|
this.queueFlushBatchRemaining = inFlightCount + this.state.queuedMessages.length;
|
|
539641
|
+
if (this.manualPromptInFlight !== void 0) this.manualPromptInFlight.flushRequested = this.queueFlushBatchRemaining > 0;
|
|
539353
539642
|
this.steerQueueFlushPrefix();
|
|
539354
539643
|
this.scheduleQueueDrain();
|
|
539355
539644
|
}
|
|
539356
539645
|
steerQueueFlushPrefix() {
|
|
539357
|
-
if (this.queueSteerInFlight !== void 0 || this.queueFlushBatchRemaining === 0 || this.deferUserMessages || this.state.appState.isCompacting || this.state.appState.streamingPhase === "idle" || this.state.appState.streamingPhase === "shell") return;
|
|
539646
|
+
if (this.queueSteerInFlight !== void 0 || this.manualPromptInFlight !== void 0 || this.queueFlushBatchRemaining === 0 || this.deferUserMessages || this.state.appState.isCompacting || this.state.appState.streamingPhase === "idle" || this.state.appState.streamingPhase === "shell") return;
|
|
539358
539647
|
const session = this.session;
|
|
539359
539648
|
if (session === void 0 || this.state.appState.model.trim().length === 0) {
|
|
539360
539649
|
this.showError(llmNotSetMessage());
|
|
@@ -539378,13 +539667,16 @@ var BlunTUI = class {
|
|
|
539378
539667
|
turnEnded: false,
|
|
539379
539668
|
onCommit: () => {
|
|
539380
539669
|
this.queueFlushBatchRemaining = Math.max(0, this.queueFlushBatchRemaining - items.length);
|
|
539381
|
-
for (const item of items)
|
|
539382
|
-
|
|
539383
|
-
|
|
539384
|
-
|
|
539385
|
-
|
|
539386
|
-
|
|
539387
|
-
|
|
539670
|
+
for (const item of items) {
|
|
539671
|
+
if (item.transcriptRendered) continue;
|
|
539672
|
+
this.appendTranscriptEntry({
|
|
539673
|
+
id: nextTranscriptId(),
|
|
539674
|
+
kind: "user",
|
|
539675
|
+
turnId,
|
|
539676
|
+
renderMode: "plain",
|
|
539677
|
+
content: item.text.trim()
|
|
539678
|
+
});
|
|
539679
|
+
}
|
|
539388
539680
|
}
|
|
539389
539681
|
};
|
|
539390
539682
|
this.queueSteerInFlight = inFlight;
|
|
@@ -539456,7 +539748,7 @@ var BlunTUI = class {
|
|
|
539456
539748
|
channelCommandEnvelope: envelope,
|
|
539457
539749
|
channelAcknowledge: acknowledge
|
|
539458
539750
|
};
|
|
539459
|
-
const busy = this.state.queuedMessages.length > 0 || this.queueSteerInFlight !== void 0 || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting;
|
|
539751
|
+
const busy = this.state.queuedMessages.length > 0 || this.queueSteerInFlight !== void 0 || this.manualPromptInFlight !== void 0 || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting;
|
|
539460
539752
|
if (this.session === void 0 || this.deferUserMessages || this.queueCommandRunning || this.editorReplacementActive || busy && !channelCommandCanInterrupt(command)) {
|
|
539461
539753
|
this.state.queuedMessages.push(item);
|
|
539462
539754
|
this.scheduleQueueDrain();
|
|
@@ -539467,7 +539759,7 @@ var BlunTUI = class {
|
|
|
539467
539759
|
}
|
|
539468
539760
|
injectChannelEnvelope({
|
|
539469
539761
|
canDeliver: () => this.session !== void 0 && this.state.appState.model.trim().length > 0,
|
|
539470
|
-
isBusy: () => this.queueCommandRunning || this.state.queuedMessages.length > 0 || this.queueSteerInFlight !== void 0 || this.deferUserMessages || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting,
|
|
539762
|
+
isBusy: () => this.queueCommandRunning || this.state.queuedMessages.length > 0 || this.queueSteerInFlight !== void 0 || this.manualPromptInFlight !== void 0 || this.deferUserMessages || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting,
|
|
539471
539763
|
deliverNow: (modelInput, displayText, origin, contextOnly) => {
|
|
539472
539764
|
this.sendChannelMessageInternal(this.requireSession(), modelInput, displayText, origin, envelope.meta.chat_id, envelope.meta["image_path"], contextOnly, false, acknowledge);
|
|
539473
539765
|
},
|
|
@@ -539771,7 +540063,8 @@ var BlunTUI = class {
|
|
|
539771
540063
|
agentId: this.harness.interactiveAgentId,
|
|
539772
540064
|
parts: options?.parts,
|
|
539773
540065
|
imageAttachmentIds: options?.imageAttachmentIds !== void 0 && options.imageAttachmentIds.length > 0 ? options.imageAttachmentIds : void 0,
|
|
539774
|
-
mode
|
|
540066
|
+
mode,
|
|
540067
|
+
transcriptRendered: options?.transcriptRendered
|
|
539775
540068
|
});
|
|
539776
540069
|
this.track("input_queue");
|
|
539777
540070
|
}
|
|
@@ -539830,7 +540123,8 @@ var BlunTUI = class {
|
|
|
539830
540123
|
this.harness.withInteractiveAgent(item.agentId ?? "main", () => {
|
|
539831
540124
|
this.sendMessageInternal(activeSession, item.text, {
|
|
539832
540125
|
parts: item.parts,
|
|
539833
|
-
imageAttachmentIds: item.imageAttachmentIds
|
|
540126
|
+
imageAttachmentIds: item.imageAttachmentIds,
|
|
540127
|
+
transcriptRendered: item.transcriptRendered
|
|
539834
540128
|
});
|
|
539835
540129
|
});
|
|
539836
540130
|
}
|
|
@@ -539849,9 +540143,13 @@ var BlunTUI = class {
|
|
|
539849
540143
|
this.sessionEventHandler.requestQueuedGoalPromotion();
|
|
539850
540144
|
}
|
|
539851
540145
|
sendMessageInternal(session, input, options) {
|
|
540146
|
+
if (this.manualPromptInFlight !== void 0) {
|
|
540147
|
+
this.enqueueMessage(input, options);
|
|
540148
|
+
return;
|
|
540149
|
+
}
|
|
539852
540150
|
armPersonalMemoryRememberIntent(session.id, input);
|
|
539853
540151
|
const imageAttachmentIds = options?.imageAttachmentIds !== void 0 && options.imageAttachmentIds.length > 0 ? options.imageAttachmentIds : void 0;
|
|
539854
|
-
this.appendTranscriptEntry({
|
|
540152
|
+
if (!options?.transcriptRendered) this.appendTranscriptEntry({
|
|
539855
540153
|
id: nextTranscriptId(),
|
|
539856
540154
|
kind: "user",
|
|
539857
540155
|
turnId: void 0,
|
|
@@ -539859,13 +540157,48 @@ var BlunTUI = class {
|
|
|
539859
540157
|
content: input,
|
|
539860
540158
|
imageAttachmentIds
|
|
539861
540159
|
});
|
|
540160
|
+
const agentId = this.harness.interactiveAgentId;
|
|
540161
|
+
const inFlight = {
|
|
540162
|
+
session,
|
|
540163
|
+
agentId
|
|
540164
|
+
};
|
|
540165
|
+
this.manualPromptInFlight = inFlight;
|
|
540166
|
+
let admissionResolved = false;
|
|
539862
540167
|
this.beginSessionRequest();
|
|
539863
540168
|
const sdkInput = options?.parts ?? input;
|
|
539864
540169
|
this.setAppState({
|
|
539865
540170
|
model: BLUN_KING_MODEL_ALIAS,
|
|
539866
540171
|
modelFallbackAllowed: false
|
|
539867
540172
|
});
|
|
539868
|
-
session.promptAccepted(sdkInput).
|
|
540173
|
+
session.promptAccepted(sdkInput).then((result) => {
|
|
540174
|
+
if (this.session !== session || this.manualPromptInFlight !== inFlight) return;
|
|
540175
|
+
admissionResolved = true;
|
|
540176
|
+
this.manualPromptInFlight = void 0;
|
|
540177
|
+
if (!result.accepted) {
|
|
540178
|
+
finishPersonalMemoryRememberIntentTurn(session.id);
|
|
540179
|
+
if (inFlight.flushRequested && this.queueFlushBatchRemaining > 0) this.queueFlushBatchRemaining += 1;
|
|
540180
|
+
this.state.queuedMessages = [{
|
|
540181
|
+
text: input,
|
|
540182
|
+
agentId,
|
|
540183
|
+
parts: options?.parts,
|
|
540184
|
+
imageAttachmentIds,
|
|
540185
|
+
transcriptRendered: true
|
|
540186
|
+
}, ...this.state.queuedMessages];
|
|
540187
|
+
this.track("input_queue");
|
|
540188
|
+
this.updateQueueDisplay();
|
|
540189
|
+
this.state.ui.requestRender();
|
|
540190
|
+
}
|
|
540191
|
+
this.syncChannelQueueDeadline();
|
|
540192
|
+
if (inFlight.flushRequested) this.steerQueueFlushPrefix();
|
|
540193
|
+
this.scheduleQueueDrain();
|
|
540194
|
+
}).catch((error) => {
|
|
540195
|
+
if (this.session !== session) return;
|
|
540196
|
+
if (admissionResolved) {
|
|
540197
|
+
this.showError(formatErrorMessage$2(error));
|
|
540198
|
+
return;
|
|
540199
|
+
}
|
|
540200
|
+
if (this.manualPromptInFlight !== inFlight) return;
|
|
540201
|
+
this.manualPromptInFlight = void 0;
|
|
539869
540202
|
finishPersonalMemoryRememberIntentTurn(session.id);
|
|
539870
540203
|
const message = formatErrorMessage$2(error);
|
|
539871
540204
|
this.failSessionRequest(uiText("blunTui.session.sendFailed", { error: message }));
|
|
@@ -539892,19 +540225,20 @@ var BlunTUI = class {
|
|
|
539892
540225
|
});
|
|
539893
540226
|
}
|
|
539894
540227
|
sendMessage(session, input, options) {
|
|
539895
|
-
if (this.deferUserMessages || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting) {
|
|
540228
|
+
if (this.deferUserMessages || this.manualPromptInFlight !== void 0 || this.state.queuedMessages.length > 0 || this.state.appState.streamingPhase !== "idle" || this.state.appState.isCompacting) {
|
|
539896
540229
|
this.enqueueMessage(input, options);
|
|
540230
|
+
this.scheduleQueueDrain();
|
|
539897
540231
|
return;
|
|
539898
540232
|
}
|
|
539899
540233
|
this.sendMessageInternal(session, input, options);
|
|
539900
540234
|
}
|
|
539901
540235
|
steerMessage(session, input) {
|
|
539902
|
-
if (this.deferUserMessages || this.state.appState.isCompacting) {
|
|
540236
|
+
if (this.deferUserMessages || this.state.appState.isCompacting || this.manualPromptInFlight !== void 0) {
|
|
539903
540237
|
for (const part of input) this.enqueueMessage(part);
|
|
539904
540238
|
return;
|
|
539905
540239
|
}
|
|
539906
540240
|
if (this.state.appState.streamingPhase === "idle") {
|
|
539907
|
-
for (const part of input) this.
|
|
540241
|
+
for (const part of input) this.sendMessage(session, part);
|
|
539908
540242
|
return;
|
|
539909
540243
|
}
|
|
539910
540244
|
for (const part of input) this.appendTranscriptEntry({
|
|
@@ -539924,6 +540258,7 @@ var BlunTUI = class {
|
|
|
539924
540258
|
this.state.startupState = "ready";
|
|
539925
540259
|
}
|
|
539926
540260
|
clearQueuedMessages() {
|
|
540261
|
+
this.manualPromptInFlight = void 0;
|
|
539927
540262
|
const discarded = [...this.state.queuedMessages, ...this.queueSteerInFlight?.items ?? []];
|
|
539928
540263
|
this.queueSteerInFlight?.onRestore?.();
|
|
539929
540264
|
this.state.queuedMessages = [];
|
|
@@ -540239,6 +540574,7 @@ var BlunTUI = class {
|
|
|
540239
540574
|
this.state.terminal.setTitle(label);
|
|
540240
540575
|
}
|
|
540241
540576
|
resetSessionRuntime() {
|
|
540577
|
+
this.manualPromptInFlight = void 0;
|
|
540242
540578
|
this.streamingUI.setSessionTotalTokens(void 0);
|
|
540243
540579
|
this.loopCountdown.dispose();
|
|
540244
540580
|
this.aborted = false;
|