Fix session resume and PSD container configuration
This commit is contained in:
@@ -40,6 +40,8 @@ export function buildApp(config: AppConfig, deps?: BuildAppDeps): FastifyInstanc
|
||||
harnessDir: config.harnessDir,
|
||||
configPath: process.env.THT_CONFIG ?? "config/tht.yaml",
|
||||
dataRoot: config.dataRoot,
|
||||
secretsFile: config.secretsFile,
|
||||
secretFiles: config.secretFiles,
|
||||
});
|
||||
const mgr = deps?.mgr ?? new PiProcessManager(config, deps?.spawnFn ? { spawnFn: deps.spawnFn } : undefined);
|
||||
const hub = deps?.hub ?? new SseHub();
|
||||
|
||||
@@ -68,9 +68,18 @@ export class PiProcessManager {
|
||||
// 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", "THT_SSL_CA",
|
||||
"THT_DWH_API_KEY", "THT_VEC_API_KEY", "THT_VEC_WRITE_API_KEY",
|
||||
] as const) {
|
||||
if (process.env[name] !== undefined) env[name] = process.env[name];
|
||||
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;
|
||||
|
||||
@@ -2,8 +2,9 @@ import { spawn } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { clearPrincipalEnvironment, principalEnvironment, type PrincipalContext } from "../auth/principal.js";
|
||||
import { secretValue, type SecretBundleConfig } from "../config/secret-bundle.js";
|
||||
|
||||
export interface ThtConfig {
|
||||
export interface ThtConfig extends SecretBundleConfig {
|
||||
thtBin: string;
|
||||
harnessDir: string;
|
||||
configPath: string;
|
||||
@@ -82,6 +83,17 @@ export class ThtRunner {
|
||||
clearPrincipalEnvironment(env);
|
||||
if (this.cfg.dataRoot !== undefined) env.THT_DATA_ROOT = this.cfg.dataRoot;
|
||||
if (this.principal) Object.assign(env, principalEnvironment(this.principal));
|
||||
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);
|
||||
if (value !== undefined) env[name] = value;
|
||||
}
|
||||
const ca = secretValue(this.cfg, "THT_SSL_CA") ?? secretValue(this.cfg, "THT_CA");
|
||||
if (ca !== undefined) {
|
||||
env.THT_CA = ca;
|
||||
env.THT_SSL_CA = ca;
|
||||
}
|
||||
const ch = spawn(this.cfg.thtBin, this.buildArgv(args, workspace), {
|
||||
cwd: this.cfg.harnessDir,
|
||||
env,
|
||||
|
||||
@@ -350,7 +350,14 @@ test("skips managed-key injection for a provider present in pi's auth store", as
|
||||
|
||||
test("session Pi spawn reads the single secret bundle and scrubs its path", async () => {
|
||||
const secret = path.resolve(__dirname, `.bundle-${process.pid}`);
|
||||
writeFileSync(secret, "THT_MODEL_API_KEY=bundle-secret\n", { mode: 0o600 });
|
||||
writeFileSync(secret, [
|
||||
"THT_MODEL_API_KEY=bundle-secret",
|
||||
"THT_DWH_API_KEY=dwh-secret",
|
||||
"THT_VEC_API_KEY=vector-reader-secret",
|
||||
"THT_VEC_WRITE_API_KEY=vector-writer-secret",
|
||||
"THT_CA=/run/secrets/ca-chain.pem",
|
||||
"",
|
||||
].join("\n"), { mode: 0o600 });
|
||||
chmodSync(secret, 0o600);
|
||||
const calls: any[][] = [];
|
||||
const child = recordingChild();
|
||||
@@ -361,6 +368,13 @@ test("session Pi spawn reads the single secret bundle and scrubs its path", asyn
|
||||
try {
|
||||
await mgr.spawnFor("bundle-session", { provider: "openai" });
|
||||
expect(calls[0][2].env.OPENAI_API_KEY).toBe("bundle-secret");
|
||||
expect(calls[0][2].env).toMatchObject({
|
||||
THT_DWH_API_KEY: "dwh-secret",
|
||||
THT_VEC_API_KEY: "vector-reader-secret",
|
||||
THT_VEC_WRITE_API_KEY: "vector-writer-secret",
|
||||
THT_CA: "/run/secrets/ca-chain.pem",
|
||||
THT_SSL_CA: "/run/secrets/ca-chain.pem",
|
||||
});
|
||||
expect(calls[0][2].env).not.toHaveProperty("THT_SECRETS_FILE");
|
||||
} finally {
|
||||
mgr.teardown("bundle-session");
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { test, expect, vi } from "vitest";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { ThtRunner } from "../src/tht/tht-runner.js";
|
||||
|
||||
// Spy on child_process.spawn so we can capture the resolved argv (incl. -c config)
|
||||
@@ -63,6 +66,41 @@ test("run passes configured THT_DATA_ROOT and preserves the remaining environmen
|
||||
}
|
||||
});
|
||||
|
||||
test("run injects DWH/vector credentials from the mounted secret bundle", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "tht-runner-bundle-"));
|
||||
const secret = join(dir, "thothii.secrets");
|
||||
writeFileSync(secret, [
|
||||
"THT_DWH_API_KEY=dwh-secret",
|
||||
"THT_VEC_API_KEY=vector-reader-secret",
|
||||
"THT_VEC_WRITE_API_KEY=vector-writer-secret",
|
||||
"THT_CA=/run/secrets/ca-chain.pem",
|
||||
"",
|
||||
].join("\n"), { mode: 0o600 });
|
||||
chmodSync(secret, 0o600);
|
||||
try {
|
||||
(spawn as any).mockClear();
|
||||
const runner = new ThtRunner({
|
||||
thtBin: "tht",
|
||||
harnessDir: "/app/harness",
|
||||
configPath: "config/tht.yaml",
|
||||
secretsFile: secret,
|
||||
} as any);
|
||||
|
||||
await runner.run(["session", "list", "--json"]);
|
||||
|
||||
const env = (spawn as any).mock.calls[0][2].env;
|
||||
expect(env).toMatchObject({
|
||||
THT_DWH_API_KEY: "dwh-secret",
|
||||
THT_VEC_API_KEY: "vector-reader-secret",
|
||||
THT_VEC_WRITE_API_KEY: "vector-writer-secret",
|
||||
THT_CA: "/run/secrets/ca-chain.pem",
|
||||
THT_SSL_CA: "/run/secrets/ca-chain.pem",
|
||||
});
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("run omits ambient THT_DATA_ROOT when config does not provide one", async () => {
|
||||
const previousDataRoot = process.env.THT_DATA_ROOT;
|
||||
const previousCredential = process.env.PI_PROVIDER_API_KEY;
|
||||
|
||||
Reference in New Issue
Block a user