feat: add YAML-first source workspace host deps
This commit is contained in:
@@ -354,6 +354,7 @@ export interface HwlabRuntimeCodeAgentRuntimeSpec {
|
||||
export interface HwlabRuntimeSourceWorkspaceSpec {
|
||||
readonly requiredCommands: readonly string[];
|
||||
readonly requiredFiles: readonly string[];
|
||||
readonly hostDependencies?: HwlabRuntimeSourceWorkspaceHostDependenciesSpec;
|
||||
readonly install: {
|
||||
readonly dependencyCommand: string;
|
||||
readonly browserCommand: string;
|
||||
@@ -361,6 +362,15 @@ export interface HwlabRuntimeSourceWorkspaceSpec {
|
||||
};
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeSourceWorkspaceHostDependenciesSpec {
|
||||
readonly checkCommands: readonly string[];
|
||||
readonly stateDir: string;
|
||||
readonly install: {
|
||||
readonly command: string;
|
||||
readonly timeoutSeconds: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeLaneSpec {
|
||||
readonly lane: HwlabRuntimeLane;
|
||||
readonly nodeId: string;
|
||||
@@ -852,6 +862,14 @@ function relativeWorkspacePathField(value: string, path: string): string {
|
||||
return value;
|
||||
}
|
||||
|
||||
function absoluteHostPathField(value: string, path: string): string {
|
||||
const parts = value.slice(1).split("/");
|
||||
if (!value.startsWith("/") || value.includes("\0") || parts.some((part) => part === ".." || part === "")) {
|
||||
throw new Error(`${path} must be an absolute host path without empty components or ..`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function sourceWorkspaceConfig(value: unknown, path: string): HwlabRuntimeSourceWorkspaceSpec | undefined {
|
||||
if (value === undefined) return undefined;
|
||||
const raw = asRecord(value, path);
|
||||
@@ -861,6 +879,7 @@ function sourceWorkspaceConfig(value: unknown, path: string): HwlabRuntimeSource
|
||||
.map((item, index) => commandNameField(item, `${path}.requiredCommands[${index}]`)),
|
||||
requiredFiles: stringArrayField(raw, "requiredFiles", path)
|
||||
.map((item, index) => relativeWorkspacePathField(item, `${path}.requiredFiles[${index}]`)),
|
||||
hostDependencies: sourceWorkspaceHostDependenciesConfig(raw.hostDependencies, `${path}.hostDependencies`),
|
||||
install: {
|
||||
dependencyCommand: stringField(install, "dependencyCommand", `${path}.install`),
|
||||
browserCommand: stringField(install, "browserCommand", `${path}.install`),
|
||||
@@ -869,6 +888,21 @@ function sourceWorkspaceConfig(value: unknown, path: string): HwlabRuntimeSource
|
||||
};
|
||||
}
|
||||
|
||||
function sourceWorkspaceHostDependenciesConfig(value: unknown, path: string): HwlabRuntimeSourceWorkspaceHostDependenciesSpec | undefined {
|
||||
if (value === undefined) return undefined;
|
||||
const raw = asRecord(value, path);
|
||||
const install = asRecord(raw.install, `${path}.install`);
|
||||
return {
|
||||
checkCommands: stringArrayField(raw, "checkCommands", path)
|
||||
.map((item, index) => commandNameField(item, `${path}.checkCommands[${index}]`)),
|
||||
stateDir: absoluteHostPathField(stringField(raw, "stateDir", path), `${path}.stateDir`),
|
||||
install: {
|
||||
command: stringField(install, "command", `${path}.install`),
|
||||
timeoutSeconds: numberField(install, "timeoutSeconds", `${path}.install`),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function externalPostgresComponentConfig(value: unknown, path: string): HwlabRuntimeExternalPostgresComponentSpec {
|
||||
const raw = asRecord(value, path);
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user