lastlight-shared 0.1.6 → 0.2.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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/repo-config-schema.d.ts +308 -0
- package/dist/repo-config-schema.js +592 -0
- package/dist/repo-config-schema.js.map +1 -0
- package/dist/workflow-loader.d.ts +104 -16
- package/dist/workflow-loader.js +192 -77
- package/dist/workflow-loader.js.map +1 -1
- package/package.json +7 -3
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PURE half of the per-repository config layer (issue #180) — the schema,
|
|
3
|
+
* the operator bounds, and the validators/merger that enforce them.
|
|
4
|
+
*
|
|
5
|
+
* A managed repo may commit a `.lastlight/` directory that overrides a BOUNDED
|
|
6
|
+
* subset of Last Light's config for runs against that repo. The directory
|
|
7
|
+
* mirrors a deployment overlay's on-disk shape exactly:
|
|
8
|
+
*
|
|
9
|
+
* .lastlight/
|
|
10
|
+
* lastlight.yml # the config override
|
|
11
|
+
* workflows/prompts/*.md # prompt overrides
|
|
12
|
+
* skills/<name>/SKILL.md # skill overrides
|
|
13
|
+
* agent-context/*.md # persona/rules additions
|
|
14
|
+
*
|
|
15
|
+
* ── Why this lives in `lastlight-shared` ──────────────────────────────────
|
|
16
|
+
* Two consumers need exactly the same answers about a `.lastlight/` tree:
|
|
17
|
+
* - `lastlight-core` at runtime, after fetching the layer from GitHub
|
|
18
|
+
* (`apps/server/src/config/repo-config.ts`, which owns the impure half —
|
|
19
|
+
* the fetch, the TTL cache, the on-disk unpack — and re-exports everything
|
|
20
|
+
* here so its import surface is unchanged); and
|
|
21
|
+
* - the `lastlight` CLI, offline, inside a user's own code repo
|
|
22
|
+
* (`lastlight repo config validate`).
|
|
23
|
+
* The CLI must never gain a dependency edge to core, so the bounds logic sits
|
|
24
|
+
* here — the one package both already depend on. Nothing in this file touches
|
|
25
|
+
* the filesystem, the network, or runtime config: it is a function of its
|
|
26
|
+
* arguments, which is also what makes it directly unit-testable.
|
|
27
|
+
*
|
|
28
|
+
* ── The trust rule ────────────────────────────────────────────────────────
|
|
29
|
+
* The layer is ALWAYS read from the repo's **default branch**. Never a PR head.
|
|
30
|
+
* Never the sandbox checkout. That rule is enforced by the fetcher in core;
|
|
31
|
+
* this module only describes what may appear in the layer once it arrives.
|
|
32
|
+
*
|
|
33
|
+
* ── The failure rule ──────────────────────────────────────────────────────
|
|
34
|
+
* Warn, drop the bad bits, run anyway. A repo's config file must never fail a
|
|
35
|
+
* run. Invalid YAML drops the whole file; an unknown or out-of-bounds key drops
|
|
36
|
+
* just that key. Every rejection becomes a structured {@link RepoConfigWarning}
|
|
37
|
+
* so the dashboard/CLI can report it back to the repo's owners.
|
|
38
|
+
*/
|
|
39
|
+
import { join, resolve, sep } from "node:path";
|
|
40
|
+
import { parse as parseYaml } from "yaml";
|
|
41
|
+
import { providerByPrefix, oauthProviderByModelPrefix } from "./providers.js";
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Bounds
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
/** Hard cap on the unpacked `.lastlight/` layer, in bytes. */
|
|
46
|
+
export const REPO_CONFIG_MAX_BYTES = 2 * 1024 * 1024;
|
|
47
|
+
/** Hard cap on the number of files in the unpacked layer. */
|
|
48
|
+
export const REPO_CONFIG_MAX_FILES = 200;
|
|
49
|
+
/** The config file inside `.lastlight/`. Exactly this name — no `.yaml` variant. */
|
|
50
|
+
export const REPO_CONFIG_FILE = "lastlight.yml";
|
|
51
|
+
/** Git filemode for a symlink blob. Rejected on sight — see {@link sanitizeRepoFiles}. */
|
|
52
|
+
const GIT_MODE_SYMLINK = "120000";
|
|
53
|
+
/**
|
|
54
|
+
* The allow-list a deployment gets when it says nothing. Kept as a constant so
|
|
55
|
+
* the normaliser, the docs, `config/default.yaml` and the CLI's offline
|
|
56
|
+
* validator can't drift apart.
|
|
57
|
+
*
|
|
58
|
+
* It MUST stay identical to `repoConfig.allowKeys` in
|
|
59
|
+
* `apps/server/config/default.yaml`: this list is what a deployment falls back
|
|
60
|
+
* to when config isn't in reach (`repoConfigPolicy()`'s no-config path, and the
|
|
61
|
+
* CLI's offline `lastlight repo config validate`), so a divergence tells repo
|
|
62
|
+
* owners their file is out of bounds when it isn't. Pinned by the
|
|
63
|
+
* `default allow-list` block in `apps/server/tests/config/repo-config-shared.test.ts`
|
|
64
|
+
* — the two drifted apart once already, silently.
|
|
65
|
+
*/
|
|
66
|
+
export const DEFAULT_REPO_CONFIG_ALLOW_KEYS = [
|
|
67
|
+
"models",
|
|
68
|
+
"variants",
|
|
69
|
+
"crons",
|
|
70
|
+
"disabled.workflows",
|
|
71
|
+
"disabled.crons",
|
|
72
|
+
"approval",
|
|
73
|
+
];
|
|
74
|
+
/**
|
|
75
|
+
* The bounds to assume when no deployment config is in reach — the shipped
|
|
76
|
+
* defaults. The offline CLI validator (`lastlight repo config validate`) uses
|
|
77
|
+
* this: it can't know the operator's narrowing, so it validates against the
|
|
78
|
+
* widest shipped policy and says so.
|
|
79
|
+
*/
|
|
80
|
+
export function defaultRepoConfigPolicy() {
|
|
81
|
+
return {
|
|
82
|
+
enabled: true,
|
|
83
|
+
allowKeys: [...DEFAULT_REPO_CONFIG_ALLOW_KEYS],
|
|
84
|
+
allowedModels: null,
|
|
85
|
+
allowAssets: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Classify a path relative to `.lastlight/`, or `null` when it is not part of
|
|
90
|
+
* the layer at all.
|
|
91
|
+
*
|
|
92
|
+
* `null` is a routine answer, not an error: `.lastlight/` is shared real
|
|
93
|
+
* estate. With `buildAssets.location: repo` the build workflow commits its
|
|
94
|
+
* handoff docs to `.lastlight/<issueKey>/*.md`, and repos are free to keep
|
|
95
|
+
* other things there. Those are simply outside the layer — the warning path is
|
|
96
|
+
* reserved for files that LOOK like layer assets but have the wrong shape
|
|
97
|
+
* (see {@link sanitizeRepoFiles}).
|
|
98
|
+
*/
|
|
99
|
+
export function repoLayerPathKind(path) {
|
|
100
|
+
if (path === REPO_CONFIG_FILE)
|
|
101
|
+
return "config";
|
|
102
|
+
if (/^workflows\/prompts\/(?:[^/]+\/)*[^/]+\.md$/.test(path))
|
|
103
|
+
return "prompt";
|
|
104
|
+
if (/^skills\/[^/]+\/(?:[^/]+\/)*[^/]+$/.test(path))
|
|
105
|
+
return "skill";
|
|
106
|
+
if (/^agent-context\/[^/]+\.md$/.test(path))
|
|
107
|
+
return "agent-context";
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/** True when a path is a workflow DEFINITION — the one thing a repo may never contribute. */
|
|
111
|
+
export function isRepoWorkflowPath(path) {
|
|
112
|
+
return /^workflows\/[^/]+\.ya?ml$/.test(path);
|
|
113
|
+
}
|
|
114
|
+
/** Top-level directories inside `.lastlight/` that the layer claims. */
|
|
115
|
+
const LAYER_DIRS = ["workflows/", "skills/", "agent-context/"];
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// File-level guards
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
/**
|
|
120
|
+
* Reject a relative path that can't be safely joined onto a root. Modelled on
|
|
121
|
+
* `assertSafeRelative` in `./workflow-loader.ts`, but returning a reason
|
|
122
|
+
* instead of throwing — a hostile repo must not be able to abort a run by
|
|
123
|
+
* committing a bad filename.
|
|
124
|
+
*/
|
|
125
|
+
function unsafeRelativeReason(path) {
|
|
126
|
+
if (!path)
|
|
127
|
+
return "path is empty";
|
|
128
|
+
if (path.startsWith("/") || /^[A-Za-z]:/.test(path))
|
|
129
|
+
return "path is absolute";
|
|
130
|
+
if (path.includes("\0"))
|
|
131
|
+
return "path contains a NUL byte";
|
|
132
|
+
if (path.includes("\\"))
|
|
133
|
+
return "path contains a backslash";
|
|
134
|
+
const segments = path.split("/");
|
|
135
|
+
if (segments.some((s) => s === "" || s === "." || s === ".."))
|
|
136
|
+
return "path contains a traversal segment";
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
/** True when `filePath` resolves inside `root`. Same check as the workflow loader's `isInside`. */
|
|
140
|
+
function isInside(filePath, root) {
|
|
141
|
+
const r = resolve(root);
|
|
142
|
+
const f = resolve(filePath);
|
|
143
|
+
return f === r || f.startsWith(r + sep);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Apply every file-level bound to a `.lastlight/` subtree.
|
|
147
|
+
*
|
|
148
|
+
* Pure — takes the blobs, returns the ones that may be written to disk plus a
|
|
149
|
+
* warning per rejection. The caps are enforced here as well as at fetch time
|
|
150
|
+
* (the client stops downloading at its own limits) because this function is the
|
|
151
|
+
* last gate before anything touches the filesystem, and defence in depth is
|
|
152
|
+
* cheap.
|
|
153
|
+
*
|
|
154
|
+
* Generic in the file type so a caller carrying a richer blob shape (core's
|
|
155
|
+
* `RepoConfigFile`) gets its own type back rather than a widened one.
|
|
156
|
+
*/
|
|
157
|
+
export function sanitizeRepoFiles(files, policy, repo) {
|
|
158
|
+
const warnings = [];
|
|
159
|
+
const accepted = [];
|
|
160
|
+
const warn = (code, path, message) => warnings.push({ code, repo, path, message });
|
|
161
|
+
let bytes = 0;
|
|
162
|
+
let unrecognised = 0;
|
|
163
|
+
let assetsDropped = 0;
|
|
164
|
+
for (const file of files) {
|
|
165
|
+
const path = file.path;
|
|
166
|
+
const unsafe = unsafeRelativeReason(path);
|
|
167
|
+
if (unsafe) {
|
|
168
|
+
warn("path-escape", path, `Ignored .lastlight/${path}: ${unsafe}.`);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
// Second, independent check: even a segment-clean path must land under the
|
|
172
|
+
// layer root once joined. Cheap, and catches anything the string checks miss.
|
|
173
|
+
if (!isInside(join("/layer-root", path), "/layer-root")) {
|
|
174
|
+
warn("path-escape", path, `Ignored .lastlight/${path}: it escapes the layer directory.`);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
// Symlinks are the classic unpack escape — a `skills/x/SKILL.md` symlink
|
|
178
|
+
// pointing at /etc/passwd or at the harness's own secrets would otherwise be
|
|
179
|
+
// read as layer content. Only regular files are ever materialized.
|
|
180
|
+
if (file.mode === GIT_MODE_SYMLINK) {
|
|
181
|
+
warn("symlink", path, `Ignored .lastlight/${path}: symlinks are not allowed in a repo config layer.`);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (file.mode !== "100644" && file.mode !== "100755") {
|
|
185
|
+
warn("symlink", path, `Ignored .lastlight/${path}: only regular files are allowed (mode ${file.mode}).`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
// Repos may contribute PROMPTS, never workflows: a workflow YAML defines
|
|
189
|
+
// phases, skills and permission profiles, which is the operator's call.
|
|
190
|
+
if (isRepoWorkflowPath(path)) {
|
|
191
|
+
warn("workflow-not-allowed", path, `Ignored .lastlight/${path}: a repo may override prompts (workflows/prompts/*.md), not workflow definitions.`);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const kind = repoLayerPathKind(path);
|
|
195
|
+
if (!kind) {
|
|
196
|
+
// Inside a layer directory but the wrong shape → the repo probably meant
|
|
197
|
+
// it as an asset. Counted and reported once, so a big stray directory
|
|
198
|
+
// can't flood the warning list.
|
|
199
|
+
if (LAYER_DIRS.some((d) => path.startsWith(d)))
|
|
200
|
+
unrecognised++;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (kind !== "config" && !policy.allowAssets) {
|
|
204
|
+
assetsDropped++;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (accepted.length >= REPO_CONFIG_MAX_FILES) {
|
|
208
|
+
warn("file-count-cap", path, `Ignored .lastlight/${path}: the repo config layer is capped at ${REPO_CONFIG_MAX_FILES} files.`);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (bytes + file.content.length > REPO_CONFIG_MAX_BYTES) {
|
|
212
|
+
warn("size-cap", path, `Ignored .lastlight/${path}: the repo config layer is capped at ${REPO_CONFIG_MAX_BYTES} bytes.`);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
bytes += file.content.length;
|
|
216
|
+
accepted.push(file);
|
|
217
|
+
}
|
|
218
|
+
if (unrecognised > 0) {
|
|
219
|
+
warn("unrecognised-asset", ".lastlight", `Ignored ${unrecognised} file(s) under .lastlight/: a repo layer may contain ${REPO_CONFIG_FILE}, ` +
|
|
220
|
+
`workflows/prompts/*.md, skills/<name>/SKILL.md and agent-context/*.md.`);
|
|
221
|
+
}
|
|
222
|
+
if (assetsDropped > 0) {
|
|
223
|
+
warn("assets-not-allowed", ".lastlight", `Ignored ${assetsDropped} asset file(s) under .lastlight/: this deployment sets repoConfig.allowAssets: false.`);
|
|
224
|
+
}
|
|
225
|
+
return { accepted, warnings };
|
|
226
|
+
}
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
// Config-level bounds
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
/**
|
|
231
|
+
* Parse a repo's `lastlight.yml`. Malformed YAML, or YAML that isn't a mapping,
|
|
232
|
+
* drops the WHOLE file — a half-understood config file is more dangerous than
|
|
233
|
+
* none, and the repo gets a warning either way.
|
|
234
|
+
*/
|
|
235
|
+
export function parseRepoConfigYaml(raw, repo) {
|
|
236
|
+
let parsed;
|
|
237
|
+
try {
|
|
238
|
+
parsed = parseYaml(raw);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
242
|
+
return {
|
|
243
|
+
warnings: [
|
|
244
|
+
{
|
|
245
|
+
code: "invalid-yaml",
|
|
246
|
+
repo,
|
|
247
|
+
path: REPO_CONFIG_FILE,
|
|
248
|
+
message: `Ignored .lastlight/${REPO_CONFIG_FILE}: it is not valid YAML (${message}).`,
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
// An empty file parses to null — legal, just carries nothing.
|
|
254
|
+
if (parsed === null || parsed === undefined)
|
|
255
|
+
return { warnings: [] };
|
|
256
|
+
if (!isPlainObject(parsed)) {
|
|
257
|
+
return {
|
|
258
|
+
warnings: [
|
|
259
|
+
{
|
|
260
|
+
code: "not-a-mapping",
|
|
261
|
+
repo,
|
|
262
|
+
path: REPO_CONFIG_FILE,
|
|
263
|
+
message: `Ignored .lastlight/${REPO_CONFIG_FILE}: the top level must be a mapping.`,
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return { config: parsed, warnings: [] };
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* True when `path` (a dotted config path) is admitted by the allow-list. An
|
|
272
|
+
* entry admits itself and everything beneath it — `models` admits
|
|
273
|
+
* `models.architect`; `disabled.workflows` does NOT admit `disabled.prompts`.
|
|
274
|
+
*/
|
|
275
|
+
function isAllowedKey(path, allowKeys) {
|
|
276
|
+
return allowKeys.some((allowed) => path === allowed || path.startsWith(`${allowed}.`));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* True when a dotted path is a PREFIX of some allow-list entry — i.e. the repo
|
|
280
|
+
* must be allowed to descend into it even though the container itself isn't
|
|
281
|
+
* directly settable (`disabled` for `disabled.workflows`).
|
|
282
|
+
*/
|
|
283
|
+
function isAllowedPrefix(path, allowKeys) {
|
|
284
|
+
return allowKeys.some((allowed) => allowed.startsWith(`${path}.`));
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Reduce a repo's raw `lastlight.yml` to the sub-tree it is actually allowed to
|
|
288
|
+
* contribute. Pure. Everything dropped produces a warning.
|
|
289
|
+
*
|
|
290
|
+
* `base` supplies the operator's current values, which the `approval` add-only
|
|
291
|
+
* rule needs: a repo may raise a gate, never lower one.
|
|
292
|
+
*/
|
|
293
|
+
export function sanitizeRepoConfigLayer(raw, policy, base, repo) {
|
|
294
|
+
const warnings = [];
|
|
295
|
+
const layer = {};
|
|
296
|
+
if (!raw)
|
|
297
|
+
return { layer, warnings };
|
|
298
|
+
const warn = (code, path, message) => warnings.push({ code, repo, path, message });
|
|
299
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
300
|
+
const allowed = isAllowedKey(key, policy.allowKeys);
|
|
301
|
+
const descendable = isAllowedPrefix(key, policy.allowKeys);
|
|
302
|
+
if (!allowed && !descendable) {
|
|
303
|
+
warn("key-not-allowed", key, `Ignored "${key}" in .lastlight/${REPO_CONFIG_FILE}: a repo may not set this key.`);
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
switch (key) {
|
|
307
|
+
case "models":
|
|
308
|
+
assignIfAny(layer, "models", sanitizeModels(value, policy, warn));
|
|
309
|
+
break;
|
|
310
|
+
case "variants":
|
|
311
|
+
assignIfAny(layer, "variants", sanitizeStringMap(value, "variants", policy, warn));
|
|
312
|
+
break;
|
|
313
|
+
case "crons":
|
|
314
|
+
// Accepted and deliberately NOT merged. Cron participation
|
|
315
|
+
// (`crons: {enable, disable}`) is read straight off the RAW layer by
|
|
316
|
+
// the scheduler at tick time — `repoCronPrefs` in
|
|
317
|
+
// `apps/server/src/cron/repo-crons.ts` — because it decides WHICH repos
|
|
318
|
+
// a cron fans out over, which happens before any run (and therefore
|
|
319
|
+
// before any merged per-run config) exists. So it is not part of
|
|
320
|
+
// {@link RepoMergedConfig} and has no merged-shape validator here; the
|
|
321
|
+
// `case` exists only to stop the `default:` branch reporting a
|
|
322
|
+
// "no repo-layer validator" warning for a block that is fully
|
|
323
|
+
// supported. Do NOT "fix" this by adding `crons` to the merged shape:
|
|
324
|
+
// that would give one block two owners with two bounds checks.
|
|
325
|
+
break;
|
|
326
|
+
case "disabled":
|
|
327
|
+
assignIfAny(layer, "disabled", sanitizeDisabled(value, policy, warn));
|
|
328
|
+
break;
|
|
329
|
+
case "approval":
|
|
330
|
+
assignIfAny(layer, "approval", sanitizeApproval(value, policy, base, warn));
|
|
331
|
+
break;
|
|
332
|
+
default:
|
|
333
|
+
// Allow-listed by the operator but not a key this module knows how to
|
|
334
|
+
// bound. Refusing is the safe direction: an unbounded pass-through
|
|
335
|
+
// would let an operator widen the layer past what's been reviewed.
|
|
336
|
+
warn("key-not-allowed", key, `Ignored "${key}" in .lastlight/${REPO_CONFIG_FILE}: it has no repo-layer validator.`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return { layer, warnings };
|
|
340
|
+
}
|
|
341
|
+
/** Drop empty sub-trees so the merge never records a `repo` provenance for nothing. */
|
|
342
|
+
function assignIfAny(target, key, value) {
|
|
343
|
+
if (value && Object.keys(value).length > 0)
|
|
344
|
+
target[key] = value;
|
|
345
|
+
}
|
|
346
|
+
function sanitizeModels(raw, policy, warn) {
|
|
347
|
+
if (!isPlainObject(raw)) {
|
|
348
|
+
warn("invalid-value", "models", `Ignored "models" in .lastlight/${REPO_CONFIG_FILE}: it must be a mapping.`);
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
const out = {};
|
|
352
|
+
for (const [task, spec] of Object.entries(raw)) {
|
|
353
|
+
const path = `models.${task}`;
|
|
354
|
+
// Re-checked per leaf, not just on the `models` container: an operator can
|
|
355
|
+
// narrow the allow-list to a single task (`models.triage`), and the leaf is
|
|
356
|
+
// the only place that distinction exists.
|
|
357
|
+
if (!isAllowedKey(path, policy.allowKeys)) {
|
|
358
|
+
warn("key-not-allowed", path, `Ignored "${path}": a repo may not set this key.`);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (typeof spec !== "string" || !spec.trim()) {
|
|
362
|
+
warn("invalid-value", path, `Ignored "${path}": a model must be a non-empty "provider/model" string.`);
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const model = spec.trim();
|
|
366
|
+
const prefix = model.split("/")[0] ?? "";
|
|
367
|
+
if (!prefix || !model.includes("/") || (!providerByPrefix(prefix) && !oauthProviderByModelPrefix(prefix))) {
|
|
368
|
+
warn("unknown-provider", path, `Ignored "${path}": "${model}" is not a "provider/model" spec Last Light can wire.`);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
// A non-null allowedModels is the operator saying "exactly these" — an
|
|
372
|
+
// exact-match list, never a prefix rule, so it can't be widened by a repo
|
|
373
|
+
// appending to a model id.
|
|
374
|
+
if (policy.allowedModels !== null && !policy.allowedModels.includes(model)) {
|
|
375
|
+
warn("model-not-allowed", path, `Ignored "${path}": "${model}" is not in this deployment's repoConfig.allowedModels.`);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
out[task] = model;
|
|
379
|
+
}
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
382
|
+
function sanitizeStringMap(raw, path, policy, warn) {
|
|
383
|
+
if (!isPlainObject(raw)) {
|
|
384
|
+
warn("invalid-value", path, `Ignored "${path}" in .lastlight/${REPO_CONFIG_FILE}: it must be a mapping.`);
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
const out = {};
|
|
388
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
389
|
+
const leaf = `${path}.${key}`;
|
|
390
|
+
if (!isAllowedKey(leaf, policy.allowKeys)) {
|
|
391
|
+
warn("key-not-allowed", leaf, `Ignored "${leaf}": a repo may not set this key.`);
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
395
|
+
warn("invalid-value", leaf, `Ignored "${leaf}": it must be a non-empty string.`);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
out[key] = value.trim();
|
|
399
|
+
}
|
|
400
|
+
return out;
|
|
401
|
+
}
|
|
402
|
+
function sanitizeDisabled(raw, policy, warn) {
|
|
403
|
+
if (!isPlainObject(raw)) {
|
|
404
|
+
warn("invalid-value", "disabled", `Ignored "disabled" in .lastlight/${REPO_CONFIG_FILE}: it must be a mapping.`);
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
const out = {};
|
|
408
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
409
|
+
const path = `disabled.${key}`;
|
|
410
|
+
if (!isAllowedKey(path, policy.allowKeys)) {
|
|
411
|
+
warn("key-not-allowed", path, `Ignored "${path}": a repo may not set this key.`);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (!Array.isArray(value) || value.some((v) => typeof v !== "string" || !v.trim())) {
|
|
415
|
+
warn("invalid-value", path, `Ignored "${path}": it must be an array of non-empty strings.`);
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
// Same unsafe-name check `validateAssets` applies to the operator's own
|
|
419
|
+
// disabled lists — a name is a logical workflow/cron name, never a path.
|
|
420
|
+
const names = value.map((v) => v.trim());
|
|
421
|
+
const bad = names.find((n) => n.includes("/") || n.includes(".."));
|
|
422
|
+
if (bad) {
|
|
423
|
+
warn("invalid-value", path, `Ignored "${path}": "${bad}" is not a valid workflow/cron name.`);
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
out[key] = names;
|
|
427
|
+
}
|
|
428
|
+
return out;
|
|
429
|
+
}
|
|
430
|
+
function sanitizeApproval(raw, policy, base, warn) {
|
|
431
|
+
if (!isPlainObject(raw)) {
|
|
432
|
+
warn("invalid-value", "approval", `Ignored "approval" in .lastlight/${REPO_CONFIG_FILE}: it must be a mapping.`);
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
const baseApproval = isPlainObject(base.value.approval) ? base.value.approval : {};
|
|
436
|
+
const out = {};
|
|
437
|
+
for (const [gate, value] of Object.entries(raw)) {
|
|
438
|
+
const path = `approval.${gate}`;
|
|
439
|
+
if (!isAllowedKey(path, policy.allowKeys)) {
|
|
440
|
+
warn("key-not-allowed", path, `Ignored "${path}": a repo may not set this key.`);
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (typeof value !== "boolean") {
|
|
444
|
+
warn("invalid-value", path, `Ignored "${path}": an approval gate must be true or false.`);
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
// ADD-ONLY. A repo may demand more human oversight of runs against itself;
|
|
448
|
+
// it may never remove oversight the operator asked for. Clearing a gate the
|
|
449
|
+
// operator never set is a no-op, so every `false` is simply dropped — with a
|
|
450
|
+
// warning, so the repo learns why nothing happened.
|
|
451
|
+
if (value === false) {
|
|
452
|
+
const code = baseApproval[gate] === true ? "approval-downgrade" : "invalid-value";
|
|
453
|
+
warn(code, path, `Ignored "${path}: false": the repo approval layer is add-only — a repo can raise an approval gate, never clear one.`);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
out[gate] = true;
|
|
457
|
+
}
|
|
458
|
+
return out;
|
|
459
|
+
}
|
|
460
|
+
// ---------------------------------------------------------------------------
|
|
461
|
+
// Resolution
|
|
462
|
+
// ---------------------------------------------------------------------------
|
|
463
|
+
/**
|
|
464
|
+
* Apply a repo's layer on top of the boot config and report what happened.
|
|
465
|
+
*
|
|
466
|
+
* PURE — no fs, no network, no runtime-config reads. Plain objects deep-merge
|
|
467
|
+
* key-by-key while arrays and scalars replace wholesale — byte-for-byte the
|
|
468
|
+
* semantics of the boot layers (see {@link mergeLayer}), so the repo layer can
|
|
469
|
+
* never acquire semantics the operator's layers don't have.
|
|
470
|
+
*
|
|
471
|
+
* Note on `disabled.*`: those are arrays, so a repo's list REPLACES the
|
|
472
|
+
* operator's rather than adding to it (locked precedence). Operators who don't
|
|
473
|
+
* want that remove `disabled.workflows` / `disabled.crons` from
|
|
474
|
+
* `repoConfig.allowKeys`.
|
|
475
|
+
*
|
|
476
|
+
* Passing `undefined` for `repoLayer` (no `.lastlight/`, fetch failed, feature
|
|
477
|
+
* disabled) returns the base unchanged with no warnings — the inert path.
|
|
478
|
+
*/
|
|
479
|
+
export function resolveRepoConfig(base, policy, repoLayer) {
|
|
480
|
+
const warnings = repoLayer ? [...repoLayer.warnings] : [];
|
|
481
|
+
let layer = {};
|
|
482
|
+
if (repoLayer && policy.enabled) {
|
|
483
|
+
const sanitized = sanitizeRepoConfigLayer(repoLayer.config, policy, base, repoLayer.repo);
|
|
484
|
+
layer = sanitized.layer;
|
|
485
|
+
warnings.push(...sanitized.warnings);
|
|
486
|
+
}
|
|
487
|
+
const value = structuredClone(base.value);
|
|
488
|
+
const sources = structuredClone(base.sources);
|
|
489
|
+
mergeLayer(value, sources, layer, "repo");
|
|
490
|
+
return {
|
|
491
|
+
merged: shapeMerged(value),
|
|
492
|
+
sources: shapeSources(sources),
|
|
493
|
+
warnings,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Merge one layer INTO `value`/`sources` in place, tagging every leaf it
|
|
498
|
+
* supplies with `source`.
|
|
499
|
+
*
|
|
500
|
+
* THE single definition of Last Light's config-merge semantics: plain objects
|
|
501
|
+
* deep-merge key-by-key so each leaf resolves (and is attributed) on its own;
|
|
502
|
+
* arrays and scalars replace wholesale. Core's boot-layer resolver
|
|
503
|
+
* (`apps/server/src/config/config-resolve.ts`) re-exports this rather than
|
|
504
|
+
* carrying its own — the repo layer must merge exactly the way default/overlay/
|
|
505
|
+
* env do, or a repo could acquire precedence the operator's own layers don't
|
|
506
|
+
* have, and two implementations is exactly how that drift starts.
|
|
507
|
+
*
|
|
508
|
+
* It lives HERE, in the leaf package, because the direction of the dependency
|
|
509
|
+
* edge only permits it here: `lastlight-shared` may never depend on core.
|
|
510
|
+
*/
|
|
511
|
+
export function mergeLayer(value, sources, layer, source) {
|
|
512
|
+
for (const [key, incoming] of Object.entries(layer)) {
|
|
513
|
+
if (isPlainObject(incoming)) {
|
|
514
|
+
const childValue = isPlainObject(value[key]) ? value[key] : {};
|
|
515
|
+
const childSources = isPlainObject(sources[key]) ? sources[key] : {};
|
|
516
|
+
mergeLayer(childValue, childSources, incoming, source);
|
|
517
|
+
value[key] = childValue;
|
|
518
|
+
sources[key] = childSources;
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
value[key] = incoming;
|
|
522
|
+
sources[key] = source;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function shapeMerged(value) {
|
|
527
|
+
const disabled = isPlainObject(value.disabled) ? value.disabled : {};
|
|
528
|
+
return {
|
|
529
|
+
models: stringMap(value.models),
|
|
530
|
+
variants: stringMap(value.variants),
|
|
531
|
+
disabled: {
|
|
532
|
+
workflows: stringList(disabled.workflows),
|
|
533
|
+
crons: stringList(disabled.crons),
|
|
534
|
+
prompts: stringList(disabled.prompts),
|
|
535
|
+
skills: stringList(disabled.skills),
|
|
536
|
+
agentContext: stringList(disabled.agentContext),
|
|
537
|
+
},
|
|
538
|
+
approval: boolMap(value.approval),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function shapeSources(sources) {
|
|
542
|
+
const disabled = isPlainObject(sources.disabled) ? sources.disabled : {};
|
|
543
|
+
const pick = (key) => (isConfigSource(disabled[key]) ? disabled[key] : "default");
|
|
544
|
+
return {
|
|
545
|
+
models: sourceMap(sources.models),
|
|
546
|
+
variants: sourceMap(sources.variants),
|
|
547
|
+
disabled: {
|
|
548
|
+
workflows: pick("workflows"),
|
|
549
|
+
crons: pick("crons"),
|
|
550
|
+
prompts: pick("prompts"),
|
|
551
|
+
skills: pick("skills"),
|
|
552
|
+
agentContext: pick("agentContext"),
|
|
553
|
+
},
|
|
554
|
+
approval: sourceMap(sources.approval),
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
/** Narrow an unknown provenance leaf to a {@link ConfigSource}. */
|
|
558
|
+
export function isConfigSource(value) {
|
|
559
|
+
return value === "default" || value === "overlay" || value === "env" || value === "repo";
|
|
560
|
+
}
|
|
561
|
+
function stringMap(raw) {
|
|
562
|
+
const out = {};
|
|
563
|
+
if (isPlainObject(raw))
|
|
564
|
+
for (const [k, v] of Object.entries(raw))
|
|
565
|
+
if (typeof v === "string")
|
|
566
|
+
out[k] = v;
|
|
567
|
+
return out;
|
|
568
|
+
}
|
|
569
|
+
function boolMap(raw) {
|
|
570
|
+
const out = {};
|
|
571
|
+
if (isPlainObject(raw))
|
|
572
|
+
for (const [k, v] of Object.entries(raw))
|
|
573
|
+
out[k] = v === true;
|
|
574
|
+
return out;
|
|
575
|
+
}
|
|
576
|
+
function sourceMap(raw) {
|
|
577
|
+
const out = {};
|
|
578
|
+
if (isConfigSource(raw))
|
|
579
|
+
return out;
|
|
580
|
+
if (isPlainObject(raw))
|
|
581
|
+
for (const [k, v] of Object.entries(raw))
|
|
582
|
+
if (isConfigSource(v))
|
|
583
|
+
out[k] = v;
|
|
584
|
+
return out;
|
|
585
|
+
}
|
|
586
|
+
function stringList(raw) {
|
|
587
|
+
return Array.isArray(raw) ? raw.filter((v) => typeof v === "string") : [];
|
|
588
|
+
}
|
|
589
|
+
function isPlainObject(value) {
|
|
590
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
591
|
+
}
|
|
592
|
+
//# sourceMappingURL=repo-config-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-config-schema.js","sourceRoot":"","sources":["../src/repo-config-schema.ts"],"names":[],"mappings":"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"}
|