MCPcopy Create free account
hub / github.com/ForestHubAI/edge-agents / deployCommand

Function deployCommand

ts/workflow-cli/cli/deploy/index.ts:258–410  ·  view source on GitHub ↗
(workflowPath: string | undefined, args: string[])

Source from the content-addressed store, hash-verified

256// defaults. Used on the non-interactive path (no prompts).
257export function configFromPartial(p: Partial<DeployConfig>, outputDirDefault: string): DeployConfig {
258 return {
259 llmKeys: p.llmKeys ?? {},
260 outputDir: p.outputDir ?? outputDirDefault,
261 force: p.force ?? false,
262 logLevel: p.logLevel ?? "info",
263 hardware: p.hardware ?? {},
264 mqtt: p.mqtt ?? {},
265 llmModels: p.llmModels ?? {},
266 mlModels: p.mlModels ?? {},
267 cameras: p.cameras ?? {},
268 webSearch: p.webSearch,
269 };
270}
271
272// Read and validate the --component folders once, up front, so a bad component.json
273// fails before any prompting (in either mode). Identical folders are deduped
274// (passing one twice is accidental); two distinct folders declaring the same name
275// collide — surfaced here rather than late at spec assembly. Each component is
276// paired with its folder for the later <name>.env step.
277async function loadFlagComponents(dirs: string[]): Promise<LoadedComponent[]> {
278 const uniqueDirs = [...new Set(dirs.map((d) => path.resolve(process.cwd(), d)))];
279 const entries = await Promise.all(
280 uniqueDirs.map(async (dir) => ({ source: path.join(dir, "component.json"), data: await readComponentJson(dir), dir })),
281 );
282 parseDeployComponents(entries); // validates all at once; throws on any gap
283 const seen = new Set<string>();
284 const loaded: LoadedComponent[] = [];
285 for (const e of entries) {
286 const component = e.data as DeployComponent;
287 if (seen.has(component.name)) {
288 throw new Error(`duplicate component name "${component.name}" (two --component folders declare it)`);
289 }
290 seen.add(component.name);
291 loaded.push({ component, dir: e.dir });
292 }
293 return loaded;
294}
295
296// Turn each component's <name>.env.example into <name>.env text. The non-interactive
297// path's env step (stubs empty values); the interactive path resolves env inline as
298// each component is added.
299async function resolveComponentsEnv(loaded: LoadedComponent[]): Promise<Record<string, string>> {
300 const env: Record<string, string> = {};
301 for (const { component, dir } of loaded) {
302 const text = await resolveComponentEnv(dir, component.name, { interactive: false });
303 if (text !== null) env[component.name] = text;
304 }
305 return env;
306}
307
308// ---------------------------------------------------------------------------
309// Entry point — wired into cli/index.ts
310// ---------------------------------------------------------------------------
311
312export async function deployCommand(workflowPath: string | undefined, args: string[]): Promise<void> {
313 // --help or missing positional → print USAGE
314 if (!workflowPath || workflowPath === "--help" || workflowPath === "-h") {
315 process.stdout.write(USAGE);

Callers 1

index.tsFile · 0.90

Calls 15

migrateFunction · 0.90
deserializeFunction · 0.90
deriveRequirementsFunction · 0.90
slugifyFunction · 0.90
promptMissingFunction · 0.90
buildDeploymentSpecFunction · 0.90
writeOutputFunction · 0.90
parseFlagsFunction · 0.85
loadValuesFunction · 0.85
partialFromFlagsFunction · 0.85
loadFlagComponentsFunction · 0.85
missingRequiredFunction · 0.85

Tested by

no test coverage detected