style(cso): format lib/cso TypeScript with pinned Prettier

Mechanical reformat only. Minified transpile output is byte-identical for
21 of 22 files; witness.ts differs only in three regex flag orders
(/mi -> /im), which JavaScript canonicalizes. Source-text assertions over
lib/cso now compare whitespace-insensitively with the same tokens.
This commit is contained in:
garrytan committed 2026-09-29 14:22:48 +00:00
1 parent dcaea52800
commit d93d61f7ba
27 files changed
+17942 -4164

No files matched your search

+360 -122
View File
@@ -8,175 +8,413 @@ import { validateScannerCatalog, type ScannerCatalog } from './scanner-catalog';
import { secureDirectory } from './state';
export interface QualifiedCatalogImage {
kind:'runtime'|'scanner';
id:string;
image:string;
platform:RuntimePlatform;
kind: 'runtime' | 'scanner';
id: string;
image: string;
platform: RuntimePlatform;
}
export interface CatalogImageSession {
readonly docker:{endpoint:string;version:string;security:string[]};
present(entry:QualifiedCatalogImage,deadline?:number):Promise<boolean>;
pull(entry:QualifiedCatalogImage,deadline?:number):Promise<void>;
close():void;
readonly docker: { endpoint: string; version: string; security: string[] };
present(entry: QualifiedCatalogImage, deadline?: number): Promise<boolean>;
pull(entry: QualifiedCatalogImage, deadline?: number): Promise<void>;
close(): void;
}
/** Doctor performs concurrent, read-only checks inside its 30-second contract. */
export const CATALOG_IMAGE_INSPECTION_BUDGET_MS=30_000;
export const DEFAULT_CATALOG_IMAGE_BUDGET_MS=30_000;
export const MIN_CATALOG_IMAGE_BUDGET_SECONDS=5;
export const MAX_CATALOG_IMAGE_BUDGET_SECONDS=300;
export const MAX_CATALOG_IMAGE_PROVISIONING_BUDGET_MS=60*60_000;
const CATALOG_IMAGE_ADMISSION_BUDGET_MS=30_000;
export interface CatalogImageProvisioningPolicy {perImageMs:number;aggregateMs:number;}
export const CATALOG_IMAGE_INSPECTION_BUDGET_MS = 30_000;
export const DEFAULT_CATALOG_IMAGE_BUDGET_MS = 30_000;
export const MIN_CATALOG_IMAGE_BUDGET_SECONDS = 5;
export const MAX_CATALOG_IMAGE_BUDGET_SECONDS = 300;
export const MAX_CATALOG_IMAGE_PROVISIONING_BUDGET_MS = 60 * 60_000;
const CATALOG_IMAGE_ADMISSION_BUDGET_MS = 30_000;
export interface CatalogImageProvisioningPolicy {
perImageMs: number;
aggregateMs: number;
}
/**
* Give every declared native-platform image a bounded opportunity to download.
* The one-hour ceiling admits the current eleven-image catalog even at the
* maximum configurable five-minute allowance.
*/
export function catalogImageProvisioningPolicy(imageCount:number,requestedSeconds?:string):CatalogImageProvisioningPolicy{
if(!Number.isSafeInteger(imageCount)||imageCount<0)throw new CsoError('INVALID_ARGUMENT','Catalog image count is invalid');
let seconds=DEFAULT_CATALOG_IMAGE_BUDGET_MS/1000;
if(requestedSeconds!==undefined){
if(!/^[0-9]+$/.test(requestedSeconds))throw new CsoError('INVALID_ARGUMENT','--per-image-seconds requires a whole number');
seconds=Number(requestedSeconds);
if(seconds<MIN_CATALOG_IMAGE_BUDGET_SECONDS||seconds>MAX_CATALOG_IMAGE_BUDGET_SECONDS)throw new CsoError('INVALID_ARGUMENT',`--per-image-seconds must be ${MIN_CATALOG_IMAGE_BUDGET_SECONDS}..${MAX_CATALOG_IMAGE_BUDGET_SECONDS}`);
export function catalogImageProvisioningPolicy(
imageCount: number,
requestedSeconds?: string,
): CatalogImageProvisioningPolicy {
if (!Number.isSafeInteger(imageCount) || imageCount < 0)
throw new CsoError('INVALID_ARGUMENT', 'Catalog image count is invalid');
let seconds = DEFAULT_CATALOG_IMAGE_BUDGET_MS / 1000;
if (requestedSeconds !== undefined) {
if (!/^[0-9]+$/.test(requestedSeconds))
throw new CsoError('INVALID_ARGUMENT', '--per-image-seconds requires a whole number');
seconds = Number(requestedSeconds);
if (seconds < MIN_CATALOG_IMAGE_BUDGET_SECONDS || seconds > MAX_CATALOG_IMAGE_BUDGET_SECONDS)
throw new CsoError(
'INVALID_ARGUMENT',
`--per-image-seconds must be ${MIN_CATALOG_IMAGE_BUDGET_SECONDS}..${MAX_CATALOG_IMAGE_BUDGET_SECONDS}`,
);
}
const perImageMs=seconds*1000,aggregateMs=CATALOG_IMAGE_ADMISSION_BUDGET_MS+imageCount*perImageMs;
if(!Number.isSafeInteger(aggregateMs)||aggregateMs>MAX_CATALOG_IMAGE_PROVISIONING_BUDGET_MS)throw new CsoError('INCOMPATIBLE_INPUT','Qualified image catalog exceeds the bounded setup preload capacity');
return{perImageMs,aggregateMs};
const perImageMs = seconds * 1000,
aggregateMs = CATALOG_IMAGE_ADMISSION_BUDGET_MS + imageCount * perImageMs;
if (!Number.isSafeInteger(aggregateMs) || aggregateMs > MAX_CATALOG_IMAGE_PROVISIONING_BUDGET_MS)
throw new CsoError(
'INCOMPATIBLE_INPUT',
'Qualified image catalog exceeds the bounded setup preload capacity',
);
return { perImageMs, aggregateMs };
}
export type CatalogImageSessionFactory=(deadline:number)=>Promise<CatalogImageSession>;
export type CatalogImageSessionFactory = (deadline: number) => Promise<CatalogImageSession>;
export interface CatalogImageAvailability extends QualifiedCatalogImage {
status:'available'|'unavailable';
reason?:string;
status: 'available' | 'unavailable';
reason?: string;
}
export interface CatalogImageInspection {
docker:{status:'ready'|'missing';detail:unknown};
images:CatalogImageAvailability[];
docker: { status: 'ready' | 'missing'; detail: unknown };
images: CatalogImageAvailability[];
}
export interface CatalogImageProvisionResult {
schemaVersion:1;
status:'complete'|'partial'|'not_available';
downloads:true;
platform:RuntimePlatform;
requested:number;
inspected:number;
alreadyPresent:number;
downloaded:number;
deadlineReached:boolean;
unavailable:CatalogImageAvailability[];
summary:string;
schemaVersion: 1;
status: 'complete' | 'partial' | 'not_available';
downloads: true;
platform: RuntimePlatform;
requested: number;
inspected: number;
alreadyPresent: number;
downloaded: number;
deadlineReached: boolean;
unavailable: CatalogImageAvailability[];
summary: string;
}
export function qualifiedCatalogImages(runtimeCatalog:RuntimeCatalog,scannerCatalog:ScannerCatalog,platform:RuntimePlatform):QualifiedCatalogImage[]{
validateRuntimeCatalog(runtimeCatalog);validateScannerCatalog(scannerCatalog);
const entries:QualifiedCatalogImage[]=[
...runtimeCatalog.runtimes.filter(item=>item.platform===platform).map(item=>({kind:'runtime' as const,id:item.id,image:item.image,platform:item.platform})),
...scannerCatalog.scanners.filter(item=>item.platform===platform).map(item=>({kind:'scanner' as const,id:item.id,image:item.image,platform:item.platform})),
export function qualifiedCatalogImages(
runtimeCatalog: RuntimeCatalog,
scannerCatalog: ScannerCatalog,
platform: RuntimePlatform,
): QualifiedCatalogImage[] {
validateRuntimeCatalog(runtimeCatalog);
validateScannerCatalog(scannerCatalog);
const entries: QualifiedCatalogImage[] = [
...runtimeCatalog.runtimes
.filter((item) => item.platform === platform)
.map((item) => ({ kind: 'runtime' as const, id: item.id, image: item.image, platform: item.platform })),
...scannerCatalog.scanners
.filter((item) => item.platform === platform)
.map((item) => ({ kind: 'scanner' as const, id: item.id, image: item.image, platform: item.platform })),
];
const identities=new Set<string>();
for(const entry of entries){
const identity=`${entry.kind}:${entry.id}`;
if(identities.has(identity))throw new CsoError('INCOMPATIBLE_INPUT','Qualified image catalogs contain a duplicate identity');
const identities = new Set<string>();
for (const entry of entries) {
const identity = `${entry.kind}:${entry.id}`;
if (identities.has(identity))
throw new CsoError('INCOMPATIBLE_INPUT', 'Qualified image catalogs contain a duplicate identity');
identities.add(identity);
}
return entries.sort((left,right)=>`${left.kind}:${left.id}`.localeCompare(`${right.kind}:${right.id}`));
return entries.sort((left, right) => `${left.kind}:${left.id}`.localeCompare(`${right.kind}:${right.id}`));
}
function controlledReason(error:unknown,fallback:string):string{
return error instanceof CsoError?error.message:fallback;
function controlledReason(error: unknown, fallback: string): string {
return error instanceof CsoError ? error.message : fallback;
}
export async function inspectCatalogImages(entries:QualifiedCatalogImage[],open:CatalogImageSessionFactory,deadline=Date.now()+CATALOG_IMAGE_INSPECTION_BUDGET_MS):Promise<CatalogImageInspection>{
let session:CatalogImageSession;
try{session=await open(deadline);}catch(error){
const detail=controlledReason(error,'Local Docker is unavailable for exact catalog image inspection');
return{docker:{status:'missing',detail},images:entries.map(entry=>({...entry,status:'unavailable',reason:detail}))};
export async function inspectCatalogImages(
entries: QualifiedCatalogImage[],
open: CatalogImageSessionFactory,
deadline = Date.now() + CATALOG_IMAGE_INSPECTION_BUDGET_MS,
): Promise<CatalogImageInspection> {
let session: CatalogImageSession;
try {
session = await open(deadline);
} catch (error) {
const detail = controlledReason(error, 'Local Docker is unavailable for exact catalog image inspection');
return {
docker: { status: 'missing', detail },
images: entries.map((entry) => ({ ...entry, status: 'unavailable', reason: detail })),
};
}
try{
try {
// Read-only daemon lookups run together so doctor remains within its
// 30-second contract even when a local Docker client is slow to fail.
const images=await Promise.all(entries.map(async(entry):Promise<CatalogImageAvailability>=>{
try{const present=await session.present(entry);if(Date.now()>=deadline)throw new CsoError('DEADLINE','Exact image inspection reached the aggregate image-provisioning deadline');return{...entry,status:present?'available':'unavailable',...(present?{}:{reason:'Exact qualified image is not present in the local Docker daemon'})};}
catch(error){return{...entry,status:'unavailable',reason:controlledReason(error,'Exact qualified image could not be inspected safely')};}
}));
return{docker:{status:'ready',detail:session.docker},images};
}finally{session.close();}
const images = await Promise.all(
entries.map(async (entry): Promise<CatalogImageAvailability> => {
try {
const present = await session.present(entry);
if (Date.now() >= deadline)
throw new CsoError(
'DEADLINE',
'Exact image inspection reached the aggregate image-provisioning deadline',
);
return {
...entry,
status: present ? 'available' : 'unavailable',
...(present ? {} : { reason: 'Exact qualified image is not present in the local Docker daemon' }),
};
} catch (error) {
return {
...entry,
status: 'unavailable',
reason: controlledReason(error, 'Exact qualified image could not be inspected safely'),
};
}
}),
);
return { docker: { status: 'ready', detail: session.docker }, images };
} finally {
session.close();
}
}
export async function provisionCatalogImages(entries:QualifiedCatalogImage[],platform:RuntimePlatform,open:CatalogImageSessionFactory,deadline=Date.now()+catalogImageProvisioningPolicy(entries.length).aggregateMs,perImageBudgetMs=DEFAULT_CATALOG_IMAGE_BUDGET_MS):Promise<CatalogImageProvisionResult>{
if(!entries.length)return{schemaVersion:1,status:'complete',downloads:true,platform,requested:0,inspected:0,alreadyPresent:0,downloaded:0,deadlineReached:false,unavailable:[],summary:'No qualified CSO images are published for this platform; static audits remain available.'};
if(!Number.isSafeInteger(perImageBudgetMs)||perImageBudgetMs<1||perImageBudgetMs>MAX_CATALOG_IMAGE_BUDGET_SECONDS*1000)throw new CsoError('INVALID_ARGUMENT','Catalog per-image budget is invalid');
const deadlineReason='The bounded aggregate CSO image preload deadline was reached';
if(Date.now()>=deadline){const unavailable=entries.map(entry=>({...entry,status:'unavailable' as const,reason:deadlineReason}));return{schemaVersion:1,status:'partial',downloads:true,platform,requested:entries.length,inspected:0,alreadyPresent:0,downloaded:0,deadlineReached:true,unavailable,summary:`Qualified CSO image preload partial: 0/${entries.length} available; ${deadlineReason.toLowerCase()}. Rerun setup to continue.`};}
let session:CatalogImageSession;
try{session=await open(deadline);}catch(error){
const reason=controlledReason(error,'Local Docker is unavailable for qualified image provisioning'),unavailable=entries.map(entry=>({...entry,status:'unavailable' as const,reason}));
const deadlineReached=error instanceof CsoError&&error.code==='DEADLINE';
return{schemaVersion:1,status:deadlineReached?'partial':'not_available',downloads:true,platform,requested:entries.length,inspected:0,alreadyPresent:0,downloaded:0,deadlineReached,unavailable,summary:deadlineReached?`Qualified CSO image preload partial: 0/${entries.length} available; ${reason}. Rerun setup to continue.`:`Qualified CSO images were not preloaded: ${reason}. Rerun setup after the prerequisite is available.`};
export async function provisionCatalogImages(
entries: QualifiedCatalogImage[],
platform: RuntimePlatform,
open: CatalogImageSessionFactory,
deadline = Date.now() + catalogImageProvisioningPolicy(entries.length).aggregateMs,
perImageBudgetMs = DEFAULT_CATALOG_IMAGE_BUDGET_MS,
): Promise<CatalogImageProvisionResult> {
if (!entries.length)
return {
schemaVersion: 1,
status: 'complete',
downloads: true,
platform,
requested: 0,
inspected: 0,
alreadyPresent: 0,
downloaded: 0,
deadlineReached: false,
unavailable: [],
summary: 'No qualified CSO images are published for this platform; static audits remain available.',
};
if (
!Number.isSafeInteger(perImageBudgetMs) ||
perImageBudgetMs < 1 ||
perImageBudgetMs > MAX_CATALOG_IMAGE_BUDGET_SECONDS * 1000
)
throw new CsoError('INVALID_ARGUMENT', 'Catalog per-image budget is invalid');
const deadlineReason = 'The bounded aggregate CSO image preload deadline was reached';
if (Date.now() >= deadline) {
const unavailable = entries.map((entry) => ({
...entry,
status: 'unavailable' as const,
reason: deadlineReason,
}));
return {
schemaVersion: 1,
status: 'partial',
downloads: true,
platform,
requested: entries.length,
inspected: 0,
alreadyPresent: 0,
downloaded: 0,
deadlineReached: true,
unavailable,
summary: `Qualified CSO image preload partial: 0/${entries.length} available; ${deadlineReason.toLowerCase()}. Rerun setup to continue.`,
};
}
let inspected=0,alreadyPresent=0,downloaded=0,pullBlocked='',deadlineReached=false,perImageTimeouts=0;const unavailable:CatalogImageAvailability[]=[];
try{
for(let index=0;index<entries.length;index++){
const entry=entries[index];
if(Date.now()>=deadline){deadlineReached=true;for(const remaining of entries.slice(index))unavailable.push({...remaining,status:'unavailable',reason:deadlineReason});break;}
const imageDeadline=Math.min(deadline,Date.now()+perImageBudgetMs),perImageReason=`The ${Math.ceil(perImageBudgetMs/1000)}-second per-image CSO preload deadline was reached`;
let present=false;
try{
present=await session.present(entry,imageDeadline);if(Date.now()>=imageDeadline)throw new CsoError('DEADLINE',imageDeadline===deadline?'Exact image inspection reached the aggregate image-provisioning deadline':perImageReason);inspected++;
if(present){alreadyPresent++;continue;}
}catch(error){
if(error instanceof CsoError&&error.code==='DEADLINE'){
if(Date.now()>=deadline){deadlineReached=true;unavailable.push({...entry,status:'unavailable',reason:error.message});for(const remaining of entries.slice(index+1))unavailable.push({...remaining,status:'unavailable',reason:deadlineReason});break;}
perImageTimeouts++;unavailable.push({...entry,status:'unavailable',reason:perImageReason});continue;
let session: CatalogImageSession;
try {
session = await open(deadline);
} catch (error) {
const reason = controlledReason(error, 'Local Docker is unavailable for qualified image provisioning'),
unavailable = entries.map((entry) => ({ ...entry, status: 'unavailable' as const, reason }));
const deadlineReached = error instanceof CsoError && error.code === 'DEADLINE';
return {
schemaVersion: 1,
status: deadlineReached ? 'partial' : 'not_available',
downloads: true,
platform,
requested: entries.length,
inspected: 0,
alreadyPresent: 0,
downloaded: 0,
deadlineReached,
unavailable,
summary: deadlineReached
? `Qualified CSO image preload partial: 0/${entries.length} available; ${reason}. Rerun setup to continue.`
: `Qualified CSO images were not preloaded: ${reason}. Rerun setup after the prerequisite is available.`,
};
}
let inspected = 0,
alreadyPresent = 0,
downloaded = 0,
pullBlocked = '',
deadlineReached = false,
perImageTimeouts = 0;
const unavailable: CatalogImageAvailability[] = [];
try {
for (let index = 0; index < entries.length; index++) {
const entry = entries[index];
if (Date.now() >= deadline) {
deadlineReached = true;
for (const remaining of entries.slice(index))
unavailable.push({ ...remaining, status: 'unavailable', reason: deadlineReason });
break;
}
const imageDeadline = Math.min(deadline, Date.now() + perImageBudgetMs),
perImageReason = `The ${Math.ceil(perImageBudgetMs / 1000)}-second per-image CSO preload deadline was reached`;
let present = false;
try {
present = await session.present(entry, imageDeadline);
if (Date.now() >= imageDeadline)
throw new CsoError(
'DEADLINE',
imageDeadline === deadline
? 'Exact image inspection reached the aggregate image-provisioning deadline'
: perImageReason,
);
inspected++;
if (present) {
alreadyPresent++;
continue;
}
unavailable.push({...entry,status:'unavailable',reason:controlledReason(error,'Exact qualified image could not be inspected safely')});continue;
} catch (error) {
if (error instanceof CsoError && error.code === 'DEADLINE') {
if (Date.now() >= deadline) {
deadlineReached = true;
unavailable.push({ ...entry, status: 'unavailable', reason: error.message });
for (const remaining of entries.slice(index + 1))
unavailable.push({ ...remaining, status: 'unavailable', reason: deadlineReason });
break;
}
perImageTimeouts++;
unavailable.push({ ...entry, status: 'unavailable', reason: perImageReason });
continue;
}
unavailable.push({
...entry,
status: 'unavailable',
reason: controlledReason(error, 'Exact qualified image could not be inspected safely'),
});
continue;
}
// A registry failure blocks further network attempts, but read-only local
// inspection continues so the setup summary never calls a cached digest
// unavailable merely because it sorts after the failed pull.
if(pullBlocked){unavailable.push({...entry,status:'unavailable',reason:`Network provisioning stopped after an anonymous registry prerequisite failed: ${pullBlocked}`});continue;}
if(Date.now()>=deadline){deadlineReached=true;unavailable.push({...entry,status:'unavailable',reason:deadlineReason});for(const remaining of entries.slice(index+1))unavailable.push({...remaining,status:'unavailable',reason:deadlineReason});break;}
try{await session.pull(entry,imageDeadline);if(Date.now()>=imageDeadline)throw new CsoError('DEADLINE',imageDeadline===deadline?'Qualified image pull reached the aggregate preload deadline':perImageReason);downloaded++;}
catch(error){
if(error instanceof CsoError&&error.code==='DEADLINE'){
if(Date.now()>=deadline){deadlineReached=true;unavailable.push({...entry,status:'unavailable',reason:error.message});for(const remaining of entries.slice(index+1))unavailable.push({...remaining,status:'unavailable',reason:deadlineReason});break;}
perImageTimeouts++;unavailable.push({...entry,status:'unavailable',reason:perImageReason});continue;
if (pullBlocked) {
unavailable.push({
...entry,
status: 'unavailable',
reason: `Network provisioning stopped after an anonymous registry prerequisite failed: ${pullBlocked}`,
});
continue;
}
if (Date.now() >= deadline) {
deadlineReached = true;
unavailable.push({ ...entry, status: 'unavailable', reason: deadlineReason });
for (const remaining of entries.slice(index + 1))
unavailable.push({ ...remaining, status: 'unavailable', reason: deadlineReason });
break;
}
try {
await session.pull(entry, imageDeadline);
if (Date.now() >= imageDeadline)
throw new CsoError(
'DEADLINE',
imageDeadline === deadline
? 'Qualified image pull reached the aggregate preload deadline'
: perImageReason,
);
downloaded++;
} catch (error) {
if (error instanceof CsoError && error.code === 'DEADLINE') {
if (Date.now() >= deadline) {
deadlineReached = true;
unavailable.push({ ...entry, status: 'unavailable', reason: error.message });
for (const remaining of entries.slice(index + 1))
unavailable.push({ ...remaining, status: 'unavailable', reason: deadlineReason });
break;
}
perImageTimeouts++;
unavailable.push({ ...entry, status: 'unavailable', reason: perImageReason });
continue;
}
pullBlocked=controlledReason(error,'Qualified image provisioning failed');unavailable.push({...entry,status:'unavailable',reason:pullBlocked});
pullBlocked = controlledReason(error, 'Qualified image provisioning failed');
unavailable.push({ ...entry, status: 'unavailable', reason: pullBlocked });
}
}
}finally{session.close();}
const status=deadlineReached?'partial':unavailable.length?(alreadyPresent||downloaded?'partial':'not_available'):'complete';
const summary=deadlineReached
?`Qualified CSO image preload partial: ${alreadyPresent+downloaded}/${entries.length} available; inspected ${inspected}/${entries.length}; the bounded aggregate deadline was reached. Rerun setup to continue.`
:perImageTimeouts
?`Qualified CSO image preload ${status}: ${alreadyPresent+downloaded}/${entries.length} available; inspected ${inspected}/${entries.length}; ${perImageTimeouts} exceeded the ${Math.ceil(perImageBudgetMs/1000)}-second per-image deadline. Increase GSTACK_CSO_IMAGE_PULL_TIMEOUT_SECONDS within 5..300 or rerun setup to continue.`
:unavailable.length
?`Qualified CSO image preload ${status}: ${alreadyPresent+downloaded}/${entries.length} available; inspected ${inspected}/${entries.length}; ${unavailable.length} require local Docker and anonymous public registry access. Rerun setup after the prerequisite is available.`
:`Qualified CSO images ready: ${entries.length} available (${downloaded} downloaded, ${alreadyPresent} already local).`;
return{schemaVersion:1,status,downloads:true,platform,requested:entries.length,inspected,alreadyPresent,downloaded,deadlineReached,unavailable,summary};
} finally {
session.close();
}
const status = deadlineReached
? 'partial'
: unavailable.length
? alreadyPresent || downloaded
? 'partial'
: 'not_available'
: 'complete';
const summary = deadlineReached
? `Qualified CSO image preload partial: ${alreadyPresent + downloaded}/${entries.length} available; inspected ${inspected}/${entries.length}; the bounded aggregate deadline was reached. Rerun setup to continue.`
: perImageTimeouts
? `Qualified CSO image preload ${status}: ${alreadyPresent + downloaded}/${entries.length} available; inspected ${inspected}/${entries.length}; ${perImageTimeouts} exceeded the ${Math.ceil(perImageBudgetMs / 1000)}-second per-image deadline. Increase GSTACK_CSO_IMAGE_PULL_TIMEOUT_SECONDS within 5..300 or rerun setup to continue.`
: unavailable.length
? `Qualified CSO image preload ${status}: ${alreadyPresent + downloaded}/${entries.length} available; inspected ${inspected}/${entries.length}; ${unavailable.length} require local Docker and anonymous public registry access. Rerun setup after the prerequisite is available.`
: `Qualified CSO images ready: ${entries.length} available (${downloaded} downloaded, ${alreadyPresent} already local).`;
return {
schemaVersion: 1,
status,
downloads: true,
platform,
requested: entries.length,
inspected,
alreadyPresent,
downloaded,
deadlineReached,
unavailable,
summary,
};
}
export async function openLocalCatalogImageSession(env:Record<string,string|undefined>=process.env,deadline=Date.now()+CATALOG_IMAGE_INSPECTION_BUDGET_MS):Promise<CatalogImageSession>{
let home='';
try{
home=secureDirectory(fs.mkdtempSync(join(fs.realpathSync(os.tmpdir()),'gstack-cso-images-')));
export async function openLocalCatalogImageSession(
env: Record<string, string | undefined> = process.env,
deadline = Date.now() + CATALOG_IMAGE_INSPECTION_BUDGET_MS,
): Promise<CatalogImageSession> {
let home = '';
try {
home = secureDirectory(fs.mkdtempSync(join(fs.realpathSync(os.tmpdir()), 'gstack-cso-images-')));
// Endpoint discovery and the daemon probe must not borrow the download
// allowance. A slow or hostile local Docker endpoint gets the same bounded
// admission window in doctor and setup; successful pulls keep the caller's
// larger aggregate deadline below.
const admissionDeadline=Math.min(deadline,Date.now()+CATALOG_IMAGE_ADMISSION_BUDGET_MS);
const endpoint=await dockerEndpoint(home,env,admissionDeadline),config=secureDirectory(join(home,'docker-config'));
const admissionDeadline = Math.min(deadline, Date.now() + CATALOG_IMAGE_ADMISSION_BUDGET_MS);
const endpoint = await dockerEndpoint(home, env, admissionDeadline),
config = secureDirectory(join(home, 'docker-config'));
// dockerEnvironment pins both HOME and DOCKER_CONFIG here. An explicit
// empty auth map prevents inherited credential stores/helpers from being
// consulted during installation-time public pulls.
fs.writeFileSync(join(config,'config.json'),'{"auths":{}}\n',{encoding:'utf8',mode:0o600,flag:'wx'});
const probe=await dockerProbe(endpoint,home,admissionDeadline),docker={endpoint:endpoint.uri,...probe};
let closed=false;
return{
fs.writeFileSync(join(config, 'config.json'), '{"auths":{}}\n', {
encoding: 'utf8',
mode: 0o600,
flag: 'wx',
});
const probe = await dockerProbe(endpoint, home, admissionDeadline),
docker = { endpoint: endpoint.uri, ...probe };
let closed = false;
return {
docker,
present:(entry,operationDeadline=deadline)=>{if(closed)throw new CsoError('ISOLATION_FAILED','Catalog image session is closed');return dockerExactImagePresent(endpoint,home,entry.image,entry.platform,Math.min(deadline,operationDeadline));},
pull:(entry,operationDeadline=deadline)=>{if(closed)throw new CsoError('ISOLATION_FAILED','Catalog image session is closed');return dockerPullExactCatalogImage(endpoint,home,entry.image,entry.platform,Math.min(deadline,operationDeadline));},
close:()=>{if(closed)return;closed=true;fs.rmSync(home,{recursive:true,force:true});},
present: (entry, operationDeadline = deadline) => {
if (closed) throw new CsoError('ISOLATION_FAILED', 'Catalog image session is closed');
return dockerExactImagePresent(
endpoint,
home,
entry.image,
entry.platform,
Math.min(deadline, operationDeadline),
);
},
pull: (entry, operationDeadline = deadline) => {
if (closed) throw new CsoError('ISOLATION_FAILED', 'Catalog image session is closed');
return dockerPullExactCatalogImage(
endpoint,
home,
entry.image,
entry.platform,
Math.min(deadline, operationDeadline),
);
},
close: () => {
if (closed) return;
closed = true;
fs.rmSync(home, { recursive: true, force: true });
},
};
}catch(error){if(home)fs.rmSync(home,{recursive:true,force:true});throw error;}
} catch (error) {
if (home) fs.rmSync(home, { recursive: true, force: true });
throw error;
}
}