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.
package/dist/cli.js CHANGED
@@ -1166,7 +1166,8 @@ async function runBackground(command, workingDirectory, options) {
1166
1166
  cwd: workingDirectory,
1167
1167
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1168
1168
  sessionId: options.sessionId,
1169
- background: true
1169
+ background: true,
1170
+ name: options.name
1170
1171
  }, workingDirectory);
1171
1172
  const logFile = join2(logDir, "output.log");
1172
1173
  const wrappedCommand = `(${command}) 2>&1 | tee -a ${shellEscape(logFile)}`;
@@ -3989,7 +3990,10 @@ terminals2.post(
3989
3990
  return c.json({ error: "tmux is not installed. Background terminals require tmux." }, 400);
3990
3991
  }
3991
3992
  const workingDirectory = body.cwd || session.workingDirectory;
3992
- const result = await runBackground(body.command, workingDirectory, { sessionId });
3993
+ const result = await runBackground(body.command, workingDirectory, {
3994
+ sessionId,
3995
+ name: body.name
3996
+ });
3993
3997
  return c.json({
3994
3998
  id: result.id,
3995
3999
  name: body.name || null,
@@ -4013,7 +4017,7 @@ terminals2.get("/:sessionId/terminals", async (c) => {
4013
4017
  const running = await isRunning(meta.id);
4014
4018
  return {
4015
4019
  id: meta.id,
4016
- name: null,
4020
+ name: meta.name || null,
4017
4021
  command: meta.command,
4018
4022
  cwd: meta.cwd,
4019
4023
  status: running ? "running" : "stopped",
@@ -4097,10 +4101,17 @@ terminals2.post(
4097
4101
  "/:sessionId/terminals/:terminalId/write",
4098
4102
  zValidator4("json", writeSchema),
4099
4103
  async (c) => {
4100
- return c.json({
4101
- error: "stdin writing not supported in tmux mode. Use tmux send-keys directly if needed.",
4102
- hint: 'tmux send-keys -t spark_{terminalId} "your input"'
4103
- }, 501);
4104
+ const terminalId = c.req.param("terminalId");
4105
+ const body = c.req.valid("json");
4106
+ const isRunning2 = await isRunning(terminalId);
4107
+ if (!isRunning2) {
4108
+ return c.json({ error: "Terminal is not running" }, 400);
4109
+ }
4110
+ const success = await sendInput(terminalId, body.input, { pressEnter: false });
4111
+ if (!success) {
4112
+ return c.json({ error: "Failed to write to terminal" }, 500);
4113
+ }
4114
+ return c.json({ success: true, written: body.input.length });
4104
4115
  }
4105
4116
  );
4106
4117
  terminals2.post("/:sessionId/terminals/kill-all", async (c) => {
@@ -4109,12 +4120,12 @@ terminals2.post("/:sessionId/terminals/kill-all", async (c) => {
4109
4120
  if (!session) {
4110
4121
  return c.json({ error: "Session not found" }, 404);
4111
4122
  }
4112
- const terminalIds = await listSessions();
4123
+ const sessionTerminals = await listSessionTerminals(sessionId, session.workingDirectory);
4113
4124
  let killed = 0;
4114
- for (const id of terminalIds) {
4115
- const meta = await getMeta(id, session.workingDirectory);
4116
- if (meta && meta.sessionId === sessionId) {
4117
- const success = await killTerminal(id);
4125
+ for (const terminal of sessionTerminals) {
4126
+ const isRunning2 = await isRunning(terminal.id);
4127
+ if (isRunning2) {
4128
+ const success = await killTerminal(terminal.id);
4118
4129
  if (success) killed++;
4119
4130
  }
4120
4131
  }
@@ -4467,6 +4478,33 @@ async function findWebPort(preferredPort) {
4467
4478
  }
4468
4479
  return { port: preferredPort, alreadyRunning: false };
4469
4480
  }
4481
+ function hasProductionBuild(webDir) {
4482
+ const buildIdPath = join3(webDir, ".next", "BUILD_ID");
4483
+ return existsSync7(buildIdPath);
4484
+ }
4485
+ function runCommand(command, args, cwd, env) {
4486
+ return new Promise((resolve8) => {
4487
+ const child = spawn(command, args, {
4488
+ cwd,
4489
+ stdio: ["ignore", "pipe", "pipe"],
4490
+ env,
4491
+ shell: true
4492
+ });
4493
+ let output = "";
4494
+ child.stdout?.on("data", (data) => {
4495
+ output += data.toString();
4496
+ });
4497
+ child.stderr?.on("data", (data) => {
4498
+ output += data.toString();
4499
+ });
4500
+ child.on("close", (code) => {
4501
+ resolve8({ success: code === 0, output });
4502
+ });
4503
+ child.on("error", (err) => {
4504
+ resolve8({ success: false, output: err.message });
4505
+ });
4506
+ });
4507
+ }
4470
4508
  async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false) {
4471
4509
  const webDir = getWebDirectory();
4472
4510
  if (!webDir) {
@@ -4478,39 +4516,90 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false) {
4478
4516
  if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
4479
4517
  return { process: null, port: actualPort };
4480
4518
  }
4481
- const useNpm = existsSync7(join3(webDir, "package-lock.json"));
4482
- const command = useNpm ? "npm" : "npx";
4483
- const args = useNpm ? ["run", "dev", "--", "-p", String(actualPort)] : ["next", "dev", "-p", String(actualPort)];
4519
+ const usePnpm = existsSync7(join3(webDir, "pnpm-lock.yaml"));
4520
+ const useNpm = !usePnpm && existsSync7(join3(webDir, "package-lock.json"));
4521
+ const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
4522
+ const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
4523
+ const webEnv = {
4524
+ ...cleanEnv,
4525
+ NEXT_PUBLIC_API_URL: `http://127.0.0.1:${apiPort}`
4526
+ };
4527
+ const isProduction = process.env.NODE_ENV === "production";
4528
+ let command;
4529
+ let args;
4530
+ if (isProduction) {
4531
+ if (!hasProductionBuild(webDir)) {
4532
+ if (!quiet) console.log(" \u{1F4E6} Building Web UI for production...");
4533
+ const buildArgs = pkgManager === "npx" ? ["next", "build"] : ["run", "build"];
4534
+ const buildResult = await runCommand(pkgManager, buildArgs, webDir, webEnv);
4535
+ if (!buildResult.success) {
4536
+ if (!quiet) console.error(" \u274C Web UI build failed");
4537
+ return { process: null, port: actualPort };
4538
+ }
4539
+ if (!quiet) console.log(" \u2713 Web UI build complete");
4540
+ }
4541
+ command = pkgManager;
4542
+ args = pkgManager === "npx" ? ["next", "start", "-p", String(actualPort)] : ["run", "start", "-p", String(actualPort)];
4543
+ } else {
4544
+ command = pkgManager;
4545
+ args = pkgManager === "npx" ? ["next", "dev", "-p", String(actualPort)] : ["run", "dev", "-p", String(actualPort)];
4546
+ }
4484
4547
  const child = spawn(command, args, {
4485
4548
  cwd: webDir,
4486
4549
  stdio: ["ignore", "pipe", "pipe"],
4487
- env: {
4488
- ...process.env,
4489
- NEXT_PUBLIC_API_URL: `http://127.0.0.1:${apiPort}`
4490
- },
4491
- detached: false
4550
+ env: webEnv,
4551
+ detached: false,
4552
+ shell: true
4492
4553
  });
4554
+ const startupTimeout = 3e4;
4493
4555
  let started = false;
4494
- child.stdout?.on("data", (data) => {
4495
- const output = data.toString();
4496
- if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
4497
- started = true;
4498
- if (!quiet) console.log(` \u2713 Web UI running at http://localhost:${actualPort}`);
4499
- }
4500
- });
4501
- if (!quiet) {
4556
+ let exited = false;
4557
+ let exitCode = null;
4558
+ const startedPromise = new Promise((resolve8) => {
4559
+ const timeout = setTimeout(() => {
4560
+ if (!started && !exited) {
4561
+ resolve8(false);
4562
+ }
4563
+ }, startupTimeout);
4564
+ child.stdout?.on("data", (data) => {
4565
+ const output = data.toString();
4566
+ if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
4567
+ started = true;
4568
+ clearTimeout(timeout);
4569
+ resolve8(true);
4570
+ }
4571
+ });
4502
4572
  child.stderr?.on("data", (data) => {
4503
4573
  const output = data.toString();
4504
4574
  if (output.toLowerCase().includes("error")) {
4505
- console.error(` Web UI error: ${output.trim()}`);
4575
+ if (!quiet) console.error(` Web UI error: ${output.trim().slice(0, 200)}`);
4506
4576
  }
4507
4577
  });
4508
- }
4509
- child.on("exit", () => {
4510
- webUIProcess = null;
4578
+ child.on("error", (err) => {
4579
+ if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
4580
+ clearTimeout(timeout);
4581
+ resolve8(false);
4582
+ });
4583
+ child.on("exit", (code) => {
4584
+ exited = true;
4585
+ exitCode = code;
4586
+ if (!started) {
4587
+ clearTimeout(timeout);
4588
+ resolve8(false);
4589
+ }
4590
+ webUIProcess = null;
4591
+ });
4511
4592
  });
4512
4593
  webUIProcess = child;
4513
- return { process: child, port: actualPort };
4594
+ const didStart = await startedPromise;
4595
+ if (!didStart) {
4596
+ if (exited && exitCode !== 0) {
4597
+ if (!quiet) console.error(` \u274C Web UI failed to start (exit code: ${exitCode})`);
4598
+ } else if (!exited) {
4599
+ if (!quiet) console.log(` \u26A0 Web UI startup timed out, continuing anyway...`);
4600
+ }
4601
+ }
4602
+ return { process: child, port: actualPort, started: didStart };
4514
4603
  }
4515
4604
  function stopWebUI() {
4516
4605
  if (webUIProcess) {
@@ -4604,11 +4693,13 @@ async function startServer(options = {}) {
4604
4693
  hostname: host
4605
4694
  });
4606
4695
  let webPort;
4696
+ let webStarted;
4607
4697
  if (options.webUI !== false) {
4608
4698
  const result = await startWebUI(port, options.webPort || DEFAULT_WEB_PORT, options.quiet);
4609
4699
  webPort = result.port;
4700
+ webStarted = result.started;
4610
4701
  }
4611
- return { app, port, host, webPort };
4702
+ return { app, port, host, webPort, webStarted };
4612
4703
  }
4613
4704
  function stopServer() {
4614
4705
  stopWebUI();
@@ -5274,7 +5365,11 @@ async function runChat(options) {
5274
5365
  });
5275
5366
  serverStartedByUs = true;
5276
5367
  const webUrl = `http://localhost:${serverResult.webPort || options.webPort || "6969"}`;
5277
- spinner.succeed(`Web UI: ${chalk.cyan(webUrl)}`);
5368
+ if (serverResult.webStarted === false) {
5369
+ spinner.warn(`Web UI failed to start at ${chalk.cyan(webUrl)}`);
5370
+ } else {
5371
+ spinner.succeed(`Web UI: ${chalk.cyan(webUrl)}`);
5372
+ }
5278
5373
  const cleanup = () => {
5279
5374
  if (serverStartedByUs) {
5280
5375
  stopServer();