@b00dea/cockpit 0.1.0 → 0.1.1
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/CHANGELOG.md +99 -0
- package/README.md +3 -0
- package/cockpit.schema.json +73 -3
- package/dist/autonomy.d.ts +3 -2
- package/dist/autonomy.js +30 -13
- package/dist/browser/commands.d.ts +57 -2
- package/dist/browser/commands.js +114 -10
- package/dist/browser/runtime.d.ts +1 -0
- package/dist/browser/runtime.js +4 -1
- package/dist/browser/surfaces.d.ts +7 -7
- package/dist/browser/surfaces.js +5 -0
- package/dist/canonical-json.d.ts +2 -0
- package/dist/canonical-json.js +5 -0
- package/dist/cli-main.d.ts +40 -3
- package/dist/cli-main.js +241 -62
- package/dist/command-table.d.ts +19 -11
- package/dist/command-table.js +190 -38
- package/dist/config/load.d.ts +16 -4
- package/dist/config/load.js +25 -3
- package/dist/config/unconfirmed-toml.d.ts +9 -0
- package/dist/config/unconfirmed-toml.js +29 -0
- package/dist/diagnostics.js +10 -1
- package/dist/docs/check.d.ts +16 -0
- package/dist/docs/check.js +197 -0
- package/dist/docs/decision-record.d.ts +34 -0
- package/dist/docs/decision-record.js +135 -0
- package/dist/docs/decisions.d.ts +16 -0
- package/dist/docs/decisions.js +103 -0
- package/dist/docs/markdown.d.ts +36 -0
- package/dist/docs/markdown.js +88 -0
- package/dist/docs/references.d.ts +32 -0
- package/dist/docs/references.js +148 -0
- package/dist/environment/application/confirm.d.ts +35 -0
- package/dist/environment/application/confirm.js +122 -0
- package/dist/environment/application/destructive.js +6 -2
- package/dist/environment/application/doctor.d.ts +12 -7
- package/dist/environment/application/doctor.js +20 -8
- package/dist/environment/application/guard.d.ts +12 -6
- package/dist/environment/application/guard.js +87 -32
- package/dist/environment/application/health.d.ts +14 -5
- package/dist/environment/application/health.js +8 -2
- package/dist/environment/application/lifecycle-context.d.ts +10 -3
- package/dist/environment/application/lifecycle-state.js +4 -3
- package/dist/environment/application/lifecycle.d.ts +2 -1
- package/dist/environment/application/lifecycle.js +11 -3
- package/dist/environment/application/services.d.ts +60 -0
- package/dist/environment/application/services.js +89 -0
- package/dist/environment/application/siblings.d.ts +43 -0
- package/dist/environment/application/siblings.js +269 -0
- package/dist/environment/claims.d.ts +10 -10
- package/dist/environment/claims.js +3 -11
- package/dist/environment/compose-topology.d.ts +15 -7
- package/dist/environment/compose-topology.js +100 -23
- package/dist/environment/contract.d.ts +42 -9
- package/dist/environment/contract.js +33 -0
- package/dist/environment/executable.d.ts +12 -0
- package/dist/environment/executable.js +46 -23
- package/dist/environment/generated-application/check.js +2 -0
- package/dist/environment/generated-application/prepare.js +2 -0
- package/dist/environment/generated-application/shared.d.ts +10 -2
- package/dist/environment/generated-application/shared.js +6 -1
- package/dist/environment/generated-application/surface-outputs.js +23 -12
- package/dist/environment/generated-application/surfaces.js +16 -11
- package/dist/environment/generated-application/sync.js +19 -4
- package/dist/environment/git-observation.d.ts +25 -6
- package/dist/environment/git-observation.js +37 -28
- package/dist/environment/health-composition.d.ts +3 -2
- package/dist/environment/health-composition.js +19 -2
- package/dist/environment/init-application.d.ts +39 -14
- package/dist/environment/init-application.js +39 -10
- package/dist/environment/lifecycle-composition.d.ts +4 -2
- package/dist/environment/lifecycle-composition.js +84 -17
- package/dist/environment/lifecycle-stage.d.ts +8 -3
- package/dist/environment/lifecycle-stage.js +11 -1
- package/dist/environment/logs.d.ts +1 -1
- package/dist/environment/map-limit.d.ts +2 -0
- package/dist/environment/map-limit.js +13 -0
- package/dist/environment/mcp-observation.d.ts +9 -0
- package/dist/environment/mcp-observation.js +46 -0
- package/dist/environment/observations.d.ts +1 -1
- package/dist/environment/observations.js +2 -0
- package/dist/environment/operator-application.d.ts +35 -6
- package/dist/environment/operator-application.js +42 -0
- package/dist/environment/presence.d.ts +4 -0
- package/dist/environment/presence.js +26 -0
- package/dist/environment/read-composition.d.ts +8 -3
- package/dist/environment/read-composition.js +53 -8
- package/dist/environment/readiness-composition.d.ts +20 -1
- package/dist/environment/readiness-composition.js +23 -2
- package/dist/environment/reclaim.d.ts +56 -19
- package/dist/environment/reclaim.js +147 -42
- package/dist/environment/runtime.d.ts +64 -18
- package/dist/environment/runtime.js +73 -3
- package/dist/environment/served-checkout-grants.d.ts +53 -0
- package/dist/environment/served-checkout-grants.js +182 -0
- package/dist/environment/sibling-checkouts.d.ts +119 -0
- package/dist/environment/sibling-checkouts.js +254 -0
- package/dist/environment/sibling-observation.d.ts +27 -0
- package/dist/environment/sibling-observation.js +45 -0
- package/dist/environment/sibling-pull.d.ts +23 -0
- package/dist/environment/sibling-pull.js +95 -0
- package/dist/environment/stack-inputs.d.ts +18 -0
- package/dist/environment/stack-inputs.js +210 -0
- package/dist/environment/stack-inventory-prune.d.ts +31 -0
- package/dist/environment/stack-inventory-prune.js +129 -0
- package/dist/environment/stack-inventory-report.d.ts +44 -0
- package/dist/environment/stack-inventory-report.js +78 -0
- package/dist/environment/stack-inventory.d.ts +94 -0
- package/dist/environment/stack-inventory.js +418 -0
- package/dist/environment/state.d.ts +48 -9
- package/dist/environment/state.js +142 -2
- package/dist/environment/tool-registration.js +3 -7
- package/dist/environment-exec/execute.d.ts +6 -3
- package/dist/environment-exec/execute.js +4 -0
- package/dist/fix-catalogue.d.ts +1 -1
- package/dist/generated-environment/adopt.js +4 -8
- package/dist/guards/answer-confirmation.d.ts +41 -0
- package/dist/guards/answer-confirmation.js +109 -0
- package/dist/guards/evaluator.d.ts +20 -3
- package/dist/guards/evaluator.js +39 -7
- package/dist/guards/local-identity.d.ts +6 -2
- package/dist/guards/local-identity.js +10 -4
- package/dist/guards/policy-change.d.ts +43 -0
- package/dist/guards/policy-change.js +159 -0
- package/dist/guards/policy-pin.d.ts +1 -1
- package/dist/guards/policy-pin.js +14 -12
- package/dist/init/command.d.ts +4 -3
- package/dist/init/command.js +36 -16
- package/dist/init/derive.d.ts +8 -6
- package/dist/init/derive.js +140 -36
- package/dist/init/draft.d.ts +13 -11
- package/dist/init/draft.js +54 -31
- package/dist/init/render.d.ts +18 -6
- package/dist/init/render.js +31 -9
- package/dist/repository-path.d.ts +9 -0
- package/dist/repository-path.js +16 -0
- package/dist/schema.d.ts +42 -75
- package/dist/schema.js +62 -11
- package/dist/surfaces/harness-registry.d.ts +80 -0
- package/dist/surfaces/harness-registry.js +29 -0
- package/dist/surfaces/mcp.d.ts +67 -17
- package/dist/surfaces/mcp.js +194 -31
- package/dist/surfaces/projection.d.ts +7 -7
- package/dist/surfaces/projection.js +41 -6
- package/dist/surfaces/render.d.ts +5 -44
- package/dist/surfaces/render.js +5 -2
- package/dist/team-metadata.js +2 -1
- package/dist/testing/test-protection.d.ts +34 -0
- package/dist/testing/test-protection.js +137 -0
- package/dist/testing/weakening.d.ts +15 -0
- package/dist/testing/weakening.js +30 -0
- package/dist/work-orders/application-closure.d.ts +7 -0
- package/dist/work-orders/application-closure.js +76 -0
- package/dist/work-orders/application-context.d.ts +162 -0
- package/dist/work-orders/application-context.js +137 -0
- package/dist/work-orders/application-decisions.d.ts +27 -0
- package/dist/work-orders/application-decisions.js +121 -0
- package/dist/work-orders/application-grading.d.ts +11 -0
- package/dist/work-orders/application-grading.js +212 -0
- package/dist/work-orders/application.d.ts +4 -97
- package/dist/work-orders/application.js +94 -254
- package/dist/work-orders/composition.d.ts +35 -58
- package/dist/work-orders/composition.js +216 -24
- package/dist/work-orders/contract.d.ts +22 -6
- package/dist/work-orders/contract.js +34 -16
- package/dist/work-orders/decisions.d.ts +23 -0
- package/dist/work-orders/decisions.js +55 -0
- package/dist/work-orders/store.d.ts +54 -5
- package/dist/work-orders/store.js +118 -24
- package/docs/README.md +6 -3
- package/docs/capability-matrix.md +17 -9
- package/docs/cli-reference.md +209 -1
- package/docs/command-list.md +12 -1
- package/docs/concepts.md +6 -4
- package/docs/configuration-essentials.md +7 -2
- package/docs/configuration-reference.md +18 -3
- package/docs/docs-check.md +76 -0
- package/docs/environment.md +194 -6
- package/docs/faq.md +5 -3
- package/docs/getting-started.md +109 -25
- package/docs/harness-surfaces.md +69 -2
- package/docs/safety-and-autonomy.md +56 -12
- package/docs/troubleshooting.md +29 -8
- package/docs/tutorial.md +3 -2
- package/docs/work-and-browser.md +180 -14
- package/libexec/browser-operation.cjs +86 -2
- package/package.json +2 -2
- package/surfaces/skills/cockpit-browser.skill.md +7 -2
- package/surfaces/skills/cockpit-explain.skill.md +1 -1
- package/surfaces/skills/cockpit-fix.skill.md +1 -1
- package/surfaces/skills/cockpit-setup.skill.md +3 -3
- package/surfaces/skills/cockpit-work.skill.md +1 -1
- package/surfaces/skills/procedures/explain.md +9 -4
- package/surfaces/skills/procedures/fix.md +25 -11
- package/surfaces/skills/procedures/setup.md +113 -40
- package/surfaces/skills/procedures/work.md +45 -16
|
@@ -162,6 +162,12 @@ function projectServices(stdout) {
|
|
|
162
162
|
const image = service.image;
|
|
163
163
|
if (image !== undefined && typeof image !== 'string')
|
|
164
164
|
return undefined;
|
|
165
|
+
const containerName = service.container_name;
|
|
166
|
+
if (containerName !== undefined && typeof containerName !== 'string')
|
|
167
|
+
return undefined;
|
|
168
|
+
const profiles = service.profiles ?? [];
|
|
169
|
+
if (!Array.isArray(profiles) || profiles.some(profile => typeof profile !== 'string'))
|
|
170
|
+
return undefined;
|
|
165
171
|
const rawVolumes = service.volumes ?? [];
|
|
166
172
|
if (!Array.isArray(rawVolumes))
|
|
167
173
|
return undefined;
|
|
@@ -176,7 +182,8 @@ function projectServices(stdout) {
|
|
|
176
182
|
}
|
|
177
183
|
projected.push({
|
|
178
184
|
id, ports, healthcheck,
|
|
179
|
-
image: image ?? null, build: service.build !== undefined, mountSources,
|
|
185
|
+
image: image ?? null, build: service.build !== undefined, containerName: containerName ?? null, mountSources,
|
|
186
|
+
...profiles.length > 0 ? { profiles: profiles } : {},
|
|
180
187
|
});
|
|
181
188
|
}
|
|
182
189
|
return projected;
|
|
@@ -300,7 +307,7 @@ async function canonicalPath(path) {
|
|
|
300
307
|
* Docker follows a symbolic link in the bind source, so `./api/cache -> /` would otherwise hand
|
|
301
308
|
* the host root to a container under a mount = "always" checkout.
|
|
302
309
|
*/
|
|
303
|
-
export async function redirectedBindSources(binds, declared) {
|
|
310
|
+
export async function redirectedBindSources(binds, declared, resolve = canonicalPath) {
|
|
304
311
|
const redirected = [];
|
|
305
312
|
for (const bind of new Set(binds)) {
|
|
306
313
|
if (declared.includes(bind))
|
|
@@ -310,7 +317,7 @@ export async function redirectedBindSources(binds, declared) {
|
|
|
310
317
|
continue;
|
|
311
318
|
let inside;
|
|
312
319
|
try {
|
|
313
|
-
inside = contains(await
|
|
320
|
+
inside = contains(await resolve(owner), await resolve(bind));
|
|
314
321
|
}
|
|
315
322
|
catch {
|
|
316
323
|
inside = false;
|
|
@@ -320,34 +327,35 @@ export async function redirectedBindSources(binds, declared) {
|
|
|
320
327
|
}
|
|
321
328
|
return redirected.sort((left, right) => left.bind.localeCompare(right.bind));
|
|
322
329
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
330
|
+
function profileOf(sibling) {
|
|
331
|
+
return sibling.mount.startsWith('profile:') ? sibling.mount.slice('profile:'.length) : undefined;
|
|
332
|
+
}
|
|
333
|
+
// Image references that differ per stack: Compose's own project name, which cockpit derives per
|
|
334
|
+
// checkout, and the leased ports.bases variables.
|
|
335
|
+
function imageVariesPerStack(image, leased) {
|
|
336
|
+
return [...image.matchAll(/\$\{?([A-Za-z_][A-Za-z0-9_]*)/g)].some(([, name]) => name === 'COMPOSE_PROJECT_NAME' || leased.has(name));
|
|
337
|
+
}
|
|
338
|
+
// The bind findings of the services one profile set runs, judged as `up` judges them under that set.
|
|
339
|
+
async function assessBinds(services, declarations, variables, active, resolve) {
|
|
331
340
|
const diagnostics = [];
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
const variables = Object.fromEntries(declarations.siblings.map(sibling => [sibling.var, sibling.path]));
|
|
338
|
-
const binds = services.flatMap(service => service.mountSources.map(source => ({
|
|
341
|
+
const running = services.filter(service => !service.profiles?.length || service.profiles.some(profile => active.includes(profile)));
|
|
342
|
+
const mounted = declarations.siblings.filter(sibling => sibling.mount === 'always'
|
|
343
|
+
|| profileOf(sibling) !== undefined && active.includes(profileOf(sibling)));
|
|
344
|
+
const declared = [...mounted.map(sibling => sibling.path), ...declarations.hostPaths.map(({ path }) => path)];
|
|
345
|
+
const binds = running.flatMap(service => service.mountSources.map(source => ({
|
|
339
346
|
service: service.id, source, path: composeBindPath(source, declarations.projectDirectory, variables)
|
|
340
347
|
})))
|
|
341
348
|
.filter(bind => bind.path !== 'volume');
|
|
342
349
|
const resolved = binds.flatMap(bind => bind.path === undefined ? [] : [bind.path]);
|
|
343
350
|
const { missing, unexpected } = compareBindSources(resolved, declared);
|
|
344
|
-
const redirected = (await redirectedBindSources(resolved, declared)).map(({ bind }) => bind);
|
|
345
|
-
|
|
351
|
+
const redirected = (await redirectedBindSources(resolved, declared, resolve)).map(({ bind }) => bind);
|
|
352
|
+
const under = active.length === 0 ? '' : ` with profile ${active.join(', ')} active`;
|
|
353
|
+
for (const sibling of mounted) {
|
|
346
354
|
if (!missing.includes(sibling.path))
|
|
347
355
|
continue;
|
|
348
356
|
diagnostics.push({
|
|
349
357
|
severity: 'error', code: 'COMPOSE_SIBLING_NOT_MOUNTED', target: `sibling:${sibling.slug}`,
|
|
350
|
-
message: `sibling ${sibling.slug} is declared mount = "
|
|
358
|
+
message: `sibling ${sibling.slug} is declared mount = "${sibling.mount}", but no Compose service${under} bind-mounts ${sibling.path} or a directory inside it; up admits a stack only when every such sibling is mounted, so it would refuse to start`,
|
|
351
359
|
fix: `bind-mount it in a service of the Compose file, whole or a directory inside it (for example "\${${sibling.var}}:/workspace/${sibling.slug}"), or set mount = "none" on sibling ${sibling.slug} in cockpit.toml, then run ${cockpitCommand('sync')}`,
|
|
352
360
|
});
|
|
353
361
|
}
|
|
@@ -364,16 +372,73 @@ export async function assessComposeTopology(services, declarations) {
|
|
|
364
372
|
for (const bind of binds) {
|
|
365
373
|
const why = bind.path === undefined
|
|
366
374
|
? 'whose host side names a variable cockpit does not supply and gives it no default'
|
|
367
|
-
: unexpected.includes(bind.path) ?
|
|
375
|
+
: unexpected.includes(bind.path) ? `which lies inside no mounted sibling${under} and is no environment.generated_host_paths entry`
|
|
368
376
|
: redirected.includes(bind.path) ? 'which resolves through a symbolic link to a path outside the sibling that holds it' : undefined;
|
|
369
377
|
if (why === undefined)
|
|
370
378
|
continue;
|
|
371
379
|
diagnostics.push({
|
|
372
380
|
severity: 'error', code: 'COMPOSE_BIND_UNDECLARED', target: `compose:${bind.service}:${bind.source}`,
|
|
373
381
|
message: `service ${bind.service} bind-mounts ${bind.path ?? bind.source}, ${why}; up hands a container only the checkouts and host paths cockpit.toml declares, so it would refuse to start`,
|
|
374
|
-
fix: `bind a directory inside a
|
|
382
|
+
fix: `bind a directory inside a mounted sibling (its variable, as in "\${VAR}/path", or a path relative to the first Compose file's directory), list the path under environment.generated_host_paths, or remove the bind mount from service ${bind.service}; then run ${cockpitCommand('check')}`,
|
|
375
383
|
});
|
|
376
384
|
}
|
|
385
|
+
return diagnostics;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Judges a cockpit's own Compose topology. A host port that is not drawn from a leased
|
|
389
|
+
* `ports.bases` variable is the same number in every worktree and parallel stack, so the second
|
|
390
|
+
* stack to start fails to bind it; a fixed `container_name` fails the same way, and a fixed tag on a
|
|
391
|
+
* built image is shared the same way. The remaining findings are warnings about what cockpit can
|
|
392
|
+
* then promise, not about whether the stack runs.
|
|
393
|
+
*/
|
|
394
|
+
export async function assessComposeTopology(services, declarations) {
|
|
395
|
+
const leased = new Set(declarations.portVariables);
|
|
396
|
+
const diagnostics = [];
|
|
397
|
+
const defined = new Set(services.flatMap(service => service.profiles ?? []));
|
|
398
|
+
const defaults = declarations.profiles ?? [];
|
|
399
|
+
for (const [profile, source] of [
|
|
400
|
+
...defaults.map(profile => [profile, 'environment.profiles names it']),
|
|
401
|
+
...declarations.siblings.flatMap(sibling => profileOf(sibling) === undefined ? []
|
|
402
|
+
: [[profileOf(sibling), `sibling ${sibling.slug} is mounted under it`]]),
|
|
403
|
+
]) {
|
|
404
|
+
if (defined.has(profile))
|
|
405
|
+
continue;
|
|
406
|
+
diagnostics.push({
|
|
407
|
+
severity: 'error', code: 'COMPOSE_PROFILE_UNDEFINED',
|
|
408
|
+
target: `profile:${profile}`,
|
|
409
|
+
message: `profile ${profile} is declared by no service in environment.compose_files, and ${source}; Compose accepts an unknown profile silently, so activating it would start nothing`,
|
|
410
|
+
fix: `add profiles: [${profile}] to the services it should start, or remove it from cockpit.toml; then run ${cockpitCommand('check')}`,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
// `up` admits the topology only when every bind of an active service lands inside a mounted
|
|
414
|
+
// checkout or a generated host path, and every such checkout is mounted; check judges the same
|
|
415
|
+
// binds the same way, or a configuration check accepts refuses every runtime command. A stack
|
|
416
|
+
// runs the default profile set plus any profiles `up --profile` adds, so the binds are judged
|
|
417
|
+
// under the default set and under the default set plus each other defined profile: a finding
|
|
418
|
+
// under several added profiles already shows under one of them.
|
|
419
|
+
const usable = defaults.filter(profile => defined.has(profile));
|
|
420
|
+
const sets = [usable, ...[...defined].filter(profile => !usable.includes(profile)).sort().map(profile => [...usable, profile])];
|
|
421
|
+
const variables = Object.fromEntries(declarations.siblings.map(sibling => [sibling.var, sibling.path]));
|
|
422
|
+
// The same binds recur in most profile sets, so each real path is resolved once.
|
|
423
|
+
const resolutions = new Map();
|
|
424
|
+
const resolve = (path) => {
|
|
425
|
+
let resolution = resolutions.get(path);
|
|
426
|
+
if (resolution === undefined) {
|
|
427
|
+
resolution = canonicalPath(path);
|
|
428
|
+
resolutions.set(path, resolution);
|
|
429
|
+
}
|
|
430
|
+
return resolution;
|
|
431
|
+
};
|
|
432
|
+
const seen = new Set();
|
|
433
|
+
for (const active of sets) {
|
|
434
|
+
for (const diagnostic of await assessBinds(services, declarations, variables, active, resolve)) {
|
|
435
|
+
const key = `${diagnostic.code}\0${diagnostic.target}`;
|
|
436
|
+
if (!seen.has(key)) {
|
|
437
|
+
seen.add(key);
|
|
438
|
+
diagnostics.push(diagnostic);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
377
442
|
for (const service of services) {
|
|
378
443
|
for (const port of service.ports) {
|
|
379
444
|
// No host interface means Docker binds every one (0.0.0.0 and ::), exposing the service
|
|
@@ -396,6 +461,12 @@ export async function assessComposeTopology(services, declarations) {
|
|
|
396
461
|
fix: `publish it as "\${${name}}:${port.target}" in the Compose file and declare ${name} under [ports.bases]`,
|
|
397
462
|
});
|
|
398
463
|
}
|
|
464
|
+
if (service.containerName !== null)
|
|
465
|
+
diagnostics.push({
|
|
466
|
+
severity: 'error', code: 'COMPOSE_CONTAINER_NAME_FIXED', target: `compose:${service.id}`,
|
|
467
|
+
message: `service ${service.id} sets container_name ${JSON.stringify(service.containerName)}, and Docker allows one container of a name, so a second checkout's stack fails to start`,
|
|
468
|
+
fix: `remove container_name from service ${service.id} and address it by its Compose service name, or with docker compose exec ${service.id}`,
|
|
469
|
+
});
|
|
399
470
|
if (!service.healthcheck)
|
|
400
471
|
diagnostics.push({
|
|
401
472
|
severity: 'warning', code: 'COMPOSE_HEALTHCHECK_MISSING', target: `compose:${service.id}`,
|
|
@@ -408,6 +479,12 @@ export async function assessComposeTopology(services, declarations) {
|
|
|
408
479
|
message: `service ${service.id} runs image ${JSON.stringify(service.image)} by tag, so a later pull can change what runs`,
|
|
409
480
|
fix: `pin it by digest, for example ${service.image.replace(/:[^/:]*$/, '')}@sha256:<digest> from docker image inspect`,
|
|
410
481
|
});
|
|
482
|
+
if (service.build && service.image !== null && !imageVariesPerStack(service.image, leased))
|
|
483
|
+
diagnostics.push({
|
|
484
|
+
severity: 'error', code: 'COMPOSE_IMAGE_TAG_SHARED', target: `compose:${service.id}`,
|
|
485
|
+
message: `service ${service.id} builds its image and tags it ${JSON.stringify(service.image)}, which is the same in every worktree and parallel stack, so one checkout's build replaces the image another checkout's stack runs`,
|
|
486
|
+
fix: `remove image from service ${service.id}, so Compose names the build after this stack's project`,
|
|
487
|
+
});
|
|
411
488
|
}
|
|
412
489
|
return diagnostics;
|
|
413
490
|
}
|
|
@@ -7,7 +7,7 @@ export interface Diagnostic<Code extends string = string> {
|
|
|
7
7
|
fix?: string;
|
|
8
8
|
}
|
|
9
9
|
export type JsonSnapshot = null | boolean | number | string | JsonSnapshotArray | JsonSnapshotObject;
|
|
10
|
-
|
|
10
|
+
interface JsonSnapshotArray extends ReadonlyArray<JsonSnapshot> {
|
|
11
11
|
}
|
|
12
12
|
export interface JsonSnapshotObject {
|
|
13
13
|
readonly [key: string]: JsonSnapshot;
|
|
@@ -113,6 +113,9 @@ export declare const ENVIRONMENT_CODES: Readonly<{
|
|
|
113
113
|
readonly RECLAIM_LABEL_ESCAPE: 'RECLAIM_LABEL_ESCAPE';
|
|
114
114
|
readonly RECLAIM_ACCOUNTING_MISMATCH: 'RECLAIM_ACCOUNTING_MISMATCH';
|
|
115
115
|
readonly DOWN_RESOURCES_KEPT: 'DOWN_RESOURCES_KEPT';
|
|
116
|
+
readonly STACKS_ORPHANED: 'STACKS_ORPHANED';
|
|
117
|
+
readonly STACKS_PRUNE_UNCONFIRMED: 'STACKS_PRUNE_UNCONFIRMED';
|
|
118
|
+
readonly STACKS_PRUNE_INCOMPLETE: 'STACKS_PRUNE_INCOMPLETE';
|
|
116
119
|
readonly CONFIG_STALE: 'CONFIG_STALE';
|
|
117
120
|
readonly SIBLING_MISSING: 'SIBLING_MISSING';
|
|
118
121
|
readonly GUIDE_CAP_EXCEEDED: 'GUIDE_CAP_EXCEEDED';
|
|
@@ -131,6 +134,7 @@ export declare const ENVIRONMENT_CODES: Readonly<{
|
|
|
131
134
|
readonly GENERATED_ENVIRONMENT_UNAVAILABLE: 'GENERATED_ENVIRONMENT_UNAVAILABLE';
|
|
132
135
|
readonly CLAIM_STATE_UNHEALTHY: 'CLAIM_STATE_UNHEALTHY';
|
|
133
136
|
readonly MOUNT_POSTURE_UNAVAILABLE: 'MOUNT_POSTURE_UNAVAILABLE';
|
|
137
|
+
readonly COMPOSE_PROFILE_UNDEFINED: 'COMPOSE_PROFILE_UNDEFINED';
|
|
134
138
|
readonly STATE_UNSEEDED: 'STATE_UNSEEDED';
|
|
135
139
|
readonly STACK_NOT_RUNNING: 'STACK_NOT_RUNNING';
|
|
136
140
|
readonly SKILL_BUDGET_UNAVAILABLE: 'SKILL_BUDGET_UNAVAILABLE';
|
|
@@ -143,6 +147,8 @@ export declare const ENVIRONMENT_CODES: Readonly<{
|
|
|
143
147
|
readonly SERVICE_NOT_DECLARED: 'SERVICE_NOT_DECLARED';
|
|
144
148
|
readonly GUARD_HOSTS_UNDECLARED: 'GUARD_HOSTS_UNDECLARED';
|
|
145
149
|
readonly TESTING_GATE_UNDECLARED: 'TESTING_GATE_UNDECLARED';
|
|
150
|
+
readonly MCP_COMMAND_UNRESOLVED: 'MCP_COMMAND_UNRESOLVED';
|
|
151
|
+
readonly MCP_ENVIRONMENT_UNSET: 'MCP_ENVIRONMENT_UNSET';
|
|
146
152
|
readonly EVALUATOR_UNAVAILABLE: 'EVALUATOR_UNAVAILABLE';
|
|
147
153
|
readonly EVALUATOR_RUN_FAILED: 'EVALUATOR_RUN_FAILED';
|
|
148
154
|
readonly EVALUATOR_FELL_BACK_TO_RUNNING_TOOL: 'EVALUATOR_FELL_BACK_TO_RUNNING_TOOL';
|
|
@@ -150,15 +156,23 @@ export declare const ENVIRONMENT_CODES: Readonly<{
|
|
|
150
156
|
readonly TEST_GATE_UNSATISFIED: 'TEST_GATE_UNSATISFIED';
|
|
151
157
|
readonly WORK_GRADE_UNSATISFIED: 'WORK_GRADE_UNSATISFIED';
|
|
152
158
|
readonly WORK_CHECK_FAILED: 'WORK_CHECK_FAILED';
|
|
159
|
+
readonly BROWSER_ROUTE_FINDINGS: 'BROWSER_ROUTE_FINDINGS';
|
|
153
160
|
readonly WORK_VERIFY_FAILED: 'WORK_VERIFY_FAILED';
|
|
154
161
|
readonly WORK_PERSON_REQUIRED: 'WORK_PERSON_REQUIRED';
|
|
162
|
+
readonly WORK_TEST_WEAKENED: 'WORK_TEST_WEAKENED';
|
|
155
163
|
readonly AUTONOMY_PERSON_REQUIRED: 'AUTONOMY_PERSON_REQUIRED';
|
|
156
164
|
readonly AUTONOMY_CI_ONLY: 'AUTONOMY_CI_ONLY';
|
|
157
165
|
readonly POLICY_WEAKENING_UNCONFIRMED: 'POLICY_WEAKENING_UNCONFIRMED';
|
|
166
|
+
readonly GUARD_CONFIG_UNSYNCED: 'GUARD_CONFIG_UNSYNCED';
|
|
167
|
+
readonly GUARD_CONFIG_WIDENED: 'GUARD_CONFIG_WIDENED';
|
|
158
168
|
readonly WORK_ORDER_CLOSED: 'WORK_ORDER_CLOSED';
|
|
159
169
|
readonly CODEX_HOOK_UNTRUSTED: 'CODEX_HOOK_UNTRUSTED';
|
|
160
170
|
readonly SELFTEST_CUSTOM_PROBE_UNSUPPORTED: 'SELFTEST_CUSTOM_PROBE_UNSUPPORTED';
|
|
161
171
|
readonly DECLARATION_UNCONFIRMED: 'DECLARATION_UNCONFIRMED';
|
|
172
|
+
readonly CONFIRM_PERSON_REQUIRED: 'CONFIRM_PERSON_REQUIRED';
|
|
173
|
+
readonly CONFIRM_ANSWER_NOT_LISTED: 'CONFIRM_ANSWER_NOT_LISTED';
|
|
174
|
+
readonly CONFIRM_OBSERVATION_FAILED: 'CONFIRM_OBSERVATION_FAILED';
|
|
175
|
+
readonly CONFIRM_RECORD_UNWRITABLE: 'CONFIRM_RECORD_UNWRITABLE';
|
|
162
176
|
readonly COCKPIT_NOT_INSTALLED: 'COCKPIT_NOT_INSTALLED';
|
|
163
177
|
readonly COCKPIT_LAUNCHER_UNPROVEN: 'COCKPIT_LAUNCHER_UNPROVEN';
|
|
164
178
|
readonly SIBLING_REMOTE_UNRECORDED: 'SIBLING_REMOTE_UNRECORDED';
|
|
@@ -178,6 +192,25 @@ export declare const ENVIRONMENT_CODES: Readonly<{
|
|
|
178
192
|
readonly UPGRADE_SYNC_FAILED: 'UPGRADE_SYNC_FAILED';
|
|
179
193
|
readonly DATA_SEED_FAILED: 'DATA_SEED_FAILED';
|
|
180
194
|
readonly DATA_SEED_UNAVAILABLE: 'DATA_SEED_UNAVAILABLE';
|
|
195
|
+
readonly DOCS_NOT_DECLARED: 'DOCS_NOT_DECLARED';
|
|
196
|
+
readonly DOCS_DECLARATION_UNMATCHED: 'DOCS_DECLARATION_UNMATCHED';
|
|
197
|
+
readonly DOCS_REFERENCE_REMOVED: 'DOCS_REFERENCE_REMOVED';
|
|
198
|
+
readonly DOCS_TARGET_REMOVED: 'DOCS_TARGET_REMOVED';
|
|
199
|
+
readonly DECISION_NUMBER_REUSED: 'DECISION_NUMBER_REUSED';
|
|
200
|
+
readonly DECISION_UNINDEXED: 'DECISION_UNINDEXED';
|
|
201
|
+
readonly DECISION_INDEX_ENTRY_MISSING: 'DECISION_INDEX_ENTRY_MISSING';
|
|
202
|
+
readonly DECISION_STATUS_MISSING: 'DECISION_STATUS_MISSING';
|
|
203
|
+
readonly DECISION_LINK_UNRESOLVED: 'DECISION_LINK_UNRESOLVED';
|
|
204
|
+
readonly DECISION_SUPERSEDED_STALE: 'DECISION_SUPERSEDED_STALE';
|
|
205
|
+
readonly SIBLING_CHECKOUT_RECORD_INVALID: 'SIBLING_CHECKOUT_RECORD_INVALID';
|
|
206
|
+
readonly SIBLING_CHECKOUT_UNKNOWN: 'SIBLING_CHECKOUT_UNKNOWN';
|
|
207
|
+
readonly SIBLING_CHECKOUT_UNAVAILABLE: 'SIBLING_CHECKOUT_UNAVAILABLE';
|
|
208
|
+
readonly SIBLING_CHECKOUT_FOREIGN: 'SIBLING_CHECKOUT_FOREIGN';
|
|
209
|
+
readonly SIBLING_CHECKOUT_OVERRIDDEN: 'SIBLING_CHECKOUT_OVERRIDDEN';
|
|
210
|
+
readonly SIBLING_ENVIRONMENT_FILE_MISSING: 'SIBLING_ENVIRONMENT_FILE_MISSING';
|
|
211
|
+
readonly PULL_SKIPPED: 'PULL_SKIPPED';
|
|
212
|
+
readonly STACK_INPUTS_CHANGED: 'STACK_INPUTS_CHANGED';
|
|
213
|
+
readonly STACK_INPUTS_UNRECORDED: 'STACK_INPUTS_UNRECORDED';
|
|
181
214
|
}>;
|
|
182
215
|
export declare const EXECUTION_FAILURE_CODES: readonly ["EXECUTION_SPAWN_FAILED", "EXECUTION_PRECONDITION_CHANGED", "EXECUTION_STAGING_FAILED", "EXECUTION_STAGING_CLEANUP_FAILED", "EXECUTION_TIMEOUT", "EXECUTION_STDOUT_LIMIT", "EXECUTION_STDERR_LIMIT", "EXECUTION_STEP_FAILED", "EXECUTION_ABORTED", "EXECUTION_TERMINATION_UNCONFIRMED"];
|
|
183
216
|
export declare const REFUSAL_CODES: readonly ["CONFIG_NOT_FOUND", "CONFIG_UNREADABLE", "CONFIG_ENCODING_INVALID", "CONFIG_PARSE_INVALID", "CONFIG_SCHEMA_INVALID", "STATE_ROOT_UNAVAILABLE", "STATE_FORMAT_UNSUPPORTED", "STATE_RECORD_MALFORMED", "STATE_LOCKED", "STATE_DURABILITY_INDETERMINATE", "SETUP_CONFIG_REQUIRED", "SETUP_OPTION_DUPLICATE", "SETUP_POSITIONAL_UNSUPPORTED", "SETUP_PATH_OUTSIDE_ROOT", "SETUP_PATH_SYMLINK", "SETUP_PATH_TYPE_INVALID", "SETUP_PARENT_MISSING", "SETUP_REMOTE_MISMATCH", "SETUP_REMOTE_UNRESOLVABLE", "SETUP_MANUAL_MISSING", "SETUP_CLONE_FAILED", "SETUP_POSTCHECK_FAILED", "SETUP_INSTRUCTION_POINTER_INVALID", "SETUP_INSTRUCTION_FILES_INVALID", "CONFIG_CHANGED_DURING_EXECUTION", "EXECUTION_SPAWN_FAILED", "EXECUTION_PRECONDITION_CHANGED", "EXECUTION_STAGING_FAILED", "EXECUTION_STAGING_CLEANUP_FAILED", "EXECUTION_TIMEOUT", "EXECUTION_STDOUT_LIMIT", "EXECUTION_STDERR_LIMIT", "EXECUTION_STEP_FAILED", "EXECUTION_ABORTED", "EXECUTION_TERMINATION_UNCONFIRMED", "EXECUTION_COMPENSATION_TIMEOUT", "EXECUTION_COMPENSATION_STEP_FAILED", "LIFECYCLE_EVIDENCE_INVALID", "LIFECYCLE_PROGRESS_INVALID", "STACK_RECORDED_UNDER_EARLIER_NAME", "PORT_FAMILY_COLLISION", "PORT_LISTENER_EVIDENCE_INVALID", "PORT_FAMILY_OUT_OF_RANGE", "PORT_LEASE_EXHAUSTED", "CLAIM_CO_TENANT", "CLAIM_LIVENESS_UNKNOWN", "DRAIN_ATTEMPT_REPAIR_REQUIRED", "INSTALL_POLICY_OBSERVATION_UNAVAILABLE", "INSTALL_POLICY_OBSERVATION_INVALID", "INSTALL_POLICY_MISSING", "INSTALL_POLICY_AMBIGUOUS", "TEMPLATE_NOT_FOUND", "TEMPLATE_NOT_TRACKED", "TEMPLATE_IGNORED", "TEMPLATE_PATH_OUTSIDE_ROOT", "ENVIRONMENT_FILE_NOT_FOUND", "ENVIRONMENT_FILE_UNREADABLE", "ENVIRONMENT_FILE_TOO_LARGE", "ENVIRONMENT_FILE_MALFORMED", "INSTANCE_NOT_IGNORED", "ENVIRONMENT_SEED_PRECONDITION_CHANGED", "ENVIRONMENT_SEED_FAILED", "RUNTIME_UNAVAILABLE", "RUNTIME_IMAGE_UNAVAILABLE", "RUNTIME_UNMEASURED", "RUNTIME_COMPOSE_REMOTE", "RUNTIME_PROBE_TIMEOUT", "RESET_CONFIRMATION_REQUIRED", "RESET_CONFIRMATION_MISMATCH", "RESET_CO_TENANT", "EVIDENCE_UNAVAILABLE", "RECLAIM_LABEL_ESCAPE", "RECLAIM_ACCOUNTING_MISMATCH", "DOWN_RESOURCES_KEPT", "GIT_COMPARISON_REMOTE_MISMATCH"];
|
|
@@ -199,7 +232,7 @@ export declare function tryParseJson(text: string): unknown;
|
|
|
199
232
|
export declare function hasErrorCode(error: unknown, ...codes: readonly string[]): boolean;
|
|
200
233
|
export declare function isMissing(error: unknown): boolean;
|
|
201
234
|
export declare function isCanonicalTimestamp(value: unknown): value is string;
|
|
202
|
-
|
|
235
|
+
type ExpectedObservation = {
|
|
203
236
|
kind: 'path';
|
|
204
237
|
target: string;
|
|
205
238
|
state: 'absent' | 'directory' | 'repository';
|
|
@@ -314,20 +347,20 @@ export interface ExecutionFence {
|
|
|
314
347
|
export interface ExecutionPlan {
|
|
315
348
|
steps: readonly ExecutionStep[];
|
|
316
349
|
}
|
|
317
|
-
|
|
350
|
+
type LifecycleResumeBoundary = Readonly<{
|
|
318
351
|
completedStepIds: readonly string[];
|
|
319
352
|
nextStepId: string | null;
|
|
320
353
|
}>;
|
|
321
354
|
type LifecycleStepBase = Readonly<{
|
|
322
355
|
id: string;
|
|
323
356
|
}>;
|
|
324
|
-
|
|
357
|
+
interface LifecycleEntrypoint {
|
|
325
358
|
readonly name: string;
|
|
326
359
|
readonly kind: EntrypointKind;
|
|
327
360
|
readonly port: string;
|
|
328
361
|
readonly value: string;
|
|
329
362
|
}
|
|
330
|
-
|
|
363
|
+
interface LifecycleCompletedService {
|
|
331
364
|
readonly service: string;
|
|
332
365
|
readonly containerId: string;
|
|
333
366
|
}
|
|
@@ -336,7 +369,7 @@ export interface LifecycleProgress {
|
|
|
336
369
|
readonly operationGeneration: string;
|
|
337
370
|
readonly completedServices: readonly LifecycleCompletedService[];
|
|
338
371
|
}
|
|
339
|
-
|
|
372
|
+
type LifecycleStep = (LifecycleStepBase & Readonly<{
|
|
340
373
|
kind: 'preflight';
|
|
341
374
|
}>) | (LifecycleStepBase & Readonly<{
|
|
342
375
|
kind: 'claim';
|
|
@@ -411,7 +444,7 @@ export type ObservationExpectation = {
|
|
|
411
444
|
} | {
|
|
412
445
|
readonly kind: 'present';
|
|
413
446
|
};
|
|
414
|
-
|
|
447
|
+
type ObservationApplicability = {
|
|
415
448
|
readonly state: 'required';
|
|
416
449
|
readonly posture: string;
|
|
417
450
|
} | {
|
|
@@ -419,13 +452,13 @@ export type ObservationApplicability = {
|
|
|
419
452
|
readonly posture: string;
|
|
420
453
|
readonly reason: string;
|
|
421
454
|
};
|
|
422
|
-
|
|
455
|
+
type ObservationFreshness = {
|
|
423
456
|
readonly state: 'current';
|
|
424
457
|
} | {
|
|
425
458
|
readonly state: 'stale' | 'missing';
|
|
426
459
|
readonly reason: string;
|
|
427
460
|
};
|
|
428
|
-
|
|
461
|
+
type EnvironmentObservedValue = {
|
|
429
462
|
readonly state: 'available';
|
|
430
463
|
readonly value: JsonSnapshot;
|
|
431
464
|
readonly observedAt: string;
|
|
@@ -194,6 +194,9 @@ export const ENVIRONMENT_CODES = Object.freeze({
|
|
|
194
194
|
RECLAIM_LABEL_ESCAPE: 'RECLAIM_LABEL_ESCAPE',
|
|
195
195
|
RECLAIM_ACCOUNTING_MISMATCH: 'RECLAIM_ACCOUNTING_MISMATCH',
|
|
196
196
|
DOWN_RESOURCES_KEPT: 'DOWN_RESOURCES_KEPT',
|
|
197
|
+
STACKS_ORPHANED: 'STACKS_ORPHANED',
|
|
198
|
+
STACKS_PRUNE_UNCONFIRMED: 'STACKS_PRUNE_UNCONFIRMED',
|
|
199
|
+
STACKS_PRUNE_INCOMPLETE: 'STACKS_PRUNE_INCOMPLETE',
|
|
197
200
|
CONFIG_STALE: 'CONFIG_STALE',
|
|
198
201
|
SIBLING_MISSING: 'SIBLING_MISSING',
|
|
199
202
|
GUIDE_CAP_EXCEEDED: 'GUIDE_CAP_EXCEEDED',
|
|
@@ -212,6 +215,7 @@ export const ENVIRONMENT_CODES = Object.freeze({
|
|
|
212
215
|
GENERATED_ENVIRONMENT_UNAVAILABLE: 'GENERATED_ENVIRONMENT_UNAVAILABLE',
|
|
213
216
|
CLAIM_STATE_UNHEALTHY: 'CLAIM_STATE_UNHEALTHY',
|
|
214
217
|
MOUNT_POSTURE_UNAVAILABLE: 'MOUNT_POSTURE_UNAVAILABLE',
|
|
218
|
+
COMPOSE_PROFILE_UNDEFINED: 'COMPOSE_PROFILE_UNDEFINED',
|
|
215
219
|
STATE_UNSEEDED: 'STATE_UNSEEDED',
|
|
216
220
|
STACK_NOT_RUNNING: 'STACK_NOT_RUNNING',
|
|
217
221
|
SKILL_BUDGET_UNAVAILABLE: 'SKILL_BUDGET_UNAVAILABLE',
|
|
@@ -224,6 +228,8 @@ export const ENVIRONMENT_CODES = Object.freeze({
|
|
|
224
228
|
SERVICE_NOT_DECLARED: 'SERVICE_NOT_DECLARED',
|
|
225
229
|
GUARD_HOSTS_UNDECLARED: 'GUARD_HOSTS_UNDECLARED',
|
|
226
230
|
TESTING_GATE_UNDECLARED: 'TESTING_GATE_UNDECLARED',
|
|
231
|
+
MCP_COMMAND_UNRESOLVED: 'MCP_COMMAND_UNRESOLVED',
|
|
232
|
+
MCP_ENVIRONMENT_UNSET: 'MCP_ENVIRONMENT_UNSET',
|
|
227
233
|
EVALUATOR_UNAVAILABLE: 'EVALUATOR_UNAVAILABLE',
|
|
228
234
|
EVALUATOR_RUN_FAILED: 'EVALUATOR_RUN_FAILED',
|
|
229
235
|
EVALUATOR_FELL_BACK_TO_RUNNING_TOOL: 'EVALUATOR_FELL_BACK_TO_RUNNING_TOOL',
|
|
@@ -231,15 +237,23 @@ export const ENVIRONMENT_CODES = Object.freeze({
|
|
|
231
237
|
TEST_GATE_UNSATISFIED: 'TEST_GATE_UNSATISFIED',
|
|
232
238
|
WORK_GRADE_UNSATISFIED: 'WORK_GRADE_UNSATISFIED',
|
|
233
239
|
WORK_CHECK_FAILED: 'WORK_CHECK_FAILED',
|
|
240
|
+
BROWSER_ROUTE_FINDINGS: 'BROWSER_ROUTE_FINDINGS',
|
|
234
241
|
WORK_VERIFY_FAILED: 'WORK_VERIFY_FAILED',
|
|
235
242
|
WORK_PERSON_REQUIRED: 'WORK_PERSON_REQUIRED',
|
|
243
|
+
WORK_TEST_WEAKENED: 'WORK_TEST_WEAKENED',
|
|
236
244
|
AUTONOMY_PERSON_REQUIRED: 'AUTONOMY_PERSON_REQUIRED',
|
|
237
245
|
AUTONOMY_CI_ONLY: 'AUTONOMY_CI_ONLY',
|
|
238
246
|
POLICY_WEAKENING_UNCONFIRMED: 'POLICY_WEAKENING_UNCONFIRMED',
|
|
247
|
+
GUARD_CONFIG_UNSYNCED: 'GUARD_CONFIG_UNSYNCED',
|
|
248
|
+
GUARD_CONFIG_WIDENED: 'GUARD_CONFIG_WIDENED',
|
|
239
249
|
WORK_ORDER_CLOSED: 'WORK_ORDER_CLOSED',
|
|
240
250
|
CODEX_HOOK_UNTRUSTED: 'CODEX_HOOK_UNTRUSTED',
|
|
241
251
|
SELFTEST_CUSTOM_PROBE_UNSUPPORTED: 'SELFTEST_CUSTOM_PROBE_UNSUPPORTED',
|
|
242
252
|
DECLARATION_UNCONFIRMED: 'DECLARATION_UNCONFIRMED',
|
|
253
|
+
CONFIRM_PERSON_REQUIRED: 'CONFIRM_PERSON_REQUIRED',
|
|
254
|
+
CONFIRM_ANSWER_NOT_LISTED: 'CONFIRM_ANSWER_NOT_LISTED',
|
|
255
|
+
CONFIRM_OBSERVATION_FAILED: 'CONFIRM_OBSERVATION_FAILED',
|
|
256
|
+
CONFIRM_RECORD_UNWRITABLE: 'CONFIRM_RECORD_UNWRITABLE',
|
|
243
257
|
COCKPIT_NOT_INSTALLED: 'COCKPIT_NOT_INSTALLED',
|
|
244
258
|
COCKPIT_LAUNCHER_UNPROVEN: 'COCKPIT_LAUNCHER_UNPROVEN',
|
|
245
259
|
SIBLING_REMOTE_UNRECORDED: 'SIBLING_REMOTE_UNRECORDED',
|
|
@@ -259,6 +273,25 @@ export const ENVIRONMENT_CODES = Object.freeze({
|
|
|
259
273
|
UPGRADE_SYNC_FAILED: 'UPGRADE_SYNC_FAILED',
|
|
260
274
|
DATA_SEED_FAILED: 'DATA_SEED_FAILED',
|
|
261
275
|
DATA_SEED_UNAVAILABLE: 'DATA_SEED_UNAVAILABLE',
|
|
276
|
+
DOCS_NOT_DECLARED: 'DOCS_NOT_DECLARED',
|
|
277
|
+
DOCS_DECLARATION_UNMATCHED: 'DOCS_DECLARATION_UNMATCHED',
|
|
278
|
+
DOCS_REFERENCE_REMOVED: 'DOCS_REFERENCE_REMOVED',
|
|
279
|
+
DOCS_TARGET_REMOVED: 'DOCS_TARGET_REMOVED',
|
|
280
|
+
DECISION_NUMBER_REUSED: 'DECISION_NUMBER_REUSED',
|
|
281
|
+
DECISION_UNINDEXED: 'DECISION_UNINDEXED',
|
|
282
|
+
DECISION_INDEX_ENTRY_MISSING: 'DECISION_INDEX_ENTRY_MISSING',
|
|
283
|
+
DECISION_STATUS_MISSING: 'DECISION_STATUS_MISSING',
|
|
284
|
+
DECISION_LINK_UNRESOLVED: 'DECISION_LINK_UNRESOLVED',
|
|
285
|
+
DECISION_SUPERSEDED_STALE: 'DECISION_SUPERSEDED_STALE',
|
|
286
|
+
SIBLING_CHECKOUT_RECORD_INVALID: 'SIBLING_CHECKOUT_RECORD_INVALID',
|
|
287
|
+
SIBLING_CHECKOUT_UNKNOWN: 'SIBLING_CHECKOUT_UNKNOWN',
|
|
288
|
+
SIBLING_CHECKOUT_UNAVAILABLE: 'SIBLING_CHECKOUT_UNAVAILABLE',
|
|
289
|
+
SIBLING_CHECKOUT_FOREIGN: 'SIBLING_CHECKOUT_FOREIGN',
|
|
290
|
+
SIBLING_CHECKOUT_OVERRIDDEN: 'SIBLING_CHECKOUT_OVERRIDDEN',
|
|
291
|
+
SIBLING_ENVIRONMENT_FILE_MISSING: 'SIBLING_ENVIRONMENT_FILE_MISSING',
|
|
292
|
+
PULL_SKIPPED: 'PULL_SKIPPED',
|
|
293
|
+
STACK_INPUTS_CHANGED: 'STACK_INPUTS_CHANGED',
|
|
294
|
+
STACK_INPUTS_UNRECORDED: 'STACK_INPUTS_UNRECORDED',
|
|
262
295
|
});
|
|
263
296
|
export const EXECUTION_FAILURE_CODES = Object.freeze([
|
|
264
297
|
ENVIRONMENT_CODES.EXECUTION_SPAWN_FAILED,
|
|
@@ -20,6 +20,18 @@ export type ExecutableResolution = Readonly<{
|
|
|
20
20
|
}>;
|
|
21
21
|
export declare function resolveExecutablePath(path: string): Promise<ExecutableResolution>;
|
|
22
22
|
export declare function resolveExecutable(name: string, env: Readonly<Record<string, string | undefined>>): Promise<ExecutableResolution>;
|
|
23
|
+
/**
|
|
24
|
+
* Whether a command can be started from `cwd`, found the way a wrapper that changes into `cwd` would
|
|
25
|
+
* find it, without hashing the file: a command with a slash is relative to `cwd`, a bare name is
|
|
26
|
+
* looked up on PATH. For callers that report presence rather than run the file.
|
|
27
|
+
*/
|
|
28
|
+
export declare function locateExecutable(command: string, env: Readonly<Record<string, string | undefined>>, cwd: string): Promise<Readonly<{
|
|
29
|
+
state: 'present';
|
|
30
|
+
path: string;
|
|
31
|
+
}> | Readonly<{
|
|
32
|
+
state: 'unavailable';
|
|
33
|
+
reason: string;
|
|
34
|
+
}>>;
|
|
23
35
|
export declare function resolveGitExecutable(env: Readonly<Record<string, string | undefined>>): Promise<ExecutableResolution>;
|
|
24
36
|
export declare function resolveDiskCapacityExecutable(): Promise<ExecutableResolution>;
|
|
25
37
|
export declare function resolveDockerExecutable(env: Readonly<Record<string, string | undefined>>): Promise<ExecutableResolution>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { constants } from 'node:fs';
|
|
3
3
|
import { lstat, open, realpath } from 'node:fs/promises';
|
|
4
|
-
import { basename, delimiter, isAbsolute, join, normalize } from 'node:path';
|
|
4
|
+
import { basename, delimiter, isAbsolute, join, normalize, resolve } from 'node:path';
|
|
5
5
|
import { errorCause } from './contract.js';
|
|
6
6
|
import { fileFingerprint, recordVerifiedDigest, verifiedDigest } from '../environment-exec/verified-digest.js';
|
|
7
7
|
import { exactKeys } from '../exact-keys.js';
|
|
@@ -101,57 +101,80 @@ async function digestRegularFile(path) {
|
|
|
101
101
|
catch { /* The caller receives unavailable; no descriptor authority survives. */ }
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
// The checks that need no file contents: an exact absolute path naming an executable file, or a
|
|
105
|
+
// link that resolves to one.
|
|
106
|
+
async function checkExecutablePath(path) {
|
|
105
107
|
if (typeof path !== 'string' || !isAbsolute(path) || normalize(path) !== path || path.includes('\0')) {
|
|
106
|
-
return unavailable
|
|
108
|
+
return { state: 'unavailable', reason: 'Executable path must be an exact absolute path' };
|
|
107
109
|
}
|
|
108
110
|
let invocation;
|
|
109
111
|
try {
|
|
110
112
|
invocation = await lstat(path);
|
|
111
113
|
}
|
|
112
114
|
catch (error) {
|
|
113
|
-
return unavailable
|
|
115
|
+
return { state: 'unavailable', reason: `Executable is unavailable: ${path}: ${errorCause(error)}` };
|
|
114
116
|
}
|
|
115
117
|
if (!(invocation.isFile() || invocation.isSymbolicLink()) || invocation.isFile() && (invocation.mode & 0o111) === 0) {
|
|
116
|
-
return unavailable
|
|
118
|
+
return { state: 'unavailable', reason: `Executable is unavailable: ${path}` };
|
|
117
119
|
}
|
|
118
|
-
let canonicalPath;
|
|
119
120
|
try {
|
|
120
|
-
canonicalPath
|
|
121
|
+
return { state: 'present', path, canonicalPath: await realpath(path) };
|
|
121
122
|
}
|
|
122
123
|
catch (error) {
|
|
123
|
-
return unavailable
|
|
124
|
+
return { state: 'unavailable', reason: `Executable is unavailable: ${path}: ${errorCause(error)}` };
|
|
124
125
|
}
|
|
125
|
-
|
|
126
|
+
}
|
|
127
|
+
export async function resolveExecutablePath(path) {
|
|
128
|
+
const checked = await checkExecutablePath(path);
|
|
129
|
+
if (checked.state === 'unavailable')
|
|
130
|
+
return unavailable(checked.reason);
|
|
131
|
+
const sha256 = await digestRegularFile(checked.canonicalPath);
|
|
126
132
|
if (sha256 === undefined)
|
|
127
133
|
return unavailable(`Executable identity is unavailable: ${path}`);
|
|
128
|
-
const value = resolvedExecutable({ canonicalPath, invocationPath: path, sha256, executionMode: 'verified-path' });
|
|
134
|
+
const value = resolvedExecutable({ canonicalPath: checked.canonicalPath, invocationPath: path, sha256, executionMode: 'verified-path' });
|
|
129
135
|
return value ? { state: 'available', value } : unavailable(`Executable identity is invalid: ${path}`);
|
|
130
136
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (isAbsolute(name))
|
|
135
|
-
return resolveExecutablePath(name);
|
|
136
|
-
if (basename(name) !== name)
|
|
137
|
-
return unavailable('Executable name must be absolute or a PATH entry name');
|
|
137
|
+
const miss = (reason) => ({ state: 'unavailable', reason });
|
|
138
|
+
// Validates the search path once and tries each directory in order; `probe` decides what a hit is.
|
|
139
|
+
async function searchExecutable(name, env, probe, found) {
|
|
138
140
|
const selected = searchPath(env);
|
|
139
141
|
if (selected.kind === 'unreadable')
|
|
140
|
-
return
|
|
142
|
+
return miss('Executable search path is not readable');
|
|
141
143
|
const pathValue = selected.path;
|
|
142
144
|
if (typeof pathValue !== 'string' || pathValue.length === 0 || pathValue.includes('\0') || Buffer.byteLength(pathValue) > 65_536) {
|
|
143
|
-
return
|
|
145
|
+
return miss('Executable search path is missing or invalid');
|
|
144
146
|
}
|
|
145
147
|
const directories = pathValue.split(delimiter);
|
|
146
148
|
if (directories.length > PATH_ENTRY_LIMIT || directories.some(path => !isAbsolute(path) || normalize(path) !== path)) {
|
|
147
|
-
return
|
|
149
|
+
return miss('Executable search path must contain bounded absolute directories');
|
|
148
150
|
}
|
|
149
151
|
for (const directory of directories) {
|
|
150
|
-
const result = await
|
|
151
|
-
if (result
|
|
152
|
+
const result = await probe(join(directory, name));
|
|
153
|
+
if (found(result))
|
|
152
154
|
return result;
|
|
153
155
|
}
|
|
154
|
-
return
|
|
156
|
+
return miss(`Executable was not found in the supplied search path: ${name}`);
|
|
157
|
+
}
|
|
158
|
+
export async function resolveExecutable(name, env) {
|
|
159
|
+
if (typeof name !== 'string' || name.length === 0 || name.includes('\0'))
|
|
160
|
+
return unavailable('Executable name is invalid');
|
|
161
|
+
if (isAbsolute(name))
|
|
162
|
+
return resolveExecutablePath(name);
|
|
163
|
+
if (basename(name) !== name)
|
|
164
|
+
return unavailable('Executable name must be absolute or a PATH entry name');
|
|
165
|
+
return searchExecutable(name, env, resolveExecutablePath, result => result.state === 'available');
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Whether a command can be started from `cwd`, found the way a wrapper that changes into `cwd` would
|
|
169
|
+
* find it, without hashing the file: a command with a slash is relative to `cwd`, a bare name is
|
|
170
|
+
* looked up on PATH. For callers that report presence rather than run the file.
|
|
171
|
+
*/
|
|
172
|
+
export async function locateExecutable(command, env, cwd) {
|
|
173
|
+
if (typeof command !== 'string' || command.length === 0 || command.includes('\0'))
|
|
174
|
+
return miss('Executable name is invalid');
|
|
175
|
+
if (command.includes('/'))
|
|
176
|
+
return checkExecutablePath(resolve(cwd, command));
|
|
177
|
+
return searchExecutable(command, env, checkExecutablePath, result => result.state === 'present');
|
|
155
178
|
}
|
|
156
179
|
export async function resolveGitExecutable(env) {
|
|
157
180
|
return resolveExecutable('git', env);
|
|
@@ -37,6 +37,7 @@ export async function prepareGeneratedEnvironmentCurrentness(input, adapterOverr
|
|
|
37
37
|
sourceRevision: installedPackageMetadata.cockpitSourceRevision },
|
|
38
38
|
fixContext: { resolvedConfigPath: prepared?.configPath ?? null,
|
|
39
39
|
generatedTargets: prepared ? [...prepared.owned, ...prepared.surfaces].map(row => row.path) : [] },
|
|
40
|
+
mcpServers: prepared?.mcpServers ?? [],
|
|
40
41
|
...(authority ? { repairAuthority: authority } : {}),
|
|
41
42
|
...('status' in publication ? { repairRefusal: publication }
|
|
42
43
|
: 'refusal' in publication ? { repairRefusal: publication.refusal } : {}),
|
|
@@ -171,6 +172,7 @@ async function composeTopologyDiagnostics(configPath, sourceSha256, adapters) {
|
|
|
171
172
|
slug: sibling.slug, var: sibling.var, mount: sibling.mount, path: resolve(siblingsRoot, sibling.dir)
|
|
172
173
|
})),
|
|
173
174
|
hostPaths: config.environment.generated_host_paths.map(declared => ({ declared, path: resolve(root, declared) })),
|
|
175
|
+
profiles: config.environment.profiles,
|
|
174
176
|
});
|
|
175
177
|
}
|
|
176
178
|
export async function checkGeneratedEnvironment(input, adapterOverrides = {}) {
|
|
@@ -9,7 +9,7 @@ import type { SurfaceFeature, SurfaceSourceFile, SurfaceTarget } from '../../sur
|
|
|
9
9
|
import type { SurfaceGenerationWarning } from '../../surfaces/generator-adapter.js';
|
|
10
10
|
import type { ActiveSurfaceOverride, SurfaceUpgradeFinding } from '../../surfaces/merge.js';
|
|
11
11
|
import { CORE_EVALUATOR_ASSET as EVALUATOR_ASSET, CORE_SKILL_ASSETS as SKILL_ASSETS } from '../../surfaces/core-declarations.js';
|
|
12
|
-
import type {
|
|
12
|
+
import type { McpServer, McpStdioDeclaration } from '../../surfaces/mcp.js';
|
|
13
13
|
import { type GeneratedPathFact, type GeneratedProducer, type SurfaceDesiredEntry } from '../../surfaces/ownership.js';
|
|
14
14
|
import type { DirectoryGrant, GuideObservation, GuideObservationRequest, StandingCost } from '../../surfaces/projection.js';
|
|
15
15
|
import type { NativeFragmentSet, SurfaceInputEvidence } from '../../surfaces/render.js';
|
|
@@ -213,6 +213,12 @@ export interface PreparedEnvironment {
|
|
|
213
213
|
sources: readonly string[];
|
|
214
214
|
sha256: string;
|
|
215
215
|
}>[];
|
|
216
|
+
readonly mcpTransportGaps: readonly Readonly<{
|
|
217
|
+
name: string;
|
|
218
|
+
targets: readonly string[];
|
|
219
|
+
reason: string;
|
|
220
|
+
}>[];
|
|
221
|
+
readonly mcpServers: readonly McpServer[];
|
|
216
222
|
readonly registry: DerivedRegistry;
|
|
217
223
|
readonly siblingRoots: AuthenticatedSiblingRoots;
|
|
218
224
|
readonly workEnvironment: ConsumedWorkEnvironmentConfig;
|
|
@@ -317,7 +323,7 @@ export interface ConsumedWorkApplicationAuthorities {
|
|
|
317
323
|
readonly browser?: Readonly<{
|
|
318
324
|
readonly runtime: BrowserRuntimeProjection;
|
|
319
325
|
readonly namespace: StateNamespace;
|
|
320
|
-
readonly mcp:
|
|
326
|
+
readonly mcp: McpStdioDeclaration;
|
|
321
327
|
readonly skillTemplate: Readonly<{
|
|
322
328
|
readonly path: string;
|
|
323
329
|
readonly bytes: string;
|
|
@@ -481,4 +487,6 @@ export interface GeneratedEnvironmentCurrentness {
|
|
|
481
487
|
readonly fixContext: FixContext;
|
|
482
488
|
readonly repairAuthority?: GeneratedEnvironmentRepairAuthority;
|
|
483
489
|
readonly repairRefusal?: RefusalOutcome;
|
|
490
|
+
/** Every declared MCP server as rendered; empty when the environment could not be prepared. */
|
|
491
|
+
readonly mcpServers: readonly McpServer[];
|
|
484
492
|
}
|