lastlight-shared 0.3.7 → 0.5.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/dist/config-types.d.ts +286 -7
- package/dist/config-types.js +46 -0
- package/dist/config-types.js.map +1 -1
- package/dist/database-url.d.ts +55 -0
- package/dist/database-url.js +118 -0
- package/dist/database-url.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/repo-config-schema.d.ts +28 -0
- package/dist/repo-config-schema.js +125 -3
- package/dist/repo-config-schema.js.map +1 -1
- package/dist/sandbox-services.d.ts +121 -0
- package/dist/sandbox-services.js +283 -0
- package/dist/sandbox-services.js.map +1 -0
- package/dist/workflow-loader.js.map +1 -1
- package/package.json +13 -5
package/dist/config-types.d.ts
CHANGED
|
@@ -132,13 +132,290 @@ export declare function isReviewTrigger(value: unknown): value is ReviewTrigger;
|
|
|
132
132
|
/** Position of a trigger mode on the automation scale. */
|
|
133
133
|
export declare function reviewTriggerRank(trigger: ReviewTrigger): number;
|
|
134
134
|
/**
|
|
135
|
-
* The `review:` block
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
135
|
+
* The `review.analysis:` sub-block — the evidence pipeline
|
|
136
|
+
* (`docs/plans/deterministic-pr-levers.md`).
|
|
137
|
+
*
|
|
138
|
+
* **OFF by default, and that is a locked decision** (README locked decision 8):
|
|
139
|
+
* `enabled: false` must reproduce today's two-phase review byte-for-byte, so
|
|
140
|
+
* every projection this block governs is *absent* from the template context
|
|
141
|
+
* rather than present-and-empty. Each stage lands dark and is switched on per
|
|
142
|
+
* deployment once it has been measured.
|
|
143
|
+
*
|
|
144
|
+
* **Operator-only, unlike every other `review:` leaf.** It buys analysis on the
|
|
145
|
+
* operator's budget, which is the same argument that made `review.trigger`
|
|
146
|
+
* clamped rather than free (#256) — except there is no "more conservative"
|
|
147
|
+
* direction here to clamp towards, so a repo asking for it answers
|
|
148
|
+
* `key-not-allowed` exactly as `fix.escalateModelAfterAttempt` does.
|
|
149
|
+
*/
|
|
150
|
+
export interface ReviewAnalysisConfig {
|
|
151
|
+
/** `false` ⇒ today's two-phase review, byte-for-byte. */
|
|
152
|
+
enabled: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* How many `spec` obligations one PR may carry.
|
|
155
|
+
*
|
|
156
|
+
* A **safety bound**, not a budget — it should never bind on a real PR. The
|
|
157
|
+
* extractor still ranks, truncates, and records in the rendered block how
|
|
158
|
+
* many it dropped (a silently truncated list is the failure locked decision 6
|
|
159
|
+
* exists to prevent); this number exists only to stop a pathological PR
|
|
160
|
+
* blowing the prompt.
|
|
161
|
+
*
|
|
162
|
+
* It shipped at 6, which was inert while the spec axis produced nothing. The
|
|
163
|
+
* moment the axis started working it bound on FIVE of the six linked cases in
|
|
164
|
+
* the gate set, discarding acceptance criteria a human wrote on the issue —
|
|
165
|
+
* the most direct statement of intent this pipeline ever gets. Capping
|
|
166
|
+
* GENERATION also inverts locked decision 2: we over-generate deliberately and
|
|
167
|
+
* let the probe oracle and WP6b's attention boundary narrow. Truncating
|
|
168
|
+
* obligations truncates DISCOVERY, which is the measured ceiling.
|
|
169
|
+
*/
|
|
170
|
+
maxSpecObligations: number;
|
|
171
|
+
/**
|
|
172
|
+
* A TOTAL BACKSTOP over the **facts-derived** obligations one PR may carry,
|
|
173
|
+
* across all five families `lastlight-facts seed` produces (`contract`,
|
|
174
|
+
* `enforcement`, `security`, `state`, `tests`). The `spec` family has its own
|
|
175
|
+
* bound above, because it is built harness-side from the issue text and
|
|
176
|
+
* shares no ranking axis with these.
|
|
177
|
+
*
|
|
178
|
+
* **Truncation is per FAMILY, not here.** The seeder caps each family at its
|
|
179
|
+
* own ceiling — `contract` 12, `enforcement` 12, `state` 8, `security` 8,
|
|
180
|
+
* `tests` 8 (`FAMILY_CAPS` in `packages/code-facts/src/seed.ts`) — because
|
|
181
|
+
* each family's obligations feed exactly ONE survey branch, so the cost is
|
|
182
|
+
* per branch rather than per document, and cross-family ranking prices
|
|
183
|
+
* incommensurable mechanism classes against each other. Measured: `contract`
|
|
184
|
+
* minted 89 across the eight gate cases while `security` minted 3, and the
|
|
185
|
+
* pooled budget went to `contract`.
|
|
186
|
+
*
|
|
187
|
+
* This number is applied AFTER those ceilings and defaults to their sum, so
|
|
188
|
+
* it cannot bind on a shipped configuration — it is there so that raising one
|
|
189
|
+
* ceiling is a bounded act. What it drops is counted in `obligations.json`
|
|
190
|
+
* with the reason (naming the ceiling or the backstop), never silently.
|
|
191
|
+
*/
|
|
192
|
+
maxObligations: number;
|
|
193
|
+
/**
|
|
194
|
+
* Which obligation BLOCK the six survey families are handed — the CONTROL for
|
|
195
|
+
* 2026-08-23, and the only key in this block that exists to make a result
|
|
196
|
+
* readable rather than to buy compute.
|
|
197
|
+
*
|
|
198
|
+
* `full` (the default) is that day's block: a mandatory discharge contract
|
|
199
|
+
* with a `discharge` field to record a code in, an un-truncated id checklist,
|
|
200
|
+
* and one worked exemplar. It moved discharge compliance 0/33 → 33/33 on
|
|
201
|
+
* `prreview__skillspro-1587-r2` — and moved the union of matched gold
|
|
202
|
+
* **4-of-5 → 0-of-5**, over three repeats, with half to two thirds of every
|
|
203
|
+
* hypothesis becoming a clean quote (`QUOTE`, `failureScenario: null`).
|
|
204
|
+
*
|
|
205
|
+
* Two variables changed in the same commit, so the run cannot say which:
|
|
206
|
+
* whether the obligations ask the WRONG QUESTION and making a wrong question
|
|
207
|
+
* mandatory turns hunting into checklist-clearing, or whether RELIABLE SEEDING
|
|
208
|
+
* itself suppresses discovery (the same commit stopped ~24% of survey branches
|
|
209
|
+
* losing their seed entirely). `minimal` renders the pre-2026-08-23 block —
|
|
210
|
+
* same obligations, delivered just as reliably, asking the old question — so
|
|
211
|
+
* one arm separates them.
|
|
212
|
+
*
|
|
213
|
+
* It reaches the five facts-derived families as `lastlight-facts seed
|
|
214
|
+
* --contract`, is stamped into `obligations.json`, and the `spec` family reads
|
|
215
|
+
* it directly (`renderSpecObligations`) because it is rendered harness-side.
|
|
216
|
+
* **`lastlight-facts discharge` degrades to its `test -s` floor under
|
|
217
|
+
* `minimal`**: measured compliance under that block was 0/31, 0/34 and 0/40,
|
|
218
|
+
* so a gate demanding a code the block never asked for would fail every family
|
|
219
|
+
* of every run.
|
|
220
|
+
*/
|
|
221
|
+
obligationContract: "full" | "minimal";
|
|
222
|
+
/**
|
|
223
|
+
* Which D2 minting arms `lastlight-facts seed` runs, as a comma-list over
|
|
224
|
+
* `all-in-diff` (contract obligations for symbols whose every reference is
|
|
225
|
+
* inside the diff) and `registrations` (security obligations for route/hook
|
|
226
|
+
* registration order).
|
|
227
|
+
*
|
|
228
|
+
* BOTH ON by default — the measured shipped shape (8-case confirm: internal
|
|
229
|
+
* paired +10/−1, p=0.006, the only lever measured to GROW the recall union
|
|
230
|
+
* rather than rotate it; external validation +7/−0, p=0.008). `""` is
|
|
231
|
+
* NEITHER — the pre-D2 baseline set, byte-identical to a run before the
|
|
232
|
+
* toggle existed. Reaches the seeder as `--mint <spec>`
|
|
233
|
+
* on the seed phase's command line, appended ONLY when non-empty, and the
|
|
234
|
+
* seeder stamps what it was asked into `obligations.json` (`minting`) so an
|
|
235
|
+
* artifact answers "which arm produced this". Kept a plain string rather
|
|
236
|
+
* than a validated union because the CLI is the loud gate: any unknown token
|
|
237
|
+
* exits 2 before the document is read — a typo'd arm can never silently run
|
|
238
|
+
* baseline and report a number for an experiment that never happened.
|
|
239
|
+
*/
|
|
240
|
+
mint: string;
|
|
241
|
+
/**
|
|
242
|
+
* How many of the six survey families actually run.
|
|
243
|
+
*
|
|
244
|
+
* **Six, and the default is not negotiable down without saying which.** The
|
|
245
|
+
* previous design defaulted this to 3 against six families and never recorded
|
|
246
|
+
* which three ran — so half the families silently never executed, and
|
|
247
|
+
* `enforcement`, the one that produced the only gold match, could have been
|
|
248
|
+
* among them (§D4). A value below 6 takes the families in the seeder's rank
|
|
249
|
+
* order and the run says so in its artifact.
|
|
250
|
+
*/
|
|
251
|
+
surveyPasses: number;
|
|
252
|
+
/**
|
|
253
|
+
* How many survey families run CONCURRENTLY (WP11c).
|
|
254
|
+
*
|
|
255
|
+
* A CEILING, not a guarantee: the run clamps it to what the active sandbox
|
|
256
|
+
* backend can actually hold. `none` and `docker` take the declared value;
|
|
257
|
+
* `gondolin` boots a QEMU micro-VM per agent session inside the harness
|
|
258
|
+
* process and pins to 1, as do `smol` and `kubernetes` until measured. So on
|
|
259
|
+
* a stock deployment (gondolin) this key changes nothing at all today.
|
|
260
|
+
*
|
|
261
|
+
* Six by default because six is what the fan-out exists for. The six families
|
|
262
|
+
* write six disjoint append-only files and never read each other's, so there
|
|
263
|
+
* was never an ordering constraint between them — only a scheduler that ran
|
|
264
|
+
* one DAG node at a time. Chained, they were 851s of a 29-minute review (49%
|
|
265
|
+
* of the wall clock); concurrent, they are the slowest single family.
|
|
266
|
+
*
|
|
267
|
+
* Lower it to bound provider rate-limit pressure or memory, not to bound
|
|
268
|
+
* spend: the six passes cost the same in tokens either way.
|
|
269
|
+
*/
|
|
270
|
+
surveyConcurrency: number;
|
|
271
|
+
/**
|
|
272
|
+
* WP4 — the `prepare` + `falsify` pair: install dependencies so a probe can be
|
|
273
|
+
* **run**, then write probes and run them.
|
|
274
|
+
*
|
|
275
|
+
* A second switch under an already-gated block, deliberately. `prepare` is the
|
|
276
|
+
* phase that decides whether the review workspace has a `node_modules`, and
|
|
277
|
+
* that one fact changes three things at once: it is what makes a
|
|
278
|
+
* package-extending `tsconfig` resolve (so `contract` can seed at all on a
|
|
279
|
+
* normal monorepo), it is the only route to a coverage artifact (so `tests`
|
|
280
|
+
* can), and it re-arms the memory profile of a *different* phase. Bundling it
|
|
281
|
+
* into `enabled` would have made "run the surveys" and "install the PR
|
|
282
|
+
* author's dependencies" the same decision.
|
|
283
|
+
*
|
|
284
|
+
* `false` reproduces WP3 exactly. Default posture is **off in production, on
|
|
285
|
+
* in the eval overlay** — the ablation rung is what decides whether it ships
|
|
286
|
+
* on, and that is a number, not a judgement call.
|
|
287
|
+
*/
|
|
288
|
+
probes: boolean;
|
|
289
|
+
/**
|
|
290
|
+
* Let `prepare`'s install run the tree's own lifecycle scripts.
|
|
291
|
+
*
|
|
292
|
+
* **Off, and it is a security default rather than a performance one.** The
|
|
293
|
+
* install runs against a PULL REQUEST HEAD, so a `postinstall` there is code
|
|
294
|
+
* the PR author wrote executing on the operator's infrastructure — and
|
|
295
|
+
* `pr-review`'s workspace has never installed anything, which makes `prepare`
|
|
296
|
+
* the first thing in the workflow that could. What `prepare` is FOR (making an
|
|
297
|
+
* `extends` resolve, putting library source on disk to be read) needs the
|
|
298
|
+
* files, not their scripts.
|
|
299
|
+
*
|
|
300
|
+
* Turning it on is legitimate for a repo whose install genuinely does not work
|
|
301
|
+
* without them; it is not the default for the same reason `probes` is not.
|
|
302
|
+
*/
|
|
303
|
+
probeLifecycleScripts: boolean;
|
|
304
|
+
/**
|
|
305
|
+
* Run the repo's own `tsc --noEmit` in `prepare` and record the diagnostics.
|
|
306
|
+
*
|
|
307
|
+
* Cheap, independent of the other two, and **not** a CI re-run: CI reports a
|
|
308
|
+
* pass/fail summary over a matrix, this reports a per-file, per-line
|
|
309
|
+
* diagnostic that can be attached to a specific hypothesis (locked decision
|
|
310
|
+
* 11 — we never re-derive what `checksState` already said).
|
|
311
|
+
*/
|
|
312
|
+
probeTypecheck: boolean;
|
|
313
|
+
/**
|
|
314
|
+
* Run a coverage command in `prepare` so the `tests` obligation family has an
|
|
315
|
+
* input for the first time.
|
|
316
|
+
*
|
|
317
|
+
* **The one step in this pipeline that runs a test suite**, which is the
|
|
318
|
+
* wall-clock item §D13 deleted along with `mutants` and `suite`. It is a
|
|
319
|
+
* separate switch because it is a separate price: everything else in `prepare`
|
|
320
|
+
* is seconds and this is minutes. It never guesses a command — only one the
|
|
321
|
+
* repo itself named (a `coverage` / `test:coverage` script) — because a
|
|
322
|
+
* guessed fifteen-minute run that produced nothing makes "no command" and "no
|
|
323
|
+
* artifact" the same row in the funnel.
|
|
324
|
+
*/
|
|
325
|
+
probeCoverage: boolean;
|
|
326
|
+
/** Ceiling on `prepare`'s dependency install, in seconds. */
|
|
327
|
+
prepareTimeoutSeconds: number;
|
|
328
|
+
/** Ceiling on `prepare`'s coverage run, in seconds. Minutes, not seconds. */
|
|
329
|
+
coverageTimeoutSeconds: number;
|
|
330
|
+
/**
|
|
331
|
+
* How many rounds `falsify` gets to write and run probes.
|
|
332
|
+
*
|
|
333
|
+
* Two. v3's lesson 3 is the sizing argument: the loop's exit condition is a
|
|
334
|
+
* five-line existence gate, not a validator — v2's full quote validator was
|
|
335
|
+
* overkill and cost 2.4× for a worse result.
|
|
336
|
+
*/
|
|
337
|
+
probeRounds: number;
|
|
338
|
+
/**
|
|
339
|
+
* The inline-comment attention budget (WP6b).
|
|
340
|
+
*
|
|
341
|
+
* Preserving internal recall and spending a human's attention are two
|
|
342
|
+
* different budgets, and conflating them is how a recall-first reviewer
|
|
343
|
+
* becomes unreadable. Everything past this rank goes to the review BODY —
|
|
344
|
+
* still posted, still visible, just not an inline comment. Nothing is dropped.
|
|
345
|
+
*
|
|
346
|
+
* Ten, on the evidence in *"Does AI Code Review Lead to Code Changes?"*
|
|
347
|
+
* (22k+ real review comments): concise, hunk-level, actionable findings are
|
|
348
|
+
* substantially likelier to lead to a change, and the wall the paper warns
|
|
349
|
+
* about is TWENTY — twenty inline comments is not twice the signal of ten,
|
|
350
|
+
* it is a muted bot. Ten is a ceiling, not a budget that bites: measured
|
|
351
|
+
* inline volume is 1–5 per PR, so this has never bound, and anything past it
|
|
352
|
+
* goes to the body rather than away.
|
|
353
|
+
*/
|
|
354
|
+
maxInlineComments: number;
|
|
355
|
+
/**
|
|
356
|
+
* Per-obligation-family confidence bar for an INLINE comment. Below the bar a
|
|
357
|
+
* finding goes to the body; it is never deleted.
|
|
358
|
+
*
|
|
359
|
+
* **Per-family, not global, and that is a measured choice.** AutoCommenter
|
|
360
|
+
* (Google Critique) found a global threshold catastrophic — at `t = 0.98`,
|
|
361
|
+
* ~80% of below-threshold predictions were still correct — while per-URL
|
|
362
|
+
* thresholds raised recall without hurting precision.
|
|
363
|
+
*
|
|
364
|
+
* **These numbers are initial guesses to be tuned on the train split, not
|
|
365
|
+
* measurements.** Record each retune in the eval journal.
|
|
366
|
+
*/
|
|
367
|
+
thresholds: Record<string, number>;
|
|
368
|
+
/**
|
|
369
|
+
* Below this confidence a finding is recorded but not posted at all.
|
|
370
|
+
*
|
|
371
|
+
* The one tier that costs recall, so it is deliberately low and deliberately
|
|
372
|
+
* auditable. A finding carrying NO confidence is never affected — see
|
|
373
|
+
* `tierFindings`; treating an absent field as zero would silently delete every
|
|
374
|
+
* finding from any prompt that has not been taught to self-score.
|
|
375
|
+
*/
|
|
376
|
+
internalFloor: number;
|
|
377
|
+
/**
|
|
378
|
+
* Cap on findings rendered into the review BODY (the "Additional findings"
|
|
379
|
+
* section) — the body-side sibling of `maxInlineComments`, and the one
|
|
380
|
+
* budget that DOES filter: everything past it is recorded `internal` with
|
|
381
|
+
* the machine reason `body-budget` in `disposition.json`, never posted.
|
|
382
|
+
* Nothing is deleted — the demotion stays auditable like every other
|
|
383
|
+
* `internal` entry.
|
|
384
|
+
*
|
|
385
|
+
* - `null` — unlimited: the legacy funnel, where everything demoted from
|
|
386
|
+
* inline lands in the body.
|
|
387
|
+
* - `0` — no overflow at all: nothing tiers to body; anything that would
|
|
388
|
+
* have gone there is recorded `internal` instead.
|
|
389
|
+
* - `N > 0` — at most N body findings, ranked by severity × confidence
|
|
390
|
+
* exactly as the inline overflow ranks (an absent confidence ranks as
|
|
391
|
+
* 1.0, so an unscored document degenerates to severity order).
|
|
392
|
+
*
|
|
393
|
+
* **`5` is the shipped default, and the number it replaced is the reason.**
|
|
394
|
+
* `0` was measured rather than assumed: under the production
|
|
395
|
+
* Sonnet-adjudicator shape no-overflow keeps 29/36 matched gold and lifts
|
|
396
|
+
* precision 0.263 → 0.492 / F1 0.362 → 0.479 on the Martian external set.
|
|
397
|
+
* But the same $0 sweep showed that result is
|
|
398
|
+
* **adjudicator-shape-conditional** — under Haiku-everywhere the body tier
|
|
399
|
+
* carries most of the matched gold and cap 0 costs posted recall 0.42 →
|
|
400
|
+
* 0.12, while MID caps keep nearly all of it (skillspro cap 4: 0.300, cap 8:
|
|
401
|
+
* 0.380 against 0.420 unlimited; Martian cap 4: 0.548, cap 8: 0.581 against
|
|
402
|
+
* 0.581 unlimited, at slightly better precision). `5` is the
|
|
403
|
+
* recall-preserving compromise pending a real boundary tune, not a measured
|
|
404
|
+
* optimum — which is also why eval overlays keep pinning this key
|
|
405
|
+
* explicitly instead of inheriting whatever it currently is.
|
|
406
|
+
*/
|
|
407
|
+
maxBodyComments: number | null;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* The `review:` block. Every key except `analysis` is repo-settable, and every
|
|
411
|
+
* one of those is CLAMPED towards less automation: `postsCheck` and `skipDraft`
|
|
412
|
+
* are add-only `true` (a repo may ask for the check and may skip drafts; it may
|
|
413
|
+
* not suppress an operator's check or force reviews onto drafts), `trigger`
|
|
414
|
+
* takes the lower {@link reviewTriggerRank} of repo and operator,
|
|
415
|
+
* `generatedPaths` is superset-only (a longer list suppresses MORE
|
|
416
|
+
* re-reviews), and `requestLabel` is free — naming a label only ever adds an
|
|
417
|
+
* explicit, human-initiated route. {@link ReviewAnalysisConfig} is
|
|
418
|
+
* operator-only; see its own doc.
|
|
142
419
|
*/
|
|
143
420
|
export interface ReviewConfig {
|
|
144
421
|
/** Post the `last-light/review` Check Run. */
|
|
@@ -168,6 +445,8 @@ export interface ReviewConfig {
|
|
|
168
445
|
* that also touched a hand-written file — see `resolveReviewTrigger`.
|
|
169
446
|
*/
|
|
170
447
|
generatedPaths: string[];
|
|
448
|
+
/** The evidence pipeline. Off by default — see {@link ReviewAnalysisConfig}. */
|
|
449
|
+
analysis: ReviewAnalysisConfig;
|
|
171
450
|
}
|
|
172
451
|
/**
|
|
173
452
|
* Where a repo's outbound notifications go — today, the weekly Slack digest.
|
package/dist/config-types.js
CHANGED
|
@@ -141,6 +141,52 @@ export function defaultReviewConfig() {
|
|
|
141
141
|
"*.generated.*",
|
|
142
142
|
"**/__generated__/**",
|
|
143
143
|
],
|
|
144
|
+
analysis: {
|
|
145
|
+
enabled: false,
|
|
146
|
+
// A safety bound, not a budget — see config/default.yaml for why this is
|
|
147
|
+
// 40 rather than the 6 it shipped with. It must not bind on a real PR:
|
|
148
|
+
// capping generation truncates discovery, which is the measured ceiling.
|
|
149
|
+
maxSpecObligations: 40,
|
|
150
|
+
// The TOTAL backstop, and it is the per-family ceilings' sum
|
|
151
|
+
// (12 + 12 + 8 + 8 + 8) so it cannot bind unless an operator raises one.
|
|
152
|
+
maxObligations: 48,
|
|
153
|
+
// `minimal` ships, measured in: under `full`, half to two-thirds of
|
|
154
|
+
// survey output arrives as clean-quote verification reports that reached
|
|
155
|
+
// real PRs as posted findings; under `minimal` the same recall union
|
|
156
|
+
// posts with 37–71% better SNR and half the run-to-run variance. `full`
|
|
157
|
+
// remains the opt-in telemetry arm (discharge codes + the
|
|
158
|
+
// clean-discharge demotion at the posting boundary).
|
|
159
|
+
obligationContract: "minimal",
|
|
160
|
+
// Both D2 rules — the measured shipped shape. See the field's doc.
|
|
161
|
+
mint: "all-in-diff,registrations",
|
|
162
|
+
surveyPasses: 6,
|
|
163
|
+
surveyConcurrency: 6,
|
|
164
|
+
probes: false,
|
|
165
|
+
probeLifecycleScripts: false,
|
|
166
|
+
probeTypecheck: false,
|
|
167
|
+
probeCoverage: false,
|
|
168
|
+
prepareTimeoutSeconds: 300,
|
|
169
|
+
coverageTimeoutSeconds: 900,
|
|
170
|
+
probeRounds: 2,
|
|
171
|
+
maxInlineComments: 10,
|
|
172
|
+
thresholds: {
|
|
173
|
+
contract: 0.35,
|
|
174
|
+
enforcement: 0.35,
|
|
175
|
+
security: 0.3,
|
|
176
|
+
state: 0.5,
|
|
177
|
+
tests: 0.6,
|
|
178
|
+
spec: 0.45,
|
|
179
|
+
},
|
|
180
|
+
internalFloor: 0.15,
|
|
181
|
+
// A bounded body overflow. Cap 0 measured better under the production
|
|
182
|
+
// Sonnet adjudicator (precision 0.263→0.492 / F1 0.362→0.479) but that
|
|
183
|
+
// win is adjudicator-shape-conditional — under Haiku-everywhere the body
|
|
184
|
+
// tier carries most of the matched gold (posted recall 0.42→0.12 at 0),
|
|
185
|
+
// and mid caps kept nearly all of it. 5 is the recall-preserving
|
|
186
|
+
// compromise; `null` restores the legacy unlimited funnel. See the
|
|
187
|
+
// field's doc.
|
|
188
|
+
maxBodyComments: 5,
|
|
189
|
+
},
|
|
144
190
|
};
|
|
145
191
|
}
|
|
146
192
|
//# sourceMappingURL=config-types.js.map
|
package/dist/config-types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-types.js","sourceRoot":"","sources":["../src/config-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,8EAA8E;AAC9E,qDAAqD;AACrD,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,cAAc;IACd,cAAc;IACd,OAAO;IACP,iBAAiB;IACjB,iBAAiB;CACT,CAAC;AAIX,6DAA6D;AAC7D,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,iBAAuC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC/F,CAAC;AAiDD,qFAAqF;AACrF,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,WAAW,EAAE,CAAC;QACd,eAAe,EAAE,CAAC;QAClB,kBAAkB,EAAE,GAAG;QACvB,yBAAyB,EAAE,CAAC;QAC5B,UAAU,EAAE,GAAG;QACf,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,CAAC,cAAc,EAAE,cAAc,CAAC;KACnD,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,4DAA4D;AAC5D,8EAA8E;AAE9E,wEAAwE;AACxE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAC;AAInF,oEAAoE;AACpE,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,wBAA8C,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACtG,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAAC,MAAwB;IAC3D,OAAO,wBAAwB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAClD,CAAC;AAuBD,2FAA2F;AAC3F,MAAM,UAAU,yBAAyB;IACvC,OAAO;QACL,kBAAkB,EAAE,QAAQ;QAC5B,oBAAoB,EAAE,IAAI;QAC1B,gBAAgB,EAAE,CAAC;QACnB,YAAY,EAAE,IAAI;KACnB,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,YAAY,CAAU,CAAC;AAIhF,2DAA2D;AAC3D,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,eAAqC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC7F,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,yBAAyB,GAAkC;IAC/D,YAAY,EAAE,CAAC;IACf,cAAc,EAAE,CAAC;IACjB,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,0DAA0D;AAC1D,MAAM,UAAU,iBAAiB,CAAC,OAAsB;IACtD,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;AAC5C,CAAC;
|
|
1
|
+
{"version":3,"file":"config-types.js","sourceRoot":"","sources":["../src/config-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State-database URL vocabulary — the small pure things both `lastlight-core`
|
|
3
|
+
* and the `lastlight` CLI have to agree about.
|
|
4
|
+
*
|
|
5
|
+
* It lives in `shared` for the same reason `repo-config-schema.ts` does: core
|
|
6
|
+
* needs it at runtime (`StateDb.open()` picks a driver, `/config` redacts a
|
|
7
|
+
* credential) and the CLI needs it offline (the setup wizard validates the URL
|
|
8
|
+
* the operator types and reports the driver it implies), and the CLI may never
|
|
9
|
+
* gain an edge to core. Nothing here imports a database driver — a `pg` import
|
|
10
|
+
* anywhere in this file would put node-postgres in the CLI's dependency graph.
|
|
11
|
+
*/
|
|
12
|
+
/** Which Postgres driver carries the `"postgres"` dialect. */
|
|
13
|
+
export type PgDriver = "pg" | "neon";
|
|
14
|
+
export declare const PG_DRIVERS: readonly PgDriver[];
|
|
15
|
+
/** Does this DB URL name Postgres (rather than a libsql `file:` / `:memory:`)? */
|
|
16
|
+
export declare function isPostgresUrl(input: string | undefined | null): boolean;
|
|
17
|
+
export declare function isPgDriver(value: unknown): value is PgDriver;
|
|
18
|
+
/**
|
|
19
|
+
* Which driver to run a `postgres://` URL over.
|
|
20
|
+
*
|
|
21
|
+
* `configured` (from `database.driver` / `DATABASE_DRIVER`) always wins — a Neon
|
|
22
|
+
* database fronted by a custom domain, or node-postgres pointed at Neon's TCP
|
|
23
|
+
* endpoint, both need to be expressible. Unset falls back to the host
|
|
24
|
+
* heuristic, which answers `"neon"` only for `*.neon.tech`.
|
|
25
|
+
*
|
|
26
|
+
* An unparseable URL resolves to `"pg"`: the driver builder is about to fail on
|
|
27
|
+
* it anyway, and node-postgres produces the better message.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolvePgDriver(url: string, configured?: PgDriver | null): PgDriver;
|
|
30
|
+
/**
|
|
31
|
+
* A DB URL safe to log or echo from the dashboard's `/config` view.
|
|
32
|
+
*
|
|
33
|
+
* Masks the userinfo and any `password=` query parameter, keeping the host,
|
|
34
|
+
* port and database name — those are not secrets, and they are the whole reason
|
|
35
|
+
* the provenance view exists. `file:` URLs and `:memory:` pass through
|
|
36
|
+
* untouched; there is nothing in them to leak.
|
|
37
|
+
*
|
|
38
|
+
* Anything that carries a `@` but does not parse is masked WHOLESALE rather
|
|
39
|
+
* than returned — a redactor that fails open is not a redactor.
|
|
40
|
+
*/
|
|
41
|
+
export declare function redactDbUrl(url: string): string;
|
|
42
|
+
/** The host of a DB URL, or undefined if it has none / does not parse. */
|
|
43
|
+
export declare function dbUrlHost(url: string): string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Host + port for a `postgres://` URL, for a reachability probe that must not
|
|
46
|
+
* import a driver (the setup wizard opens a bare TCP socket).
|
|
47
|
+
*
|
|
48
|
+
* Hand-parsed for the same reason {@link redactDbUrl} is: `new URL()` throws on
|
|
49
|
+
* an unencoded `@` in the password, and a wizard that rejects a working URL
|
|
50
|
+
* because of its punctuation is worse than no check at all.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parsePgEndpoint(url: string): {
|
|
53
|
+
host: string;
|
|
54
|
+
port: number;
|
|
55
|
+
} | undefined;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State-database URL vocabulary — the small pure things both `lastlight-core`
|
|
3
|
+
* and the `lastlight` CLI have to agree about.
|
|
4
|
+
*
|
|
5
|
+
* It lives in `shared` for the same reason `repo-config-schema.ts` does: core
|
|
6
|
+
* needs it at runtime (`StateDb.open()` picks a driver, `/config` redacts a
|
|
7
|
+
* credential) and the CLI needs it offline (the setup wizard validates the URL
|
|
8
|
+
* the operator types and reports the driver it implies), and the CLI may never
|
|
9
|
+
* gain an edge to core. Nothing here imports a database driver — a `pg` import
|
|
10
|
+
* anywhere in this file would put node-postgres in the CLI's dependency graph.
|
|
11
|
+
*/
|
|
12
|
+
export const PG_DRIVERS = ["pg", "neon"];
|
|
13
|
+
/** Case-insensitive, because URL schemes are and `POSTGRES://` is a real typo. */
|
|
14
|
+
const POSTGRES_URL_RE = /^postgres(ql)?:\/\//i;
|
|
15
|
+
/** Does this DB URL name Postgres (rather than a libsql `file:` / `:memory:`)? */
|
|
16
|
+
export function isPostgresUrl(input) {
|
|
17
|
+
return !!input && POSTGRES_URL_RE.test(input.trim());
|
|
18
|
+
}
|
|
19
|
+
export function isPgDriver(value) {
|
|
20
|
+
return value === "pg" || value === "neon";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Hosts served by Neon's WebSocket pooler. An explicit `database.driver` always
|
|
24
|
+
* beats this, so the list only has to cover the common case.
|
|
25
|
+
*/
|
|
26
|
+
const NEON_HOST_RE = /(^|\.)neon\.tech$/i;
|
|
27
|
+
/**
|
|
28
|
+
* Which driver to run a `postgres://` URL over.
|
|
29
|
+
*
|
|
30
|
+
* `configured` (from `database.driver` / `DATABASE_DRIVER`) always wins — a Neon
|
|
31
|
+
* database fronted by a custom domain, or node-postgres pointed at Neon's TCP
|
|
32
|
+
* endpoint, both need to be expressible. Unset falls back to the host
|
|
33
|
+
* heuristic, which answers `"neon"` only for `*.neon.tech`.
|
|
34
|
+
*
|
|
35
|
+
* An unparseable URL resolves to `"pg"`: the driver builder is about to fail on
|
|
36
|
+
* it anyway, and node-postgres produces the better message.
|
|
37
|
+
*/
|
|
38
|
+
export function resolvePgDriver(url, configured) {
|
|
39
|
+
if (isPgDriver(configured))
|
|
40
|
+
return configured;
|
|
41
|
+
return NEON_HOST_RE.test(dbUrlHost(url) ?? "") ? "neon" : "pg";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Splits `scheme://` + userinfo + the rest without `new URL()`, which rejects a
|
|
45
|
+
* password containing an unencoded `@` — exactly the case where getting
|
|
46
|
+
* redaction right matters most. The userinfo group is GREEDY so it runs to the
|
|
47
|
+
* LAST `@` in the authority: a lazy match would leave the tail of such a
|
|
48
|
+
* password in the "safe" remainder.
|
|
49
|
+
*/
|
|
50
|
+
const USERINFO_RE = /^([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^/?#]*@)(.*)$/s;
|
|
51
|
+
/**
|
|
52
|
+
* A DB URL safe to log or echo from the dashboard's `/config` view.
|
|
53
|
+
*
|
|
54
|
+
* Masks the userinfo and any `password=` query parameter, keeping the host,
|
|
55
|
+
* port and database name — those are not secrets, and they are the whole reason
|
|
56
|
+
* the provenance view exists. `file:` URLs and `:memory:` pass through
|
|
57
|
+
* untouched; there is nothing in them to leak.
|
|
58
|
+
*
|
|
59
|
+
* Anything that carries a `@` but does not parse is masked WHOLESALE rather
|
|
60
|
+
* than returned — a redactor that fails open is not a redactor.
|
|
61
|
+
*/
|
|
62
|
+
export function redactDbUrl(url) {
|
|
63
|
+
const trimmed = url.trim();
|
|
64
|
+
if (!trimmed || trimmed === ":memory:" || /^file:/i.test(trimmed))
|
|
65
|
+
return url;
|
|
66
|
+
const match = USERINFO_RE.exec(trimmed);
|
|
67
|
+
if (!match) {
|
|
68
|
+
// No userinfo at all (`postgres://host/db`) — nothing to mask beyond the
|
|
69
|
+
// query string. A string with an `@` that reached here is unparseable, so
|
|
70
|
+
// it gets the blunt treatment.
|
|
71
|
+
if (trimmed.includes("@"))
|
|
72
|
+
return "[redacted]";
|
|
73
|
+
return maskPasswordParam(trimmed);
|
|
74
|
+
}
|
|
75
|
+
const [, scheme, , rest] = match;
|
|
76
|
+
// Fail CLOSED. A surviving `@` means the authority did not end where the
|
|
77
|
+
// regex thought (an unencoded `/` in the password, say), so what looks like
|
|
78
|
+
// the safe remainder may still be part of the credential.
|
|
79
|
+
if (rest.includes("@"))
|
|
80
|
+
return "[redacted]";
|
|
81
|
+
return `${scheme}***:***@${maskPasswordParam(rest)}`;
|
|
82
|
+
}
|
|
83
|
+
function maskPasswordParam(input) {
|
|
84
|
+
return input.replace(/([?&](?:password|pgpassword)=)[^&]*/gi, "$1***");
|
|
85
|
+
}
|
|
86
|
+
/** The host of a DB URL, or undefined if it has none / does not parse. */
|
|
87
|
+
export function dbUrlHost(url) {
|
|
88
|
+
return parsePgEndpoint(url)?.host;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Host + port for a `postgres://` URL, for a reachability probe that must not
|
|
92
|
+
* import a driver (the setup wizard opens a bare TCP socket).
|
|
93
|
+
*
|
|
94
|
+
* Hand-parsed for the same reason {@link redactDbUrl} is: `new URL()` throws on
|
|
95
|
+
* an unencoded `@` in the password, and a wizard that rejects a working URL
|
|
96
|
+
* because of its punctuation is worse than no check at all.
|
|
97
|
+
*/
|
|
98
|
+
export function parsePgEndpoint(url) {
|
|
99
|
+
const trimmed = url.trim();
|
|
100
|
+
const afterScheme = trimmed.replace(/^[A-Za-z][A-Za-z0-9+.-]*:\/\//, "");
|
|
101
|
+
if (afterScheme === trimmed)
|
|
102
|
+
return undefined; // no scheme → not a URL
|
|
103
|
+
// Authority ends at the first `/`, `?` or `#`; userinfo ends at the LAST `@`
|
|
104
|
+
// inside it, so a password containing `@` does not steal the host.
|
|
105
|
+
const authority = afterScheme.split(/[/?#]/, 1)[0] ?? "";
|
|
106
|
+
const hostPort = authority.slice(authority.lastIndexOf("@") + 1);
|
|
107
|
+
if (!hostPort)
|
|
108
|
+
return undefined;
|
|
109
|
+
// IPv6 literal: `[::1]:5432`.
|
|
110
|
+
const v6 = /^\[([^\]]+)\](?::(\d+))?$/.exec(hostPort);
|
|
111
|
+
if (v6)
|
|
112
|
+
return { host: v6[1], port: v6[2] ? Number(v6[2]) : 5432 };
|
|
113
|
+
const [host, port] = hostPort.split(":");
|
|
114
|
+
if (!host)
|
|
115
|
+
return undefined;
|
|
116
|
+
return { host, port: port && /^\d+$/.test(port) ? Number(port) : 5432 };
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=database-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-url.js","sourceRoot":"","sources":["../src/database-url.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `lastlight-shared` — the light modules shared by the published `lastlight`
|
|
3
3
|
* CLI and `lastlight-core`. Node built-ins + `yaml` / `@clack/prompts` /
|
|
4
4
|
* `chalk` / `@earendil-works/pi-ai` (oauth subpath) / `lastlight-workflow-engine`
|
|
5
|
-
* (workflow schema) only — no heavy runtime deps (
|
|
5
|
+
* (workflow schema) only — no heavy runtime deps (drizzle/libsql, slack,
|
|
6
6
|
* octokit, hono, otel, agentic-pi) are ever reachable from here (fence F4).
|
|
7
7
|
*
|
|
8
8
|
* Invariant: no edge from `shared` back to `lastlight-core`.
|
|
@@ -19,4 +19,6 @@ export * from "./overlay-assets.js";
|
|
|
19
19
|
export * from "./core-pin.js";
|
|
20
20
|
export * from "./workflow-loader.js";
|
|
21
21
|
export * from "./config-types.js";
|
|
22
|
+
export * from "./database-url.js";
|
|
22
23
|
export * from "./repo-config-schema.js";
|
|
24
|
+
export * from "./sandbox-services.js";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `lastlight-shared` — the light modules shared by the published `lastlight`
|
|
3
3
|
* CLI and `lastlight-core`. Node built-ins + `yaml` / `@clack/prompts` /
|
|
4
4
|
* `chalk` / `@earendil-works/pi-ai` (oauth subpath) / `lastlight-workflow-engine`
|
|
5
|
-
* (workflow schema) only — no heavy runtime deps (
|
|
5
|
+
* (workflow schema) only — no heavy runtime deps (drizzle/libsql, slack,
|
|
6
6
|
* octokit, hono, otel, agentic-pi) are ever reachable from here (fence F4).
|
|
7
7
|
*
|
|
8
8
|
* Invariant: no edge from `shared` back to `lastlight-core`.
|
|
@@ -19,5 +19,7 @@ export * from "./overlay-assets.js";
|
|
|
19
19
|
export * from "./core-pin.js";
|
|
20
20
|
export * from "./workflow-loader.js";
|
|
21
21
|
export * from "./config-types.js";
|
|
22
|
+
export * from "./database-url.js";
|
|
22
23
|
export * from "./repo-config-schema.js";
|
|
24
|
+
export * from "./sandbox-services.js";
|
|
23
25
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
|
|
@@ -76,6 +76,20 @@ export interface RepoConfigPolicy {
|
|
|
76
76
|
* `false` keeps `lastlight.yml` only.
|
|
77
77
|
*/
|
|
78
78
|
allowAssets: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Container images a repo may declare as dependency services, registry-qualified
|
|
81
|
+
* (`docker.io/library/postgres:*`, `mcr.microsoft.com/mssql/server:*`).
|
|
82
|
+
*
|
|
83
|
+
* NOTE the polarity is the INVERSE of {@link allowedModels}: `null` (the default)
|
|
84
|
+
* denies EVERY image, where a null `allowedModels` is permissive. A model spec is a
|
|
85
|
+
* choice among providers the harness already knows how to talk to; a service image is
|
|
86
|
+
* arbitrary code pulled onto operator infrastructure, outside the sandbox's egress
|
|
87
|
+
* policy. Deny-all is the only safe default, so this stays inert until an operator
|
|
88
|
+
* opts in. See `docs/plans/sandbox-services/README.md` decision 7.
|
|
89
|
+
*/
|
|
90
|
+
allowedImages: string[] | null;
|
|
91
|
+
/** Ceiling on dependency services per phase. */
|
|
92
|
+
maxServices: number;
|
|
79
93
|
}
|
|
80
94
|
/**
|
|
81
95
|
* The allow-list a deployment gets when it says nothing. Kept as a constant so
|
|
@@ -114,6 +128,8 @@ export type RepoConfigWarningCode =
|
|
|
114
128
|
| "model-not-allowed"
|
|
115
129
|
/** A model spec whose `provider/` prefix isn't a provider we can wire. */
|
|
116
130
|
| "unknown-provider"
|
|
131
|
+
/** A service image outside `repoConfig.allowedImages`. */
|
|
132
|
+
| "service-not-allowed"
|
|
117
133
|
/** An `approval` entry that would clear a gate — the layer is add-only. */
|
|
118
134
|
| "approval-downgrade"
|
|
119
135
|
/**
|
|
@@ -227,6 +243,18 @@ export interface RepoMergedConfig {
|
|
|
227
243
|
* clamp the three blocks above share does not apply here.
|
|
228
244
|
*/
|
|
229
245
|
notifications: NotificationsConfig;
|
|
246
|
+
/**
|
|
247
|
+
* Dependency services this repo's phases run against, as RAW declarations keyed by
|
|
248
|
+
* service name (`docs/plans/sandbox-services`).
|
|
249
|
+
*
|
|
250
|
+
* Deliberately untyped plain data rather than parsed `ServiceSpec`s: this record is
|
|
251
|
+
* persisted as JSON on the run and rehydrated on resume, and a `PortMapping` is a
|
|
252
|
+
* class instance that would not survive the round trip. Consumers re-parse through
|
|
253
|
+
* `parseServiceSpec`. There is no operator-side value to merge onto — an operator
|
|
254
|
+
* bounds services via `repoConfig.allowedImages`, not by declaring any — so this is
|
|
255
|
+
* always either the repo's own block or empty.
|
|
256
|
+
*/
|
|
257
|
+
services: Record<string, unknown>;
|
|
230
258
|
}
|
|
231
259
|
/** Provenance mirror of {@link RepoMergedConfig} — each leaf tagged with its winning layer. */
|
|
232
260
|
export interface RepoConfigSources {
|