@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.
Files changed (223) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/advisories.d.ts +23 -0
  4. package/dist/advisories.d.ts.map +1 -0
  5. package/dist/advisories.js +376 -0
  6. package/dist/advisories.js.map +1 -0
  7. package/dist/aws-sigv4.d.ts +88 -0
  8. package/dist/aws-sigv4.d.ts.map +1 -0
  9. package/dist/aws-sigv4.js +117 -0
  10. package/dist/aws-sigv4.js.map +1 -0
  11. package/dist/baseline.d.ts +171 -0
  12. package/dist/baseline.d.ts.map +1 -0
  13. package/dist/baseline.js +273 -0
  14. package/dist/baseline.js.map +1 -0
  15. package/dist/cache.d.ts +26 -0
  16. package/dist/cache.d.ts.map +1 -0
  17. package/dist/cache.js +28 -0
  18. package/dist/cache.js.map +1 -0
  19. package/dist/changes.d.ts +29 -0
  20. package/dist/changes.d.ts.map +1 -0
  21. package/dist/changes.js +142 -0
  22. package/dist/changes.js.map +1 -0
  23. package/dist/compare.d.ts +65 -0
  24. package/dist/compare.d.ts.map +1 -0
  25. package/dist/compare.js +58 -0
  26. package/dist/compare.js.map +1 -0
  27. package/dist/config-schema.d.ts +118 -0
  28. package/dist/config-schema.d.ts.map +1 -0
  29. package/dist/config-schema.js +315 -0
  30. package/dist/config-schema.js.map +1 -0
  31. package/dist/config.d.ts +47 -0
  32. package/dist/config.d.ts.map +1 -0
  33. package/dist/config.js +112 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/detect.d.ts +70 -0
  36. package/dist/detect.d.ts.map +1 -0
  37. package/dist/detect.js +228 -0
  38. package/dist/detect.js.map +1 -0
  39. package/dist/evaluate.d.ts +98 -0
  40. package/dist/evaluate.d.ts.map +1 -0
  41. package/dist/evaluate.js +110 -0
  42. package/dist/evaluate.js.map +1 -0
  43. package/dist/extract.d.ts +81 -0
  44. package/dist/extract.d.ts.map +1 -0
  45. package/dist/extract.js +280 -0
  46. package/dist/extract.js.map +1 -0
  47. package/dist/gcp-auth.d.ts +58 -0
  48. package/dist/gcp-auth.d.ts.map +1 -0
  49. package/dist/gcp-auth.js +113 -0
  50. package/dist/gcp-auth.js.map +1 -0
  51. package/dist/glob.d.ts +49 -0
  52. package/dist/glob.d.ts.map +1 -0
  53. package/dist/glob.js +154 -0
  54. package/dist/glob.js.map +1 -0
  55. package/dist/host.d.ts +30 -0
  56. package/dist/host.d.ts.map +1 -0
  57. package/dist/host.js +69 -0
  58. package/dist/host.js.map +1 -0
  59. package/dist/i18n/en.d.ts +4 -0
  60. package/dist/i18n/en.d.ts.map +1 -0
  61. package/dist/i18n/en.js +168 -0
  62. package/dist/i18n/en.js.map +1 -0
  63. package/dist/i18n/es.d.ts +4 -0
  64. package/dist/i18n/es.d.ts.map +1 -0
  65. package/dist/i18n/es.js +168 -0
  66. package/dist/i18n/es.js.map +1 -0
  67. package/dist/i18n/index.d.ts +36 -0
  68. package/dist/i18n/index.d.ts.map +1 -0
  69. package/dist/i18n/index.js +50 -0
  70. package/dist/i18n/index.js.map +1 -0
  71. package/dist/i18n/types.d.ts +180 -0
  72. package/dist/i18n/types.d.ts.map +1 -0
  73. package/dist/i18n/types.js +11 -0
  74. package/dist/i18n/types.js.map +1 -0
  75. package/dist/index.d.ts +66 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +69 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/llm.d.ts +226 -0
  80. package/dist/llm.d.ts.map +1 -0
  81. package/dist/llm.js +485 -0
  82. package/dist/llm.js.map +1 -0
  83. package/dist/nearest.d.ts +20 -0
  84. package/dist/nearest.d.ts.map +1 -0
  85. package/dist/nearest.js +54 -0
  86. package/dist/nearest.js.map +1 -0
  87. package/dist/net.d.ts +90 -0
  88. package/dist/net.d.ts.map +1 -0
  89. package/dist/net.js +203 -0
  90. package/dist/net.js.map +1 -0
  91. package/dist/node.d.ts +32 -0
  92. package/dist/node.d.ts.map +1 -0
  93. package/dist/node.js +33 -0
  94. package/dist/node.js.map +1 -0
  95. package/dist/openrouter.d.ts +25 -0
  96. package/dist/openrouter.d.ts.map +1 -0
  97. package/dist/openrouter.js +72 -0
  98. package/dist/openrouter.js.map +1 -0
  99. package/dist/optimize.d.ts +38 -0
  100. package/dist/optimize.d.ts.map +1 -0
  101. package/dist/optimize.js +183 -0
  102. package/dist/optimize.js.map +1 -0
  103. package/dist/otlp.d.ts +91 -0
  104. package/dist/otlp.d.ts.map +1 -0
  105. package/dist/otlp.js +102 -0
  106. package/dist/otlp.js.map +1 -0
  107. package/dist/phrases.d.ts +169 -0
  108. package/dist/phrases.d.ts.map +1 -0
  109. package/dist/phrases.js +939 -0
  110. package/dist/phrases.js.map +1 -0
  111. package/dist/pricing-overlay.d.ts +55 -0
  112. package/dist/pricing-overlay.d.ts.map +1 -0
  113. package/dist/pricing-overlay.js +241 -0
  114. package/dist/pricing-overlay.js.map +1 -0
  115. package/dist/pricing.d.ts +115 -0
  116. package/dist/pricing.d.ts.map +1 -0
  117. package/dist/pricing.js +400 -0
  118. package/dist/pricing.js.map +1 -0
  119. package/dist/profile.d.ts +71 -0
  120. package/dist/profile.d.ts.map +1 -0
  121. package/dist/profile.js +55 -0
  122. package/dist/profile.js.map +1 -0
  123. package/dist/promptfoo.d.ts +58 -0
  124. package/dist/promptfoo.d.ts.map +1 -0
  125. package/dist/promptfoo.js +149 -0
  126. package/dist/promptfoo.js.map +1 -0
  127. package/dist/prune.d.ts +91 -0
  128. package/dist/prune.d.ts.map +1 -0
  129. package/dist/prune.js +110 -0
  130. package/dist/prune.js.map +1 -0
  131. package/dist/reorder.d.ts +82 -0
  132. package/dist/reorder.d.ts.map +1 -0
  133. package/dist/reorder.js +215 -0
  134. package/dist/reorder.js.map +1 -0
  135. package/dist/review.d.ts +54 -0
  136. package/dist/review.d.ts.map +1 -0
  137. package/dist/review.js +131 -0
  138. package/dist/review.js.map +1 -0
  139. package/dist/rules.d.ts +5 -0
  140. package/dist/rules.d.ts.map +1 -0
  141. package/dist/rules.js +279 -0
  142. package/dist/rules.js.map +1 -0
  143. package/dist/savings.d.ts +36 -0
  144. package/dist/savings.d.ts.map +1 -0
  145. package/dist/savings.js +83 -0
  146. package/dist/savings.js.map +1 -0
  147. package/dist/segment.d.ts +8 -0
  148. package/dist/segment.d.ts.map +1 -0
  149. package/dist/segment.js +74 -0
  150. package/dist/segment.js.map +1 -0
  151. package/dist/shared-prefix.d.ts +63 -0
  152. package/dist/shared-prefix.d.ts.map +1 -0
  153. package/dist/shared-prefix.js +151 -0
  154. package/dist/shared-prefix.js.map +1 -0
  155. package/dist/similarity.d.ts +13 -0
  156. package/dist/similarity.d.ts.map +1 -0
  157. package/dist/similarity.js +30 -0
  158. package/dist/similarity.js.map +1 -0
  159. package/dist/structure.d.ts +144 -0
  160. package/dist/structure.d.ts.map +1 -0
  161. package/dist/structure.js +455 -0
  162. package/dist/structure.js.map +1 -0
  163. package/dist/suggest.d.ts +100 -0
  164. package/dist/suggest.d.ts.map +1 -0
  165. package/dist/suggest.js +151 -0
  166. package/dist/suggest.js.map +1 -0
  167. package/dist/tokenizer.d.ts +57 -0
  168. package/dist/tokenizer.d.ts.map +1 -0
  169. package/dist/tokenizer.js +157 -0
  170. package/dist/tokenizer.js.map +1 -0
  171. package/dist/types.d.ts +296 -0
  172. package/dist/types.d.ts.map +1 -0
  173. package/dist/types.js +3 -0
  174. package/dist/types.js.map +1 -0
  175. package/dist/walk.d.ts +40 -0
  176. package/dist/walk.d.ts.map +1 -0
  177. package/dist/walk.js +95 -0
  178. package/dist/walk.js.map +1 -0
  179. package/package.json +56 -0
  180. package/src/advisories.ts +431 -0
  181. package/src/aws-sigv4.ts +174 -0
  182. package/src/baseline.ts +390 -0
  183. package/src/cache.ts +54 -0
  184. package/src/changes.ts +158 -0
  185. package/src/compare.ts +131 -0
  186. package/src/config-schema.ts +451 -0
  187. package/src/config.ts +161 -0
  188. package/src/detect.ts +312 -0
  189. package/src/evaluate.ts +188 -0
  190. package/src/extract.ts +336 -0
  191. package/src/gcp-auth.ts +166 -0
  192. package/src/glob.ts +160 -0
  193. package/src/host.ts +90 -0
  194. package/src/i18n/en.ts +236 -0
  195. package/src/i18n/es.ts +236 -0
  196. package/src/i18n/index.ts +68 -0
  197. package/src/i18n/types.ts +230 -0
  198. package/src/index.ts +228 -0
  199. package/src/llm.ts +708 -0
  200. package/src/nearest.ts +61 -0
  201. package/src/net.ts +233 -0
  202. package/src/node.ts +63 -0
  203. package/src/openrouter.ts +125 -0
  204. package/src/optimize.ts +228 -0
  205. package/src/otlp.ts +179 -0
  206. package/src/phrases.ts +1047 -0
  207. package/src/pricing-overlay.ts +319 -0
  208. package/src/pricing.ts +468 -0
  209. package/src/profile.ts +124 -0
  210. package/src/promptfoo.ts +213 -0
  211. package/src/prune.ts +211 -0
  212. package/src/reorder.ts +307 -0
  213. package/src/review.ts +180 -0
  214. package/src/rules.ts +324 -0
  215. package/src/savings.ts +121 -0
  216. package/src/segment.ts +106 -0
  217. package/src/shared-prefix.ts +198 -0
  218. package/src/similarity.ts +28 -0
  219. package/src/structure.ts +652 -0
  220. package/src/suggest.ts +254 -0
  221. package/src/tokenizer.ts +190 -0
  222. package/src/types.ts +323 -0
  223. package/src/walk.ts +117 -0
@@ -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"}
@@ -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"}