async function main()

in packages/tiny-agents/src/cli.ts [35:224]


async function main() {
	const {
		values: { help, version },
		positionals,
	} = parseArgs({
		options: {
			help: {
				type: "boolean",
				short: "h",
			},
			version: {
				type: "boolean",
				short: "v",
			},
		},
		allowPositionals: true,
	});
	if (version) {
		console.log(packageVersion);
		process.exit(0);
	}
	const command = positionals[0];
	const loadFrom = positionals[1];
	if (help) {
		console.log(USAGE_HELP);
		process.exit(0);
	}
	if (positionals.length !== 2 || !isValidCommand(command)) {
		error(`You need to call run or serve, followed by an agent id (local path or Hub identifier).`);
		console.log(USAGE_HELP);
		process.exit(1);
	}

	const { configJson, prompt } = await loadConfigFrom(loadFrom);

	const ConfigSchema = z
		.object({
			model: z.string(),
			provider: z.enum(PROVIDERS_OR_POLICIES).optional(),
			endpointUrl: z.string().optional(),
			apiKey: z.string().optional(),
			inputs: z.array(InputConfigSchema).optional(),
			servers: z.array(ServerConfigSchema),
		})
		.refine((data) => data.provider !== undefined || data.endpointUrl !== undefined, {
			message: "At least one of 'provider' or 'endpointUrl' is required",
		});

	let config: z.infer<typeof ConfigSchema>;
	try {
		const parsedConfig = JSON.parse(configJson);
		config = ConfigSchema.parse(parsedConfig);
	} catch (err) {
		error("Invalid configuration file:", err instanceof Error ? err.message : err);
		process.exit(1);
	}

	// Handle inputs (i.e. env variables injection)
	if (config.inputs && config.inputs.length > 0) {
		const rl = readline.createInterface({ input: stdin, output: stdout });

		stdout.write(ANSI.BLUE);
		stdout.write("Some initial inputs are required by the agent. ");
		stdout.write("Please provide a value or leave empty to load from env.");
		stdout.write(ANSI.RESET);
		stdout.write("\n");

		for (const inputItem of config.inputs) {
			const inputId = inputItem.id;
			const description = inputItem.description;
			const envSpecialValue = `\${input:${inputId}}`; // Special value to indicate env variable injection

			// Check env variables that will use this input
			const inputVars = new Set<string>();
			for (const server of config.servers) {
				if (server.type === "stdio" && server.config.env) {
					for (const [key, value] of Object.entries(server.config.env)) {
						if (value === envSpecialValue) {
							inputVars.add(key);
						}
					}
				}
				if ((server.type === "http" || server.type === "sse") && server.config.options?.requestInit?.headers) {
					for (const [key, value] of Object.entries(server.config.options.requestInit.headers)) {
						if (value.includes(envSpecialValue)) {
							inputVars.add(key);
						}
					}
				}
			}

			if (inputVars.size === 0) {
				stdout.write(ANSI.YELLOW);
				stdout.write(`Input ${inputId} defined in config but not used by any server.`);
				stdout.write(ANSI.RESET);
				stdout.write("\n");
				continue;
			}

			// Prompt user for input
			stdout.write(ANSI.BLUE);
			stdout.write(` • ${inputId}`);
			stdout.write(ANSI.RESET);
			stdout.write(`: ${description}. (default: load from ${Array.from(inputVars).join(", ")}) `);

			const userInput = (await rl.question("")).trim();

			// Inject user input (or env variable) into servers' env
			for (const server of config.servers) {
				if (server.type === "stdio" && server.config.env) {
					for (const [key, value] of Object.entries(server.config.env)) {
						if (value === envSpecialValue) {
							if (userInput) {
								server.config.env[key] = userInput;
							} else {
								const valueFromEnv = process.env[key] || "";
								server.config.env[key] = valueFromEnv;
								if (valueFromEnv) {
									stdout.write(ANSI.GREEN);
									stdout.write(`Value successfully loaded from '${key}'`);
									stdout.write(ANSI.RESET);
									stdout.write("\n");
								} else {
									stdout.write(ANSI.YELLOW);
									stdout.write(`No value found for '${key}' in environment variables. Continuing.`);
									stdout.write(ANSI.RESET);
									stdout.write("\n");
								}
							}
						}
					}
				}
				if ((server.type === "http" || server.type === "sse") && server.config.options?.requestInit?.headers) {
					for (const [key, value] of Object.entries(server.config.options.requestInit.headers)) {
						if (value.includes(envSpecialValue)) {
							if (userInput) {
								server.config.options.requestInit.headers[key] = value.replace(envSpecialValue, userInput);
							} else {
								const valueFromEnv = process.env[key] || "";
								server.config.options.requestInit.headers[key] = value.replace(envSpecialValue, valueFromEnv);
								if (valueFromEnv) {
									stdout.write(ANSI.GREEN);
									stdout.write(`Value successfully loaded from '${key}'`);
									stdout.write(ANSI.RESET);
									stdout.write("\n");
								} else {
									stdout.write(ANSI.YELLOW);
									stdout.write(`No value found for '${key}' in environment variables. Continuing.`);
									stdout.write(ANSI.RESET);
									stdout.write("\n");
								}
							}
						}
					}
				}
			}
		}

		stdout.write("\n");
		rl.close();
	}

	const agent = new Agent(
		config.endpointUrl
			? {
					endpointUrl: config.endpointUrl,
					model: config.model,
					apiKey: config.apiKey ?? process.env.API_KEY ?? process.env.HF_TOKEN,
					servers: config.servers,
					prompt,
			  }
			: {
					// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
					provider: config.provider!,
					model: config.model,
					apiKey: config.apiKey ?? process.env.API_KEY ?? process.env.HF_TOKEN,
					servers: config.servers,
					prompt,
			  }
	);

	debug(agent);
	await agent.loadTools();

	if (command === "run") {
		mainCliLoop(agent);
	} else {
		startServer(agent);
	}
}