Skip to content

CLI-Eingang und Verteilung

源码版本v0.73.1

main.ts ist der Prozess-Eingang des pi-Befehls. Es parst die Kommandozeilen-Argumente in einen AppMode, baut das AgentSessionRuntime zusammen und verteilt an drei Laufmodi: interaktives TUI, einmaliges print, JSON-RPC. Außer der Modus-Verteilung übernimmt es Migrationen, Session-Auflösung, Modellauswahl und Vorverarbeitung dateibasierter Argumente. Man kann es als vorgelagerte Schicht verstehen, die einen Shell-Aufruf in einen Runtime-Aufruf übersetzt.

Verantwortung

  1. Parse und verteilen: parseArgs erzeugt Args, resolveAppMode berechnet AppMode, danach wird runRpcMode / InteractiveMode / runPrintMode gewählt. Siehe packages/coding-agent/src/main.ts:98-109, packages/coding-agent/src/main.ts:673-726.
  2. Session-Auflösung: Konflikt-Prüfung und Pfad-Auflösung zwischen --continue, --resume, --session <id|path>, --fork <id>, --no-session, zeigt die Session-Kennung auf die .jsonl auf der Festplatte. Siehe packages/coding-agent/src/main.ts:188-212, packages/coding-agent/src/main.ts:147-172.
  3. Service-Zusammenbau: Ruft createAgentSessionServices + createAgentSessionRuntime auf, verdrahtet cwd, agentDir, authStorage und Extensions-/Skills-/Theme-Pfade. Siehe packages/coding-agent/src/main.ts:522-560.
  4. Vorlauf-Kurzschluss: pi --version, pi --export, pi package ..., pi config ... gehen nicht in die Runtime, sondern direkt process.exit. Siehe packages/coding-agent/src/main.ts:431-478.

Entwurfsmotivation

Warum den Eingang in eine eigene Datei auslagern? Weil CLI-Parsing, Session-Auswahl und Migrationen nur einmal passieren, und nur beim Prozess-Start. Wenn man das in createAgentSession (SDK-Eingang) steckt, verschmutzt man die Wiederverwendbarkeit des SDK - SDK-Aufrufer sind meistens Programme, die schon einen Runtime-Kontext haben, müssen kein argv parsen und brauchen keine prozessweite stdout-Übernahme. main.ts übernimmt deshalb nur "Prozess-Sachen": stdout-Übernahme, Signal-Behandlung, migrations, Versions-Check, fork-Prüfung, der Rest geht an die SDK- und Runtime-Fabriken.

Wichtige Dateien

resolveAppMode schaut zuerst auf das --mode-Flag und fällt dann auf "ist stdin ein TTY" zurück, damit die Pipe-Nutzung echo x | pi automatisch in den print-Modus geht:

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";
}

An der Drehscheibe wird direkt per appMode der Zweig gewählt, print und json teilen sich runPrintMode, der Unterschied liegt in 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,
	});
	// ...
}

Datenfluss

Verteilungspfad, nachdem argv reinkommt:

Grenzen und Fehler

  • fork-Konflikt: --fork kann nicht mit --session, --continue, --resume, --no-session gleichzeitig verwendet werden, siehe packages/coding-agent/src/main.ts:188-202.
  • stdout-Uebernahme: Im nicht-interaktiven Modus wird takeOverStdout() aufgerufen, das console.log auf eine interne Pipe umleitet, um zu verhindern, dass TUI-Escapes den print/rpc-Output verschmutzen, beim Beenden restoreStdout.
  • session cwd fehlt: Wenn --session auf ein anderes Projekt zeigt, poppingt interactive einen Prompt zur Auswahl, print/rpc beendet sich direkt mit Fehler, siehe packages/coding-agent/src/main.ts:502-514.
  • benchmark-Modus: Bei startupBenchmark wird nur interactiveMode.init() ausgeführt und sofort stop(), ohne in die Hauptschleife zu gehen.

Zusammenfassung

main.ts ist eine dünne Hülle: Es übersetzt argv und stdin in einen Runtime-Fabrik-Closure und übergibt ihn an einen der drei Modi. Wie die Runtime-Fabrik intern zusammenbaut, siehe createAgentSession Zusammenbau; die interaktiven Modus-Details siehe TUI interaktiver Modus; die nicht-interaktiven Modi siehe print und rpc Modi.