sparkecoder 0.1.6 → 0.1.8

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.
@@ -19,6 +19,7 @@ declare function startServer(options?: ServerOptions): Promise<{
19
19
  port: number;
20
20
  host: string;
21
21
  webPort: number | undefined;
22
+ webStarted: boolean | undefined;
22
23
  }>;
23
24
  declare function stopServer(): void;
24
25
 
@@ -1131,7 +1131,8 @@ async function runBackground(command, workingDirectory, options) {
1131
1131
  cwd: workingDirectory,
1132
1132
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1133
1133
  sessionId: options.sessionId,
1134
- background: true
1134
+ background: true,
1135
+ name: options.name
1135
1136
  }, workingDirectory);
1136
1137
  const logFile = join2(logDir, "output.log");
1137
1138
  const wrappedCommand = `(${command}) 2>&1 | tee -a ${shellEscape(logFile)}`;
@@ -3954,7 +3955,10 @@ terminals2.post(
3954
3955
  return c.json({ error: "tmux is not installed. Background terminals require tmux." }, 400);
3955
3956
  }
3956
3957
  const workingDirectory = body.cwd || session.workingDirectory;
3957
- const result = await runBackground(body.command, workingDirectory, { sessionId });
3958
+ const result = await runBackground(body.command, workingDirectory, {
3959
+ sessionId,
3960
+ name: body.name
3961
+ });
3958
3962
  return c.json({
3959
3963
  id: result.id,
3960
3964
  name: body.name || null,
@@ -3978,7 +3982,7 @@ terminals2.get("/:sessionId/terminals", async (c) => {
3978
3982
  const running = await isRunning(meta.id);
3979
3983
  return {
3980
3984
  id: meta.id,
3981
- name: null,
3985
+ name: meta.name || null,
3982
3986
  command: meta.command,
3983
3987
  cwd: meta.cwd,
3984
3988
  status: running ? "running" : "stopped",
@@ -4062,10 +4066,17 @@ terminals2.post(
4062
4066
  "/:sessionId/terminals/:terminalId/write",
4063
4067
  zValidator4("json", writeSchema),
4064
4068
  async (c) => {
4065
- return c.json({
4066
- error: "stdin writing not supported in tmux mode. Use tmux send-keys directly if needed.",
4067
- hint: 'tmux send-keys -t spark_{terminalId} "your input"'
4068
- }, 501);
4069
+ const terminalId = c.req.param("terminalId");
4070
+ const body = c.req.valid("json");
4071
+ const isRunning2 = await isRunning(terminalId);
4072
+ if (!isRunning2) {
4073
+ return c.json({ error: "Terminal is not running" }, 400);
4074
+ }
4075
+ const success = await sendInput(terminalId, body.input, { pressEnter: false });
4076
+ if (!success) {
4077
+ return c.json({ error: "Failed to write to terminal" }, 500);
4078
+ }
4079
+ return c.json({ success: true, written: body.input.length });
4069
4080
  }
4070
4081
  );
4071
4082
  terminals2.post("/:sessionId/terminals/kill-all", async (c) => {
@@ -4074,12 +4085,12 @@ terminals2.post("/:sessionId/terminals/kill-all", async (c) => {
4074
4085
  if (!session) {
4075
4086
  return c.json({ error: "Session not found" }, 404);
4076
4087
  }
4077
- const terminalIds = await listSessions();
4088
+ const sessionTerminals = await listSessionTerminals(sessionId, session.workingDirectory);
4078
4089
  let killed = 0;
4079
- for (const id of terminalIds) {
4080
- const meta = await getMeta(id, session.workingDirectory);
4081
- if (meta && meta.sessionId === sessionId) {
4082
- const success = await killTerminal(id);
4090
+ for (const terminal of sessionTerminals) {
4091
+ const isRunning2 = await isRunning(terminal.id);
4092
+ if (isRunning2) {
4093
+ const success = await killTerminal(terminal.id);
4083
4094
  if (success) killed++;
4084
4095
  }
4085
4096
  }
@@ -4377,6 +4388,33 @@ async function findWebPort(preferredPort) {
4377
4388
  }
4378
4389
  return { port: preferredPort, alreadyRunning: false };
4379
4390
  }
4391
+ function hasProductionBuild(webDir) {
4392
+ const buildIdPath = join3(webDir, ".next", "BUILD_ID");
4393
+ return existsSync7(buildIdPath);
4394
+ }
4395
+ function runCommand(command, args, cwd, env) {
4396
+ return new Promise((resolve7) => {
4397
+ const child = spawn(command, args, {
4398
+ cwd,
4399
+ stdio: ["ignore", "pipe", "pipe"],
4400
+ env,
4401
+ shell: true
4402
+ });
4403
+ let output = "";
4404
+ child.stdout?.on("data", (data) => {
4405
+ output += data.toString();
4406
+ });
4407
+ child.stderr?.on("data", (data) => {
4408
+ output += data.toString();
4409
+ });
4410
+ child.on("close", (code) => {
4411
+ resolve7({ success: code === 0, output });
4412
+ });
4413
+ child.on("error", (err) => {
4414
+ resolve7({ success: false, output: err.message });
4415
+ });
4416
+ });
4417
+ }
4380
4418
  async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false) {
4381
4419
  const webDir = getWebDirectory();
4382
4420
  if (!webDir) {
@@ -4388,39 +4426,90 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false) {
4388
4426
  if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
4389
4427
  return { process: null, port: actualPort };
4390
4428
  }
4391
- const useNpm = existsSync7(join3(webDir, "package-lock.json"));
4392
- const command = useNpm ? "npm" : "npx";
4393
- const args = useNpm ? ["run", "dev", "--", "-p", String(actualPort)] : ["next", "dev", "-p", String(actualPort)];
4429
+ const usePnpm = existsSync7(join3(webDir, "pnpm-lock.yaml"));
4430
+ const useNpm = !usePnpm && existsSync7(join3(webDir, "package-lock.json"));
4431
+ const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
4432
+ const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
4433
+ const webEnv = {
4434
+ ...cleanEnv,
4435
+ NEXT_PUBLIC_API_URL: `http://127.0.0.1:${apiPort}`
4436
+ };
4437
+ const isProduction = process.env.NODE_ENV === "production";
4438
+ let command;
4439
+ let args;
4440
+ if (isProduction) {
4441
+ if (!hasProductionBuild(webDir)) {
4442
+ if (!quiet) console.log(" \u{1F4E6} Building Web UI for production...");
4443
+ const buildArgs = pkgManager === "npx" ? ["next", "build"] : ["run", "build"];
4444
+ const buildResult = await runCommand(pkgManager, buildArgs, webDir, webEnv);
4445
+ if (!buildResult.success) {
4446
+ if (!quiet) console.error(" \u274C Web UI build failed");
4447
+ return { process: null, port: actualPort };
4448
+ }
4449
+ if (!quiet) console.log(" \u2713 Web UI build complete");
4450
+ }
4451
+ command = pkgManager;
4452
+ args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start", "-p", String(actualPort)];
4453
+ } else {
4454
+ command = pkgManager;
4455
+ args = pkgManager === "npx" ? ["next", "dev", "-p", String(actualPort)] : ["run", "dev", "-p", String(actualPort)];
4456
+ }
4394
4457
  const child = spawn(command, args, {
4395
4458
  cwd: webDir,
4396
4459
  stdio: ["ignore", "pipe", "pipe"],
4397
- env: {
4398
- ...process.env,
4399
- NEXT_PUBLIC_API_URL: `http://127.0.0.1:${apiPort}`
4400
- },
4401
- detached: false
4460
+ env: webEnv,
4461
+ detached: false,
4462
+ shell: true
4402
4463
  });
4464
+ const startupTimeout = 3e4;
4403
4465
  let started = false;
4404
- child.stdout?.on("data", (data) => {
4405
- const output = data.toString();
4406
- if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
4407
- started = true;
4408
- if (!quiet) console.log(` \u2713 Web UI running at http://localhost:${actualPort}`);
4409
- }
4410
- });
4411
- if (!quiet) {
4466
+ let exited = false;
4467
+ let exitCode = null;
4468
+ const startedPromise = new Promise((resolve7) => {
4469
+ const timeout = setTimeout(() => {
4470
+ if (!started && !exited) {
4471
+ resolve7(false);
4472
+ }
4473
+ }, startupTimeout);
4474
+ child.stdout?.on("data", (data) => {
4475
+ const output = data.toString();
4476
+ if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
4477
+ started = true;
4478
+ clearTimeout(timeout);
4479
+ resolve7(true);
4480
+ }
4481
+ });
4412
4482
  child.stderr?.on("data", (data) => {
4413
4483
  const output = data.toString();
4414
4484
  if (output.toLowerCase().includes("error")) {
4415
- console.error(` Web UI error: ${output.trim()}`);
4485
+ if (!quiet) console.error(` Web UI error: ${output.trim().slice(0, 200)}`);
4416
4486
  }
4417
4487
  });
4418
- }
4419
- child.on("exit", () => {
4420
- webUIProcess = null;
4488
+ child.on("error", (err) => {
4489
+ if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
4490
+ clearTimeout(timeout);
4491
+ resolve7(false);
4492
+ });
4493
+ child.on("exit", (code) => {
4494
+ exited = true;
4495
+ exitCode = code;
4496
+ if (!started) {
4497
+ clearTimeout(timeout);
4498
+ resolve7(false);
4499
+ }
4500
+ webUIProcess = null;
4501
+ });
4421
4502
  });
4422
4503
  webUIProcess = child;
4423
- return { process: child, port: actualPort };
4504
+ const didStart = await startedPromise;
4505
+ if (!didStart) {
4506
+ if (exited && exitCode !== 0) {
4507
+ if (!quiet) console.error(` \u274C Web UI failed to start (exit code: ${exitCode})`);
4508
+ } else if (!exited) {
4509
+ if (!quiet) console.log(` \u26A0 Web UI startup timed out, continuing anyway...`);
4510
+ }
4511
+ }
4512
+ return { process: child, port: actualPort, started: didStart };
4424
4513
  }
4425
4514
  function stopWebUI() {
4426
4515
  if (webUIProcess) {
@@ -4514,11 +4603,13 @@ async function startServer(options = {}) {
4514
4603
  hostname: host
4515
4604
  });
4516
4605
  let webPort;
4606
+ let webStarted;
4517
4607
  if (options.webUI !== false) {
4518
4608
  const result = await startWebUI(port, options.webPort || DEFAULT_WEB_PORT, options.quiet);
4519
4609
  webPort = result.port;
4610
+ webStarted = result.started;
4520
4611
  }
4521
- return { app, port, host, webPort };
4612
+ return { app, port, host, webPort, webStarted };
4522
4613
  }
4523
4614
  function stopServer() {
4524
4615
  stopWebUI();