typebulb 0.28.2 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/agents/claude/client.js +174 -178
- package/dist/agents/pi/client.js +175 -179
- package/dist/agents/pi/matchu-patchu.ts +116 -53
- package/dist/dts/tbTypings.d.ts +2 -2
- package/dist/dts/tbTypings.d.ts.map +1 -1
- package/dist/dts/tbTypings.js +3 -2
- package/dist/dts/tbTypings.js.map +1 -1
- package/dist/index.js +199 -193
- package/dist/render.js +1 -1
- package/package.json +107 -107
|
@@ -5,7 +5,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { Type } from "typebox";
|
|
7
7
|
|
|
8
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
8
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/utils/textUtils.js
|
|
9
9
|
var TextUtils = class _TextUtils {
|
|
10
10
|
static NormalizeToLf(text) {
|
|
11
11
|
return (text ?? "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
@@ -103,7 +103,7 @@ var TextUtils = class _TextUtils {
|
|
|
103
103
|
}
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
106
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/utils/arrayUtils.js
|
|
107
107
|
var ArrayUtils = class {
|
|
108
108
|
/// <summary>
|
|
109
109
|
/// Groups items by key, preserving first-seen key order (like LINQ's GroupBy).
|
|
@@ -141,7 +141,7 @@ var ArrayUtils = class {
|
|
|
141
141
|
}
|
|
142
142
|
};
|
|
143
143
|
|
|
144
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
144
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/models.js
|
|
145
145
|
var PatchInputFile = class {
|
|
146
146
|
Key;
|
|
147
147
|
InputFullText;
|
|
@@ -201,12 +201,6 @@ var PatchOutput = class {
|
|
|
201
201
|
constructor(Files) {
|
|
202
202
|
this.Files = Files;
|
|
203
203
|
}
|
|
204
|
-
// Patch-scoped errors that belong to no input file: hunks naming files outside
|
|
205
|
-
// the input set (FileMismatch). Kept off the per-file channel so one foreign
|
|
206
|
-
// hunk cannot mark otherwise-clean files as failed — per-file Errors are about
|
|
207
|
-
// that file only. Loudness is preserved: throw-mode still throws these, and
|
|
208
|
-
// callers composing success replies must consult BOTH channels.
|
|
209
|
-
Errors = [];
|
|
210
204
|
};
|
|
211
205
|
var LineType;
|
|
212
206
|
(function(LineType2) {
|
|
@@ -252,6 +246,11 @@ var Hunk = class {
|
|
|
252
246
|
// Set by the parser under ControlCharPolicy 'warn' when this hunk's insert
|
|
253
247
|
// lines carried raw control characters.
|
|
254
248
|
ControlCharsSuspected = false;
|
|
249
|
+
// Error-reporting stand-in Chunk for a hunk that was never anchored: its lines
|
|
250
|
+
// and diff location, no match.
|
|
251
|
+
ToUnanchoredChunk() {
|
|
252
|
+
return new Chunk([], this.Lines.filter((l) => l.Type == LineType.Delete).map((l) => l.Text), this.Lines.filter((l) => l.Type == LineType.Insert).map((l) => l.Text), [], UniqueMatch.NotFound, new DiffLocation(this));
|
|
253
|
+
}
|
|
255
254
|
};
|
|
256
255
|
var Edit = class _Edit {
|
|
257
256
|
LineIndex;
|
|
@@ -370,7 +369,7 @@ var UniqueMatch = class _UniqueMatch {
|
|
|
370
369
|
}
|
|
371
370
|
};
|
|
372
371
|
|
|
373
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
372
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/utils/yaml.js
|
|
374
373
|
var Yaml = class _Yaml {
|
|
375
374
|
_sb = [];
|
|
376
375
|
// -------- Public API --------
|
|
@@ -427,7 +426,7 @@ var Yaml = class _Yaml {
|
|
|
427
426
|
}
|
|
428
427
|
};
|
|
429
428
|
|
|
430
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
429
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/exceptions.js
|
|
431
430
|
var PatchException = class extends Error {
|
|
432
431
|
Error;
|
|
433
432
|
constructor(error) {
|
|
@@ -443,13 +442,17 @@ var PatchParserException = class extends Error {
|
|
|
443
442
|
var PatchError = class {
|
|
444
443
|
Type;
|
|
445
444
|
FailedMatch;
|
|
446
|
-
//
|
|
447
|
-
//
|
|
445
|
+
// The file key(s) the error concerns: the foreign key a FileMismatch named, or
|
|
446
|
+
// the comma-joined candidate keys of an ambiguous headerless routing; null otherwise.
|
|
448
447
|
FileKey;
|
|
449
|
-
|
|
448
|
+
// Diagnosis computed against the file at error time (e.g. the quoted line
|
|
449
|
+
// occurs inside one longer file line); null when no confident diagnosis.
|
|
450
|
+
Hint;
|
|
451
|
+
constructor(errorType, chunk, fileKey = null, hint = null) {
|
|
450
452
|
this.Type = errorType;
|
|
451
453
|
this.FailedMatch = chunk;
|
|
452
454
|
this.FileKey = fileKey;
|
|
455
|
+
this.Hint = hint;
|
|
453
456
|
}
|
|
454
457
|
toString() {
|
|
455
458
|
return `Failed to patch text with the diff provided.
|
|
@@ -470,6 +473,8 @@ ${this.SuggestedFixYaml}`;
|
|
|
470
473
|
if (this.FileKey != null)
|
|
471
474
|
y.Scalar(" File", this.FileKey);
|
|
472
475
|
y.Folded(" Summary", SummaryFor(this.Type));
|
|
476
|
+
if (this.Hint != null)
|
|
477
|
+
y.Folded(" Hint", this.Hint);
|
|
473
478
|
y.Section(" Details");
|
|
474
479
|
y.Section(" FailedMatch");
|
|
475
480
|
y.Block(" ContextBefore", this.FailedMatch.ContextBefore);
|
|
@@ -483,7 +488,7 @@ ${this.SuggestedFixYaml}`;
|
|
|
483
488
|
}
|
|
484
489
|
};
|
|
485
490
|
|
|
486
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
491
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/applier/guards.js
|
|
487
492
|
var DuplicateGuard = class _DuplicateGuard {
|
|
488
493
|
// Two chunks that resolve to the same anchor with the same deletes and inserts are
|
|
489
494
|
// provably the same edit (an LLM re-emitting a hunk). Applying it once is
|
|
@@ -533,7 +538,7 @@ var OverlapGuard = class {
|
|
|
533
538
|
}
|
|
534
539
|
};
|
|
535
540
|
|
|
536
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
541
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/applier/editMinimizer.js
|
|
537
542
|
var EditMinimizer = class _EditMinimizer {
|
|
538
543
|
static Same(a, b) {
|
|
539
544
|
return a === b;
|
|
@@ -567,7 +572,7 @@ var EditMinimizer = class _EditMinimizer {
|
|
|
567
572
|
}
|
|
568
573
|
};
|
|
569
574
|
|
|
570
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
575
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/applier/indenter.js
|
|
571
576
|
var Anchor = class {
|
|
572
577
|
BaseIndent;
|
|
573
578
|
Shift;
|
|
@@ -658,7 +663,40 @@ var Indenter = class _Indenter {
|
|
|
658
663
|
}
|
|
659
664
|
};
|
|
660
665
|
|
|
661
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
666
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/applier/matchHint.js
|
|
667
|
+
var MatchHint = class _MatchHint {
|
|
668
|
+
// A quoted line inside exactly one longer file line is the prose line-model
|
|
669
|
+
// mistake: the author's "line" (a sentence of an unwrapped paragraph, or a
|
|
670
|
+
// re-flowed hard wrap) is a slice of the file's physical line. Matching stays
|
|
671
|
+
// refused — a head fragment is byte-identical to an elided whole-line delete,
|
|
672
|
+
// so either auto-reading risks silent corruption — but naming the line is
|
|
673
|
+
// correct under both readings: the retry re-quotes it whole.
|
|
674
|
+
static SubLine(chunk, lines) {
|
|
675
|
+
for (const q of chunk.DeleteLinesWithContext()) {
|
|
676
|
+
if (q.trim().length < _MatchHint.MinHintLineContent)
|
|
677
|
+
continue;
|
|
678
|
+
if (lines.includes(q))
|
|
679
|
+
continue;
|
|
680
|
+
const containing = [];
|
|
681
|
+
for (let i = 0; i < lines.length && containing.length < 2; i++)
|
|
682
|
+
if (lines[i].length > q.length && lines[i].includes(q))
|
|
683
|
+
containing.push(i);
|
|
684
|
+
if (containing.length != 1)
|
|
685
|
+
continue;
|
|
686
|
+
return `The diff line "${_MatchHint.Preview(q)}" matches only PART of line ${containing[0] + 1}, which is ${lines[containing[0]].length} characters long \u2014 in prose, a whole paragraph is often one physical line. Re-send the diff quoting entire lines exactly.`;
|
|
687
|
+
}
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
static Preview(s) {
|
|
691
|
+
const t = s.trim();
|
|
692
|
+
return t.length <= 60 ? t : t.slice(0, 57) + "...";
|
|
693
|
+
}
|
|
694
|
+
// Trimmed length a quoted line must reach before containment counts as
|
|
695
|
+
// evidence: short strings occur inside longer lines by coincidence.
|
|
696
|
+
static MinHintLineContent = 12;
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/applier/chunkApplier.js
|
|
662
700
|
var InputChunks = class {
|
|
663
701
|
InputFullText;
|
|
664
702
|
Chunks;
|
|
@@ -722,12 +760,12 @@ var ChunkApplier = class _ChunkApplier {
|
|
|
722
760
|
if (match.IsAmbiguous)
|
|
723
761
|
throw new PatchException(new PatchError("MatchAmbiguous", chunk));
|
|
724
762
|
if (match.IsNotFound)
|
|
725
|
-
throw new PatchException(new PatchError("MatchNotFound", chunk));
|
|
763
|
+
throw new PatchException(new PatchError("MatchNotFound", chunk, null, MatchHint.SubLine(chunk, lines)));
|
|
726
764
|
return new Edit(match.LineIndex, Array.from(chunk.DeleteLines), Indenter.AlignInsert(lines, chunk), match.Fuzz);
|
|
727
765
|
}
|
|
728
766
|
};
|
|
729
767
|
|
|
730
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
768
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/heuristics.js
|
|
731
769
|
var DiffContentHeuristics = class _DiffContentHeuristics {
|
|
732
770
|
/**
|
|
733
771
|
* Checks if a line starts with a doubled marker (++ or --)
|
|
@@ -952,7 +990,7 @@ var DiffContentHeuristics = class _DiffContentHeuristics {
|
|
|
952
990
|
}
|
|
953
991
|
};
|
|
954
992
|
|
|
955
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
993
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/hunkBuilder.js
|
|
956
994
|
var Header = class _Header {
|
|
957
995
|
Path;
|
|
958
996
|
OldStart;
|
|
@@ -1160,7 +1198,7 @@ ${h.NewText}`;
|
|
|
1160
1198
|
}
|
|
1161
1199
|
};
|
|
1162
1200
|
|
|
1163
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
1201
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/unifiedDiffParser.js
|
|
1164
1202
|
var UnifiedDiffParser = class _UnifiedDiffParser {
|
|
1165
1203
|
/**
|
|
1166
1204
|
* Parse a unified diff into structured hunks.
|
|
@@ -1399,7 +1437,7 @@ var UnifiedDiffParser = class _UnifiedDiffParser {
|
|
|
1399
1437
|
}
|
|
1400
1438
|
};
|
|
1401
1439
|
|
|
1402
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
1440
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/anchorer/hunkSlicer.js
|
|
1403
1441
|
var Block = class {
|
|
1404
1442
|
StartIndex;
|
|
1405
1443
|
EndIndex;
|
|
@@ -1452,7 +1490,7 @@ var HunkSlicer = class _HunkSlicer {
|
|
|
1452
1490
|
}
|
|
1453
1491
|
};
|
|
1454
1492
|
|
|
1455
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
1493
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/anchorer/search.js
|
|
1456
1494
|
var Match = class {
|
|
1457
1495
|
LineIndex;
|
|
1458
1496
|
Fuzz;
|
|
@@ -1590,7 +1628,7 @@ var Search = class _Search {
|
|
|
1590
1628
|
}
|
|
1591
1629
|
};
|
|
1592
1630
|
|
|
1593
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
1631
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/anchorer/blockAnchorer.js
|
|
1594
1632
|
var BlockAnchorer = class _BlockAnchorer {
|
|
1595
1633
|
static AnchorBlocks(hunk, blocks, lines, options) {
|
|
1596
1634
|
return blocks.map((focus, index) => {
|
|
@@ -1708,7 +1746,7 @@ var BlockAnchorer = class _BlockAnchorer {
|
|
|
1708
1746
|
}
|
|
1709
1747
|
};
|
|
1710
1748
|
|
|
1711
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
1749
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/anchorer/hunkAnchorer.js
|
|
1712
1750
|
var AnchorOutcome = class {
|
|
1713
1751
|
Chunks;
|
|
1714
1752
|
AlreadyAppliedCount;
|
|
@@ -1876,7 +1914,7 @@ var HunkAnchorer = class _HunkAnchorer {
|
|
|
1876
1914
|
}
|
|
1877
1915
|
};
|
|
1878
1916
|
|
|
1879
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
1917
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/selectionTarget.js
|
|
1880
1918
|
var SelectionTarget = class {
|
|
1881
1919
|
FullText;
|
|
1882
1920
|
SelectedText;
|
|
@@ -1908,7 +1946,7 @@ var SelectionTarget = class {
|
|
|
1908
1946
|
}
|
|
1909
1947
|
};
|
|
1910
1948
|
|
|
1911
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
1949
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/sanitizers/shared.js
|
|
1912
1950
|
function isDiffMarker(line) {
|
|
1913
1951
|
return line.startsWith("--- ") || line.startsWith("+++ ") || line.startsWith("@@") || line.startsWith("diff --git");
|
|
1914
1952
|
}
|
|
@@ -1965,7 +2003,7 @@ function isLikelyFileHeader(trimmed) {
|
|
|
1965
2003
|
return trimmed.startsWith("diff --git") || (trimmed.startsWith("--- ") || trimmed.startsWith("+++ ")) && (trimmed.includes(".") || trimmed.includes("/") || trimmed.includes("\\"));
|
|
1966
2004
|
}
|
|
1967
2005
|
|
|
1968
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
2006
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/sanitizers/customFormatSanitizer.js
|
|
1969
2007
|
var CustomFormatSanitizer = class _CustomFormatSanitizer {
|
|
1970
2008
|
static OPERATIONS = [
|
|
1971
2009
|
{ header: /^\s*\*\*\* Update File: (.+)$/i, fromPath: (p) => `a/${p}`, toPath: (p) => `b/${p}` },
|
|
@@ -1990,7 +2028,7 @@ var CustomFormatSanitizer = class _CustomFormatSanitizer {
|
|
|
1990
2028
|
}
|
|
1991
2029
|
};
|
|
1992
2030
|
|
|
1993
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
2031
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/sanitizers/decoratedHeaderSanitizer.js
|
|
1994
2032
|
var DecoratedHeaderSanitizer = class _DecoratedHeaderSanitizer {
|
|
1995
2033
|
static DECORATED_HEADER_REGEX = /^(---|\+\+\+)\s+(.+?)\s+(---|\+\+\+)+\s*$/;
|
|
1996
2034
|
static process(text) {
|
|
@@ -2003,7 +2041,7 @@ var DecoratedHeaderSanitizer = class _DecoratedHeaderSanitizer {
|
|
|
2003
2041
|
}
|
|
2004
2042
|
};
|
|
2005
2043
|
|
|
2006
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
2044
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/sanitizers/decorativeMarkerSanitizer.js
|
|
2007
2045
|
var DecorativeMarkerSanitizer = class _DecorativeMarkerSanitizer {
|
|
2008
2046
|
static process(text) {
|
|
2009
2047
|
return transformOutsideHunkBodies(text, (line) => _DecorativeMarkerSanitizer.isDecorativeMarker(line.trim()) ? null : line);
|
|
@@ -2024,14 +2062,14 @@ var DecorativeMarkerSanitizer = class _DecorativeMarkerSanitizer {
|
|
|
2024
2062
|
}
|
|
2025
2063
|
};
|
|
2026
2064
|
|
|
2027
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
2065
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/sanitizers/backtickSanitizer.js
|
|
2028
2066
|
var BacktickSanitizer = class {
|
|
2029
2067
|
static process(text) {
|
|
2030
2068
|
return text.replace(/^```+\s*diff\b/gmi, "```diff").replace(/^```+$/gm, "```");
|
|
2031
2069
|
}
|
|
2032
2070
|
};
|
|
2033
2071
|
|
|
2034
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
2072
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/sanitizers/diffBlockReunifier.js
|
|
2035
2073
|
var DiffBlockReunifier = class {
|
|
2036
2074
|
static process(text) {
|
|
2037
2075
|
const lines = text.split("\n");
|
|
@@ -2067,7 +2105,7 @@ var DiffBlockReunifier = class {
|
|
|
2067
2105
|
}
|
|
2068
2106
|
};
|
|
2069
2107
|
|
|
2070
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
2108
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/sanitizers/devNullSanitizer.js
|
|
2071
2109
|
var DevNullSanitizer = class {
|
|
2072
2110
|
static process(text, fileKeys) {
|
|
2073
2111
|
if (!fileKeys?.length)
|
|
@@ -2110,7 +2148,7 @@ var DevNullSanitizer = class {
|
|
|
2110
2148
|
}
|
|
2111
2149
|
};
|
|
2112
2150
|
|
|
2113
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
2151
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/sanitizers/prefixInjector.js
|
|
2114
2152
|
var PrefixInjector = class {
|
|
2115
2153
|
static process(text) {
|
|
2116
2154
|
const lines = text.split("\n");
|
|
@@ -2169,7 +2207,7 @@ var PrefixInjector = class {
|
|
|
2169
2207
|
}
|
|
2170
2208
|
};
|
|
2171
2209
|
|
|
2172
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
2210
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/parser/diffSanitizer.js
|
|
2173
2211
|
var DiffSanitizer = class {
|
|
2174
2212
|
static Process(diff, fileKeys) {
|
|
2175
2213
|
if (!diff?.trim())
|
|
@@ -2185,17 +2223,49 @@ var DiffSanitizer = class {
|
|
|
2185
2223
|
}
|
|
2186
2224
|
};
|
|
2187
2225
|
|
|
2188
|
-
// ../node_modules/.pnpm/matchu-patchu@0.
|
|
2189
|
-
var
|
|
2226
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/headerlessRouter.js
|
|
2227
|
+
var HeaderlessRouter = class {
|
|
2228
|
+
static Route(fileHunks, files, options) {
|
|
2229
|
+
const group = fileHunks.find((g) => g.Key == "");
|
|
2230
|
+
if (group == null || files.some((f) => f.Key == ""))
|
|
2231
|
+
return [];
|
|
2232
|
+
const probeOptions = Object.assign(new PatchOptions(), options, { ContinueOnError: true });
|
|
2233
|
+
const candidates = files.filter((f) => {
|
|
2234
|
+
const target = new SelectionTarget(f.InputFullText, f.InputSelectedText).TargetText;
|
|
2235
|
+
const anchored = HunkAnchorer.Anchor(target, group.Hunks, probeOptions);
|
|
2236
|
+
return ChunkApplier.Apply(target, anchored.Chunks, probeOptions).Errors.length == 0;
|
|
2237
|
+
});
|
|
2238
|
+
if (candidates.length == 1) {
|
|
2239
|
+
fileHunks.splice(fileHunks.indexOf(group), 1);
|
|
2240
|
+
const existing = fileHunks.find((g) => g.Key == candidates[0].Key);
|
|
2241
|
+
if (existing)
|
|
2242
|
+
existing.Hunks.push(...group.Hunks);
|
|
2243
|
+
else
|
|
2244
|
+
fileHunks.push(new FileHunkGroup(candidates[0].Key, group.Hunks));
|
|
2245
|
+
return [];
|
|
2246
|
+
}
|
|
2247
|
+
return [candidates.length == 0 ? new PatchError("MatchNotFound", group.Hunks[0].ToUnanchoredChunk(), null, "The diff has no file headers and its content did not match any file being patched. Make sure context and deleted lines match the target file, and add '--- <file>' / '+++ <file>' headers naming it.") : new PatchError("MatchAmbiguous", group.Hunks[0].ToUnanchoredChunk(), candidates.map((f) => f.Key).join(", "), "The diff has no file headers and its content matched more than one file being patched. Add '--- <file>' / '+++ <file>' headers naming the intended file.")];
|
|
2248
|
+
}
|
|
2249
|
+
};
|
|
2250
|
+
|
|
2251
|
+
// ../node_modules/.pnpm/matchu-patchu@0.3.1/node_modules/matchu-patchu/dist/patcher.js
|
|
2252
|
+
var Patcher = class {
|
|
2190
2253
|
static Apply(diff, files, options) {
|
|
2191
2254
|
options = options ?? new PatchOptions();
|
|
2192
2255
|
const fileKeys = files.map((f) => f.Key);
|
|
2193
2256
|
const fileContents = new Map(files.map((f) => [f.Key, f.InputFullText]));
|
|
2194
2257
|
diff = options.SanitizeDiff ? DiffSanitizer.Process(diff, fileKeys) : diff;
|
|
2195
2258
|
const fileHunks = UnifiedDiffParser.Parse(diff, fileContents, options.Truncation, options.ControlChars);
|
|
2196
|
-
const
|
|
2197
|
-
|
|
2198
|
-
|
|
2259
|
+
const routingErrors = HeaderlessRouter.Route(fileHunks, files, options);
|
|
2260
|
+
const namedKeys = fileKeys.filter((k) => k != "");
|
|
2261
|
+
const rosterHint = namedKeys.length > 0 ? `Files being patched: ${namedKeys.join(", ")}.` : null;
|
|
2262
|
+
const reportEntry = (key, errors) => new PatchOutputFile(key, 0, [], "", "", "", errors);
|
|
2263
|
+
const reportFiles = fileHunks.filter((g) => g.Key != "" && !fileContents.has(g.Key)).map((g) => reportEntry(g.Key, [new PatchError("FileMismatch", g.Hunks[0].ToUnanchoredChunk(), g.Key, rosterHint)]));
|
|
2264
|
+
if (routingErrors.length > 0)
|
|
2265
|
+
reportFiles.unshift(reportEntry("", routingErrors));
|
|
2266
|
+
const reportErrors = reportFiles.flatMap((f) => f.Errors);
|
|
2267
|
+
if (reportErrors.length > 0 && !options.ContinueOnError)
|
|
2268
|
+
throw new PatchException(reportErrors[0]);
|
|
2199
2269
|
const patchOutputFiles = files.map((f) => {
|
|
2200
2270
|
const targetSelection = new SelectionTarget(f.InputFullText, f.InputSelectedText);
|
|
2201
2271
|
const targetText = targetSelection.TargetText;
|
|
@@ -2204,20 +2274,13 @@ var Patcher = class _Patcher {
|
|
|
2204
2274
|
const outputEdits = ChunkApplier.Apply(targetText, anchored.Chunks, options);
|
|
2205
2275
|
return new PatchOutputFile(f.Key, outputEdits.Edits.map((h) => h.Fuzz).reduce((a, b) => a + b, 0), outputEdits.Edits.map((e) => e.Shift(targetSelection.LineOffset)), f.InputSelectedText, f.InputFullText, targetSelection.Replace(outputEdits.OutputText), [...outputEdits.Errors], anchored.AlreadyAppliedCount, hunks.flatMap((h) => h.Lines).filter((l) => l.CollapsedFrom != null).map((l) => l.CollapsedFrom), hunks.some((h) => h.TruncationSuspected), hunks.some((h) => h.ControlCharsSuspected));
|
|
2206
2276
|
});
|
|
2207
|
-
|
|
2208
|
-
output.Errors = foreignErrors;
|
|
2209
|
-
return output;
|
|
2210
|
-
}
|
|
2211
|
-
// Error-reporting stand-in for a hunk that was never anchored (it names a file
|
|
2212
|
-
// that isn't being patched): its lines and diff location, no match.
|
|
2213
|
-
static ForeignChunk(h) {
|
|
2214
|
-
return new Chunk([], h.Lines.filter((l) => l.Type == LineType.Delete).map((l) => l.Text), h.Lines.filter((l) => l.Type == LineType.Insert).map((l) => l.Text), [], UniqueMatch.NotFound, new DiffLocation(h));
|
|
2277
|
+
return new PatchOutput([...patchOutputFiles, ...reportFiles]);
|
|
2215
2278
|
}
|
|
2216
2279
|
};
|
|
2217
2280
|
|
|
2218
2281
|
// cli/agents/pi/server/piPatcherExtension.ts
|
|
2219
2282
|
var DESCRIPTION = true ? "Apply unified diffs to files \u2014 tolerant of form, strict about intent. Repairs sloppy AI-generated diffs (mangled headers, whitespace drift, missing prefixes; line numbers can be wrong or absent \u2014 hunks anchor by fuzzy-matched context lines) and applies all hunks atomically when the intent is unambiguous; fails with a precise, typed error when it isn't. Use for multi-hunk edits in one step, or when exact string-replacement editing fails on whitespace or invisible characters." : "Apply unified diffs to files.";
|
|
2220
|
-
var BUILD_TAG = true ? "matchu-patchu-pi 0.
|
|
2283
|
+
var BUILD_TAG = true ? "matchu-patchu-pi 0.3.1, built 2026-07-17 07:41:42" : "matchu-patchu-pi dev";
|
|
2221
2284
|
var errorBlocks = (errors) => {
|
|
2222
2285
|
const parts = [`Patch failed with ${errors.length} error(s):`];
|
|
2223
2286
|
for (const e of errors) parts.push("", e.toString());
|
|
@@ -2227,8 +2290,8 @@ var noOpMessage = (target, alreadyApplied) => alreadyApplied > 0 ? `Applied 0 ed
|
|
|
2227
2290
|
var fuzzNote = (fuzz) => fuzz > 0 ? ` (applied with fuzz factor ${fuzz})` : "";
|
|
2228
2291
|
function discoverDiffKeys(diff) {
|
|
2229
2292
|
const keys = [];
|
|
2230
|
-
for (const
|
|
2231
|
-
if (
|
|
2293
|
+
for (const f of Patcher.Apply(diff, []).Files) {
|
|
2294
|
+
if (f.Key && !keys.includes(f.Key)) keys.push(f.Key);
|
|
2232
2295
|
}
|
|
2233
2296
|
return keys;
|
|
2234
2297
|
}
|
|
@@ -2256,7 +2319,7 @@ function applyMulti(cwd, diff, dryRun) {
|
|
|
2256
2319
|
return { ok: false, message: missing.map((k) => `Error: file not found: ${k}`).join("\n") };
|
|
2257
2320
|
const inputs = keys.map((k) => new PatchInputFile(k, readFileSync(resolve(cwd, k), "utf-8")));
|
|
2258
2321
|
const result = Patcher.Apply(diff, inputs);
|
|
2259
|
-
const errors =
|
|
2322
|
+
const errors = result.Files.flatMap((f) => f.Errors);
|
|
2260
2323
|
if (errors.length > 0) return { ok: false, message: errorBlocks(errors) };
|
|
2261
2324
|
const lines = [];
|
|
2262
2325
|
const dryOutputs = [];
|
package/dist/dts/tbTypings.d.ts
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* TypeChecker, and the CLI's emitted typecheck dirs.
|
|
10
10
|
*/
|
|
11
11
|
/** Typebulb globals available in browser-side code (code.tsx). */
|
|
12
|
-
export declare const clientTbTypings = "\n/** A single streamed delta from `tb.ai.stream()`. Discriminated by `kind`. */\ntype AiChunk =\n | { kind: \"text\"; text: string }\n | { kind: \"reasoning\"; text: string };\n\n/**\n * Typebulb utilities namespace.\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * Get raw data chunk from the Data tab.\n * @param index - Chunk index (0-based). Separate chunks with 2 blank lines.\n */\n data(index: number): string;\n /**\n * Get data chunk parsed as JSON (handles JSON-ish with unquoted keys).\n * @param index - Chunk index (0-based)\n * @throws If chunk is not valid JSON/JSON-ish\n */\n json<T = unknown>(index: number): T;\n /**\n * Async value inspector for tensor-like objects.\n *\n * Materializes lazy values (like GPU tensors) and logs them with metadata.\n * Handles objects with `.js()`, `.data()`, `.array()`, `.arraySync()`, etc.\n *\n * @remarks\n * - Always use `await` - materialization may be async (GPU\u2192CPU readback)\n * - Large values are truncated (max 1000 elements)\n * - Promises are logged as `[Promise]` (not awaited - could hang)\n */\n dump(...args: any[]): Promise<void>;\n /**\n * Trigger inference to generate new insight data.\n *\n * Opens a confirmation modal showing the data to be analyzed, then streams\n * the inference result. On success, updates the insight so subsequent\n * `tb.insight()` calls return the new value.\n *\n * @param opts - Options for inference\n * @param opts.data - Data to pre-populate in the modal (string or array of strings). If omitted, modal opens with empty textarea for user to paste.\n * @returns Promise that resolves with the parsed insight JSON\n * @throws If inference is already in progress, or on network/parse/rate limit errors\n */\n infer<T = unknown>(opts?: { data?: string | string[] }): Promise<T>;\n /**\n * Get the current inference state.\n *\n * @returns 'idle' | 'running' | 'complete' | 'error'\n */\n inferenceState(): 'idle' | 'running' | 'complete' | 'error';\n /**\n * Set a data chunk for the next inference call.\n *\n * Use this to programmatically set data that will be sent when `tb.infer()` is called\n * without the `data` option.\n *\n * @param index - The chunk index (0-based)\n * @param content - The content for this chunk\n */\n setData(index: number, content: string): void;\n /**\n * Proxy a CDN URL through the sandbox origin for Web Worker/WASM same-origin loading.\n *\n * In the sandbox, prepends `/proxy/` so the URL is served from the same origin.\n * Outside the sandbox (exported HTML, CLI), returns the URL unchanged.\n *\n * @param url - Full HTTPS URL to an allowlisted CDN (esm.sh, unpkg.com, cdn.jsdelivr.net, cdnjs.cloudflare.com)\n * @returns The proxied URL (sandbox) or the original URL (standalone/CLI)\n */\n proxy(url: string): string;\n /**\n * Copy text to clipboard.\n * Must be called synchronously within a user gesture (click/keydown).\n * @returns true if successful, false otherwise\n */\n copy(text: string): Promise<boolean>;\n /**\n * Get the canonical URL of this bulb.\n *\n * Returns the parent typebulb.com URL (including path, query, and `#tb=` fragment),\n * resolving correctly from inside the cross-origin sandbox iframe.\n * Use this instead of `location.href` or `document.referrer`.\n *\n * @returns The full canonical URL\n */\n url(): Promise<string>;\n /**\n * Get the insight data produced by the inference layer.\n *\n * Returns the parsed JSON from insight.json, populated by the inference LLM.\n * Use a type parameter to get typed access to the insight data.\n *\n * @returns The parsed insight JSON, or undefined if no insight is available\n */\n insight<T = unknown>(): T | undefined;\n /**\n * Server-side function proxy.\n *\n * In the CLI, calls exported functions from the `**server.ts**` section.\n * `tb.server.log(...)` is a built-in that prints to CLI stdout (falls back to console.log on web).\n * User exports override built-ins of the same name.\n *\n * A normal export is awaited for its result (`await tb.server.fn()`). An `async function*`\n * export streams: `for await (const chunk of tb.server.gen())`. The call object supports both;\n * break the `for await` to cancel and tear down the server generator.\n */\n server: Record<string, (...args: any[]) => Promise<any> & AsyncIterable<any>>;\n /**\n * Subscribe to a value pushed from the terminal via `typebulb send <file> [message]`.\n *\n * The dual of `tb.server.log` (data out): a value sent *in* from the CLI, no `--trust` required.\n * Use it to start expensive work on demand instead of on load \u2014 e.g. `tb.onMessage(() => start())`\n * \u2014 so hot reloads don't re-trigger it while you edit, and an agent kicks off one run when ready.\n *\n * The message is the JSON-parsed value of what `send` was given (or the raw string if it isn't\n * JSON; `undefined` for a bare `typebulb send <file>`). Returns an unsubscribe function. Inert in\n * an embedded bulb (no sender) \u2014 the handler is registered but never fires.\n *\n * @param handler - Called with each pushed message.\n * @returns An unsubscribe function.\n */\n onMessage(handler: (message: any) => void): () => void;\n /**\n * General-purpose AI call. `tb.ai(opts)` resolves with the full text; `tb.ai.stream(opts)`\n * returns an async iterable of {@link AiChunk} deltas you consume with `for await`.\n *\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai: {\n (options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): Promise<{ text: string }>;\n /**\n * Streaming counterpart of `tb.ai()`. Yields `{ kind: \"text\" | \"reasoning\", text }` deltas\n * as they arrive; break the loop (or abort `signal`) to cancel and stop the upstream.\n *\n * `kind: \"reasoning\"` deltas only arrive when you pass `effort: 1-3` AND use a\n * thinking-capable model; otherwise the stream is `text`-only.\n *\n * @example\n * let answer = \"\";\n * for await (const c of tb.ai.stream({ messages })) {\n * if (c.kind === \"text\") answer += c.text;\n * }\n */\n stream(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): AsyncIterable<AiChunk>;\n };\n /**\n * Local filesystem access (CLI only).\n *\n * Relative paths resolve against the bulb's data folder (`tb.dir` \u2014\n * `<bulb-dir>/<filename-stem>/`, created on demand), so\n * `tb.fs.write('results.json')` lands beside the bulb. `../` reaches sibling\n * bulbs' folders; everything stays confined to the project (the launch cwd).\n * Throws in editor/published mode.\n */\n fs: {\n /** Read a file as UTF-8 text. Throws if the file is not valid UTF-8 \u2014 use readBytes for binary. */\n read(path: string): Promise<string>;\n /** Read a file as raw bytes. */\n readBytes(path: string): Promise<Uint8Array>;\n /** Write text or raw bytes to a file. Creates parent directories if needed. */\n write(path: string, content: string | Uint8Array): Promise<boolean>;\n };\n /**\n * The bulb's data folder \u2014 absolute path to `<bulb-dir>/<filename-stem>/`.\n *\n * For interop only (handing a path to `server.ts` or a spawned tool):\n * `tb.fs` already resolves relative paths against it, so bulb code writing\n * its own files never needs it. CLI only \u2014 throws in editor/published/embedded mode.\n */\n readonly dir: string;\n /**\n * Returns AI models available to the current user.\n * Models are filtered by the user's configured API keys.\n * If no keys are configured, returns only the courtesy model.\n */\n models(): Promise<Array<{\n /** Provider protocol: \"anthropic\", \"openai\", \"gemini\", \"openrouter\" */\n provider: string;\n /** Model identifier, e.g. \"claude-sonnet-4-6\" */\n name: string;\n /** Human-readable display name, e.g. \"Sonnet 4.6\" */\n friendlyName: string;\n /** Provider display name, e.g. \"Anthropic\" */\n providerName: string;\n }>>;\n /**\n * Whether the user's own AI keys back `tb.ai`. False means only the\n * quota-limited courtesy model is available (or no AI at all) \u2014 a bulb\n * making many AI calls should check this and show a \"use your own keys\"\n * notice instead of running.\n */\n hasOwnKeys(): Promise<boolean>;\n /**\n * The bulb's theme override (`<html data-theme>`).\n *\n * - Get: the current override \u2014 `'dark'` | `'light'`, or `undefined` when\n * following the OS preference.\n * - Set `'dark'`/`'light'` to force and persist it (per-bulb); set\n * `undefined` to clear the override and follow the OS again.\n *\n * Drives `<html data-theme>`, so render off `html[data-theme=\"\u2026\"]` selectors\n * (or observe the attribute) rather than reading `tb.theme`.\n */\n theme: 'light' | 'dark' | undefined;\n /**\n * The mode this bulb is running in.\n *\n * - `'local'` \u2014 Running via the typebulb CLI\n * - `'editor'` \u2014 Running in the typebulb.com editor\n * - `'published'` \u2014 Running as a published/standalone bulb on typebulb.com\n * - `'embedded'` \u2014 Running as a bulb embedded inside another bulb (sandboxed,\n * client-only: AI, filesystem, and server RPC are unavailable)\n */\n mode: 'local' | 'editor' | 'published' | 'embedded';\n};\n";
|
|
12
|
+
export declare const clientTbTypings = "\n/** A single streamed delta from `tb.ai.stream()`. Discriminated by `kind`. */\ntype AiChunk =\n | { kind: \"text\"; text: string }\n | { kind: \"reasoning\"; text: string };\n\n/**\n * Typebulb utilities namespace.\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * Get raw data chunk from the Data tab.\n * @param index - Chunk index (0-based). Separate chunks with 2 blank lines.\n */\n data(index: number): string;\n /**\n * Get data chunk parsed as JSON (handles JSON-ish with unquoted keys).\n * @param index - Chunk index (0-based)\n * @throws If chunk is not valid JSON/JSON-ish\n */\n json<T = unknown>(index: number): T;\n /**\n * Async value inspector for tensor-like objects.\n *\n * Materializes lazy values (like GPU tensors) and logs them with metadata.\n * Handles objects with `.js()`, `.data()`, `.array()`, `.arraySync()`, etc.\n *\n * @remarks\n * - Always use `await` - materialization may be async (GPU\u2192CPU readback)\n * - Large values are truncated (max 1000 elements)\n * - Promises are logged as `[Promise]` (not awaited - could hang)\n */\n dump(...args: any[]): Promise<void>;\n /**\n * Trigger inference to generate new insight data.\n *\n * Opens a confirmation modal showing the data to be analyzed, then streams\n * the inference result. On success, updates the insight so subsequent\n * `tb.insight()` calls return the new value.\n *\n * @param opts - Options for inference\n * @param opts.data - Data to pre-populate in the modal (string or array of strings). If omitted, modal opens with empty textarea for user to paste.\n * @returns Promise that resolves with the parsed insight JSON\n * @throws If inference is already in progress, or on network/parse/rate limit errors\n */\n infer<T = unknown>(opts?: { data?: string | string[] }): Promise<T>;\n /**\n * Get the current inference state.\n *\n * @returns 'idle' | 'running' | 'complete' | 'error'\n */\n inferenceState(): 'idle' | 'running' | 'complete' | 'error';\n /**\n * Set a data chunk for the next inference call.\n *\n * Use this to programmatically set data that will be sent when `tb.infer()` is called\n * without the `data` option.\n *\n * @param index - The chunk index (0-based)\n * @param content - The content for this chunk\n */\n setData(index: number, content: string): void;\n /**\n * Proxy a CDN URL through the sandbox origin for Web Worker/WASM same-origin loading.\n *\n * In the sandbox, prepends `/proxy/` so the URL is served from the same origin.\n * Outside the sandbox (exported HTML, CLI), returns the URL unchanged.\n *\n * @param url - Full HTTPS URL to an allowlisted CDN (esm.sh, unpkg.com, cdn.jsdelivr.net, cdnjs.cloudflare.com)\n * @returns The proxied URL (sandbox) or the original URL (standalone/CLI)\n */\n proxy(url: string): string;\n /**\n * Copy text to clipboard.\n * Must be called synchronously within a user gesture (click/keydown).\n * @returns true if successful, false otherwise\n */\n copy(text: string): Promise<boolean>;\n /**\n * Get the canonical URL of this bulb.\n *\n * Returns the parent typebulb.com URL (including path, query, and `#tb=` fragment),\n * resolving correctly from inside the cross-origin sandbox iframe.\n * Use this instead of `location.href` or `document.referrer`.\n *\n * @returns The full canonical URL\n */\n url(): Promise<string>;\n /**\n * Get the insight data produced by the inference layer.\n *\n * Returns the parsed JSON from insight.json, populated by the inference LLM.\n * Use a type parameter to get typed access to the insight data.\n *\n * @returns The parsed insight JSON, or undefined if no insight is available\n */\n insight<T = unknown>(): T | undefined;\n /**\n * Server-side function proxy.\n *\n * In the CLI, calls exported functions from the `**server.ts**` section.\n * `tb.server.log(...)` is a built-in that prints to CLI stdout (falls back to console.log on web).\n * User exports override built-ins of the same name.\n *\n * A normal export is awaited for its result (`await tb.server.fn()`). An `async function*`\n * export streams: `for await (const chunk of tb.server.gen())`. The call object supports both;\n * break the `for await` to cancel and tear down the server generator.\n */\n server: Record<string, (...args: any[]) => Promise<any> & AsyncIterable<any>>;\n /**\n * Subscribe to a value pushed from the terminal via `typebulb send <file> [message]`.\n *\n * The dual of `tb.server.log` (data out): a value sent *in* from the CLI, no `--trust` required.\n * Use it to start expensive work on demand instead of on load \u2014 e.g. `tb.onMessage(() => start())`\n * \u2014 so hot reloads don't re-trigger it while you edit, and an agent kicks off one run when ready.\n *\n * The message is the JSON-parsed value of what `send` was given (or the raw string if it isn't\n * JSON; `undefined` for a bare `typebulb send <file>`). Returns an unsubscribe function. Inert in\n * an embedded bulb (no sender) \u2014 the handler is registered but never fires.\n *\n * @param handler - Called with each pushed message.\n * @returns An unsubscribe function.\n */\n onMessage(handler: (message: any) => void): () => void;\n /**\n * General-purpose AI call. `tb.ai(opts)` resolves with the full text; `tb.ai.stream(opts)`\n * returns an async iterable of {@link AiChunk} deltas you consume with `for await`.\n *\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai: {\n (options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): Promise<{ text: string }>;\n /**\n * Streaming counterpart of `tb.ai()`. Yields `{ kind: \"text\" | \"reasoning\", text }` deltas\n * as they arrive; break the loop (or abort `signal`) to cancel and stop the upstream.\n *\n * `kind: \"reasoning\"` deltas only arrive when you pass `effort: 1-3` AND use a\n * thinking-capable model; otherwise the stream is `text`-only.\n *\n * @example\n * let answer = \"\";\n * for await (const c of tb.ai.stream({ messages })) {\n * if (c.kind === \"text\") answer += c.text;\n * }\n */\n stream(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): AsyncIterable<AiChunk>;\n };\n /**\n * Local filesystem access (CLI only).\n *\n * Relative paths resolve against the bulb's folder (`tb.dir` \u2014\n * `<bulb-dir>/<filename-stem>/`, created on demand), so\n * `tb.fs.write('results.json')` lands beside the bulb. `../` reaches sibling\n * bulbs' folders; everything stays confined to the project (the launch cwd).\n * Throws in editor/published mode.\n */\n fs: {\n /** Read a file as UTF-8 text. Throws if the file is not valid UTF-8 \u2014 use readBytes for binary. */\n read(path: string): Promise<string>;\n /** Read a file as raw bytes. */\n readBytes(path: string): Promise<Uint8Array>;\n /** Write text or raw bytes to a file. Creates parent directories if needed. */\n write(path: string, content: string | Uint8Array): Promise<boolean>;\n };\n /**\n * The bulb's folder \u2014 absolute path to `<bulb-dir>/<filename-stem>/`\n * (or its subfolder when the run is scoped with `--dir`).\n *\n * For interop only (handing a path to `server.ts` or a spawned tool):\n * `tb.fs` already resolves relative paths against it, so bulb code writing\n * its own files never needs it. CLI only \u2014 throws in editor/published/embedded mode.\n */\n readonly dir: string;\n /**\n * Returns AI models available to the current user.\n * Models are filtered by the user's configured API keys.\n * If no keys are configured, returns only the courtesy model.\n */\n models(): Promise<Array<{\n /** Provider protocol: \"anthropic\", \"openai\", \"gemini\", \"openrouter\" */\n provider: string;\n /** Model identifier, e.g. \"claude-sonnet-4-6\" */\n name: string;\n /** Human-readable display name, e.g. \"Sonnet 4.6\" */\n friendlyName: string;\n /** Provider display name, e.g. \"Anthropic\" */\n providerName: string;\n }>>;\n /**\n * Whether the user's own AI keys back `tb.ai`. False means only the\n * quota-limited courtesy model is available (or no AI at all) \u2014 a bulb\n * making many AI calls should check this and show a \"use your own keys\"\n * notice instead of running.\n */\n hasOwnKeys(): Promise<boolean>;\n /**\n * The bulb's theme override (`<html data-theme>`).\n *\n * - Get: the current override \u2014 `'dark'` | `'light'`, or `undefined` when\n * following the OS preference.\n * - Set `'dark'`/`'light'` to force and persist it (per-bulb); set\n * `undefined` to clear the override and follow the OS again.\n *\n * Drives `<html data-theme>`, so render off `html[data-theme=\"\u2026\"]` selectors\n * (or observe the attribute) rather than reading `tb.theme`.\n */\n theme: 'light' | 'dark' | undefined;\n /**\n * The mode this bulb is running in.\n *\n * - `'local'` \u2014 Running via the typebulb CLI\n * - `'editor'` \u2014 Running in the typebulb.com editor\n * - `'published'` \u2014 Running as a published/standalone bulb on typebulb.com\n * - `'embedded'` \u2014 Running as a bulb embedded inside another bulb (sandboxed,\n * client-only: AI, filesystem, and server RPC are unavailable)\n */\n mode: 'local' | 'editor' | 'published' | 'embedded';\n};\n";
|
|
13
13
|
/** Typebulb globals available in Node-side code (server.ts).
|
|
14
14
|
* The AI subset only — server.ts is otherwise plain Node (its own `fs`, `console.log`), so the
|
|
15
15
|
* browser-only `tb` helpers are intentionally absent here and must match serverTb.ts's runtime surface. */
|
|
16
|
-
export declare const serverTbTypings = "\n/** A single streamed delta from `tb.ai.stream()`. Discriminated by `kind`. */\ntype AiChunk =\n | { kind: \"text\"; text: string }\n | { kind: \"reasoning\"; text: string };\n\n/**\n * Typebulb utilities namespace (server-side).\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * General-purpose AI call. `tb.ai(opts)` resolves with the full text; `tb.ai.stream(opts)`\n * returns an async iterable of {@link AiChunk} deltas you consume with `for await`.\n *\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai: {\n (options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): Promise<{ text: string }>;\n /**\n * Streaming counterpart of `tb.ai()`. Yields `{ kind: \"text\" | \"reasoning\", text }` deltas\n * as they arrive; break the loop (or abort `signal`) to cancel and stop the upstream.\n *\n * `kind: \"reasoning\"` deltas only arrive when you pass `effort: 1-3` AND use a\n * thinking-capable model; otherwise the stream is `text`-only.\n *\n * @example\n * let answer = \"\";\n * for await (const c of tb.ai.stream({ messages })) {\n * if (c.kind === \"text\") answer += c.text;\n * }\n */\n stream(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): AsyncIterable<AiChunk>;\n };\n /**\n * The bulb's
|
|
16
|
+
export declare const serverTbTypings = "\n/** A single streamed delta from `tb.ai.stream()`. Discriminated by `kind`. */\ntype AiChunk =\n | { kind: \"text\"; text: string }\n | { kind: \"reasoning\"; text: string };\n\n/**\n * Typebulb utilities namespace (server-side).\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * General-purpose AI call. `tb.ai(opts)` resolves with the full text; `tb.ai.stream(opts)`\n * returns an async iterable of {@link AiChunk} deltas you consume with `for await`.\n *\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai: {\n (options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): Promise<{ text: string }>;\n /**\n * Streaming counterpart of `tb.ai()`. Yields `{ kind: \"text\" | \"reasoning\", text }` deltas\n * as they arrive; break the loop (or abort `signal`) to cancel and stop the upstream.\n *\n * `kind: \"reasoning\"` deltas only arrive when you pass `effort: 1-3` AND use a\n * thinking-capable model; otherwise the stream is `text`-only.\n *\n * @example\n * let answer = \"\";\n * for await (const c of tb.ai.stream({ messages })) {\n * if (c.kind === \"text\") answer += c.text;\n * }\n */\n stream(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): AsyncIterable<AiChunk>;\n };\n /**\n * The bulb's folder \u2014 absolute path to `<bulb-dir>/<filename-stem>/`\n * (or its subfolder when the run is scoped with `--dir`).\n *\n * For interop only (handing a path to `server.ts` or a spawned tool):\n * `tb.fs` already resolves relative paths against it, so bulb code writing\n * its own files never needs it. CLI only \u2014 throws in editor/published/embedded mode.\n */\n readonly dir: string;\n /**\n * Returns AI models available to the current user.\n * Models are filtered by the user's configured API keys.\n * If no keys are configured, returns only the courtesy model.\n */\n models(): Promise<Array<{\n /** Provider protocol: \"anthropic\", \"openai\", \"gemini\", \"openrouter\" */\n provider: string;\n /** Model identifier, e.g. \"claude-sonnet-4-6\" */\n name: string;\n /** Human-readable display name, e.g. \"Sonnet 4.6\" */\n friendlyName: string;\n /** Provider display name, e.g. \"Anthropic\" */\n providerName: string;\n }>>;\n /**\n * Whether the user's own AI keys back `tb.ai`. False means only the\n * quota-limited courtesy model is available (or no AI at all) \u2014 a bulb\n * making many AI calls should check this and show a \"use your own keys\"\n * notice instead of running.\n */\n hasOwnKeys(): Promise<boolean>;\n /**\n * The mode this bulb is running in.\n *\n * - `'local'` \u2014 Running via the typebulb CLI\n * - `'editor'` \u2014 Running in the typebulb.com editor\n * - `'published'` \u2014 Running as a published/standalone bulb on typebulb.com\n * - `'embedded'` \u2014 Running as a bulb embedded inside another bulb (sandboxed,\n * client-only: AI, filesystem, and server RPC are unavailable)\n */\n mode: 'local' | 'editor' | 'published' | 'embedded';\n};\n";
|
|
17
17
|
//# sourceMappingURL=tbTypings.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tbTypings.d.ts","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"tbTypings.d.ts","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA8PH,kEAAkE;AAClE,eAAO,MAAM,eAAe,0hWAO3B,CAAA;AAED;;4GAE4G;AAC5G,eAAO,MAAM,eAAe,yxJAO3B,CAAA"}
|
package/dist/dts/tbTypings.js
CHANGED
|
@@ -126,7 +126,7 @@ const fs = `
|
|
|
126
126
|
/**
|
|
127
127
|
* Local filesystem access (CLI only).
|
|
128
128
|
*
|
|
129
|
-
* Relative paths resolve against the bulb's
|
|
129
|
+
* Relative paths resolve against the bulb's folder (\`tb.dir\` —
|
|
130
130
|
* \`<bulb-dir>/<filename-stem>/\`, created on demand), so
|
|
131
131
|
* \`tb.fs.write('results.json')\` lands beside the bulb. \`../\` reaches sibling
|
|
132
132
|
* bulbs' folders; everything stays confined to the project (the launch cwd).
|
|
@@ -142,7 +142,8 @@ const fs = `
|
|
|
142
142
|
};`;
|
|
143
143
|
const dir = `
|
|
144
144
|
/**
|
|
145
|
-
* The bulb's
|
|
145
|
+
* The bulb's folder — absolute path to \`<bulb-dir>/<filename-stem>/\`
|
|
146
|
+
* (or its subfolder when the run is scoped with \`--dir\`).
|
|
146
147
|
*
|
|
147
148
|
* For interop only (handing a path to \`server.ts\` or a spawned tool):
|
|
148
149
|
* \`tb.fs\` already resolves relative paths against it, so bulb code writing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tbTypings.js","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,GAAG;;;;;;;;;;;uCAWmB,CAAA;AAEvC,MAAM,OAAO,GAAG;;;;;;;;;yCASyB,CAAA;AAEzC,2DAA2D;AAC3D,MAAM,SAAS,GAAG;;;;;;;;;;;IAWd,CAAA;AAEJ,oGAAoG;AACpG,MAAM,WAAW,GAAG;;;;;CAKnB,CAAA;AAED,MAAM,EAAE,GAAG;;;;;;;;;OASJ,SAAS;;;;;;;;;;;;;;aAcH,SAAS;KACjB,CAAA;AAEL,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;kCAsBmB,CAAA;AAElC,MAAM,KAAK,GAAG;;;;;;;;;;;;uCAYyB,CAAA;AAEvC,MAAM,IAAI,GAAG;;;;;;;;;;uDAU0C,CAAA;AAEvD,MAAM,EAAE,GAAG;;;;;;;;;;;;;;;;;KAiBN,CAAA;AAEL,MAAM,GAAG,GAAG
|
|
1
|
+
{"version":3,"file":"tbTypings.js","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,GAAG;;;;;;;;;;;uCAWmB,CAAA;AAEvC,MAAM,OAAO,GAAG;;;;;;;;;yCASyB,CAAA;AAEzC,2DAA2D;AAC3D,MAAM,SAAS,GAAG;;;;;;;;;;;IAWd,CAAA;AAEJ,oGAAoG;AACpG,MAAM,WAAW,GAAG;;;;;CAKnB,CAAA;AAED,MAAM,EAAE,GAAG;;;;;;;;;OASJ,SAAS;;;;;;;;;;;;;;aAcH,SAAS;KACjB,CAAA;AAEL,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;kCAsBmB,CAAA;AAElC,MAAM,KAAK,GAAG;;;;;;;;;;;;uCAYyB,CAAA;AAEvC,MAAM,IAAI,GAAG;;;;;;;;;;uDAU0C,CAAA;AAEvD,MAAM,EAAE,GAAG;;;;;;;;;;;;;;;;;KAiBN,CAAA;AAEL,MAAM,GAAG,GAAG;;;;;;;;;wBASY,CAAA;AAExB,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAmEA,CAAA;AAE1B,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;0DAewC,CAAA;AAE1D,MAAM,iBAAiB,GAAG;;;;;;;;;;;;iFAYuD,CAAA;AAEjF,kEAAkE;AAClE,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,WAAW;;;;;qBAKxB,WAAW,GAAG,iBAAiB,GAAG,OAAO,GAAG,iBAAiB,GAAG,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,MAAM,GAAG,KAAK,GAAG,IAAI;;CAErI,CAAA;AAED;;4GAE4G;AAC5G,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,WAAW;;;;;qBAKxB,EAAE,GAAG,GAAG,GAAG,MAAM,GAAG,IAAI;;CAE5C,CAAA"}
|