Skip to content

Entrée et dispatch CLI

源码版本v0.73.1

main.ts est le point d'entrée du processus pour la commande pi. Il parse les arguments en AppMode, assemble AgentSessionRuntime, puis dispatch vers l'un des trois modes d'exécution : TUI interactif, print one-shot, ou JSON-RPC. Au-delà du dispatch de mode, il gère aussi les migrations, la résolution de session, le choix du modèle et le pré-traitement des arguments de type fichier. C'est en quelque sorte la couche frontale qui « traduit l'invocation shell en appel runtime ».

Responsabilités

  1. Parsing et dispatch : parseArgs produit Args, resolveAppMode calcule AppMode, et on choisit entre runRpcMode / InteractiveMode / runPrintMode. Voir packages/coding-agent/src/main.ts:98-109 et packages/coding-agent/src/main.ts:673-726.
  2. Résolution de session : gestion des conflits entre --continue, --resume, --session <id|path>, --fork <id>, --no-session, et résolution du chemin pour localiser le .jsonl sur disque. Voir packages/coding-agent/src/main.ts:188-212 et packages/coding-agent/src/main.ts:147-172.
  3. Assemblage des services : appelle createAgentSessionServices + createAgentSessionRuntime pour relier cwd, agentDir, authStorage, et les chemins d'extensions/skills/thèmes. Voir packages/coding-agent/src/main.ts:522-560.
  4. Court-circuit pré-exécution : pi --version, pi --export, pi package ..., pi config ... ne passent pas par le runtime, ils font directement process.exit. Voir packages/coding-agent/src/main.ts:431-478.

Motivation de design

Pourquoi isoler l'entrée dans un fichier dédié ? Parce que le parsing CLI, le choix de session et les migrations n'arrivent qu'une seule fois, au démarrage du processus. Mettre tout ça dans createAgentSession (l'entrée SDK) polluerait sa réutilisabilité — un appelant SDK a généralement déjà un context runtime et n'a pas besoin de parser argv ni de prendre le contrôle de stdout au niveau processus. main.ts ne gère donc que les « trivialités de niveau processus » : prise en main de stdout, traitement des signaux, migrations, vérification de version, validation des forks ; tout le reste est délégué au SDK et à la factory runtime.

Fichiers clés

resolveAppMode regarde d'abord le flag --mode, puis retombe sur la détection TTY de stdin, de sorte que l'usage en pipe echo x | pi bascule automatiquement en mode print :

typescript
// packages/coding-agent/src/main.ts:98-109
function resolveAppMode(parsed: Args, stdinIsTTY: boolean): AppMode {
	if (parsed.mode === "rpc") {
		return "rpc";
	}
	if (parsed.mode === "json") {
		return "json";
	}
	if (parsed.print || !stdinIsTTY) {
		return "print";
	}
	return "interactive";
}

Au point de dispatch triptyque, on choisit la branche directement depuis appMode ; print et json partagent runPrintMode, la différence se fait via toPrintOutputMode(appMode) :

typescript
// packages/coding-agent/src/main.ts:673-726
if (appMode === "rpc") {
	printTimings();
	await runRpcMode(runtime);
} else if (appMode === "interactive") {
	// ...
	const interactiveMode = new InteractiveMode(runtime, {
		migratedProviders,
		modelFallbackMessage,
		initialMessage,
		initialImages,
		initialMessages: parsed.messages,
		verbose: parsed.verbose,
	});
	// ...
	await interactiveMode.run();
} else {
	printTimings();
	const exitCode = await runPrintMode(runtime, {
		mode: toPrintOutputMode(appMode),
		messages: parsed.messages,
		initialMessage,
		initialImages,
	});
	// ...
}

Flux de données

Le chemin de dispatch une fois argv arrivé :

Limites et échecs

  • Conflit de fork : --fork ne peut pas être combiné avec --session, --continue, --resume, --no-session. Voir packages/coding-agent/src/main.ts:188-202.
  • Prise en main de stdout : en mode non interactif, on appelle takeOverStdout() pour rediriger console.log vers un pipe interne, afin que les séquences d'échappement du TUI ne polluent pas la sortie print/rpc ; restoreStdout est appelé à la sortie.
  • cwd de session absent : quand --session pointe vers un autre projet, le mode interactif ouvre un prompt pour laisser l'utilisateur choisir, tandis que print/rpc quittent en erreur. Voir packages/coding-agent/src/main.ts:502-514.
  • Mode benchmark : en startupBenchmark, on ne fait qu'appeler interactiveMode.init() puis stop() immédiatement, sans entrer dans la boucle principale.

Résumé

main.ts est une coquille fine : il traduit argv et stdin en une closure de factory runtime, puis confie le reste à l'un des trois modes. Pour le détail de l'assemblage interne de la factory runtime, voir Assemblage createAgentSession ; pour le mode interactif, voir Mode interactif TUI ; pour les modes non interactifs, voir Modes print et rpc.