blun-king-cli 9.1.37 → 9.1.39
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/blun.mjs +287 -15
- package/package.json +1 -1
package/blun.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// BLUN_BUILD_INPUT_SHA256:
|
|
2
|
+
// BLUN_BUILD_INPUT_SHA256:100000256c8742e28ac1b44fd85c258bacd68037bbbe180b9d79717e466d1d89
|
|
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);
|
|
@@ -3021,6 +3021,8 @@ async function generate(provider, systemPrompt, tools, history, callbacks, optio
|
|
|
3021
3021
|
serverDecodeMs,
|
|
3022
3022
|
clientConsumeMs
|
|
3023
3023
|
});
|
|
3024
|
+
const hasToolCall = message.toolCalls.length > 0 || pendingPart !== null && pendingPart.type === "function";
|
|
3025
|
+
if (stream.finishReason === null && hasToolCall) throw new APIConnectionError("The provider stream ended before its terminal finish reason. The incomplete response was discarded and no tool call was executed.");
|
|
3024
3026
|
if (pendingPart !== null) flushPart(message, pendingPart, toolCallIndexMap);
|
|
3025
3027
|
if (message.content.length === 0 && message.toolCalls.length === 0) throw new APIEmptyResponseError("The API returned an empty response (no content, no tool calls)." + formatFinishReasonHint(stream) + ` Provider: ${provider.name}, model: ${provider.modelName}`, {
|
|
3026
3028
|
finishReason: stream.finishReason,
|
|
@@ -258792,8 +258794,132 @@ var init_read_media = __esmMin((() => {
|
|
|
258792
258794
|
//#region ../../packages/agent-core/src/tools/builtin/file/write.md?raw
|
|
258793
258795
|
var write_default;
|
|
258794
258796
|
var init_write$1 = __esmMin((() => {
|
|
258795
|
-
write_default = "Create, append to, or replace a file entirely.\n\n- Missing parent directories are created automatically (like `mkdir(parents=True, exist_ok=True)`).\n- Mode defaults to overwrite; append adds content at EOF without adding a newline.\n- Write is NOT ALLOWED for incremental changes to existing files, including trivial, one-line, quick, or cosmetic edits. Use Edit instead.\n- Use Write only when the file does not exist, you intend a complete replacement, or the new contents have little continuity with the old contents.\n- Do not create unsolicited documentation files (`*.md` write-ups, `README`s, summaries) just because a task finished — write one only when the user asks for it, or when a task or project instruction requires it (e.g. the plan-mode plan file, created with Write when plan mode directs you to, or a changeset the repo mandates).\n- Read before overwriting an existing file.\n- Write ignores the Read/Edit line-number view. NEVER include line prefixes.\n- Write outputs content literally, including supplied line endings: \\n stays LF, \\r\\n stays CRLF.\n- Source files may contain at most 500 lines. Split larger implementations into focused files; append cannot bypass the limit.\n- Set `single_file_override=true` only when the latest direct user message explicitly requires one single file. The accepted override is reported visibly.\n- For
|
|
258796
|
-
}))
|
|
258797
|
+
write_default = "Create, append to, or replace a file entirely.\n\n- Missing parent directories are created automatically (like `mkdir(parents=True, exist_ok=True)`).\n- Mode defaults to overwrite; append adds content at EOF without adding a newline.\n- Write is NOT ALLOWED for incremental changes to existing files, including trivial, one-line, quick, or cosmetic edits. Use Edit instead.\n- Use Write only when the file does not exist, you intend a complete replacement, or the new contents have little continuity with the old contents.\n- Do not create unsolicited documentation files (`*.md` write-ups, `README`s, summaries) just because a task finished — write one only when the user asks for it, or when a task or project instruction requires it (e.g. the plan-mode plan file, created with Write when plan mode directs you to, or a changeset the repo mandates).\n- Read before overwriting an existing file.\n- Write ignores the Read/Edit line-number view. NEVER include line prefixes.\n- Write outputs content literally, including supplied line endings: \\n stays LF, \\r\\n stays CRLF.\n- Source files may contain at most 500 lines. Split larger implementations into focused files; append cannot bypass the limit.\n- Set `single_file_override=true` only when the latest direct user message explicitly requires one single file. The accepted override is reported visibly.\n- For any new or completely replaced file with content too large for one call, use `continuation`. Keep each chunk at or below the configured limit. The default is 2,048 UTF-8 bytes, a conservative safety margin for unstable model output rather than a measured hard model limit; operators can set `BLUN_WRITE_CONTINUATION_MAX_BYTES` from 512 through 16,384. End every chunk on a complete line with `\\n`. Set the exact final `expected_lines`; include `expected_sha256` when a source hash is available. Use overwrite and `start_line=1` for the first chunk, then use append for every later chunk and copy the exact `next_start_line` returned by the previous Write result; never continue from a planned boundary. Use consecutive part numbers. The target file remains unchanged until the final line-count and optional SHA-256 checks pass. To discard an unfinished sequence, restart with part 1, overwrite, `start_line=1`, and `reset=true`.\n- Never use continuation to modify an existing file incrementally. Use Edit for incremental changes.\n- Runaway generated JavaScript/TypeScript identifiers are rejected before disk I/O. Regenerate only the affected section as a smaller focused chunk.\n";
|
|
258798
|
+
}));
|
|
258799
|
+
//#endregion
|
|
258800
|
+
//#region ../../packages/agent-core/src/tools/builtin/file/generated-source-health.ts
|
|
258801
|
+
/**
|
|
258802
|
+
* Find runaway generated identifiers while ignoring comments and strings.
|
|
258803
|
+
* A 512-character identifier is already far beyond normal generated source,
|
|
258804
|
+
* while quoted assets and long comments remain valid input.
|
|
258805
|
+
*/
|
|
258806
|
+
function findDegenerateGeneratedIdentifier(path, content) {
|
|
258807
|
+
if (!JAVASCRIPT_LIKE_EXTENSIONS.has(extname$1(path).toLowerCase())) return null;
|
|
258808
|
+
let index = 0;
|
|
258809
|
+
let line = 1;
|
|
258810
|
+
while (index < content.length) {
|
|
258811
|
+
const current = content[index] ?? "";
|
|
258812
|
+
const next = content[index + 1] ?? "";
|
|
258813
|
+
if (current === "\n") {
|
|
258814
|
+
line += 1;
|
|
258815
|
+
index += 1;
|
|
258816
|
+
continue;
|
|
258817
|
+
}
|
|
258818
|
+
if (current === "/" && next === "/") {
|
|
258819
|
+
index = skipLineComment(content, index + 2);
|
|
258820
|
+
continue;
|
|
258821
|
+
}
|
|
258822
|
+
if (current === "/" && next === "*") {
|
|
258823
|
+
const skipped = skipBlockComment(content, index + 2, line);
|
|
258824
|
+
index = skipped.index;
|
|
258825
|
+
line = skipped.line;
|
|
258826
|
+
continue;
|
|
258827
|
+
}
|
|
258828
|
+
if (current === "'" || current === "\"" || current === "`") {
|
|
258829
|
+
const skipped = skipQuoted(content, index + 1, line, current);
|
|
258830
|
+
index = skipped.index;
|
|
258831
|
+
line = skipped.line;
|
|
258832
|
+
continue;
|
|
258833
|
+
}
|
|
258834
|
+
if (isIdentifierStart(current)) {
|
|
258835
|
+
const start = index;
|
|
258836
|
+
index += 1;
|
|
258837
|
+
while (index < content.length && isIdentifierPart(content[index] ?? "")) index += 1;
|
|
258838
|
+
const length = index - start;
|
|
258839
|
+
if (length > 512) return {
|
|
258840
|
+
length,
|
|
258841
|
+
line,
|
|
258842
|
+
preview: content.slice(start, start + 80)
|
|
258843
|
+
};
|
|
258844
|
+
continue;
|
|
258845
|
+
}
|
|
258846
|
+
index += 1;
|
|
258847
|
+
}
|
|
258848
|
+
return null;
|
|
258849
|
+
}
|
|
258850
|
+
function skipLineComment(content, index) {
|
|
258851
|
+
const newline = content.indexOf("\n", index);
|
|
258852
|
+
return newline < 0 ? content.length : newline;
|
|
258853
|
+
}
|
|
258854
|
+
function skipBlockComment(content, start, initialLine) {
|
|
258855
|
+
let index = start;
|
|
258856
|
+
let line = initialLine;
|
|
258857
|
+
while (index < content.length) {
|
|
258858
|
+
if (content[index] === "\n") line += 1;
|
|
258859
|
+
if (content[index] === "*" && content[index + 1] === "/") return {
|
|
258860
|
+
index: index + 2,
|
|
258861
|
+
line
|
|
258862
|
+
};
|
|
258863
|
+
index += 1;
|
|
258864
|
+
}
|
|
258865
|
+
return {
|
|
258866
|
+
index: content.length,
|
|
258867
|
+
line
|
|
258868
|
+
};
|
|
258869
|
+
}
|
|
258870
|
+
function skipQuoted(content, start, initialLine, quote) {
|
|
258871
|
+
let index = start;
|
|
258872
|
+
let line = initialLine;
|
|
258873
|
+
while (index < content.length) {
|
|
258874
|
+
const current = content[index] ?? "";
|
|
258875
|
+
if (current === "\\") {
|
|
258876
|
+
index += 2;
|
|
258877
|
+
continue;
|
|
258878
|
+
}
|
|
258879
|
+
if (current === "\n") line += 1;
|
|
258880
|
+
index += 1;
|
|
258881
|
+
if (current === quote) break;
|
|
258882
|
+
}
|
|
258883
|
+
return {
|
|
258884
|
+
index,
|
|
258885
|
+
line
|
|
258886
|
+
};
|
|
258887
|
+
}
|
|
258888
|
+
function isIdentifierStart(value) {
|
|
258889
|
+
return /[A-Za-z_$]/.test(value);
|
|
258890
|
+
}
|
|
258891
|
+
function isIdentifierPart(value) {
|
|
258892
|
+
return /[A-Za-z0-9_$]/.test(value);
|
|
258893
|
+
}
|
|
258894
|
+
var JAVASCRIPT_LIKE_EXTENSIONS;
|
|
258895
|
+
var init_generated_source_health = __esmMin((() => {
|
|
258896
|
+
init_dist$6();
|
|
258897
|
+
JAVASCRIPT_LIKE_EXTENSIONS = new Set([
|
|
258898
|
+
".cjs",
|
|
258899
|
+
".cts",
|
|
258900
|
+
".js",
|
|
258901
|
+
".jsx",
|
|
258902
|
+
".mjs",
|
|
258903
|
+
".mts",
|
|
258904
|
+
".ts",
|
|
258905
|
+
".tsx"
|
|
258906
|
+
]);
|
|
258907
|
+
}));
|
|
258908
|
+
//#endregion
|
|
258909
|
+
//#region ../../packages/agent-core/src/tools/builtin/file/write.ts
|
|
258910
|
+
function resolveWriteContinuationChunkBytes(env = process.env) {
|
|
258911
|
+
const raw = env[CONTINUATION_CHUNK_BYTES_ENV]?.trim();
|
|
258912
|
+
if (raw === void 0 || raw === "") return DEFAULT_CONTINUATION_CHUNK_BYTES;
|
|
258913
|
+
const parsed = Number(raw);
|
|
258914
|
+
if (!Number.isSafeInteger(parsed) || parsed < MIN_CONTINUATION_CHUNK_BYTES || parsed > MAX_CONTINUATION_CHUNK_BYTES) throw new Error(`${CONTINUATION_CHUNK_BYTES_ENV} must be an integer between ${String(MIN_CONTINUATION_CHUNK_BYTES)} and ${String(MAX_CONTINUATION_CHUNK_BYTES)} bytes; received ${JSON.stringify(raw)}.`);
|
|
258915
|
+
return parsed;
|
|
258916
|
+
}
|
|
258917
|
+
function countCompletedLines(content) {
|
|
258918
|
+
let count = 0;
|
|
258919
|
+
for (const character of content) if (character === "\n") count += 1;
|
|
258920
|
+
return count;
|
|
258921
|
+
}
|
|
258922
|
+
var S_IFMT, S_IFDIR, DEFAULT_CONTINUATION_CHUNK_BYTES, MIN_CONTINUATION_CHUNK_BYTES, MAX_CONTINUATION_CHUNK_BYTES, CONTINUATION_CHUNK_BYTES_ENV, WriteContinuationSchema, WriteInputSchema, WriteTool;
|
|
258797
258923
|
var init_write = __esmMin((() => {
|
|
258798
258924
|
init_dist$6();
|
|
258799
258925
|
init_zod$1();
|
|
@@ -258803,14 +258929,28 @@ var init_write = __esmMin((() => {
|
|
|
258803
258929
|
init_rule_match();
|
|
258804
258930
|
init_source_file_line_limit();
|
|
258805
258931
|
init_write$1();
|
|
258932
|
+
init_generated_source_health();
|
|
258806
258933
|
init_lsp_diagnostics();
|
|
258807
258934
|
S_IFMT = 61440;
|
|
258808
258935
|
S_IFDIR = 16384;
|
|
258936
|
+
DEFAULT_CONTINUATION_CHUNK_BYTES = 2048;
|
|
258937
|
+
MIN_CONTINUATION_CHUNK_BYTES = 512;
|
|
258938
|
+
MAX_CONTINUATION_CHUNK_BYTES = 16384;
|
|
258939
|
+
CONTINUATION_CHUNK_BYTES_ENV = "BLUN_WRITE_CONTINUATION_MAX_BYTES";
|
|
258940
|
+
WriteContinuationSchema = object({
|
|
258941
|
+
part: number$1().int().positive().describe("One-based chunk number. Start with 1 and increment by exactly one."),
|
|
258942
|
+
start_line: number$1().int().positive().describe("One-based line where this chunk starts. Use 1 for part 1; for every later part, copy next_start_line from the preceding tool result instead of using a planned boundary."),
|
|
258943
|
+
expected_lines: number$1().int().positive().describe("Exact completed-line count expected in the final file."),
|
|
258944
|
+
final: boolean$1().describe("Set true only for the last chunk, when the complete file is present."),
|
|
258945
|
+
expected_sha256: string().regex(/^[0-9a-f]{64}$/i).optional().describe("Optional SHA-256 of the complete expected file for byte-for-byte verification."),
|
|
258946
|
+
reset: boolean$1().optional().describe("Set true only with part 1 and overwrite to discard an unfinished staged write.")
|
|
258947
|
+
});
|
|
258809
258948
|
WriteInputSchema = object({
|
|
258810
258949
|
path: string().describe("Path to the file to create, append to, or completely overwrite. Relative paths resolve against the working directory; a path outside the working directory must be absolute. Missing parent directories are created automatically."),
|
|
258811
258950
|
content: string().describe("Raw full file content to write exactly as provided. This does not use the Read/Edit text view."),
|
|
258812
258951
|
mode: _enum(["overwrite", "append"]).optional().describe("Write mode. Defaults to overwrite. append adds content to the end exactly as provided and does not add a newline."),
|
|
258813
|
-
single_file_override: boolean$1().optional().describe("Set true only when the latest direct user message explicitly requires one single source file. The tool verifies that request and reports the override visibly.")
|
|
258952
|
+
single_file_override: boolean$1().optional().describe("Set true only when the latest direct user message explicitly requires one single source file. The tool verifies that request and reports the override visibly."),
|
|
258953
|
+
continuation: WriteContinuationSchema.optional().describe("Safe long-write protocol. Split generated files into complete-line chunks within the configured limit (2,048 UTF-8 bytes by default; operator override BLUN_WRITE_CONTINUATION_MAX_BYTES). Part 1 uses overwrite and start_line=1; later parts use append and the exact next_start_line returned by the previous call. The target file is changed only after the final line-count and optional SHA-256 checks pass.")
|
|
258814
258954
|
});
|
|
258815
258955
|
object({
|
|
258816
258956
|
/** Number of UTF-8 bytes written to disk by this call. */
|
|
@@ -258820,14 +258960,17 @@ bytesWritten: number$1().int().nonnegative() });
|
|
|
258820
258960
|
workspace;
|
|
258821
258961
|
history;
|
|
258822
258962
|
lsp;
|
|
258963
|
+
maxContinuationChunkBytes;
|
|
258823
258964
|
name = "Write";
|
|
258824
258965
|
description = write_default;
|
|
258825
258966
|
parameters = toInputJsonSchema(WriteInputSchema);
|
|
258826
|
-
|
|
258967
|
+
pendingContinuations = /* @__PURE__ */ new Map();
|
|
258968
|
+
constructor(kaos, workspace, history, lsp, maxContinuationChunkBytes = resolveWriteContinuationChunkBytes()) {
|
|
258827
258969
|
this.kaos = kaos;
|
|
258828
258970
|
this.workspace = workspace;
|
|
258829
258971
|
this.history = history;
|
|
258830
258972
|
this.lsp = lsp;
|
|
258973
|
+
this.maxContinuationChunkBytes = maxContinuationChunkBytes;
|
|
258831
258974
|
}
|
|
258832
258975
|
resolveExecution(args) {
|
|
258833
258976
|
const path = resolvePathAccessPath(args.path, {
|
|
@@ -258850,11 +258993,125 @@ bytesWritten: number$1().int().nonnegative() });
|
|
|
258850
258993
|
pathClass: this.kaos.pathClass(),
|
|
258851
258994
|
homeDir: this.kaos.gethome()
|
|
258852
258995
|
}),
|
|
258853
|
-
execute: () => this.execution(args, path)
|
|
258996
|
+
execute: (ctx) => this.execution(args, path, ctx)
|
|
258854
258997
|
};
|
|
258855
258998
|
}
|
|
258856
|
-
async execution(args, safePath) {
|
|
258999
|
+
async execution(args, safePath, ctx) {
|
|
259000
|
+
if (args.continuation !== void 0) return this.continueWrite(args, safePath, ctx);
|
|
259001
|
+
if (this.pendingContinuations.has(safePath)) return {
|
|
259002
|
+
isError: true,
|
|
259003
|
+
output: `Refused to write ${args.path}: a long-write continuation is active for this path. Resume it with the next numbered continuation part. No file was written.`
|
|
259004
|
+
};
|
|
259005
|
+
return this.writeContent(args, safePath);
|
|
259006
|
+
}
|
|
259007
|
+
async continueWrite(args, safePath, ctx) {
|
|
259008
|
+
const continuation = args.continuation;
|
|
259009
|
+
if (continuation === void 0) throw new Error("continuation is required");
|
|
259010
|
+
const mode = args.mode ?? "overwrite";
|
|
259011
|
+
const chunkBytes = Buffer.byteLength(args.content, "utf8");
|
|
259012
|
+
if (chunkBytes > this.maxContinuationChunkBytes) return {
|
|
259013
|
+
isError: true,
|
|
259014
|
+
output: `Refused continuation part ${String(continuation.part)} for ${args.path}: chunks may contain at most ${this.maxContinuationChunkBytes.toLocaleString("en-US")} UTF-8 bytes (${String(chunkBytes)} received). No content was staged and no file was written.`
|
|
259015
|
+
};
|
|
259016
|
+
if (args.content.length === 0 || !args.content.endsWith("\n")) return {
|
|
259017
|
+
isError: true,
|
|
259018
|
+
output: `Refused continuation part ${String(continuation.part)} for ${args.path}: every chunk must end on a complete line with a newline. No content was staged and no file was written.`
|
|
259019
|
+
};
|
|
259020
|
+
if (continuation.reset === true && (continuation.part !== 1 || mode !== "overwrite")) return {
|
|
259021
|
+
isError: true,
|
|
259022
|
+
output: `Refused continuation reset for ${args.path}: reset requires part 1 with mode=overwrite. No content was staged and no file was written.`
|
|
259023
|
+
};
|
|
259024
|
+
if (continuation.reset === true) this.pendingContinuations.delete(safePath);
|
|
259025
|
+
const current = this.pendingContinuations.get(safePath);
|
|
259026
|
+
const expectedPart = current === void 0 ? 1 : current.chunks.length + 1;
|
|
259027
|
+
if (continuation.part !== expectedPart) return {
|
|
259028
|
+
isError: true,
|
|
259029
|
+
output: `Refused continuation part ${String(continuation.part)} for ${args.path}: expected part ${String(expectedPart)}. No content was staged and no file was written.`
|
|
259030
|
+
};
|
|
259031
|
+
const expectedStartLine = (current?.completedLines ?? 0) + 1;
|
|
259032
|
+
if (continuation.start_line !== expectedStartLine) return {
|
|
259033
|
+
isError: true,
|
|
259034
|
+
output: `Refused continuation part ${String(continuation.part)} for ${args.path}: expected start_line ${String(expectedStartLine)} from the actually staged content, received ${String(continuation.start_line)}. No content was staged and no file was written.`
|
|
259035
|
+
};
|
|
259036
|
+
if (continuation.part === 1 && mode !== "overwrite" || continuation.part > 1 && mode !== "append") return {
|
|
259037
|
+
isError: true,
|
|
259038
|
+
output: `Refused continuation part ${String(continuation.part)} for ${args.path}: part 1 must use overwrite and later parts must use append. No content was staged and no file was written.`
|
|
259039
|
+
};
|
|
259040
|
+
if (current !== void 0 && continuation.expected_lines !== current.expectedLines) return {
|
|
259041
|
+
isError: true,
|
|
259042
|
+
output: `Refused continuation part ${String(continuation.part)} for ${args.path}: expected_lines changed from ${String(current.expectedLines)} to ${String(continuation.expected_lines)}. No content was staged and no file was written.`
|
|
259043
|
+
};
|
|
259044
|
+
const expectedSha256 = continuation.expected_sha256?.toLowerCase();
|
|
259045
|
+
if (current?.expectedSha256 !== void 0 && expectedSha256 !== void 0 && expectedSha256 !== current.expectedSha256) return {
|
|
259046
|
+
isError: true,
|
|
259047
|
+
output: `Refused continuation part ${String(continuation.part)} for ${args.path}: expected_sha256 changed between parts. No content was staged and no file was written.`
|
|
259048
|
+
};
|
|
259049
|
+
const chunkLines = countCompletedLines(args.content);
|
|
259050
|
+
const completedBefore = current?.completedLines ?? 0;
|
|
259051
|
+
const completedLines = completedBefore + chunkLines;
|
|
259052
|
+
if (completedLines > continuation.expected_lines) return {
|
|
259053
|
+
isError: true,
|
|
259054
|
+
output: `Refused continuation part ${String(continuation.part)} for ${args.path}: it would stage ${String(completedLines)} lines, exceeding the expected ${String(continuation.expected_lines)}. No content was staged and no file was written.`
|
|
259055
|
+
};
|
|
259056
|
+
if (!continuation.final && completedLines === continuation.expected_lines) return {
|
|
259057
|
+
isError: true,
|
|
259058
|
+
output: `Refused continuation part ${String(continuation.part)} for ${args.path}: all ${String(completedLines)} expected lines are present but final is false. Retry this part with final=true. No content was staged and no file was written.`
|
|
259059
|
+
};
|
|
259060
|
+
if (continuation.final && completedLines !== continuation.expected_lines) return {
|
|
259061
|
+
isError: true,
|
|
259062
|
+
output: `Refused final continuation part ${String(continuation.part)} for ${args.path}: expected ${String(continuation.expected_lines)} lines but would have ${String(completedLines)}. No content was staged and no file was written.`
|
|
259063
|
+
};
|
|
259064
|
+
const startLine = completedBefore + 1;
|
|
259065
|
+
const boundary = `${String(startLine)}-${String(completedLines)}`;
|
|
259066
|
+
const chunks = [...current?.chunks ?? [], args.content];
|
|
259067
|
+
const boundaries = [...current?.boundaries ?? [], boundary];
|
|
259068
|
+
const totalBytes = (current?.bytes ?? 0) + chunkBytes;
|
|
259069
|
+
const stableExpectedSha256 = current?.expectedSha256 ?? expectedSha256;
|
|
259070
|
+
const staged = {
|
|
259071
|
+
expectedLines: continuation.expected_lines,
|
|
259072
|
+
expectedSha256: stableExpectedSha256,
|
|
259073
|
+
chunks,
|
|
259074
|
+
boundaries,
|
|
259075
|
+
completedLines,
|
|
259076
|
+
bytes: totalBytes
|
|
259077
|
+
};
|
|
259078
|
+
const percent = Math.floor(completedLines / continuation.expected_lines * 100);
|
|
259079
|
+
ctx?.onUpdate?.({
|
|
259080
|
+
kind: "progress",
|
|
259081
|
+
text: `Write continuation ${String(continuation.part)}: lines ${boundary}`,
|
|
259082
|
+
percent
|
|
259083
|
+
});
|
|
259084
|
+
if (!continuation.final) {
|
|
259085
|
+
this.pendingContinuations.set(safePath, staged);
|
|
259086
|
+
return { output: `Write continuation part ${String(continuation.part)} staged lines ${boundary} of ${String(continuation.expected_lines)} (${String(percent)}%, ${String(chunkBytes)} bytes). Target unchanged. Continue with mode=append, part=${String(continuation.part + 1)}, next_start_line=${String(completedLines + 1)}.` };
|
|
259087
|
+
}
|
|
259088
|
+
const content = chunks.join("");
|
|
259089
|
+
const actualSha256 = createHash("sha256").update(content).digest("hex");
|
|
259090
|
+
if (stableExpectedSha256 !== void 0 && actualSha256 !== stableExpectedSha256) return {
|
|
259091
|
+
isError: true,
|
|
259092
|
+
output: `Refused final continuation part ${String(continuation.part)} for ${args.path}: SHA-256 mismatch (expected ${stableExpectedSha256}, got ${actualSha256}). No file was written; the first ${String(current?.completedLines ?? 0)} staged lines remain available for retry.`
|
|
259093
|
+
};
|
|
259094
|
+
const result = await this.writeContent({
|
|
259095
|
+
...args,
|
|
259096
|
+
content,
|
|
259097
|
+
mode: "overwrite",
|
|
259098
|
+
continuation: void 0
|
|
259099
|
+
}, safePath, true);
|
|
259100
|
+
if (result.isError === true) return result;
|
|
259101
|
+
this.pendingContinuations.delete(safePath);
|
|
259102
|
+
const writeOutput = typeof result.output === "string" ? result.output : "Write completed.";
|
|
259103
|
+
return {
|
|
259104
|
+
...result,
|
|
259105
|
+
output: `Write continuation complete: ${String(completedLines)} lines, ${String(totalBytes)} bytes, SHA-256 ${actualSha256}. Chunk boundaries: ${boundaries.join(", ")}. ${writeOutput}`
|
|
259106
|
+
};
|
|
259107
|
+
}
|
|
259108
|
+
async writeContent(args, safePath, allowLargeContent = false) {
|
|
258857
259109
|
const mode = args.mode ?? "overwrite";
|
|
259110
|
+
const degenerateIdentifier = findDegenerateGeneratedIdentifier(safePath, args.content);
|
|
259111
|
+
if (degenerateIdentifier !== null) return {
|
|
259112
|
+
isError: true,
|
|
259113
|
+
output: `Refused to write ${args.path}: generated source contains a runaway ${String(degenerateIdentifier.length)}-character identifier on line ${String(degenerateIdentifier.line)} (starts with ${JSON.stringify(degenerateIdentifier.preview)}). Regenerate that section in a smaller focused chunk. No file was written.`
|
|
259114
|
+
};
|
|
258858
259115
|
let currentContent;
|
|
258859
259116
|
if (isSourceFilePath(safePath)) try {
|
|
258860
259117
|
currentContent = await this.kaos.readText(safePath);
|
|
@@ -258874,6 +259131,11 @@ bytesWritten: number$1().int().nonnegative() });
|
|
|
258874
259131
|
isError: true,
|
|
258875
259132
|
output: lineLimit.error
|
|
258876
259133
|
};
|
|
259134
|
+
const contentBytes = Buffer.byteLength(args.content, "utf8");
|
|
259135
|
+
if (!allowLargeContent && contentBytes > this.maxContinuationChunkBytes) return {
|
|
259136
|
+
isError: true,
|
|
259137
|
+
output: `Refused to write ${args.path}: the content is ${String(contentBytes)} UTF-8 bytes. Long generated writes must use the continuation protocol with complete-line chunks of at most ${this.maxContinuationChunkBytes.toLocaleString("en-US")} bytes. No file was written.`
|
|
259138
|
+
};
|
|
258877
259139
|
const parentError = await this.ensureParentDirectory(safePath);
|
|
258878
259140
|
if (parentError !== void 0) return {
|
|
258879
259141
|
isError: true,
|
|
@@ -369966,7 +370228,7 @@ var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
369966
370228
|
};
|
|
369967
370229
|
}));
|
|
369968
370230
|
//#endregion
|
|
369969
|
-
//#region
|
|
370231
|
+
//#region ../../../node_modules/node-gyp-build/node-gyp-build.js
|
|
369970
370232
|
var require_node_gyp_build$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
369971
370233
|
var fs$3 = __require("fs");
|
|
369972
370234
|
var path$5 = __require("path");
|
|
@@ -370122,14 +370384,14 @@ var require_node_gyp_build$1 = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
370122
370384
|
load.compareTuples = compareTuples;
|
|
370123
370385
|
}));
|
|
370124
370386
|
//#endregion
|
|
370125
|
-
//#region
|
|
370387
|
+
//#region ../../../node_modules/node-gyp-build/index.js
|
|
370126
370388
|
var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
370127
370389
|
const runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
370128
370390
|
if (typeof runtimeRequire.addon === "function") module.exports = runtimeRequire.addon.bind(runtimeRequire);
|
|
370129
370391
|
else module.exports = require_node_gyp_build$1();
|
|
370130
370392
|
}));
|
|
370131
370393
|
//#endregion
|
|
370132
|
-
//#region
|
|
370394
|
+
//#region ../../../node_modules/bufferutil/fallback.js
|
|
370133
370395
|
var require_fallback$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
370134
370396
|
/**
|
|
370135
370397
|
* Masks a buffer using the given mask.
|
|
@@ -370161,7 +370423,7 @@ var require_fallback$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
370161
370423
|
};
|
|
370162
370424
|
}));
|
|
370163
370425
|
//#endregion
|
|
370164
|
-
//#region
|
|
370426
|
+
//#region ../../../node_modules/bufferutil/index.js
|
|
370165
370427
|
var require_bufferutil = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
370166
370428
|
try {
|
|
370167
370429
|
module.exports = require_node_gyp_build()(__dirname);
|
|
@@ -370654,7 +370916,7 @@ var require_permessage_deflate = /* @__PURE__ */ __commonJSMin(((exports, module
|
|
|
370654
370916
|
}
|
|
370655
370917
|
}));
|
|
370656
370918
|
//#endregion
|
|
370657
|
-
//#region
|
|
370919
|
+
//#region ../../../node_modules/utf-8-validate/fallback.js
|
|
370658
370920
|
var require_fallback = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
370659
370921
|
/**
|
|
370660
370922
|
* Checks if a given buffer contains only correct UTF-8.
|
|
@@ -370684,7 +370946,7 @@ var require_fallback = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
370684
370946
|
module.exports = isValidUTF8;
|
|
370685
370947
|
}));
|
|
370686
370948
|
//#endregion
|
|
370687
|
-
//#region
|
|
370949
|
+
//#region ../../../node_modules/utf-8-validate/index.js
|
|
370688
370950
|
var require_utf_8_validate = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
370689
370951
|
try {
|
|
370690
370952
|
module.exports = require_node_gyp_build()(__dirname);
|
|
@@ -419738,6 +420000,15 @@ function isBusy(host) {
|
|
|
419738
420000
|
//#endregion
|
|
419739
420001
|
//#region src/tui/commands/loop.ts
|
|
419740
420002
|
const LOOP_ARGUMENT_HINT = "/loop [interval] [prompt]";
|
|
420003
|
+
const LOOP_CREATE_FILLERS = new Set([
|
|
420004
|
+
"mit",
|
|
420005
|
+
"alle",
|
|
420006
|
+
"jede",
|
|
420007
|
+
"jeden",
|
|
420008
|
+
"every",
|
|
420009
|
+
"each"
|
|
420010
|
+
]);
|
|
420011
|
+
const LOOP_INTERVAL_TOKEN = /^\d+(?:m|h|d)$/i;
|
|
419741
420012
|
function parseLoopCommand(rawArgs) {
|
|
419742
420013
|
const args = rawArgs.trim();
|
|
419743
420014
|
if (args.length === 0 || args.toLowerCase() === "status") return { kind: "status" };
|
|
@@ -419748,10 +420019,11 @@ function parseLoopCommand(rawArgs) {
|
|
|
419748
420019
|
if (action === "resume") return { kind: "resume" };
|
|
419749
420020
|
if (action === "stop") return { kind: "stop" };
|
|
419750
420021
|
}
|
|
419751
|
-
|
|
420022
|
+
let startIndex = action === "start" ? 1 : 0;
|
|
420023
|
+
if (LOOP_CREATE_FILLERS.has(tokens[startIndex]?.toLowerCase() ?? "")) startIndex += 1;
|
|
419752
420024
|
const interval = tokens[startIndex];
|
|
419753
420025
|
const prompt = tokens.slice(startIndex + 1).join(" ").trim();
|
|
419754
|
-
if (interval === void 0 || prompt.length === 0) return { kind: "error" };
|
|
420026
|
+
if (interval === void 0 || !LOOP_INTERVAL_TOKEN.test(interval) || prompt.length === 0) return { kind: "error" };
|
|
419755
420027
|
return {
|
|
419756
420028
|
kind: "create",
|
|
419757
420029
|
interval,
|