fix: bind pi runtime config at spawn
This commit is contained in:
@@ -7,6 +7,7 @@ import { buildPiChildEnv, canonicalPiProvider } from "./provider-credentials.js"
|
||||
import { loadPiAuthProviders } from "./auth-providers.js";
|
||||
import { secretValue } from "../config/secret-bundle.js";
|
||||
import { clearPrincipalEnvironment, principalEnvironment, type PrincipalContext } from "../auth/principal.js";
|
||||
import { createPiRuntimeAgentSnapshot } from "./managed-config.js";
|
||||
|
||||
export interface SessionRuntime {
|
||||
rpc: RpcClient;
|
||||
@@ -37,13 +38,14 @@ export class PiProcessManager {
|
||||
private spawnFn: (
|
||||
sessionId: string, author: string, provider: string | undefined, principal?: PrincipalContext,
|
||||
) => ChildProcessWithoutNullStreams;
|
||||
private loadAuthProviders: () => ReadonlySet<string>;
|
||||
private loadAuthProviders: (agentDir: string) => ReadonlySet<string>;
|
||||
|
||||
constructor(
|
||||
private cfg: AppConfig,
|
||||
opts?: { spawnFn?: SpawnFn; authProviders?: () => ReadonlySet<string> },
|
||||
opts?: { spawnFn?: SpawnFn; authProviders?: (agentDir: string) => ReadonlySet<string> },
|
||||
) {
|
||||
this.loadAuthProviders = opts?.authProviders ?? (() => loadPiAuthProviders());
|
||||
this.loadAuthProviders = opts?.authProviders
|
||||
?? ((agentDir) => loadPiAuthProviders({ agentDir }));
|
||||
if (opts?.spawnFn) {
|
||||
this.spawnFn = (sessionId, author, provider, principal) =>
|
||||
this.spawnPi(opts.spawnFn!, sessionId, author, provider, principal);
|
||||
@@ -56,45 +58,57 @@ export class PiProcessManager {
|
||||
private spawnPi(
|
||||
spawnFn: SpawnFn, sessionId: string, author: string, provider: string | undefined, principal?: PrincipalContext,
|
||||
): ChildProcessWithoutNullStreams {
|
||||
const env = buildPiChildEnv({
|
||||
provider,
|
||||
authProviders: this.loadAuthProviders(),
|
||||
credentialValue: secretValue(this.cfg, "THT_MODEL_API_KEY"),
|
||||
credentialFile: this.cfg.modelApiKeyFile,
|
||||
additions: { THT_SESSION: sessionId, THT_AUTHOR: author },
|
||||
});
|
||||
clearPrincipalEnvironment(env);
|
||||
if (principal) Object.assign(env, principalEnvironment(principal));
|
||||
// The Thoth gate executes the deterministic `tht` CLI as a Pi tool. Give only
|
||||
// this managed session process the adapter values already loaded by the core
|
||||
// entrypoint; the generic provider helper continues to scrub them by default.
|
||||
for (const name of [
|
||||
"THT_DWH_API_KEY", "THT_VEC_API_KEY", "THT_VEC_WRITE_API_KEY",
|
||||
] as const) {
|
||||
const value = secretValue(this.cfg, name) ?? process.env[name];
|
||||
if (value !== undefined) env[name] = value;
|
||||
}
|
||||
const ca = secretValue(this.cfg, "THT_SSL_CA")
|
||||
?? secretValue(this.cfg, "THT_CA")
|
||||
?? process.env.THT_SSL_CA
|
||||
?? process.env.THT_CA;
|
||||
if (ca !== undefined) {
|
||||
env.THT_CA = ca;
|
||||
env.THT_SSL_CA = ca;
|
||||
}
|
||||
delete env.THT_DATA_ROOT;
|
||||
if (this.cfg.dataRoot !== undefined) env.THT_DATA_ROOT = this.cfg.dataRoot;
|
||||
// pi 0.73 removed `--approve`: rpc mode is headless and its argv is intentionally minimal.
|
||||
const child = spawnFn(this.cfg.piBin, ["--mode", "rpc"], {
|
||||
cwd: this.cfg.harnessDir,
|
||||
env,
|
||||
});
|
||||
// This is the final shared boundary for createFor(), spawnFor(), and resume(). Validate
|
||||
// before auth-provider inspection, then make Pi consume the exact copied bytes rather than
|
||||
// reopening mutable mounted auth/models files after this check.
|
||||
const agent = createPiRuntimeAgentSnapshot();
|
||||
let child: ChildProcessWithoutNullStreams | undefined;
|
||||
try {
|
||||
const env = buildPiChildEnv({
|
||||
provider,
|
||||
authProviders: this.loadAuthProviders(agent.agentDir),
|
||||
credentialValue: secretValue(this.cfg, "THT_MODEL_API_KEY"),
|
||||
credentialFile: this.cfg.modelApiKeyFile,
|
||||
additions: { THT_SESSION: sessionId, THT_AUTHOR: author },
|
||||
});
|
||||
env.PI_CODING_AGENT_DIR = agent.agentDir;
|
||||
env.PI_CODING_AGENT_SESSION_DIR = agent.sessionDir;
|
||||
clearPrincipalEnvironment(env);
|
||||
if (principal) Object.assign(env, principalEnvironment(principal));
|
||||
// The Thoth gate executes the deterministic `tht` CLI as a Pi tool. Give only
|
||||
// this managed session process the adapter values already loaded by the core
|
||||
// entrypoint; the generic provider helper continues to scrub them by default.
|
||||
for (const name of [
|
||||
"THT_DWH_API_KEY", "THT_VEC_API_KEY", "THT_VEC_WRITE_API_KEY",
|
||||
] as const) {
|
||||
const value = secretValue(this.cfg, name) ?? process.env[name];
|
||||
if (value !== undefined) env[name] = value;
|
||||
}
|
||||
const ca = secretValue(this.cfg, "THT_SSL_CA")
|
||||
?? secretValue(this.cfg, "THT_CA")
|
||||
?? process.env.THT_SSL_CA
|
||||
?? process.env.THT_CA;
|
||||
if (ca !== undefined) {
|
||||
env.THT_CA = ca;
|
||||
env.THT_SSL_CA = ca;
|
||||
}
|
||||
delete env.THT_DATA_ROOT;
|
||||
if (this.cfg.dataRoot !== undefined) env.THT_DATA_ROOT = this.cfg.dataRoot;
|
||||
// pi 0.73 removed `--approve`: rpc mode is headless and its argv is intentionally minimal.
|
||||
child = spawnFn(this.cfg.piBin, ["--mode", "rpc"], {
|
||||
cwd: this.cfg.harnessDir,
|
||||
env,
|
||||
});
|
||||
child.once("exit", agent.cleanup);
|
||||
child.once("close", agent.cleanup);
|
||||
// Log stderr for debugging (was silently drained)
|
||||
child.stderr.on("data", (d: Buffer) => console.error(`[pi:${sessionId}] stderr:`, d.toString().trim()));
|
||||
return child;
|
||||
} catch (error) {
|
||||
try { child.kill(); } catch { /* preserve the initialization error */ }
|
||||
if (child) {
|
||||
try { child.kill(); } catch { /* preserve the initialization error */ }
|
||||
}
|
||||
agent.cleanup();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user