@trazum/core 1.8.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/LICENSE +21 -0
- package/README.md +163 -0
- package/dist/advisories.d.ts +23 -0
- package/dist/advisories.d.ts.map +1 -0
- package/dist/advisories.js +376 -0
- package/dist/advisories.js.map +1 -0
- package/dist/aws-sigv4.d.ts +88 -0
- package/dist/aws-sigv4.d.ts.map +1 -0
- package/dist/aws-sigv4.js +117 -0
- package/dist/aws-sigv4.js.map +1 -0
- package/dist/baseline.d.ts +171 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +273 -0
- package/dist/baseline.js.map +1 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +28 -0
- package/dist/cache.js.map +1 -0
- package/dist/changes.d.ts +29 -0
- package/dist/changes.d.ts.map +1 -0
- package/dist/changes.js +142 -0
- package/dist/changes.js.map +1 -0
- package/dist/compare.d.ts +65 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +58 -0
- package/dist/compare.js.map +1 -0
- package/dist/config-schema.d.ts +118 -0
- package/dist/config-schema.d.ts.map +1 -0
- package/dist/config-schema.js +315 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +112 -0
- package/dist/config.js.map +1 -0
- package/dist/detect.d.ts +70 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +228 -0
- package/dist/detect.js.map +1 -0
- package/dist/evaluate.d.ts +98 -0
- package/dist/evaluate.d.ts.map +1 -0
- package/dist/evaluate.js +110 -0
- package/dist/evaluate.js.map +1 -0
- package/dist/extract.d.ts +81 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +280 -0
- package/dist/extract.js.map +1 -0
- package/dist/gcp-auth.d.ts +58 -0
- package/dist/gcp-auth.d.ts.map +1 -0
- package/dist/gcp-auth.js +113 -0
- package/dist/gcp-auth.js.map +1 -0
- package/dist/glob.d.ts +49 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +154 -0
- package/dist/glob.js.map +1 -0
- package/dist/host.d.ts +30 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +69 -0
- package/dist/host.js.map +1 -0
- package/dist/i18n/en.d.ts +4 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +168 -0
- package/dist/i18n/en.js.map +1 -0
- package/dist/i18n/es.d.ts +4 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +168 -0
- package/dist/i18n/es.js.map +1 -0
- package/dist/i18n/index.d.ts +36 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +50 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/types.d.ts +180 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +11 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/llm.d.ts +226 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +485 -0
- package/dist/llm.js.map +1 -0
- package/dist/nearest.d.ts +20 -0
- package/dist/nearest.d.ts.map +1 -0
- package/dist/nearest.js +54 -0
- package/dist/nearest.js.map +1 -0
- package/dist/net.d.ts +90 -0
- package/dist/net.d.ts.map +1 -0
- package/dist/net.js +203 -0
- package/dist/net.js.map +1 -0
- package/dist/node.d.ts +32 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +33 -0
- package/dist/node.js.map +1 -0
- package/dist/openrouter.d.ts +25 -0
- package/dist/openrouter.d.ts.map +1 -0
- package/dist/openrouter.js +72 -0
- package/dist/openrouter.js.map +1 -0
- package/dist/optimize.d.ts +38 -0
- package/dist/optimize.d.ts.map +1 -0
- package/dist/optimize.js +183 -0
- package/dist/optimize.js.map +1 -0
- package/dist/otlp.d.ts +91 -0
- package/dist/otlp.d.ts.map +1 -0
- package/dist/otlp.js +102 -0
- package/dist/otlp.js.map +1 -0
- package/dist/phrases.d.ts +169 -0
- package/dist/phrases.d.ts.map +1 -0
- package/dist/phrases.js +939 -0
- package/dist/phrases.js.map +1 -0
- package/dist/pricing-overlay.d.ts +55 -0
- package/dist/pricing-overlay.d.ts.map +1 -0
- package/dist/pricing-overlay.js +241 -0
- package/dist/pricing-overlay.js.map +1 -0
- package/dist/pricing.d.ts +115 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +400 -0
- package/dist/pricing.js.map +1 -0
- package/dist/profile.d.ts +71 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +55 -0
- package/dist/profile.js.map +1 -0
- package/dist/promptfoo.d.ts +58 -0
- package/dist/promptfoo.d.ts.map +1 -0
- package/dist/promptfoo.js +149 -0
- package/dist/promptfoo.js.map +1 -0
- package/dist/prune.d.ts +91 -0
- package/dist/prune.d.ts.map +1 -0
- package/dist/prune.js +110 -0
- package/dist/prune.js.map +1 -0
- package/dist/reorder.d.ts +82 -0
- package/dist/reorder.d.ts.map +1 -0
- package/dist/reorder.js +215 -0
- package/dist/reorder.js.map +1 -0
- package/dist/review.d.ts +54 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +131 -0
- package/dist/review.js.map +1 -0
- package/dist/rules.d.ts +5 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +279 -0
- package/dist/rules.js.map +1 -0
- package/dist/savings.d.ts +36 -0
- package/dist/savings.d.ts.map +1 -0
- package/dist/savings.js +83 -0
- package/dist/savings.js.map +1 -0
- package/dist/segment.d.ts +8 -0
- package/dist/segment.d.ts.map +1 -0
- package/dist/segment.js +74 -0
- package/dist/segment.js.map +1 -0
- package/dist/shared-prefix.d.ts +63 -0
- package/dist/shared-prefix.d.ts.map +1 -0
- package/dist/shared-prefix.js +151 -0
- package/dist/shared-prefix.js.map +1 -0
- package/dist/similarity.d.ts +13 -0
- package/dist/similarity.d.ts.map +1 -0
- package/dist/similarity.js +30 -0
- package/dist/similarity.js.map +1 -0
- package/dist/structure.d.ts +144 -0
- package/dist/structure.d.ts.map +1 -0
- package/dist/structure.js +455 -0
- package/dist/structure.js.map +1 -0
- package/dist/suggest.d.ts +100 -0
- package/dist/suggest.d.ts.map +1 -0
- package/dist/suggest.js +151 -0
- package/dist/suggest.js.map +1 -0
- package/dist/tokenizer.d.ts +57 -0
- package/dist/tokenizer.d.ts.map +1 -0
- package/dist/tokenizer.js +157 -0
- package/dist/tokenizer.js.map +1 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/walk.d.ts +40 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +95 -0
- package/dist/walk.js.map +1 -0
- package/package.json +56 -0
- package/src/advisories.ts +431 -0
- package/src/aws-sigv4.ts +174 -0
- package/src/baseline.ts +390 -0
- package/src/cache.ts +54 -0
- package/src/changes.ts +158 -0
- package/src/compare.ts +131 -0
- package/src/config-schema.ts +451 -0
- package/src/config.ts +161 -0
- package/src/detect.ts +312 -0
- package/src/evaluate.ts +188 -0
- package/src/extract.ts +336 -0
- package/src/gcp-auth.ts +166 -0
- package/src/glob.ts +160 -0
- package/src/host.ts +90 -0
- package/src/i18n/en.ts +236 -0
- package/src/i18n/es.ts +236 -0
- package/src/i18n/index.ts +68 -0
- package/src/i18n/types.ts +230 -0
- package/src/index.ts +228 -0
- package/src/llm.ts +708 -0
- package/src/nearest.ts +61 -0
- package/src/net.ts +233 -0
- package/src/node.ts +63 -0
- package/src/openrouter.ts +125 -0
- package/src/optimize.ts +228 -0
- package/src/otlp.ts +179 -0
- package/src/phrases.ts +1047 -0
- package/src/pricing-overlay.ts +319 -0
- package/src/pricing.ts +468 -0
- package/src/profile.ts +124 -0
- package/src/promptfoo.ts +213 -0
- package/src/prune.ts +211 -0
- package/src/reorder.ts +307 -0
- package/src/review.ts +180 -0
- package/src/rules.ts +324 -0
- package/src/savings.ts +121 -0
- package/src/segment.ts +106 -0
- package/src/shared-prefix.ts +198 -0
- package/src/similarity.ts +28 -0
- package/src/structure.ts +652 -0
- package/src/suggest.ts +254 -0
- package/src/tokenizer.ts +190 -0
- package/src/types.ts +323 -0
- package/src/walk.ts +117 -0
package/dist/extract.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts embedded in source files.
|
|
3
|
+
*
|
|
4
|
+
* `check` and `diff` read `.txt`, `.md`, `.prompt` and `.tmpl`. Real prompts live
|
|
5
|
+
* in TypeScript template literals, Python triple-quoted strings and YAML blocks,
|
|
6
|
+
* so adopting Trazum has meant first refactoring them out into standalone files —
|
|
7
|
+
* a change to somebody's application as the price of admission.
|
|
8
|
+
*
|
|
9
|
+
* **It reads a marker, it does not guess.** `// trazum:prompt` on the line before
|
|
10
|
+
* the literal, and then the literal by delimiter matching. Guessing which string
|
|
11
|
+
* in a file is a prompt is a heuristic, and a heuristic inside a tool used as a
|
|
12
|
+
* CI gate produces failures on strings nobody meant to govern. A marker is one
|
|
13
|
+
* line of noise in exchange for never being wrong about what it picked up.
|
|
14
|
+
*
|
|
15
|
+
* **Interpolation already works and is not a special case.** `${x}` inside a
|
|
16
|
+
* template literal is exactly the placeholder shape `segment.ts` protects, so an
|
|
17
|
+
* embedded prompt gets the same cache-prefix analysis, the same protection from
|
|
18
|
+
* the rules, and the same `--reorder` treatment as a `{{x}}` template. Nothing
|
|
19
|
+
* here has to know about it.
|
|
20
|
+
*
|
|
21
|
+
* **The honest limit:** a prompt assembled from concatenated pieces cannot be
|
|
22
|
+
* read this way. `` `You are ${role}.` + rules.join('\n') `` is a prompt whose
|
|
23
|
+
* text does not exist until it runs. This module declines it and says so rather
|
|
24
|
+
* than governing the half it can see — a budget enforced against a fragment is a
|
|
25
|
+
* green build for a prompt nobody measured.
|
|
26
|
+
*
|
|
27
|
+
* Scanned character by character rather than with a regex. The module it most
|
|
28
|
+
* resembles shipped two quadratic patterns this week, and delimiter matching over
|
|
29
|
+
* untrusted source is exactly the shape that goes wrong.
|
|
30
|
+
*/
|
|
31
|
+
/** The marker, in the comment syntaxes that cover the languages prompts live in. */
|
|
32
|
+
const MARKERS = ['//', '#', '--', '<!--'];
|
|
33
|
+
const TAG = 'trazum:prompt';
|
|
34
|
+
/**
|
|
35
|
+
* Both ways an HTML comment can close.
|
|
36
|
+
*
|
|
37
|
+
* `--!>` is the "comment end bang" the HTML parser also accepts, and stripping
|
|
38
|
+
* only `-->` put it in the name: `<!-- trazum:prompt greeting--!>` produced the
|
|
39
|
+
* name `greeting--!>`. Found by CodeQL, which is the second time this week a
|
|
40
|
+
* pattern has been right about the case in front of it and wrong about the one
|
|
41
|
+
* beside it.
|
|
42
|
+
*/
|
|
43
|
+
const COMMENT_CLOSE = /--!?>\s*$/;
|
|
44
|
+
/**
|
|
45
|
+
* What a name may be.
|
|
46
|
+
*
|
|
47
|
+
* An identifier charset rather than "whatever is left on the line". The name
|
|
48
|
+
* flows into `promptId`, which is printed in reports and matched against the
|
|
49
|
+
* budget patterns in `trazum.config.json` — a name is an identifier, and letting
|
|
50
|
+
* it be arbitrary text is how a stray terminator became part of one above. A
|
|
51
|
+
* candidate that does not fit falls back to the `file:line` form, which always
|
|
52
|
+
* works.
|
|
53
|
+
*/
|
|
54
|
+
const NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
55
|
+
/**
|
|
56
|
+
* How far past the marker a literal may start.
|
|
57
|
+
*
|
|
58
|
+
* A prompt is expected on the next line or two — `const SYSTEM = \`` and the
|
|
59
|
+
* occasional type annotation. Scanning further would let a marker attach itself
|
|
60
|
+
* to an unrelated string much later in the file, which is the false positive this
|
|
61
|
+
* design exists to avoid.
|
|
62
|
+
*/
|
|
63
|
+
const MAX_GAP_LINES = 3;
|
|
64
|
+
/**
|
|
65
|
+
* A forward-only line counter.
|
|
66
|
+
*
|
|
67
|
+
* Markers are found in increasing order, so each character is counted once
|
|
68
|
+
* across the whole scan. Recomputing the line by counting from zero at every
|
|
69
|
+
* marker is the obvious version and is quadratic in the number of markers —
|
|
70
|
+
* 15.5 seconds on a file of 20,000 of them, which the hostile-input tests
|
|
71
|
+
* caught before this shipped.
|
|
72
|
+
*/
|
|
73
|
+
function lineCounter(source) {
|
|
74
|
+
let seen = 0;
|
|
75
|
+
let line = 1;
|
|
76
|
+
return (offset) => {
|
|
77
|
+
for (; seen < offset && seen < source.length; seen++) {
|
|
78
|
+
if (source[seen] === '\n')
|
|
79
|
+
line++;
|
|
80
|
+
}
|
|
81
|
+
return line;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const ESCAPES = {
|
|
85
|
+
n: '\n',
|
|
86
|
+
t: '\t',
|
|
87
|
+
r: '\r',
|
|
88
|
+
'\\': '\\',
|
|
89
|
+
'"': '"',
|
|
90
|
+
"'": "'",
|
|
91
|
+
'`': '`',
|
|
92
|
+
'0': '\0',
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Reads a string literal starting at `start`, which must be its opening quote.
|
|
96
|
+
*
|
|
97
|
+
* Returns the resolved text and the offset just past the closing delimiter, or
|
|
98
|
+
* null when the literal never closes. Escapes are resolved for single- and
|
|
99
|
+
* double-quoted strings, where `\n` is one newline written as two characters;
|
|
100
|
+
* backtick and triple-quoted literals carry their newlines literally and only
|
|
101
|
+
* need the delimiter itself unescaped.
|
|
102
|
+
*/
|
|
103
|
+
function readLiteral(source, start) {
|
|
104
|
+
const triple = source.slice(start, start + 3);
|
|
105
|
+
const quote = triple === '"""' || triple === "'''"
|
|
106
|
+
? triple
|
|
107
|
+
: source[start] === '`' || source[start] === '"' || source[start] === "'"
|
|
108
|
+
? source[start]
|
|
109
|
+
: null;
|
|
110
|
+
if (quote === null)
|
|
111
|
+
return null;
|
|
112
|
+
const raw = quote === '`' || quote.length === 3;
|
|
113
|
+
const textStart = start + quote.length;
|
|
114
|
+
let out = '';
|
|
115
|
+
let i = textStart;
|
|
116
|
+
while (i < source.length) {
|
|
117
|
+
const ch = source[i];
|
|
118
|
+
if (ch === '\\') {
|
|
119
|
+
const next = source[i + 1];
|
|
120
|
+
if (next === undefined)
|
|
121
|
+
break;
|
|
122
|
+
// In a raw literal only the delimiter and the backslash itself are
|
|
123
|
+
// escapes; `\n` there is a literal backslash followed by an n, and
|
|
124
|
+
// resolving it would invent a newline the prompt does not contain.
|
|
125
|
+
if (raw) {
|
|
126
|
+
out += next === quote[0] || next === '\\' ? next : `\\${next}`;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
out += ESCAPES[next] ?? `\\${next}`;
|
|
130
|
+
}
|
|
131
|
+
i += 2;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (source.startsWith(quote, i)) {
|
|
135
|
+
return { text: out, quote, end: i + quote.length, textStart };
|
|
136
|
+
}
|
|
137
|
+
// A single-quoted or double-quoted literal cannot span lines. Stopping here
|
|
138
|
+
// rather than running to the end of the file is what keeps a missing quote
|
|
139
|
+
// from swallowing the rest of the source.
|
|
140
|
+
if (quote.length === 1 && quote !== '`' && ch === '\n')
|
|
141
|
+
return null;
|
|
142
|
+
out += ch;
|
|
143
|
+
i++;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
/** Whether the expression continues past the literal with a concatenation. */
|
|
148
|
+
function isConcatenated(source, end) {
|
|
149
|
+
let i = end;
|
|
150
|
+
while (i < source.length && (source[i] === ' ' || source[i] === '\t'))
|
|
151
|
+
i++;
|
|
152
|
+
// `+` in JS/TS, `.` in PHP-ish sources, and a bare newline followed by `+` is
|
|
153
|
+
// the wrapped form of the same thing. A trailing comma or semicolon is not.
|
|
154
|
+
if (source[i] === '+')
|
|
155
|
+
return true;
|
|
156
|
+
if (source[i] === '\n') {
|
|
157
|
+
let j = i + 1;
|
|
158
|
+
while (j < source.length && /[ \t\n]/.test(source[j]))
|
|
159
|
+
j++;
|
|
160
|
+
return source[j] === '+';
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Finds every marked prompt in a source file.
|
|
166
|
+
*
|
|
167
|
+
* Returns what it found and what it refused, because a marker that produced
|
|
168
|
+
* nothing is the case the author most needs to hear about: they asked for the
|
|
169
|
+
* prompt to be governed and it is not being governed.
|
|
170
|
+
*/
|
|
171
|
+
export function extractPrompts(source) {
|
|
172
|
+
const prompts = [];
|
|
173
|
+
const declined = [];
|
|
174
|
+
const lineAt = lineCounter(source);
|
|
175
|
+
let cursor = 0;
|
|
176
|
+
while (cursor < source.length) {
|
|
177
|
+
const at = source.indexOf(TAG, cursor);
|
|
178
|
+
if (at === -1)
|
|
179
|
+
break;
|
|
180
|
+
cursor = at + TAG.length;
|
|
181
|
+
// The tag has to be inside a comment. Without this, the string 'trazum:prompt'
|
|
182
|
+
// appearing in a prompt's own text would mark the prompt after it.
|
|
183
|
+
const lineStart = source.lastIndexOf('\n', at) + 1;
|
|
184
|
+
const before = source.slice(lineStart, at).trimStart();
|
|
185
|
+
if (!MARKERS.some((m) => before.startsWith(m)))
|
|
186
|
+
continue;
|
|
187
|
+
const markerLine = lineAt(at);
|
|
188
|
+
// The rest of the marker line is an optional name: `trazum:prompt greeting`.
|
|
189
|
+
const lineEnd = source.indexOf('\n', at);
|
|
190
|
+
const rest = source.slice(cursor, lineEnd === -1 ? source.length : lineEnd);
|
|
191
|
+
const candidate = rest.replace(COMMENT_CLOSE, '').trim().split(/\s+/)[0] ?? '';
|
|
192
|
+
const name = NAME.test(candidate) ? candidate : undefined;
|
|
193
|
+
// Scan forward for the opening delimiter, bounded so a marker cannot adopt
|
|
194
|
+
// a string much further down the file.
|
|
195
|
+
let i = lineEnd === -1 ? source.length : lineEnd + 1;
|
|
196
|
+
let lines = 0;
|
|
197
|
+
let opened = null;
|
|
198
|
+
let openAt = -1;
|
|
199
|
+
while (i < source.length && lines <= MAX_GAP_LINES) {
|
|
200
|
+
const ch = source[i];
|
|
201
|
+
if (ch === '\n') {
|
|
202
|
+
lines++;
|
|
203
|
+
i++;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (ch === '`' || ch === '"' || ch === "'") {
|
|
207
|
+
openAt = i;
|
|
208
|
+
opened = readLiteral(source, i);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
i++;
|
|
212
|
+
}
|
|
213
|
+
if (openAt === -1) {
|
|
214
|
+
declined.push({
|
|
215
|
+
line: markerLine,
|
|
216
|
+
reason: 'no-literal',
|
|
217
|
+
detail: `no string literal within ${MAX_GAP_LINES} lines of the marker`,
|
|
218
|
+
});
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (opened === null) {
|
|
222
|
+
declined.push({
|
|
223
|
+
line: markerLine,
|
|
224
|
+
reason: 'unterminated',
|
|
225
|
+
detail: 'the literal is never closed',
|
|
226
|
+
});
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (isConcatenated(source, opened.end)) {
|
|
230
|
+
declined.push({
|
|
231
|
+
line: markerLine,
|
|
232
|
+
reason: 'concatenated',
|
|
233
|
+
detail: 'the prompt is built by concatenation, so its text does not exist until it runs',
|
|
234
|
+
});
|
|
235
|
+
cursor = opened.end;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
prompts.push({
|
|
239
|
+
name,
|
|
240
|
+
line: markerLine,
|
|
241
|
+
text: opened.text,
|
|
242
|
+
quote: opened.quote,
|
|
243
|
+
start: opened.textStart,
|
|
244
|
+
end: opened.end - opened.quote.length,
|
|
245
|
+
});
|
|
246
|
+
cursor = opened.end;
|
|
247
|
+
}
|
|
248
|
+
return { prompts, declined };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* A stable identifier for an embedded prompt, for budgets and reports.
|
|
252
|
+
*
|
|
253
|
+
* `src/prompts.ts#support` when the marker was named, `src/prompts.ts:12` when it
|
|
254
|
+
* was not. Both are path-prefixed so the glob patterns in `trazum.config.json`
|
|
255
|
+
* cover embedded prompts without learning a new syntax — `src/**` matches either.
|
|
256
|
+
*/
|
|
257
|
+
export function promptId(path, prompt) {
|
|
258
|
+
return prompt.name ? `${path}#${prompt.name}` : `${path}:${prompt.line}`;
|
|
259
|
+
}
|
|
260
|
+
/** Whether a file is worth opening for extraction at all. */
|
|
261
|
+
export const SOURCE_EXTENSIONS = [
|
|
262
|
+
'.ts',
|
|
263
|
+
'.tsx',
|
|
264
|
+
'.js',
|
|
265
|
+
'.jsx',
|
|
266
|
+
'.mjs',
|
|
267
|
+
'.py',
|
|
268
|
+
'.rb',
|
|
269
|
+
'.go',
|
|
270
|
+
'.rs',
|
|
271
|
+
'.java',
|
|
272
|
+
'.kt',
|
|
273
|
+
'.cs',
|
|
274
|
+
'.php',
|
|
275
|
+
'.yaml',
|
|
276
|
+
'.yml',
|
|
277
|
+
];
|
|
278
|
+
/** Cheap pre-filter: a source file with no marker has nothing to extract. */
|
|
279
|
+
export const hasMarker = (source) => source.includes(TAG);
|
|
280
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Google service-account access token, without the SDK.
|
|
3
|
+
*
|
|
4
|
+
* Vertex will not take an API key. It wants an OAuth access token, and the only
|
|
5
|
+
* way to get one unattended is the two-legged flow: build a JWT, sign it with
|
|
6
|
+
* the service account's private key, and trade it at the token endpoint.
|
|
7
|
+
* `google-auth-library` is ninety-odd packages to do that; this library has zero
|
|
8
|
+
* runtime dependencies and a test that fails the build if one appears, because
|
|
9
|
+
* every dependency is somebody else's code reading your prompts.
|
|
10
|
+
*
|
|
11
|
+
* WebCrypto rather than `node:crypto`, so the browser-safe entry point stays
|
|
12
|
+
* browser-safe. `RSASSA-PKCS1-v1_5` with SHA-256 is what Google calls RS256.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* PEM to the DER bytes `importKey` wants.
|
|
16
|
+
*
|
|
17
|
+
* The header, the footer and every newline come out. A PEM that still has them
|
|
18
|
+
* fails inside WebCrypto with `DataError`, which says nothing about which of the
|
|
19
|
+
* fourteen things that can be wrong with a key is wrong with this one.
|
|
20
|
+
*/
|
|
21
|
+
export declare function pkcs8FromPem(pem: string): ArrayBuffer;
|
|
22
|
+
export interface ServiceAccount {
|
|
23
|
+
client_email: string;
|
|
24
|
+
private_key: string;
|
|
25
|
+
token_uri?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The assertion Google trades for a token.
|
|
29
|
+
*
|
|
30
|
+
* Exported so a test can read it: this is the document where a wrong `aud`, a
|
|
31
|
+
* clock an hour out, or a scope nobody granted turns into `invalid_grant`, an
|
|
32
|
+
* error message that names none of the three.
|
|
33
|
+
*/
|
|
34
|
+
export declare function signedJwt(account: ServiceAccount, scope: string, now: Date): Promise<string>;
|
|
35
|
+
export interface CachedToken {
|
|
36
|
+
token: string;
|
|
37
|
+
/** Epoch seconds. */
|
|
38
|
+
expiresAt: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Trades the assertion for an access token, caching until shortly before expiry.
|
|
42
|
+
*
|
|
43
|
+
* The cache is the point. A token lasts an hour and `optimize --suggest` over a
|
|
44
|
+
* directory makes one call per prompt: fetching a token each time turns forty
|
|
45
|
+
* prompts into eighty requests, half of them to an endpoint that rate-limits.
|
|
46
|
+
*
|
|
47
|
+
* Sixty seconds of margin, because a token that expires in flight fails the
|
|
48
|
+
* request it was fetched for, and the clock here is not the clock there.
|
|
49
|
+
*/
|
|
50
|
+
export declare function accessToken(account: ServiceAccount, options?: {
|
|
51
|
+
scope?: string;
|
|
52
|
+
fetchImpl?: typeof fetch;
|
|
53
|
+
now?: () => Date;
|
|
54
|
+
cache?: {
|
|
55
|
+
current: CachedToken | null;
|
|
56
|
+
};
|
|
57
|
+
}): Promise<string>;
|
|
58
|
+
//# sourceMappingURL=gcp-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-auth.d.ts","sourceRoot":"","sources":["../src/gcp-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAiBrD;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,IAAI,GACR,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,CAAC;CACpC,GACL,OAAO,CAAC,MAAM,CAAC,CAyCjB"}
|
package/dist/gcp-auth.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Google service-account access token, without the SDK.
|
|
3
|
+
*
|
|
4
|
+
* Vertex will not take an API key. It wants an OAuth access token, and the only
|
|
5
|
+
* way to get one unattended is the two-legged flow: build a JWT, sign it with
|
|
6
|
+
* the service account's private key, and trade it at the token endpoint.
|
|
7
|
+
* `google-auth-library` is ninety-odd packages to do that; this library has zero
|
|
8
|
+
* runtime dependencies and a test that fails the build if one appears, because
|
|
9
|
+
* every dependency is somebody else's code reading your prompts.
|
|
10
|
+
*
|
|
11
|
+
* WebCrypto rather than `node:crypto`, so the browser-safe entry point stays
|
|
12
|
+
* browser-safe. `RSASSA-PKCS1-v1_5` with SHA-256 is what Google calls RS256.
|
|
13
|
+
*/
|
|
14
|
+
/** Base64url, no padding — what a JWT uses and what `btoa` does not produce. */
|
|
15
|
+
function base64url(bytes) {
|
|
16
|
+
let binary = '';
|
|
17
|
+
for (const byte of bytes)
|
|
18
|
+
binary += String.fromCharCode(byte);
|
|
19
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
20
|
+
}
|
|
21
|
+
const encoder = new TextEncoder();
|
|
22
|
+
const encodeJson = (value) => base64url(encoder.encode(JSON.stringify(value)));
|
|
23
|
+
/**
|
|
24
|
+
* PEM to the DER bytes `importKey` wants.
|
|
25
|
+
*
|
|
26
|
+
* The header, the footer and every newline come out. A PEM that still has them
|
|
27
|
+
* fails inside WebCrypto with `DataError`, which says nothing about which of the
|
|
28
|
+
* fourteen things that can be wrong with a key is wrong with this one.
|
|
29
|
+
*/
|
|
30
|
+
export function pkcs8FromPem(pem) {
|
|
31
|
+
const body = pem
|
|
32
|
+
.replace(/-----BEGIN [^-]+-----/, '')
|
|
33
|
+
.replace(/-----END [^-]+-----/, '')
|
|
34
|
+
.replace(/\s+/g, '');
|
|
35
|
+
if (!body)
|
|
36
|
+
throw new Error('The service account private key is empty.');
|
|
37
|
+
let binary;
|
|
38
|
+
try {
|
|
39
|
+
binary = atob(body);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new Error('The service account private key is not valid base64.');
|
|
43
|
+
}
|
|
44
|
+
// Returned as an ArrayBuffer rather than a view: `importKey` accepts a
|
|
45
|
+
// `BufferSource`, and a `Uint8Array` over a `SharedArrayBuffer` is not one
|
|
46
|
+
// as far as the type checker is concerned.
|
|
47
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0)).buffer;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The assertion Google trades for a token.
|
|
51
|
+
*
|
|
52
|
+
* Exported so a test can read it: this is the document where a wrong `aud`, a
|
|
53
|
+
* clock an hour out, or a scope nobody granted turns into `invalid_grant`, an
|
|
54
|
+
* error message that names none of the three.
|
|
55
|
+
*/
|
|
56
|
+
export async function signedJwt(account, scope, now) {
|
|
57
|
+
const tokenUri = account.token_uri ?? 'https://oauth2.googleapis.com/token';
|
|
58
|
+
const issued = Math.floor(now.getTime() / 1000);
|
|
59
|
+
const header = encodeJson({ alg: 'RS256', typ: 'JWT' });
|
|
60
|
+
const claims = encodeJson({
|
|
61
|
+
iss: account.client_email,
|
|
62
|
+
scope,
|
|
63
|
+
aud: tokenUri,
|
|
64
|
+
iat: issued,
|
|
65
|
+
// An hour is Google's maximum. Longer is rejected outright rather than
|
|
66
|
+
// clamped, which is a confusing way to learn about a limit.
|
|
67
|
+
exp: issued + 3600,
|
|
68
|
+
});
|
|
69
|
+
const key = await crypto.subtle.importKey('pkcs8', pkcs8FromPem(account.private_key), { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, ['sign']);
|
|
70
|
+
const signature = await crypto.subtle.sign('RSASSA-PKCS1-v1_5', key, encoder.encode(`${header}.${claims}`));
|
|
71
|
+
return `${header}.${claims}.${base64url(new Uint8Array(signature))}`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Trades the assertion for an access token, caching until shortly before expiry.
|
|
75
|
+
*
|
|
76
|
+
* The cache is the point. A token lasts an hour and `optimize --suggest` over a
|
|
77
|
+
* directory makes one call per prompt: fetching a token each time turns forty
|
|
78
|
+
* prompts into eighty requests, half of them to an endpoint that rate-limits.
|
|
79
|
+
*
|
|
80
|
+
* Sixty seconds of margin, because a token that expires in flight fails the
|
|
81
|
+
* request it was fetched for, and the clock here is not the clock there.
|
|
82
|
+
*/
|
|
83
|
+
export async function accessToken(account, options = {}) {
|
|
84
|
+
const { scope = 'https://www.googleapis.com/auth/cloud-platform', fetchImpl = fetch, now = () => new Date(), cache, } = options;
|
|
85
|
+
const at = now();
|
|
86
|
+
const seconds = Math.floor(at.getTime() / 1000);
|
|
87
|
+
if (cache?.current && cache.current.expiresAt - 60 > seconds) {
|
|
88
|
+
return cache.current.token;
|
|
89
|
+
}
|
|
90
|
+
const assertion = await signedJwt(account, scope, at);
|
|
91
|
+
const tokenUri = account.token_uri ?? 'https://oauth2.googleapis.com/token';
|
|
92
|
+
const res = await fetchImpl(tokenUri, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
redirect: 'error',
|
|
95
|
+
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
96
|
+
body: new URLSearchParams({
|
|
97
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
98
|
+
assertion,
|
|
99
|
+
}).toString(),
|
|
100
|
+
});
|
|
101
|
+
if (!res.ok) {
|
|
102
|
+
throw new Error(`Google refused the service account assertion (${res.status}): ${await res.text()}`);
|
|
103
|
+
}
|
|
104
|
+
const data = (await res.json());
|
|
105
|
+
if (typeof data.access_token !== 'string' || !data.access_token) {
|
|
106
|
+
throw new Error('Google returned no access_token for the service account.');
|
|
107
|
+
}
|
|
108
|
+
const lifetime = typeof data.expires_in === 'number' ? data.expires_in : 3600;
|
|
109
|
+
if (cache)
|
|
110
|
+
cache.current = { token: data.access_token, expiresAt: seconds + lifetime };
|
|
111
|
+
return data.access_token;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=gcp-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-auth.js","sourceRoot":"","sources":["../src/gcp-auth.ts"],"names":[],"mappings":"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"}
|
package/dist/glob.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small, bounded glob matcher.
|
|
3
|
+
*
|
|
4
|
+
* Trazum has zero runtime dependencies and that is a security property, not a
|
|
5
|
+
* preference: a prompt optimiser reads your prompts, and every dependency is
|
|
6
|
+
* someone else's code doing that too. So the config file's budget patterns are
|
|
7
|
+
* matched here rather than by pulling in a glob library.
|
|
8
|
+
*
|
|
9
|
+
* **It is deliberately not a regex translation.** Turning `**` into
|
|
10
|
+
* `(?:[^/]*\/)*` produces exactly the nested-quantifier shape that makes a
|
|
11
|
+
* pattern take exponential time on the wrong input, and these patterns come
|
|
12
|
+
* from a file in a repository — which on a pull request means from whoever
|
|
13
|
+
* opened it. A segment-wise dynamic program has no such shape: it is O(pattern
|
|
14
|
+
* segments x path segments), and each segment match is O(pattern chars x path
|
|
15
|
+
* chars) with no backtracking beyond a single remembered star.
|
|
16
|
+
*
|
|
17
|
+
* Supported, and nothing else:
|
|
18
|
+
* `*` any run of characters within one path segment
|
|
19
|
+
* `**` any number of whole path segments, including none
|
|
20
|
+
* `?` exactly one character within one path segment
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* True when `path` matches `pattern`.
|
|
24
|
+
*
|
|
25
|
+
* Both sides are treated as relative paths with `/` separators; a leading `./`
|
|
26
|
+
* and duplicate separators are ignored, and backslashes are read as separators
|
|
27
|
+
* so a Windows-shaped path still matches a pattern written with slashes.
|
|
28
|
+
*/
|
|
29
|
+
export declare function matchGlob(pattern: string, path: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* How specific a pattern is, for deciding which of two matching patterns wins.
|
|
32
|
+
*
|
|
33
|
+
* "Most specific" needs a definition that is stated rather than felt, because
|
|
34
|
+
* a budget silently resolved from the wrong pattern is a budget nobody can
|
|
35
|
+
* debug. The rule: **more literal characters wins**, and a longer pattern
|
|
36
|
+
* breaks a tie. So `prompts/system.txt` beats `prompts/*.txt`, which beats
|
|
37
|
+
* `prompts/**`, which beats `**`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function specificity(pattern: string): number;
|
|
40
|
+
/**
|
|
41
|
+
* The most specific pattern in `patterns` that matches `path`, or null.
|
|
42
|
+
*
|
|
43
|
+
* Ties on specificity are broken by lexical order rather than by insertion
|
|
44
|
+
* order: object key order is easy to reorder by accident, and a budget that
|
|
45
|
+
* changes because two keys swapped places is the kind of bug that gets blamed
|
|
46
|
+
* on the tool.
|
|
47
|
+
*/
|
|
48
|
+
export declare function mostSpecificMatch(patterns: readonly string[], path: string): string | null;
|
|
49
|
+
//# sourceMappingURL=glob.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAoDH;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAmChE;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAcf"}
|