@molecule/api-ai-tools 1.5.13 → 1.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +99 -12
- package/dist/utilities.d.ts +18 -0
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +39 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
|
3
3
|
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
4
|
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
5
|
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
-
Generated: 2026-10-
|
|
6
|
+
Generated: 2026-10-16T05:16:44.196Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-ai-tools
|
|
@@ -887,6 +887,22 @@ function parseCheckProblem(result: {
|
|
|
887
887
|
|
|
888
888
|
**Returns:** The message for the tool result, or null.
|
|
889
889
|
|
|
890
|
+
#### `fusedLine(content, newString)`
|
|
891
|
+
|
|
892
|
+
The line a replacement fused when it dropped the newline its `old_string`
|
|
893
|
+
ended with: `new_string`'s text is followed on the same line by what used to
|
|
894
|
+
be the next line. Read off the file as it now is, so the line shown is the
|
|
895
|
+
line the parser is actually complaining about.
|
|
896
|
+
|
|
897
|
+
```typescript
|
|
898
|
+
function fusedLine(content: string, newString: string): { line: number; text: string } | null
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
- `content` — The file's content after the edit.
|
|
902
|
+
- `newString` — A replacement's `new_string`, whose `old_string` ended with a newline.
|
|
903
|
+
|
|
904
|
+
**Returns:** The fused line's 1-based number and its text, or null when the replacement's text is not followed by more of the same line (it ends the file, the following newline survived, or another replacement rewrote it).
|
|
905
|
+
|
|
890
906
|
### Constants
|
|
891
907
|
|
|
892
908
|
#### `TOOL_SCHEMAS`
|
|
@@ -967,6 +983,14 @@ needs.
|
|
|
967
983
|
const MAX_READ_RETURN_CHARS: number
|
|
968
984
|
```
|
|
969
985
|
|
|
986
|
+
#### `SMALL_FILE_WHOLE_CHARS`
|
|
987
|
+
|
|
988
|
+
A file this small is returned whole even when a window is asked for (paging it costs a call each).
|
|
989
|
+
|
|
990
|
+
```typescript
|
|
991
|
+
const SMALL_FILE_WHOLE_CHARS: number
|
|
992
|
+
```
|
|
993
|
+
|
|
970
994
|
#### `MAX_SEARCH_RESULTS`
|
|
971
995
|
|
|
972
996
|
Max search results.
|
|
@@ -1053,6 +1077,15 @@ Peer dependencies:
|
|
|
1053
1077
|
Line numbers count the way `read_file` does, so they are valid offsets. The
|
|
1054
1078
|
excerpt is redacted like a read. The existing fields (`ok`,
|
|
1055
1079
|
`replacementsApplied`, `alreadyApplied`, `syntaxError`) are unchanged.
|
|
1080
|
+
- **A dropped line break and the parse error it causes are reported as ONE
|
|
1081
|
+
thing.** When a replacement's `old_string` ended with a newline and its
|
|
1082
|
+
`new_string` does not (so the next line is pulled up onto it) and the file
|
|
1083
|
+
then fails its parse check, `edit_file` returns a single `syntaxError` that
|
|
1084
|
+
names the replacement, shows the fused line with its number, and says to
|
|
1085
|
+
re-send that replacement with the newline restored — instead of the
|
|
1086
|
+
`newlineNote` hint beside an unrelated-looking parse error, which an executor
|
|
1087
|
+
repairs by rewriting the fused line's text and fuses the next one. The
|
|
1088
|
+
`newlineNote` alone is still returned when the file parses.
|
|
1056
1089
|
- **A parameter under another name is not a broken tool.** Every tool's input
|
|
1057
1090
|
is normalized first (`cmd` → `command`, `file` → `path`, camelCase ⇄
|
|
1058
1091
|
snake_case), and a handler that throws returns an error naming the missing
|
package/dist/index.d.ts
CHANGED
|
@@ -38,6 +38,15 @@
|
|
|
38
38
|
* Line numbers count the way `read_file` does, so they are valid offsets. The
|
|
39
39
|
* excerpt is redacted like a read. The existing fields (`ok`,
|
|
40
40
|
* `replacementsApplied`, `alreadyApplied`, `syntaxError`) are unchanged.
|
|
41
|
+
* - **A dropped line break and the parse error it causes are reported as ONE
|
|
42
|
+
* thing.** When a replacement's `old_string` ended with a newline and its
|
|
43
|
+
* `new_string` does not (so the next line is pulled up onto it) and the file
|
|
44
|
+
* then fails its parse check, `edit_file` returns a single `syntaxError` that
|
|
45
|
+
* names the replacement, shows the fused line with its number, and says to
|
|
46
|
+
* re-send that replacement with the newline restored — instead of the
|
|
47
|
+
* `newlineNote` hint beside an unrelated-looking parse error, which an executor
|
|
48
|
+
* repairs by rewriting the fused line's text and fuses the next one. The
|
|
49
|
+
* `newlineNote` alone is still returned when the file parses.
|
|
41
50
|
* - **A parameter under another name is not a broken tool.** Every tool's input
|
|
42
51
|
* is normalized first (`cmd` → `command`, `file` → `path`, camelCase ⇄
|
|
43
52
|
* snake_case), and a handler that throws returns an error naming the missing
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkHG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,15 @@
|
|
|
38
38
|
* Line numbers count the way `read_file` does, so they are valid offsets. The
|
|
39
39
|
* excerpt is redacted like a read. The existing fields (`ok`,
|
|
40
40
|
* `replacementsApplied`, `alreadyApplied`, `syntaxError`) are unchanged.
|
|
41
|
+
* - **A dropped line break and the parse error it causes are reported as ONE
|
|
42
|
+
* thing.** When a replacement's `old_string` ended with a newline and its
|
|
43
|
+
* `new_string` does not (so the next line is pulled up onto it) and the file
|
|
44
|
+
* then fails its parse check, `edit_file` returns a single `syntaxError` that
|
|
45
|
+
* names the replacement, shows the fused line with its number, and says to
|
|
46
|
+
* re-send that replacement with the newline restored — instead of the
|
|
47
|
+
* `newlineNote` hint beside an unrelated-looking parse error, which an executor
|
|
48
|
+
* repairs by rewriting the fused line's text and fuses the next one. The
|
|
49
|
+
* `newlineNote` alone is still returned when the file parses.
|
|
41
50
|
* - **A parameter under another name is not a broken tool.** Every tool's input
|
|
42
51
|
* is normalized first (`cmd` → `command`, `file` → `path`, camelCase ⇄
|
|
43
52
|
* snake_case), and a handler that throws returns an error naming the missing
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AA8B9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AA8B9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA0CnE;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAooDxF"}
|
package/dist/tools.js
CHANGED
|
@@ -25,7 +25,7 @@ const waitedByTask = new Map();
|
|
|
25
25
|
const logBytesByTask = new Map();
|
|
26
26
|
/** The file a background command writes its own output to, when it redirects it. */
|
|
27
27
|
const outputFileByTask = new Map();
|
|
28
|
-
import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, isEnvFilePath, isOutsideRoot, isValidGlob, MAX_BATCH_READ_BYTES, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_OUTPUT_SIZE, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, outputRedirectTarget, parseCheckCommand, parseCheckProblem, pathArgError, redactSecrets, redactSecretsInCode, resolvePath, shellQuote, sliceLines, streamingBackgroundCommand, stripControlChars, truncateMiddle, whitespaceTolerantReplace, } from './utilities.js';
|
|
28
|
+
import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, fusedLine, isEnvFilePath, isOutsideRoot, isValidGlob, MAX_BATCH_READ_BYTES, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_OUTPUT_SIZE, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, outputRedirectTarget, parseCheckCommand, parseCheckProblem, pathArgError, redactSecrets, redactSecretsInCode, resolvePath, shellQuote, sliceLines, SMALL_FILE_WHOLE_CHARS, streamingBackgroundCommand, stripControlChars, truncateMiddle, whitespaceTolerantReplace, } from './utilities.js';
|
|
29
29
|
/**
|
|
30
30
|
* How long exec_command waits for a command it backgrounded only because the
|
|
31
31
|
* command asked for more than the tool's ceiling, before handing back a task id.
|
|
@@ -133,6 +133,8 @@ export function buildTools(backend, config) {
|
|
|
133
133
|
}
|
|
134
134
|
return pathGuards ? resolvePath(path, root) : path;
|
|
135
135
|
}
|
|
136
|
+
/** Paths read with a window in this tool set, to tell paging from one targeted read. */
|
|
137
|
+
const windowedReads = new Set();
|
|
136
138
|
/**
|
|
137
139
|
* Verify that `path` does not symlink outside the project root when guards are enabled.
|
|
138
140
|
*
|
|
@@ -391,6 +393,20 @@ export function buildTools(backend, config) {
|
|
|
391
393
|
const effectiveWindow = !sliceLines(content, window) && content.length > MAX_READ_RETURN_CHARS
|
|
392
394
|
? { offset: 1, limit: linesWithin(content, MAX_READ_RETURN_CHARS) }
|
|
393
395
|
: window;
|
|
396
|
+
// A SECOND window of the same small file: hand back all of it (one window is a targeted read). X1 flash-m50 paged files that fit in one
|
|
397
|
+
// call 13 times in a row, each window answered with "read it again with no offset/limit".
|
|
398
|
+
const small = sliceLines(content, window);
|
|
399
|
+
const pagedBefore = small?.truncated ? windowedReads.has(path) : false;
|
|
400
|
+
if (small?.truncated)
|
|
401
|
+
windowedReads.add(path);
|
|
402
|
+
if (small?.truncated && pagedBefore && content.length <= SMALL_FILE_WHOLE_CHARS) {
|
|
403
|
+
return {
|
|
404
|
+
path,
|
|
405
|
+
content: sanitizeFileContent(content, path),
|
|
406
|
+
totalLines: small.totalLines,
|
|
407
|
+
note: `The whole file is returned (${Math.round(content.length / 1024)} KB, ${small.totalLines} lines): a file this small fits in one call, so it is not paged.`,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
394
410
|
const windowed = sliceLines(content, effectiveWindow);
|
|
395
411
|
if (windowed) {
|
|
396
412
|
return {
|
|
@@ -826,7 +842,14 @@ export function buildTools(backend, config) {
|
|
|
826
842
|
? `${alreadyApplied} of these edits ${alreadyApplied === 1 ? 'was' : 'were'} already in the file (its new_string is there and its old_string is not) — nothing to do for ${alreadyApplied === 1 ? 'it' : 'them'}.`
|
|
827
843
|
: null;
|
|
828
844
|
if (content === oldContent) {
|
|
829
|
-
|
|
845
|
+
// A replacement whose new_string IS its old_string changes nothing; `ok` alone read
|
|
846
|
+
// as a fix applied (X1 flash-m51).
|
|
847
|
+
const identical = replacements.filter((r) => typeof r.old_string === 'string' && r.old_string === r.new_string).length;
|
|
848
|
+
const note = alreadyNote ??
|
|
849
|
+
(identical > 0
|
|
850
|
+
? `Nothing changed: ${identical === 1 ? 'the replacement’s new_string is' : `${identical} replacements’ new_string are`} identical to old_string, so the file is exactly as it was. Write the text you want in new_string.`
|
|
851
|
+
: 'Nothing changed: the file is exactly as it was.');
|
|
852
|
+
return { path, ok: true, replacementsApplied: 0, alreadyApplied, note };
|
|
830
853
|
}
|
|
831
854
|
if (onFileDiff)
|
|
832
855
|
onFileDiff({ path, oldContent, newContent: content });
|
|
@@ -853,24 +876,61 @@ export function buildTools(backend, config) {
|
|
|
853
876
|
// A replacement whose old text ended in a newline and whose new text does not
|
|
854
877
|
// joins the line after it onto its last line. Applied as written, but said:
|
|
855
878
|
// flash-m28 saw two lines merged only one round trip later.
|
|
856
|
-
const
|
|
879
|
+
const dropped = replacements
|
|
880
|
+
.map((r, i) => ({ ...r, position: i + 1 }))
|
|
881
|
+
.filter((r) => typeof r.old_string === 'string' &&
|
|
857
882
|
typeof r.new_string === 'string' &&
|
|
858
883
|
r.old_string.endsWith('\n') &&
|
|
859
884
|
r.new_string !== '' &&
|
|
860
|
-
!r.new_string.endsWith('\n'))
|
|
885
|
+
!r.new_string.endsWith('\n'));
|
|
886
|
+
const syntax = await syntaxErrorAfterWrite(path);
|
|
887
|
+
// Those two facts are ONE accident, and reported apart the model repairs the
|
|
888
|
+
// wrong one: flash-m55 dropped the boundary on two consecutive edit_file calls,
|
|
889
|
+
// both left `handlers/conduit.ts` unparseable, and it fixed the fused LINE's
|
|
890
|
+
// text — which re-fused the next line in the same batch. When the parse error
|
|
891
|
+
// follows a dropped newline, name the fused line and the single edit that
|
|
892
|
+
// undoes it, instead of a style note beside an unrelated-looking parse error.
|
|
893
|
+
const which = dropped.length === 1
|
|
894
|
+
? `replacement ${dropped[0].position}`
|
|
895
|
+
: `replacements ${dropped.map((r) => r.position).join(', ')}`;
|
|
896
|
+
const located = dropped.flatMap((r) => {
|
|
897
|
+
const line = fusedLine(finalContent, r.new_string);
|
|
898
|
+
return line ? [line] : [];
|
|
899
|
+
});
|
|
900
|
+
const fused = located.slice(0, 3);
|
|
901
|
+
const fusedOmitted = located.length - fused.length;
|
|
902
|
+
const shownFused = fused
|
|
903
|
+
.map((l) => ` ${l.line}: ${l.text.length > 200 ? `${l.text.slice(0, 200)}…` : l.text}`)
|
|
904
|
+
.join('\n');
|
|
905
|
+
const fusedNote = syntax.syntaxError && dropped.length > 0
|
|
906
|
+
? 'The file no longer parses after this change, and ' +
|
|
907
|
+
`${which} removed the line break that followed the replaced text — so the next line ` +
|
|
908
|
+
'now continues on the line before it, and that fused line is what the parser rejects:\n' +
|
|
909
|
+
(shownFused || ` (not located as the file now reads — read_file ${path})`) +
|
|
910
|
+
(fusedOmitted > 0
|
|
911
|
+
? `\n (${fusedOmitted} more fused line(s) — read_file ${path})`
|
|
912
|
+
: '') +
|
|
913
|
+
`\nRe-send ${which} with the trailing newline restored on new_string (change nothing ` +
|
|
914
|
+
"else about it). Editing the FUSED LINE's text instead fuses the next line the same " +
|
|
915
|
+
`way (flash-m55: three edits in a row).\n${syntax.syntaxError}`
|
|
916
|
+
: null;
|
|
861
917
|
return {
|
|
862
918
|
path,
|
|
863
919
|
ok: true,
|
|
864
920
|
replacementsApplied: replacements.length - alreadyApplied,
|
|
865
|
-
...(
|
|
866
|
-
? {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
921
|
+
...(fusedNote
|
|
922
|
+
? { syntaxError: fusedNote }
|
|
923
|
+
: {
|
|
924
|
+
...(dropped.length > 0
|
|
925
|
+
? {
|
|
926
|
+
newlineNote: `${dropped.length === 1 ? 'A replacement' : `${dropped.length} replacements`} removed the line break after the replaced text (old_string ended with a newline, new_string does not), so the next line now continues on the same line. Check the excerpt; add the newline back if that was not intended.`,
|
|
927
|
+
}
|
|
928
|
+
: {}),
|
|
929
|
+
...syntax,
|
|
930
|
+
}),
|
|
870
931
|
...(alreadyApplied > 0 ? { alreadyApplied, note: alreadyNote } : {}),
|
|
871
932
|
totalLines: countLines(finalContent),
|
|
872
933
|
...(excerpt ? { excerpt: sanitizeFileContent(excerpt, path) } : {}),
|
|
873
|
-
...(await syntaxErrorAfterWrite(path)),
|
|
874
934
|
};
|
|
875
935
|
}
|
|
876
936
|
catch (e) {
|
|
@@ -1395,13 +1455,26 @@ export function buildTools(backend, config) {
|
|
|
1395
1455
|
`kill $(cat /tmp/${taskId}.pid) 2>/dev/null; pkill -f ${taskId}.sh) and move on; do not wait on it again.`,
|
|
1396
1456
|
};
|
|
1397
1457
|
}
|
|
1458
|
+
// The output so far rides the result: a wait that shows nothing was waited blind
|
|
1459
|
+
// (X1 flash-m50: 4m42s across two waits on an 11-minute run).
|
|
1460
|
+
const own = outputFileByTask.get(taskId);
|
|
1461
|
+
let soFar;
|
|
1462
|
+
try {
|
|
1463
|
+
const r = await backend.run(`cat ${log}${own ? ` ${shellQuote(own)}` : ''} 2>/dev/null | tail -c 3000`, { timeout: 5_000 });
|
|
1464
|
+
soFar = r.stdout ?? '';
|
|
1465
|
+
}
|
|
1466
|
+
catch (_error) {
|
|
1467
|
+
// No tail: the note still says how to peek.
|
|
1468
|
+
soFar = '';
|
|
1469
|
+
}
|
|
1398
1470
|
return {
|
|
1399
1471
|
taskId,
|
|
1400
1472
|
status: 'running',
|
|
1401
1473
|
waitedMs,
|
|
1474
|
+
...(soFar.trim() ? { outputSoFar: sanitizeOutput(soFar) } : {}),
|
|
1402
1475
|
note: `Still running after ${Math.round(waitedMs / 1000)}s. Call wait_for_task again to ` +
|
|
1403
|
-
`keep waiting — its result carries the log output.
|
|
1404
|
-
`
|
|
1476
|
+
`keep waiting — its result carries the log output${soFar.trim() ? '; outputSoFar is the end of the log as it stands, so there is no need to tail it' : ''}. ` +
|
|
1477
|
+
`Do other work in the meantime (another suite, a fix you have already found) rather than waiting.`,
|
|
1405
1478
|
};
|
|
1406
1479
|
}
|
|
1407
1480
|
let output;
|
|
@@ -1413,6 +1486,20 @@ export function buildTools(backend, config) {
|
|
|
1413
1486
|
// an empty output with a real exit code is still a result.
|
|
1414
1487
|
output = '';
|
|
1415
1488
|
}
|
|
1489
|
+
// A command that wrote its output to its own file (`> /tmp/e2e.log`) left the task log
|
|
1490
|
+
// holding a word: read that file too, so the result (and every note built on it) has the
|
|
1491
|
+
// run's output (X1 flash-m48: 60 passed / 14 failed, and the notes saw "started").
|
|
1492
|
+
const ownFile = outputFileByTask.get(taskId);
|
|
1493
|
+
if (ownFile && output.trim().length < 2000) {
|
|
1494
|
+
try {
|
|
1495
|
+
const ownText = await backend.readFile(ownFile);
|
|
1496
|
+
if (ownText.trim())
|
|
1497
|
+
output = `${output.trim() ? `${output.trimEnd()}\n` : ''}${ownText}`;
|
|
1498
|
+
}
|
|
1499
|
+
catch (_error) {
|
|
1500
|
+
// The file is gone or unreadable: the task log is the whole result, as before.
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1416
1503
|
// Same shaping as a foreground exec result, so the executor reads both
|
|
1417
1504
|
// the same way.
|
|
1418
1505
|
return {
|
package/dist/utilities.d.ts
CHANGED
|
@@ -249,6 +249,8 @@ export declare const MAX_BATCH_READ_BYTES: number;
|
|
|
249
249
|
* needs.
|
|
250
250
|
*/
|
|
251
251
|
export declare const MAX_READ_RETURN_CHARS: number;
|
|
252
|
+
/** A file this small is returned whole even when a window is asked for (paging it costs a call each). */
|
|
253
|
+
export declare const SMALL_FILE_WHOLE_CHARS: number;
|
|
252
254
|
/** Max search results. */
|
|
253
255
|
export declare const MAX_SEARCH_RESULTS = 50;
|
|
254
256
|
/** Max find results. */
|
|
@@ -337,4 +339,20 @@ export declare function parseCheckProblem(result: {
|
|
|
337
339
|
stderr: string;
|
|
338
340
|
exitCode: number;
|
|
339
341
|
}): string | null;
|
|
342
|
+
/**
|
|
343
|
+
* The line a replacement fused when it dropped the newline its `old_string`
|
|
344
|
+
* ended with: `new_string`'s text is followed on the same line by what used to
|
|
345
|
+
* be the next line. Read off the file as it now is, so the line shown is the
|
|
346
|
+
* line the parser is actually complaining about.
|
|
347
|
+
*
|
|
348
|
+
* @param content - The file's content after the edit.
|
|
349
|
+
* @param newString - A replacement's `new_string`, whose `old_string` ended with a newline.
|
|
350
|
+
* @returns The fused line's 1-based number and its text, or null when the
|
|
351
|
+
* replacement's text is not followed by more of the same line (it ends the
|
|
352
|
+
* file, the following newline survived, or another replacement rewrote it).
|
|
353
|
+
*/
|
|
354
|
+
export declare function fusedLine(content: string, newString: string): {
|
|
355
|
+
line: number;
|
|
356
|
+
text: string;
|
|
357
|
+
} | null;
|
|
340
358
|
//# sourceMappingURL=utilities.d.ts.map
|
package/dist/utilities.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAS5C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,GAAG,MAAM,KAAG,MAEE,CAAA;
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
|
package/dist/utilities.js
CHANGED
|
@@ -101,7 +101,13 @@ const NON_SECRET_VALUES = new Set([
|
|
|
101
101
|
const jsonValueReplacer = (quote) => (match, prefix) => {
|
|
102
102
|
const value = match.slice(prefix.length);
|
|
103
103
|
const inner = value.slice(1, -1);
|
|
104
|
-
|
|
104
|
+
// An empty value hides nothing, and a suite fixture's `"password": ""` is the fact a
|
|
105
|
+
// failing assertion turns on (X1 flash-m52).
|
|
106
|
+
// A version (`"jsonwebtoken": "9.0.2"`, a package name that holds a keyword) is not a
|
|
107
|
+
// credential, and a masked one hid which version is installed (X1 flash-m57).
|
|
108
|
+
if (/^[\^~]?\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/.test(inner))
|
|
109
|
+
return match;
|
|
110
|
+
if (inner === '' || NON_SECRET_VALUES.has(inner.toLowerCase()))
|
|
105
111
|
return match;
|
|
106
112
|
return `${prefix}${quote}[REDACTED]${quote}`;
|
|
107
113
|
};
|
|
@@ -492,6 +498,8 @@ export const MAX_BATCH_READ_BYTES = 200 * 1024;
|
|
|
492
498
|
* needs.
|
|
493
499
|
*/
|
|
494
500
|
export const MAX_READ_RETURN_CHARS = 80 * 1024;
|
|
501
|
+
/** A file this small is returned whole even when a window is asked for (paging it costs a call each). */
|
|
502
|
+
export const SMALL_FILE_WHOLE_CHARS = 24 * 1024;
|
|
495
503
|
/** Max search results. */
|
|
496
504
|
export const MAX_SEARCH_RESULTS = 50;
|
|
497
505
|
/** Max find results. */
|
|
@@ -716,3 +724,33 @@ export function parseCheckProblem(result) {
|
|
|
716
724
|
return null;
|
|
717
725
|
return text.slice(0, 1200);
|
|
718
726
|
}
|
|
727
|
+
/**
|
|
728
|
+
* The line a replacement fused when it dropped the newline its `old_string`
|
|
729
|
+
* ended with: `new_string`'s text is followed on the same line by what used to
|
|
730
|
+
* be the next line. Read off the file as it now is, so the line shown is the
|
|
731
|
+
* line the parser is actually complaining about.
|
|
732
|
+
*
|
|
733
|
+
* @param content - The file's content after the edit.
|
|
734
|
+
* @param newString - A replacement's `new_string`, whose `old_string` ended with a newline.
|
|
735
|
+
* @returns The fused line's 1-based number and its text, or null when the
|
|
736
|
+
* replacement's text is not followed by more of the same line (it ends the
|
|
737
|
+
* file, the following newline survived, or another replacement rewrote it).
|
|
738
|
+
*/
|
|
739
|
+
export function fusedLine(content, newString) {
|
|
740
|
+
for (let at = content.indexOf(newString); at >= 0; at = content.indexOf(newString, at + 1)) {
|
|
741
|
+
const endsAt = at + newString.length;
|
|
742
|
+
const next = content[endsAt];
|
|
743
|
+
// End of file (nothing was joined) or a newline (the boundary survived) are
|
|
744
|
+
// both "not fused". A later replacement in the same batch may have rewritten
|
|
745
|
+
// this text, so keep looking rather than giving up on the first occurrence.
|
|
746
|
+
if (next === undefined || next === '\n')
|
|
747
|
+
continue;
|
|
748
|
+
const lineStart = content.lastIndexOf('\n', at) + 1;
|
|
749
|
+
const lineEnd = content.indexOf('\n', endsAt);
|
|
750
|
+
return {
|
|
751
|
+
line: content.slice(0, lineStart).split('\n').length,
|
|
752
|
+
text: content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd),
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
return null;
|
|
756
|
+
}
|