* chore: remove legacy orchestrator, trading and one-off script leftovers The repository still carried the pre-evOo prototype: a Python orchestrator, the old HTML "evoo-platform" cockpit (whose test ran in the root unit suite), an apex-trading package, the hot-reload deploy.sh for it, a permissions manifest, an architecture note with local file links, agent handoff notes, a Steve Jobs persona template and about ninety one-off scripts that probe a developer database with hard-coded local URLs. None of it is used by the product, CI or the real deploy in ops/evo-deploy. Delete those files, drop the evoo-platform test glob from the root vitest config, the Cockpit section from .env.example and the dead biome and gitignore entries, and update the docs that listed the removed tools. scripts/start-tmux.sh no longer starts the removed sentinel and resolves the checkout from its own location instead of a machine-specific path; scripts/seed-ollama.ts requires DATABASE_URL instead of falling back to a local URL with a username. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * fix(web): drop trading and persona prompts from the conversation shell The web shell still showed leftovers from the trading prototype: composer chips with German trading and "Steve Jobs" prompts, trading suggestion chips under the empty conversation line, a German trading hero for the empty transcript, and a German "data protection" status panel in the sidebar. They suggested actions the product does not have and added persistent chrome. Remove the composer chips and their helper, the empty-state chips and the hero (the empty conversation keeps its existing one-line start hint), the sidebar trading indicator and the data trust panel. The new-bot E2E now asserts the plain empty state; the sidebar spec no longer expects the trading status, and the placeholder Steve Jobs spec with no assertions is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * refactor: remove the trading bot status integration With the sidebar trading indicator gone, the only remaining consumer of the trading bot status was a banner in General settings that reused the same component. evOo is a personal assistant; a status proxy and poller for one external trading service, configured through service-specific TRADING_BOT_URL and VITE_TRADING_BOT_URL variables, does not belong in the core product. Remove the settings banner and its prop plumbing, the web poller, the indicator component, the /api/trading-bot/status proxy, the shared core contract, the env loading and both variables from .env.example, together with their tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * chore: remove one-off database scripts and a stale review note packages/db carried four hand-run scripts outside the package build that query or seed a developer database with a hard-coded connection string, one of them for a specific account. docs/VOICE-FIRST-REVIEW.md was a one-time verification note for a workflow that no longer exists and named private machines and local paths. Nothing references them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * chore: replace machine-specific paths and addresses with placeholders ORIGIN.md pinned the intended checkout to an absolute path under a personal home directory, docs/LOCAL-SETUP.md listed folders on one developer's machine, and two tests used what looks like a real tailnet address. Use a home-relative path, drop the machine-specific folder list, and use 100.100.100.100 in the tests; it is still inside 100.64.0.0/10, so both tests check the same thing as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * test(web): drop the trading status from the shell contrast test main's contrast test now waits for the sidebar trading status before measuring; this branch removes that status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
215 lines
7.3 KiB
TypeScript
215 lines
7.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { loadEnv } from "./env.js";
|
|
|
|
const base = {
|
|
DATABASE_URL: "postgres://rakazo:rakazo@127.0.0.1:5433/rakazo",
|
|
NODE_ENV: "test",
|
|
};
|
|
|
|
describe("loadEnv", () => {
|
|
it("defaults the product path to Pi, Docker, and Graphile Worker", () => {
|
|
const env = loadEnv(base);
|
|
expect(env.agentRuntime).toBe("pi");
|
|
expect(env.sandboxProvider).toBe("docker");
|
|
expect(env.wakeupDriver).toBe("graphile");
|
|
expect(env.apiHost).toBe("127.0.0.1");
|
|
expect(env.nodeEnv).toBe("test");
|
|
});
|
|
|
|
it("defaults Pi JSONL session recording to off", () => {
|
|
expect(loadEnv(base).piSessionRecording).toBe(false);
|
|
expect(loadEnv({ ...base, PI_SESSION_RECORDING: "false" }).piSessionRecording).toBe(false);
|
|
expect(loadEnv({ ...base, PI_SESSION_RECORDING: "1" }).piSessionRecording).toBe(false);
|
|
expect(loadEnv({ ...base, PI_SESSION_RECORDING: "true" }).piSessionRecording).toBe(true);
|
|
});
|
|
|
|
it("keeps explicit emulator settings for pnpm test", () => {
|
|
const env = loadEnv({
|
|
...base,
|
|
AGENT_RUNTIME: "scripted",
|
|
SANDBOX_PROVIDER: "fake",
|
|
WAKEUP_DRIVER: "memory",
|
|
});
|
|
expect(env.agentRuntime).toBe("scripted");
|
|
expect(env.sandboxProvider).toBe("fake");
|
|
expect(env.wakeupDriver).toBe("memory");
|
|
});
|
|
|
|
it("keeps the onboarding scan off the host unless a scan home is configured", () => {
|
|
expect(loadEnv(base).onboardingScanHome).toBeUndefined();
|
|
expect(loadEnv({ ...base, ONBOARDING_SCAN_HOME: " /srv/home " }).onboardingScanHome).toBe(
|
|
"/srv/home",
|
|
);
|
|
});
|
|
|
|
it("loads an optional integrations catalog mirror", () => {
|
|
expect(loadEnv(base).integrationsCatalogUrl).toBeUndefined();
|
|
expect(
|
|
loadEnv({ ...base, INTEGRATIONS_CATALOG_URL: " https://catalog.example.test/feed " })
|
|
.integrationsCatalogUrl,
|
|
).toBe("https://catalog.example.test/feed");
|
|
});
|
|
|
|
it("falls back to none when a remote provider key is missing", () => {
|
|
expect(
|
|
loadEnv({
|
|
...base,
|
|
SANDBOX_PROVIDER: "e2b",
|
|
}).sandboxProvider,
|
|
).toBe("none");
|
|
expect(
|
|
loadEnv({
|
|
...base,
|
|
SANDBOX_PROVIDER: "none",
|
|
}).sandboxProvider,
|
|
).toBe("none");
|
|
expect(
|
|
loadEnv({
|
|
...base,
|
|
SANDBOX_PROVIDER: "",
|
|
}).sandboxProvider,
|
|
).toBe("none");
|
|
});
|
|
|
|
it("loads provider-specific Daytona configuration", () => {
|
|
const env = loadEnv({
|
|
...base,
|
|
SANDBOX_PROVIDER: "daytona",
|
|
DAYTONA_API_KEY: "test-daytona-key",
|
|
DAYTONA_API_URL: "https://daytona.test/api",
|
|
DAYTONA_TARGET: "test-target",
|
|
});
|
|
expect(env).toMatchObject({
|
|
sandboxProvider: "daytona",
|
|
daytonaApiKey: "test-daytona-key",
|
|
daytonaApiUrl: "https://daytona.test/api",
|
|
daytonaTarget: "test-target",
|
|
});
|
|
});
|
|
|
|
it("loads provider-specific Box configuration", () => {
|
|
const env = loadEnv({
|
|
...base,
|
|
SANDBOX_PROVIDER: "box",
|
|
BOX_API_KEY: "test-box-key",
|
|
BOX_API_URL: "https://box.test/api/v1",
|
|
});
|
|
expect(env).toMatchObject({
|
|
sandboxProvider: "box",
|
|
boxApiKey: "test-box-key",
|
|
boxApiUrl: "https://box.test/api/v1",
|
|
});
|
|
});
|
|
|
|
it("throws when production omits secrets", () => {
|
|
expect(() =>
|
|
loadEnv({
|
|
DATABASE_URL: base.DATABASE_URL,
|
|
NODE_ENV: "production",
|
|
}),
|
|
).toThrow(/BETTER_AUTH_SECRET/);
|
|
});
|
|
|
|
it("throws when production uses placeholder secrets", () => {
|
|
expect(() =>
|
|
loadEnv({
|
|
DATABASE_URL: base.DATABASE_URL,
|
|
NODE_ENV: "production",
|
|
BETTER_AUTH_SECRET: "dev-secret-change-me-please-32chars",
|
|
ENCRYPTION_KEY: "real-encryption-key-value",
|
|
SANDBOX_SUPERVISOR_TOKEN: "real-supervisor-token-with-enough-length",
|
|
SCREEN_PROXY_SECRET: "real-screen-proxy-secret-with-enough-length",
|
|
}),
|
|
).toThrow(/BETTER_AUTH_SECRET/);
|
|
});
|
|
|
|
it("loads real secrets in production", () => {
|
|
const env = loadEnv({
|
|
DATABASE_URL: base.DATABASE_URL,
|
|
NODE_ENV: "production",
|
|
BETTER_AUTH_SECRET: "prod-auth-secret-with-enough-length",
|
|
ENCRYPTION_KEY: "prod-encryption-key-with-enough-length",
|
|
SCREEN_PROXY_SECRET: "prod-screen-proxy-secret-with-enough-length",
|
|
SANDBOX_PROVIDER: "e2b",
|
|
API_HOST: "0.0.0.0",
|
|
});
|
|
expect(env.authSecret).toBe("prod-auth-secret-with-enough-length");
|
|
expect(env.encryptionKey).toBe("prod-encryption-key-with-enough-length");
|
|
expect(env.sandboxSupervisorToken).toBeUndefined();
|
|
expect(env.screenProxySecret).toBe("prod-screen-proxy-secret-with-enough-length");
|
|
expect(env.apiHost).toBe("0.0.0.0");
|
|
});
|
|
|
|
it("falls back to none in production when Docker has no supervisor token", () => {
|
|
const env = loadEnv({
|
|
DATABASE_URL: base.DATABASE_URL,
|
|
NODE_ENV: "production",
|
|
BETTER_AUTH_SECRET: "prod-auth-secret-with-enough-length",
|
|
ENCRYPTION_KEY: "prod-encryption-key-with-enough-length",
|
|
SCREEN_PROXY_SECRET: "prod-screen-proxy-secret-with-enough-length",
|
|
SANDBOX_PROVIDER: "docker",
|
|
});
|
|
expect(env.sandboxProvider).toBe("none");
|
|
expect(env.sandboxSupervisorToken).toBeUndefined();
|
|
});
|
|
|
|
it("requires a dedicated supervisor token when Docker stays selected", () => {
|
|
expect(() =>
|
|
loadEnv({
|
|
DATABASE_URL: base.DATABASE_URL,
|
|
NODE_ENV: "production",
|
|
BETTER_AUTH_SECRET: "prod-auth-secret-with-enough-length",
|
|
ENCRYPTION_KEY: "prod-encryption-key-with-enough-length",
|
|
SCREEN_PROXY_SECRET: "prod-screen-proxy-secret-with-enough-length",
|
|
SANDBOX_PROVIDER: "docker",
|
|
SANDBOX_SUPERVISOR_TOKEN: "too-short",
|
|
}),
|
|
).toThrow(/SANDBOX_SUPERVISOR_TOKEN/);
|
|
});
|
|
|
|
it("exposes a deployed git revision when GIT_SHA is set", () => {
|
|
expect(loadEnv(base).gitSha).toBeUndefined();
|
|
expect(loadEnv({ ...base, GIT_SHA: " 3c6e209 " }).gitSha).toBe("3c6e209");
|
|
expect(loadEnv({ ...base, RAKAZO_GIT_SHA: "abc1234" }).gitSha).toBe("abc1234");
|
|
});
|
|
|
|
it("loads optional updater sidecar wiring without requiring the token at boot", () => {
|
|
expect(loadEnv(base).updaterUrl).toBeUndefined();
|
|
expect(loadEnv(base).updaterToken).toBeUndefined();
|
|
const env = loadEnv({
|
|
...base,
|
|
RAKAZO_UPDATER_URL: " http://updater:7092 ",
|
|
RAKAZO_UPDATER_TOKEN: " fake-review-updater-token-000000000000 ",
|
|
});
|
|
expect(env.updaterUrl).toBe("http://updater:7092");
|
|
expect(env.updaterToken).toBe("fake-review-updater-token-000000000000");
|
|
});
|
|
|
|
it("loads SMTP configuration and keeps the email emulator out of production", () => {
|
|
expect(
|
|
loadEnv({
|
|
...base,
|
|
SMTP_URL: " smtps://user:secret@smtp.example.test:465 ",
|
|
EMAIL_FROM: " Rakazo <no-reply@example.test> ",
|
|
EMAIL_EMULATOR: "true",
|
|
}),
|
|
).toMatchObject({
|
|
smtpUrl: "smtps://user:secret@smtp.example.test:465",
|
|
emailFrom: "Rakazo <no-reply@example.test>",
|
|
emailEmulator: true,
|
|
});
|
|
expect(
|
|
loadEnv({
|
|
...base,
|
|
NODE_ENV: "production",
|
|
BETTER_AUTH_SECRET: "prod-auth-secret-with-enough-length",
|
|
ENCRYPTION_KEY: "prod-encryption-key-with-enough-length",
|
|
SCREEN_PROXY_SECRET: "prod-screen-proxy-secret-with-enough-length",
|
|
SANDBOX_PROVIDER: "none",
|
|
EMAIL_EMULATOR: "true",
|
|
}).emailEmulator,
|
|
).toBe(false);
|
|
expect(loadEnv({ ...base, NODE_ENV: "development" }).nodeEnv).toBe("development");
|
|
});
|
|
});
|