traicebox 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  ## Prerequisites
19
19
 
20
- Traicebox requires [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) to be installed and running on your system.
20
+ Traicebox requires either [Docker](https://www.docker.com/) with Docker Compose or [Podman](https://podman.io/) with Podman Compose to be installed and running on your system.
21
21
 
22
22
  ## Installation
23
23
 
@@ -219,10 +219,11 @@ By default, it is located at:
219
219
 
220
220
  You can customize the stack behavior by creating or editing `${TRAICEBOX_HOME}/traicebox.yaml`.
221
221
 
222
- | Option | Type | Default | Description |
223
- | :----- | :------- | :---------- | :----------------------------------------------------------- |
224
- | `host` | `string` | `127.0.0.1` | The host address that Traicebox services will bind to. |
225
- | `port` | `number` | `5483` | The port that Traicebox services will be accessible through. |
222
+ | Option | Type | Default | Description |
223
+ | :-------- | :------- | :---------- | :------------------------------------------------------------------------------- |
224
+ | `host` | `string` | `127.0.0.1` | The host address that Traicebox services will bind to. |
225
+ | `port` | `number` | `5483` | The port that Traicebox services will be accessible through. |
226
+ | `runtime` | `string` | auto-detect | Optional. Force Traicebox to use `docker` or `podman` instead of auto-detecting. |
226
227
 
227
228
  Example:
228
229
 
@@ -137,7 +137,7 @@ services:
137
137
  LANGFUSE_INIT_USER_NAME: ${LANGFUSE_INIT_USER_NAME}
138
138
  LANGFUSE_INIT_USER_PASSWORD: ${LANGFUSE_INIT_USER_PASSWORD}
139
139
  healthcheck:
140
- test: ["CMD", "node", "-e", "const host = process.env.HOSTNAME || 'localhost'; fetch('http://' + host + ':3000/api/auth/csrf').then(async (r) => { const ok = r.ok && (await r.text()).includes('csrfToken'); process.exit(ok ? 0 : 1); }).catch(() => process.exit(1))"]
140
+ test: ["CMD", "node", "-e", "const { hostname } = require('node:os'); fetch('http://' + hostname() + ':3000/api/auth/csrf').then(async (r) => { const ok = r.ok && (await r.text()).includes('csrfToken'); process.exit(ok ? 0 : 1); }).catch(() => process.exit(1))"]
141
141
  interval: 5s
142
142
  timeout: 5s
143
143
  retries: 24
package/dist/index.js CHANGED
@@ -12894,32 +12894,73 @@ var LANGFUSE_HOST = "langfuse.localhost";
12894
12894
 
12895
12895
  // cli/lib/docker.ts
12896
12896
  import { spawn } from "node:child_process";
12897
- async function ensureDockerReady(executor = executeCommand) {
12898
- const composeResult = await executor(["docker", "compose", "version"]);
12899
- validateDockerComposeResult(composeResult);
12900
- const engineResult = await executor([
12901
- "docker",
12902
- "info",
12903
- "--format",
12904
- "{{.ServerVersion}}"
12905
- ]);
12906
- validateDockerEngineResult(engineResult);
12907
- }
12908
- function validateDockerComposeResult(result) {
12909
- if (result.errorCode === "ENOENT") {
12910
- throw new Error("Docker CLI is not available. Install Docker and try again.");
12897
+ var SUPPORTED_CONTAINER_RUNTIME_COMMANDS = [
12898
+ "docker",
12899
+ "podman"
12900
+ ];
12901
+ var SUPPORTED_RUNTIMES = [
12902
+ { command: "docker" },
12903
+ { command: "podman" }
12904
+ ];
12905
+ async function ensureDockerReady(executor = executeCommand, preferredRuntime) {
12906
+ if (preferredRuntime) {
12907
+ return ensurePreferredRuntimeReady(preferredRuntime, executor);
12908
+ }
12909
+ let sawInstalledRuntime = false;
12910
+ let sawComposeCommand = false;
12911
+ for (const runtime of SUPPORTED_RUNTIMES) {
12912
+ const composeResult = await executor([
12913
+ runtime.command,
12914
+ "compose",
12915
+ "version"
12916
+ ]);
12917
+ if (composeResult.errorCode === "ENOENT") {
12918
+ continue;
12919
+ }
12920
+ sawInstalledRuntime = true;
12921
+ if (composeResult.exitCode !== 0) {
12922
+ continue;
12923
+ }
12924
+ sawComposeCommand = true;
12925
+ const engineResult = await executor(getEngineProbeCommand(runtime.command));
12926
+ if (engineResult.exitCode === 0) {
12927
+ return runtime;
12928
+ }
12911
12929
  }
12912
- if (result.exitCode !== 0) {
12913
- throw new Error("Docker Compose is not available. Check your Docker installation and try again.");
12930
+ if (!sawInstalledRuntime) {
12931
+ throw new Error("Neither Docker nor Podman CLI is available. Install one and try again.");
12914
12932
  }
12933
+ if (!sawComposeCommand) {
12934
+ throw new Error("Neither Docker Compose nor Podman Compose is available. Check your installation and try again.");
12935
+ }
12936
+ throw new Error("Docker or Podman is installed but the engine is not reachable. Start it and try again.");
12915
12937
  }
12916
- function validateDockerEngineResult(result) {
12917
- if (result.errorCode === "ENOENT") {
12918
- throw new Error("Docker CLI is not available. Install Docker and try again.");
12938
+ async function ensurePreferredRuntimeReady(preferredRuntime, executor) {
12939
+ const composeResult = await executor([
12940
+ preferredRuntime,
12941
+ "compose",
12942
+ "version"
12943
+ ]);
12944
+ if (composeResult.errorCode === "ENOENT") {
12945
+ throw new Error(`${displayRuntimeName(preferredRuntime)} CLI is not available. Install ${displayRuntimeName(preferredRuntime)} and try again.`);
12946
+ }
12947
+ if (composeResult.exitCode !== 0) {
12948
+ throw new Error(`${displayRuntimeName(preferredRuntime)} Compose is not available. Check your ${displayRuntimeName(preferredRuntime)} installation and try again.`);
12919
12949
  }
12920
- if (result.exitCode !== 0) {
12921
- throw new Error("Docker is installed but the engine is not reachable. Start Docker and try again.");
12950
+ const engineResult = await executor(getEngineProbeCommand(preferredRuntime));
12951
+ if (engineResult.errorCode === "ENOENT") {
12952
+ throw new Error(`${displayRuntimeName(preferredRuntime)} CLI is not available. Install ${displayRuntimeName(preferredRuntime)} and try again.`);
12922
12953
  }
12954
+ if (engineResult.exitCode !== 0) {
12955
+ throw new Error(`${displayRuntimeName(preferredRuntime)} is installed but the engine is not reachable. Start ${displayRuntimeName(preferredRuntime)} and try again.`);
12956
+ }
12957
+ return { command: preferredRuntime };
12958
+ }
12959
+ function displayRuntimeName(runtime) {
12960
+ return runtime === "docker" ? "Docker" : "Podman";
12961
+ }
12962
+ function getEngineProbeCommand(runtime) {
12963
+ return runtime === "docker" ? [runtime, "info", "--format", "{{.ServerVersion}}"] : [runtime, "info"];
12923
12964
  }
12924
12965
  async function executeCommand(command2) {
12925
12966
  try {
@@ -13053,6 +13094,7 @@ function initializeRuntime() {
13053
13094
  xdgConfigHome: env2.XDG_CONFIG_HOME
13054
13095
  });
13055
13096
  const configPath = join2(resolvedHome.home, "traicebox.yaml");
13097
+ let containerRuntime;
13056
13098
  if (existsSync2(configPath)) {
13057
13099
  try {
13058
13100
  const configObj = import_yaml2.default.parse(readFileSync4(configPath, "utf-8"));
@@ -13063,6 +13105,7 @@ function initializeRuntime() {
13063
13105
  if (!env2.TRAICEBOX_PORT && (typeof configObj.port === "string" || typeof configObj.port === "number")) {
13064
13106
  env2.TRAICEBOX_PORT = String(configObj.port);
13065
13107
  }
13108
+ containerRuntime = resolveConfiguredContainerRuntime(configObj.runtime);
13066
13109
  }
13067
13110
  } catch {
13068
13111
  fail(`Failed to parse config file: ${configPath}`);
@@ -13071,7 +13114,8 @@ function initializeRuntime() {
13071
13114
  runtimeContext = {
13072
13115
  home: resolvedHome.home,
13073
13116
  dev: resolvedHome.dev,
13074
- stackEnv: resolveStackEnv(env2)
13117
+ stackEnv: resolveStackEnv(env2),
13118
+ containerRuntime
13075
13119
  };
13076
13120
  return runtimeContext;
13077
13121
  }
@@ -13130,6 +13174,15 @@ function isTruthy(value) {
13130
13174
  function toAbsolutePath(cwd, value) {
13131
13175
  return isAbsolute(value) ? value : join2(cwd, value);
13132
13176
  }
13177
+ function resolveConfiguredContainerRuntime(value) {
13178
+ if (value == null) {
13179
+ return;
13180
+ }
13181
+ if (typeof value === "string" && SUPPORTED_CONTAINER_RUNTIME_COMMANDS.includes(value)) {
13182
+ return value;
13183
+ }
13184
+ throw new Error("Invalid runtime in traicebox.yaml. Expected 'docker' or 'podman'.");
13185
+ }
13133
13186
 
13134
13187
  // cli/handlers/docker-lifecycle.ts
13135
13188
  var TMPDIR = process.env.TMPDIR ?? "/tmp";
@@ -13218,7 +13271,7 @@ function cleanupOpenAICompatibleApiKeySecretMaterialSync() {
13218
13271
  activeSecretDirectory = null;
13219
13272
  }
13220
13273
  }
13221
- async function runDockerCompose(args, logs) {
13274
+ async function runDockerComposeWithRuntime(containerRuntime, args, logs) {
13222
13275
  const runtime = getRuntime();
13223
13276
  const env2 = {
13224
13277
  ...process.env,
@@ -13226,7 +13279,7 @@ async function runDockerCompose(args, logs) {
13226
13279
  [SECRET_FILE_ENV]: prepareOpenAICompatibleApiKeySecretFile()
13227
13280
  };
13228
13281
  try {
13229
- const proc = spawn2("docker", ["compose", ...args], {
13282
+ const proc = spawn2(containerRuntime.command, ["compose", ...args], {
13230
13283
  cwd: runtime.home,
13231
13284
  env: env2,
13232
13285
  stdio: [
@@ -13258,7 +13311,7 @@ async function runDockerCompose(args, logs) {
13258
13311
  if (stderr.trim()) {
13259
13312
  process.stderr.write(stderr);
13260
13313
  }
13261
- fail(`docker compose ${args.join(" ")} failed with exit code ${exitCode}`);
13314
+ fail(`${containerRuntime.command} compose ${args.join(" ")} failed with exit code ${exitCode}`);
13262
13315
  }
13263
13316
  } finally {
13264
13317
  activeDockerComposeProcess = null;
@@ -13299,7 +13352,8 @@ function installDockerLifecycleSignalHandlers() {
13299
13352
  }
13300
13353
  async function runStackCommand(command2, logs) {
13301
13354
  ensureActiveHomeExists();
13302
- await ensureDockerReady().catch((error) => {
13355
+ const runtime = getRuntime();
13356
+ const containerRuntime = await ensureDockerReady(undefined, runtime.containerRuntime).catch((error) => {
13303
13357
  fail(error instanceof Error ? error.message : String(error));
13304
13358
  });
13305
13359
  if (command2 === "start" || command2 === "restart") {
@@ -13318,17 +13372,17 @@ async function runStackCommand(command2, logs) {
13318
13372
  ...LONG_RUNNING_SERVICES
13319
13373
  ] : ["up", "-d", "--remove-orphans", "--wait", ...LONG_RUNNING_SERVICES];
13320
13374
  await runStep("Starting stack", logs, async () => {
13321
- await runDockerCompose(upArgs, logs);
13375
+ await runDockerComposeWithRuntime(containerRuntime, upArgs, logs);
13322
13376
  });
13323
13377
  await runStep("Bootstrapping LiteLLM", logs, async () => {
13324
- await runDockerCompose(["run", "--rm", "--no-deps", "litellm-bootstrap"], logs);
13378
+ await runDockerComposeWithRuntime(containerRuntime, ["run", "--rm", "--no-deps", "litellm-bootstrap"], logs);
13325
13379
  });
13326
13380
  printAccessInfo(command2);
13327
13381
  return;
13328
13382
  }
13329
13383
  const downArgs = command2 === "stop" ? ["stop"] : ["down", "--remove-orphans", "--volumes"];
13330
13384
  await runStep(command2 === "stop" ? "Stopping stack" : "Destroying stack", logs, async () => {
13331
- await runDockerCompose(downArgs, logs);
13385
+ await runDockerComposeWithRuntime(containerRuntime, downArgs, logs);
13332
13386
  });
13333
13387
  }
13334
13388
 
@@ -13464,7 +13518,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
13464
13518
  var Caddyfile_default = "./Caddyfile-at2nzhxs.";
13465
13519
 
13466
13520
  // template/compose.yml
13467
- var compose_default = "./compose-bfc4a606.yml";
13521
+ var compose_default = "./compose-r1mahfqb.yml";
13468
13522
 
13469
13523
  // template/langfuse-proxy/Dockerfile
13470
13524
  var Dockerfile_default = "./Dockerfile-bzexf8bh.";
@@ -13565,10 +13619,10 @@ async function writeDevelopmentDotenv(home, stackEnv) {
13565
13619
 
13566
13620
  // cli/handlers/setup.ts
13567
13621
  async function runSetup(force = false) {
13568
- await ensureDockerReady().catch((error) => {
13622
+ const runtime = getRuntime();
13623
+ await ensureDockerReady(undefined, runtime.containerRuntime).catch((error) => {
13569
13624
  fail(error instanceof Error ? error.message : String(error));
13570
13625
  });
13571
- const runtime = getRuntime();
13572
13626
  if (force) {
13573
13627
  rmSync2(runtime.home, { recursive: true, force: true });
13574
13628
  }
@@ -13603,28 +13657,28 @@ await yargs_default(hideBin(process.argv)).scriptName("traicebox").command("setu
13603
13657
  alias: "l",
13604
13658
  type: "boolean",
13605
13659
  default: false,
13606
- describe: "Show docker compose output while starting"
13660
+ describe: "Show compose output while starting"
13607
13661
  }), async (argv) => {
13608
13662
  await runStackCommand("start", argv.logs);
13609
13663
  }).command("stop", "Stop the stack", (cmd) => cmd.option("logs", {
13610
13664
  alias: "l",
13611
13665
  type: "boolean",
13612
13666
  default: false,
13613
- describe: "Show docker compose output while stopping"
13667
+ describe: "Show compose output while stopping"
13614
13668
  }), async (argv) => {
13615
13669
  await runStackCommand("stop", argv.logs);
13616
13670
  }).command("restart", "Recreate the stack and wait until it is ready", (cmd) => cmd.option("logs", {
13617
13671
  alias: "l",
13618
13672
  type: "boolean",
13619
13673
  default: false,
13620
- describe: "Show docker compose output while restarting"
13674
+ describe: "Show compose output while restarting"
13621
13675
  }), async (argv) => {
13622
13676
  await runStackCommand("restart", argv.logs);
13623
13677
  }).command("destroy", "Remove the stack and local data volumes", (cmd) => cmd.option("logs", {
13624
13678
  alias: "l",
13625
13679
  type: "boolean",
13626
13680
  default: false,
13627
- describe: "Show docker compose output while destroying"
13681
+ describe: "Show compose output while destroying"
13628
13682
  }), async (argv) => {
13629
13683
  await runStackCommand("destroy", argv.logs);
13630
13684
  }).command("models <command>", "Manage imported LiteLLM models", (cmd) => cmd.command("import-from-openai-api", "Import models from an OpenAI-compatible endpoint into LiteLLM config", (subcmd) => subcmd.option("endpoint", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traicebox",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "A local developer stack for tracing and session tracking around LLM and AI model workflows",
5
5
  "license": "MIT",
6
6
  "author": "Fardjad Davari <public@fardjad.com>",
@@ -35,7 +35,7 @@
35
35
  "fix": "biome check --write . && dprint fmt"
36
36
  },
37
37
  "devDependencies": {
38
- "@biomejs/biome": "^2.4.12",
38
+ "@biomejs/biome": "^2.4.13",
39
39
  "@types/bun": "^1.3.13",
40
40
  "@types/yargs": "^17.0.35",
41
41
  "dprint": "^0.54.0",