kaizenai 0.5.0 → 0.6.0

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.
@@ -1,12 +1,13 @@
1
1
  // @bun
2
2
  // src/server/cli.ts
3
3
  import { existsSync as existsSync10 } from "fs";
4
- import process9 from "process";
4
+ import process10 from "process";
5
+ import { fileURLToPath } from "url";
5
6
  // package.json
6
7
  var package_default = {
7
8
  name: "kaizenai",
8
9
  type: "module",
9
- version: "0.5.0",
10
+ version: "0.6.0",
10
11
  description: "Local web UI for coding agents",
11
12
  keywords: [
12
13
  "claude",
@@ -43,15 +44,15 @@ var package_default = {
43
44
  bun: ">=1.3.5"
44
45
  },
45
46
  scripts: {
46
- build: "vite build && bun run ./scripts/build-server.ts",
47
+ build: "vite build && bun ./scripts/build-server.ts",
47
48
  check: "tsc --noEmit && bun run build",
48
- dev: "bun run ./scripts/dev.ts",
49
+ dev: "bun ./scripts/dev.ts",
49
50
  "dev:client": "vite --host 0.0.0.0 --port 5174",
50
- "dev:server": "bun run ./scripts/dev-server.ts --no-open --port 5175",
51
+ "dev:server": "bun ./scripts/dev-server.ts --no-open --port 5175",
51
52
  fmt: "prettier package.json README.md --check",
52
53
  "install:dev": "bun install && bun run build && bun install -g .",
53
54
  lint: "tsc --noEmit",
54
- start: "bun run ./dist/server/cli.js",
55
+ start: "bun ./dist/server/cli.js",
55
56
  test: "bun test",
56
57
  typecheck: "tsc --noEmit",
57
58
  prepublishOnly: "bun run build"
@@ -67,6 +68,7 @@ var package_default = {
67
68
  "@xterm/xterm": "^6.0.0",
68
69
  "default-shell": "^2.2.0",
69
70
  "puppeteer-core": "^24.40.0",
71
+ qrcode: "^1.5.4",
70
72
  "react-resizable-panels": "^4.7.3",
71
73
  sonner: "^2.0.7"
72
74
  },
@@ -158,8 +160,8 @@ function getProviderSettingsFilePath(homeDir, env = getRuntimeEnv()) {
158
160
  }
159
161
 
160
162
  // src/server/cli-runtime.ts
161
- import process2 from "process";
162
- import { spawnSync as spawnSync2 } from "child_process";
163
+ import process3 from "process";
164
+ import { spawnSync as spawnSync3 } from "child_process";
163
165
 
164
166
  // src/server/process-utils.ts
165
167
  import { spawn, spawnSync } from "child_process";
@@ -184,6 +186,277 @@ function canOpenMacApp(appName) {
184
186
 
185
187
  // src/shared/ports.ts
186
188
  var PROD_SERVER_PORT = 3210;
189
+ var DEV_CLIENT_PORT = 5174;
190
+
191
+ // src/server/remote-access.ts
192
+ import { spawnSync as spawnSync2 } from "child_process";
193
+ import { isIP } from "net";
194
+ import QRCode from "qrcode";
195
+ var TAILSCALE_COMMAND = "tailscale";
196
+ var HTTPS_PORT = 443;
197
+ var COMMAND_TIMEOUT_MS = 5000;
198
+ function normalizeTailscaleHostname(hostname) {
199
+ if (!hostname)
200
+ return null;
201
+ const normalized = hostname.trim().replace(/\.+$/, "");
202
+ return normalized || null;
203
+ }
204
+ function selectPreferredTailscaleHost(status) {
205
+ const dnsName = normalizeTailscaleHostname(status.Self?.DNSName);
206
+ if (dnsName)
207
+ return dnsName;
208
+ const ip = status.Self?.TailscaleIPs?.find((candidate) => candidate.trim().length > 0)?.trim();
209
+ return ip || null;
210
+ }
211
+ function buildRemoteBaseUrl(host, port, protocol = "http") {
212
+ const url = new URL(`${protocol}://${host}`);
213
+ if (protocol === "https" && port !== HTTPS_PORT || protocol === "http" && port !== 80) {
214
+ url.port = String(port);
215
+ }
216
+ return url.toString().replace(/\/$/, "");
217
+ }
218
+ function buildHandoffUrl(remoteBaseUrl) {
219
+ return new URL("/open", `${remoteBaseUrl}/`).toString();
220
+ }
221
+ function buildRemoteSetupUrl(baseUrl, localPort) {
222
+ return buildRemoteSetupUrlWithOptions(baseUrl, localPort, {});
223
+ }
224
+ function buildRemoteSetupUrlWithOptions(baseUrl, localPort, options) {
225
+ const url = new URL("/remote-setup", `${baseUrl}/`);
226
+ url.searchParams.set("localPort", String(localPort));
227
+ if (options.setupReason) {
228
+ url.searchParams.set("reason", options.setupReason);
229
+ }
230
+ if (options.setupCommand) {
231
+ url.searchParams.set("setupCommand", options.setupCommand);
232
+ }
233
+ return url.toString();
234
+ }
235
+ function createRemoteAccessWarning(reason) {
236
+ if (reason === "missing") {
237
+ return "remote mode is available, but Tailscale is not installed. Install Tailscale to print a stable handoff QR code.";
238
+ }
239
+ if (reason === "disconnected") {
240
+ return "remote mode bound all interfaces, but Tailscale is not connected. Run `tailscale up` to enable QR handoff.";
241
+ }
242
+ if (reason === "https_requires_dns") {
243
+ return "Tailscale HTTPS handoff requires this machine\u2019s MagicDNS name. Falling back to HTTP because only a Tailscale IP was available.";
244
+ }
245
+ if (reason === "serve_unavailable") {
246
+ return "Tailscale HTTPS Serve was unavailable, so Kaizen fell back to an HTTP handoff URL.";
247
+ }
248
+ if (reason === "serve_not_enabled") {
249
+ return "Tailscale HTTPS Serve is not enabled on this tailnet, so Kaizen fell back to an HTTP handoff URL.";
250
+ }
251
+ if (reason === "serve_conflict") {
252
+ return "Tailscale HTTPS Serve is already configured for another local service on this machine, so Kaizen fell back to an HTTP handoff URL.";
253
+ }
254
+ if (reason === "serve_permission_denied") {
255
+ return "Kaizen could not enable Tailscale HTTPS Serve because this user is not allowed to change Tailscale Serve config yet.";
256
+ }
257
+ if (reason === "serve_failed") {
258
+ return "Kaizen could not enable Tailscale HTTPS Serve and fell back to an HTTP handoff URL.";
259
+ }
260
+ return "remote mode bound all interfaces, but Kaizen could not resolve this machine\u2019s Tailscale address.";
261
+ }
262
+ function readTailscaleStatus() {
263
+ const result = spawnSync2(TAILSCALE_COMMAND, ["status", "--json"], {
264
+ stdio: ["ignore", "pipe", "pipe"],
265
+ encoding: "utf8",
266
+ timeout: COMMAND_TIMEOUT_MS
267
+ });
268
+ if (result.status !== 0) {
269
+ return null;
270
+ }
271
+ try {
272
+ return JSON.parse(result.stdout);
273
+ } catch {
274
+ return null;
275
+ }
276
+ }
277
+ function readTailscaleServeStatus() {
278
+ const result = spawnSync2(TAILSCALE_COMMAND, ["serve", "status", "--json"], {
279
+ stdio: ["ignore", "pipe", "pipe"],
280
+ encoding: "utf8",
281
+ timeout: COMMAND_TIMEOUT_MS
282
+ });
283
+ if (result.status !== 0) {
284
+ return null;
285
+ }
286
+ try {
287
+ return JSON.parse(result.stdout);
288
+ } catch {
289
+ return null;
290
+ }
291
+ }
292
+ function enableTailscaleServeHttps(targetUrl) {
293
+ const result = spawnSync2(TAILSCALE_COMMAND, ["serve", "--bg", "--yes", "--https", String(HTTPS_PORT), targetUrl], {
294
+ stdio: ["ignore", "pipe", "pipe"],
295
+ encoding: "utf8",
296
+ timeout: COMMAND_TIMEOUT_MS
297
+ });
298
+ return {
299
+ ok: result.status === 0,
300
+ output: `${result.stdout ?? ""}
301
+ ${result.stderr ?? ""}`.trim()
302
+ };
303
+ }
304
+ function isRecord(value) {
305
+ return typeof value === "object" && value !== null;
306
+ }
307
+ function isEmptyTailscaleServeStatus(status) {
308
+ const topLevelValues = Object.values(status);
309
+ if (topLevelValues.length === 0)
310
+ return true;
311
+ return topLevelValues.every((value) => isRecord(value) && Object.keys(value).length === 0);
312
+ }
313
+ function buildServeTargetCandidates(localUrl, port) {
314
+ return [
315
+ localUrl,
316
+ `http://127.0.0.1:${String(port)}`,
317
+ `http://localhost:${String(port)}`,
318
+ `127.0.0.1:${String(port)}`,
319
+ `localhost:${String(port)}`,
320
+ String(port)
321
+ ];
322
+ }
323
+ function isKaizenServeConfig(status, localUrl, port, runtimeProfile) {
324
+ const serialized = JSON.stringify(status);
325
+ const currentTargets = buildServeTargetCandidates(localUrl, port);
326
+ if (currentTargets.some((candidate) => serialized.includes(candidate))) {
327
+ return true;
328
+ }
329
+ if (runtimeProfile !== "dev") {
330
+ const defaultDevTargets = [
331
+ `http://127.0.0.1:${String(DEV_CLIENT_PORT)}`,
332
+ `http://localhost:${String(DEV_CLIENT_PORT)}`,
333
+ `127.0.0.1:${String(DEV_CLIENT_PORT)}`,
334
+ `localhost:${String(DEV_CLIENT_PORT)}`
335
+ ];
336
+ return defaultDevTargets.some((candidate) => serialized.includes(candidate));
337
+ }
338
+ return false;
339
+ }
340
+ function canUseTailscaleHttps(host) {
341
+ return isIP(host) === 0 && host.includes(".");
342
+ }
343
+ function createServeOperatorSetupCommand() {
344
+ return "sudo tailscale set --operator=$USER";
345
+ }
346
+ function createDefaultRemoteAccessDeps() {
347
+ return {
348
+ hasCommand,
349
+ readTailscaleStatus,
350
+ readTailscaleServeStatus,
351
+ enableTailscaleServeHttps
352
+ };
353
+ }
354
+ async function resolveRemoteAccessInfo(options, deps = createDefaultRemoteAccessDeps()) {
355
+ const fallback = {
356
+ localUrl: options.localUrl,
357
+ remoteBaseUrl: null,
358
+ handoffUrl: null,
359
+ warning: null,
360
+ setupCommand: null,
361
+ setupReason: null
362
+ };
363
+ if (!deps.hasCommand(TAILSCALE_COMMAND)) {
364
+ return {
365
+ ...fallback,
366
+ warning: createRemoteAccessWarning("missing")
367
+ };
368
+ }
369
+ const status = deps.readTailscaleStatus();
370
+ if (!status) {
371
+ return {
372
+ ...fallback,
373
+ warning: createRemoteAccessWarning("unavailable")
374
+ };
375
+ }
376
+ if (status.BackendState && status.BackendState !== "Running") {
377
+ return {
378
+ ...fallback,
379
+ warning: createRemoteAccessWarning("disconnected")
380
+ };
381
+ }
382
+ const host = selectPreferredTailscaleHost(status);
383
+ if (!host) {
384
+ return {
385
+ ...fallback,
386
+ warning: createRemoteAccessWarning("unavailable")
387
+ };
388
+ }
389
+ let warning = null;
390
+ let remoteBaseUrl;
391
+ let setupCommand = null;
392
+ let setupReason = null;
393
+ const runtimeProfile = options.runtimeProfile ?? getRuntimeProfile();
394
+ if (canUseTailscaleHttps(host)) {
395
+ const serveStatus = deps.readTailscaleServeStatus();
396
+ if (!serveStatus) {
397
+ warning = createRemoteAccessWarning("serve_unavailable");
398
+ remoteBaseUrl = buildRemoteBaseUrl(host, options.port);
399
+ } else if (!isEmptyTailscaleServeStatus(serveStatus) && !isKaizenServeConfig(serveStatus, options.localUrl, options.port, runtimeProfile)) {
400
+ warning = createRemoteAccessWarning("serve_conflict");
401
+ remoteBaseUrl = buildRemoteBaseUrl(host, options.port);
402
+ } else {
403
+ const serveResult = deps.enableTailscaleServeHttps(`http://127.0.0.1:${String(options.port)}`);
404
+ if (serveResult.ok) {
405
+ remoteBaseUrl = buildRemoteBaseUrl(host, HTTPS_PORT, "https");
406
+ } else {
407
+ const output = serveResult.output.toLowerCase();
408
+ if (output.includes("serve config denied") || output.includes("to not require root") || output.includes("set --operator")) {
409
+ warning = createRemoteAccessWarning("serve_permission_denied");
410
+ setupCommand = createServeOperatorSetupCommand();
411
+ setupReason = "serve_permission_denied";
412
+ } else {
413
+ warning = createRemoteAccessWarning(output.includes("serve is not enabled on your tailnet") ? "serve_not_enabled" : "serve_failed");
414
+ }
415
+ remoteBaseUrl = buildRemoteBaseUrl(host, options.port);
416
+ }
417
+ }
418
+ } else {
419
+ warning = createRemoteAccessWarning("https_requires_dns");
420
+ remoteBaseUrl = buildRemoteBaseUrl(host, options.port);
421
+ }
422
+ return {
423
+ ...fallback,
424
+ remoteBaseUrl,
425
+ handoffUrl: buildHandoffUrl(remoteBaseUrl),
426
+ warning,
427
+ setupCommand,
428
+ setupReason
429
+ };
430
+ }
431
+ async function renderTerminalQrCode(url) {
432
+ return QRCode.toString(url, {
433
+ type: "terminal",
434
+ small: true,
435
+ margin: 1
436
+ });
437
+ }
438
+
439
+ // src/server/terminal-output.ts
440
+ import process2 from "process";
441
+ var ANSI_RESET = "\x1B[0m";
442
+ var ANSI_BOLD = "\x1B[1m";
443
+ var ANSI_CYAN = "\x1B[36m";
444
+ var ANSI_YELLOW = "\x1B[33m";
445
+ function shouldUseAnsiColor() {
446
+ return process2.stdout.isTTY === true || process2.stderr.isTTY === true;
447
+ }
448
+ function formatTerminalHighlight(label, value) {
449
+ if (!shouldUseAnsiColor()) {
450
+ return `${label}${value}`;
451
+ }
452
+ return `${ANSI_BOLD}${ANSI_CYAN}${label}${ANSI_RESET}${value}`;
453
+ }
454
+ function formatTerminalWarning(message) {
455
+ if (!shouldUseAnsiColor()) {
456
+ return message;
457
+ }
458
+ return `${ANSI_BOLD}${ANSI_YELLOW}${message}${ANSI_RESET}`;
459
+ }
187
460
 
188
461
  // src/server/restart.ts
189
462
  var CLI_CHILD_MODE_ENV_VAR = "KAIZEN_CLI_MODE";
@@ -240,6 +513,7 @@ function parseChildArgsEnv(value) {
240
513
 
241
514
  // src/server/cli-runtime.ts
242
515
  var MINIMUM_BUN_VERSION = "1.3.5";
516
+ var SUPPRESS_REMOTE_STARTUP_OUTPUT_ENV_VAR = "KAIZEN_SUPPRESS_REMOTE_STARTUP_OUTPUT";
243
517
  function printHelp() {
244
518
  console.log(`${APP_NAME} \u2014 local-only project chat UI
245
519
 
@@ -249,7 +523,7 @@ Usage:
249
523
  Options:
250
524
  --port <number> Port to listen on (default: ${PROD_SERVER_PORT})
251
525
  --host <host> Bind to a specific host or IP
252
- --remote Shortcut for --host 0.0.0.0
526
+ --remote Share to your Tailscale devices and print a QR handoff
253
527
  --strict-port Fail instead of trying another port
254
528
  --no-open Don't open browser automatically
255
529
  --version Print version and exit
@@ -260,6 +534,7 @@ function parseArgs(argv) {
260
534
  let host = "127.0.0.1";
261
535
  let openBrowser = true;
262
536
  let strictPort = false;
537
+ let remoteMode = false;
263
538
  for (let index = 0;index < argv.length; index += 1) {
264
539
  const arg = argv[index];
265
540
  if (arg === "--version" || arg === "-v") {
@@ -286,6 +561,7 @@ function parseArgs(argv) {
286
561
  }
287
562
  if (arg === "--remote") {
288
563
  host = "0.0.0.0";
564
+ remoteMode = true;
289
565
  continue;
290
566
  }
291
567
  if (arg === "--no-open") {
@@ -305,7 +581,8 @@ function parseArgs(argv) {
305
581
  port,
306
582
  host,
307
583
  openBrowser,
308
- strictPort
584
+ strictPort,
585
+ remoteMode
309
586
  }
310
587
  };
311
588
  }
@@ -322,14 +599,25 @@ function compareVersions(currentVersion, latestVersion) {
322
599
  }
323
600
  return 0;
324
601
  }
602
+ function isNpxRuntimePath(value) {
603
+ return /(^|[\\/])_npx([\\/]|$)/.test(value);
604
+ }
605
+ function createUnsupportedNpxInstallResult() {
606
+ return {
607
+ ok: false,
608
+ errorCode: "install_failed",
609
+ userTitle: "Update unavailable in npx",
610
+ userMessage: "Kaizen cannot self-update while running from npx. Rerun `npx kaizenai` to get the latest version, or install it globally with `npm install -g kaizenai`."
611
+ };
612
+ }
325
613
  function normalizeVersion(version) {
326
614
  return version.trim().replace(/^v/i, "").split("-")[0].split(".").map((part) => Number.parseInt(part, 10)).filter((part) => Number.isFinite(part));
327
615
  }
328
616
  async function maybeSelfUpdate(_argv, deps) {
329
- if (process2.env[DISABLE_SELF_UPDATE_ENV_VAR] === "1") {
617
+ if (process3.env[DISABLE_SELF_UPDATE_ENV_VAR] === "1") {
330
618
  return null;
331
619
  }
332
- if (shouldSkipStartupUpdateOnce(process2.env[CLI_SKIP_STARTUP_UPDATE_ONCE_ENV_VAR])) {
620
+ if (shouldSkipStartupUpdateOnce(process3.env[CLI_SKIP_STARTUP_UPDATE_ONCE_ENV_VAR])) {
333
621
  deps.warn(`${LOG_PREFIX} skipping startup update after restart to avoid an update loop`);
334
622
  return null;
335
623
  }
@@ -394,8 +682,34 @@ async function runCli(argv, deps) {
394
682
  const displayHost = bindHost === "127.0.0.1" || bindHost === "0.0.0.0" ? "localhost" : bindHost;
395
683
  const launchUrl = `http://${displayHost}:${port}`;
396
684
  deps.log(`${LOG_PREFIX} listening on http://${bindHost}:${port}`);
685
+ deps.log(`${LOG_PREFIX} local URL: ${launchUrl}`);
397
686
  deps.log(`${LOG_PREFIX} data dir: ${getDataDirDisplay()}`);
398
- const suppressOpenBrowser = process2.env[CLI_SUPPRESS_OPEN_ONCE_ENV_VAR] === "1";
687
+ if (parsedArgs.options.remoteMode && process3.env[SUPPRESS_REMOTE_STARTUP_OUTPUT_ENV_VAR] !== "1") {
688
+ const remoteAccess = await deps.resolveRemoteAccessInfo({
689
+ localUrl: launchUrl,
690
+ port,
691
+ runtimeProfile: getRuntimeProfile()
692
+ });
693
+ deps.log(formatTerminalHighlight(`${LOG_PREFIX} remote setup help: `, buildRemoteSetupUrlWithOptions(launchUrl, port, {
694
+ setupReason: remoteAccess.setupReason,
695
+ setupCommand: remoteAccess.setupCommand
696
+ })));
697
+ if (remoteAccess.warning) {
698
+ deps.warn(formatTerminalWarning(`${LOG_PREFIX} ${remoteAccess.warning}`));
699
+ if (remoteAccess.setupCommand) {
700
+ deps.warn(formatTerminalHighlight(`${LOG_PREFIX} one-time Tailscale fix: `, remoteAccess.setupCommand));
701
+ }
702
+ }
703
+ if (remoteAccess.remoteBaseUrl && remoteAccess.handoffUrl) {
704
+ deps.log(`${LOG_PREFIX} Tailscale URL: ${remoteAccess.remoteBaseUrl}`);
705
+ deps.log(`${LOG_PREFIX} handoff URL: ${remoteAccess.handoffUrl}`);
706
+ deps.log(`${LOG_PREFIX} scan to open on another Tailscale device:`);
707
+ deps.log((await deps.renderTerminalQrCode(remoteAccess.handoffUrl)).trimEnd());
708
+ }
709
+ } else {
710
+ deps.log(formatTerminalHighlight(`${LOG_PREFIX} remote setup help: `, buildRemoteSetupUrl(launchUrl, port)));
711
+ }
712
+ const suppressOpenBrowser = process3.env[CLI_SUPPRESS_OPEN_ONCE_ENV_VAR] === "1";
399
713
  if (parsedArgs.options.openBrowser && !suppressOpenBrowser) {
400
714
  deps.openUrl(launchUrl);
401
715
  }
@@ -405,7 +719,7 @@ async function runCli(argv, deps) {
405
719
  };
406
720
  }
407
721
  function openUrl(url) {
408
- const platform = process2.platform;
722
+ const platform = process3.platform;
409
723
  if (platform === "darwin") {
410
724
  spawnDetached("open", [url]);
411
725
  } else if (platform === "win32") {
@@ -452,16 +766,16 @@ function installPackageVersion(packageName, version) {
452
766
  userMessage: "Kaizen could not find Bun to install the update."
453
767
  };
454
768
  }
455
- const result = spawnSync2("bun", ["install", "-g", `${packageName}@${version}`], {
769
+ const result = spawnSync3("bun", ["install", "-g", `${packageName}@${version}`], {
456
770
  stdio: ["ignore", "pipe", "pipe"],
457
771
  encoding: "utf8"
458
772
  });
459
773
  const stdout = result.stdout ?? "";
460
774
  const stderr = result.stderr ?? "";
461
775
  if (stdout)
462
- process2.stdout.write(stdout);
776
+ process3.stdout.write(stdout);
463
777
  if (stderr)
464
- process2.stderr.write(stderr);
778
+ process3.stderr.write(stderr);
465
779
  if (result.status === 0) {
466
780
  return {
467
781
  ok: true,
@@ -2437,7 +2751,9 @@ class EventStore {
2437
2751
  }
2438
2752
 
2439
2753
  // src/server/agent.ts
2440
- import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
2754
+ import {
2755
+ query as query2
2756
+ } from "@anthropic-ai/claude-agent-sdk";
2441
2757
 
2442
2758
  // src/shared/tools.ts
2443
2759
  function normalizeToolCall(args) {
@@ -5622,7 +5938,7 @@ function codexServiceTierFromModelOptions(modelOptions) {
5622
5938
  import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync, writeFileSync as writeFileSync2 } from "fs";
5623
5939
  import { homedir as homedir4 } from "os";
5624
5940
  import path7 from "path";
5625
- import process3 from "process";
5941
+ import process4 from "process";
5626
5942
 
5627
5943
  // src/server/usage/base-provider-usage.ts
5628
5944
  import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
@@ -6154,7 +6470,7 @@ function collectClaudeUsageScreen(args) {
6154
6470
  stderr: "pipe",
6155
6471
  cwd: args?.cwd,
6156
6472
  env: {
6157
- ...process3.env,
6473
+ ...process4.env,
6158
6474
  ...args?.cwd ? { CLAUDE_USAGE_CWD: args.cwd } : {},
6159
6475
  CLAUDE_USAGE_CONTINUE: args?.continueSession ? "1" : "0"
6160
6476
  }
@@ -6163,7 +6479,7 @@ function collectClaudeUsageScreen(args) {
6163
6479
  }
6164
6480
  function isRunningPid(pid) {
6165
6481
  try {
6166
- process3.kill(pid, 0);
6482
+ process4.kill(pid, 0);
6167
6483
  return true;
6168
6484
  } catch {
6169
6485
  return false;
@@ -6511,7 +6827,7 @@ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync
6511
6827
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
6512
6828
  import { tmpdir as tmpdir3 } from "os";
6513
6829
  import path10 from "path";
6514
- import process5 from "process";
6830
+ import process6 from "process";
6515
6831
  import puppeteer from "puppeteer-core";
6516
6832
 
6517
6833
  // src/server/usage/cursor-cookies.ts
@@ -6520,7 +6836,7 @@ import { copyFileSync, existsSync as existsSync5, mkdtempSync, readdirSync as re
6520
6836
  import { createDecipheriv, pbkdf2Sync } from "crypto";
6521
6837
  import { homedir as homedir6, tmpdir as tmpdir2 } from "os";
6522
6838
  import path9 from "path";
6523
- import process4 from "process";
6839
+ import process5 from "process";
6524
6840
  var CHROME_EPOCH_OFFSET_MS = Date.UTC(1601, 0, 1);
6525
6841
  var CURSOR_SESSION_COOKIE_NAME = "WorkosCursorSessionToken";
6526
6842
  function normalizeCookieDomain(domain) {
@@ -6817,7 +7133,7 @@ function decryptChromiumCookieValue(encryptedValue, key, platform) {
6817
7133
  return null;
6818
7134
  }
6819
7135
  }
6820
- function bootstrapCursorSessionFromBrowser(platform = process4.platform) {
7136
+ function bootstrapCursorSessionFromBrowser(platform = process5.platform) {
6821
7137
  if (platform !== "linux" && platform !== "darwin")
6822
7138
  return null;
6823
7139
  for (const source of discoverChromiumCookieSources(platform)) {
@@ -7151,7 +7467,7 @@ class CursorUsage extends BaseProviderUsage {
7151
7467
  return null;
7152
7468
  }
7153
7469
  }
7154
- async refresh(platform = process5.platform, force = false) {
7470
+ async refresh(platform = process6.platform, force = false) {
7155
7471
  const now = Date.now();
7156
7472
  const persistedEntry = this.loadPersistedEntry();
7157
7473
  const lastRequestedAt = persistedEntry?.lastRequestedAt ?? this.readLastRequestedAt(this.provider);
@@ -7216,7 +7532,7 @@ class CursorUsage extends BaseProviderUsage {
7216
7532
  this.persistUsageEntry(entry);
7217
7533
  return entry;
7218
7534
  }
7219
- async importFromCurl(curlCommand, platform = process5.platform) {
7535
+ async importFromCurl(curlCommand, platform = process6.platform) {
7220
7536
  const imported = importCursorSessionFromCurl(curlCommand);
7221
7537
  if (!imported) {
7222
7538
  return cursorStatusEntry({
@@ -7234,7 +7550,7 @@ class CursorUsage extends BaseProviderUsage {
7234
7550
  this.persistSession(session);
7235
7551
  return this.refresh(platform);
7236
7552
  }
7237
- async signInWithBrowser(platform = process5.platform) {
7553
+ async signInWithBrowser(platform = process6.platform) {
7238
7554
  const executablePath = resolveBrowserExecutable(platform);
7239
7555
  if (!executablePath) {
7240
7556
  return cursorStatusEntry({
@@ -7307,13 +7623,13 @@ function getCursorUsage(dataDir) {
7307
7623
  }
7308
7624
  return _instances3.get(dataDir);
7309
7625
  }
7310
- async function refreshCursorUsage(dataDir, platform = process5.platform, force = false) {
7626
+ async function refreshCursorUsage(dataDir, platform = process6.platform, force = false) {
7311
7627
  return getCursorUsage(dataDir).refresh(platform, force);
7312
7628
  }
7313
- async function importCursorUsageFromCurl(dataDir, curlCommand, platform = process5.platform) {
7629
+ async function importCursorUsageFromCurl(dataDir, curlCommand, platform = process6.platform) {
7314
7630
  return getCursorUsage(dataDir).importFromCurl(curlCommand, platform);
7315
7631
  }
7316
- async function signInToCursorWithBrowser(dataDir, platform = process5.platform) {
7632
+ async function signInToCursorWithBrowser(dataDir, platform = process6.platform) {
7317
7633
  return getCursorUsage(dataDir).signInWithBrowser(platform);
7318
7634
  }
7319
7635
 
@@ -7461,10 +7777,21 @@ function formatRecoveredAskUserQuestionFollowUp(tool, result) {
7461
7777
  ].join(`
7462
7778
  `);
7463
7779
  }
7464
- function planModeFollowUp(result) {
7780
+ function planModeFollowUp(result, plan) {
7465
7781
  let text = "";
7466
7782
  if (result.confirmed) {
7467
- text = result.message ? `Proceed with the approved plan. Additional guidance: ${result.message}` : "Proceed with the approved plan.";
7783
+ const trimmedPlan = plan?.trim();
7784
+ const sections = [
7785
+ trimmedPlan ? "Proceed with the approved plan below." : "Proceed with the approved plan."
7786
+ ];
7787
+ if (trimmedPlan) {
7788
+ sections.push("", "Approved plan:", trimmedPlan);
7789
+ }
7790
+ if (result.message) {
7791
+ sections.push("", `Additional guidance: ${result.message}`);
7792
+ }
7793
+ text = sections.join(`
7794
+ `);
7468
7795
  } else {
7469
7796
  text = result.message ? `Revise the plan using this feedback: ${result.message}` : "Revise the plan using this feedback.";
7470
7797
  }
@@ -7560,7 +7887,14 @@ function normalizeClaudeStreamMessage(message) {
7560
7887
  ];
7561
7888
  }
7562
7889
  if (message.type === "system" && message.subtype === "status" && typeof message.status === "string") {
7563
- return [timestamped3({ kind: "status", messageId, status: message.status, debugRaw })];
7890
+ return [
7891
+ timestamped3({
7892
+ kind: "status",
7893
+ messageId,
7894
+ status: message.status,
7895
+ debugRaw
7896
+ })
7897
+ ];
7564
7898
  }
7565
7899
  if (message.type === "system" && message.subtype === "compact_boundary") {
7566
7900
  return [timestamped3({ kind: "compact_boundary", messageId, debugRaw })];
@@ -7569,7 +7903,14 @@ function normalizeClaudeStreamMessage(message) {
7569
7903
  return [timestamped3({ kind: "context_cleared", messageId, debugRaw })];
7570
7904
  }
7571
7905
  if (message.type === "user" && message.message?.role === "user" && typeof message.message.content === "string" && message.message.content.startsWith("This session is being continued")) {
7572
- return [timestamped3({ kind: "compact_summary", messageId, summary: message.message.content, debugRaw })];
7906
+ return [
7907
+ timestamped3({
7908
+ kind: "compact_summary",
7909
+ messageId,
7910
+ summary: message.message.content,
7911
+ debugRaw
7912
+ })
7913
+ ];
7573
7914
  }
7574
7915
  return [];
7575
7916
  }
@@ -8207,7 +8548,7 @@ class AgentCoordinator {
8207
8548
  await this.store.setSessionToken(command.chatId, null);
8208
8549
  await this.store.appendMessage(command.chatId, timestamped3({ kind: "context_cleared" }));
8209
8550
  }
8210
- const followUp = planModeFollowUp(result);
8551
+ const followUp = planModeFollowUp(result, recoveredPending.input.plan);
8211
8552
  const messageEntry = timestamped3({
8212
8553
  kind: "user_prompt",
8213
8554
  content: followUp.content
@@ -8255,7 +8596,7 @@ class AgentCoordinator {
8255
8596
  await this.store.appendMessage(command.chatId, timestamped3({ kind: "context_cleared" }));
8256
8597
  }
8257
8598
  if (shouldUseSyntheticPlanFollowUp(active.provider, pending.tool)) {
8258
- active.postToolFollowUp = planModeFollowUp(result);
8599
+ active.postToolFollowUp = planModeFollowUp(result, pending.tool.input.plan);
8259
8600
  }
8260
8601
  }
8261
8602
  pending.resolve(command.result);
@@ -9941,17 +10282,17 @@ function formatDisplayPath3(filePath) {
9941
10282
 
9942
10283
  // src/server/machine-name.ts
9943
10284
  import { hostname } from "os";
9944
- import process6 from "process";
9945
- import { spawnSync as spawnSync3 } from "child_process";
10285
+ import process7 from "process";
10286
+ import { spawnSync as spawnSync4 } from "child_process";
9946
10287
  function runAndRead(command, args) {
9947
- const result = spawnSync3(command, args, { encoding: "utf8" });
10288
+ const result = spawnSync4(command, args, { encoding: "utf8" });
9948
10289
  if (result.status !== 0)
9949
10290
  return null;
9950
10291
  const value = result.stdout.trim();
9951
10292
  return value || null;
9952
10293
  }
9953
10294
  function getMachineDisplayName() {
9954
- if (process6.platform === "darwin") {
10295
+ if (process7.platform === "darwin") {
9955
10296
  const computerName = runAndRead("scutil", ["--get", "ComputerName"]);
9956
10297
  if (computerName) {
9957
10298
  return computerName;
@@ -9963,15 +10304,15 @@ function getMachineDisplayName() {
9963
10304
 
9964
10305
  // src/server/terminal-manager.ts
9965
10306
  import path16 from "path";
9966
- import process7 from "process";
10307
+ import process8 from "process";
9967
10308
  import defaultShell, { detectDefaultShell } from "default-shell";
9968
10309
  import { Terminal } from "@xterm/headless";
9969
10310
  import { SerializeAddon } from "@xterm/addon-serialize";
9970
10311
  var DEFAULT_COLS = 80;
9971
10312
  var DEFAULT_ROWS = 24;
9972
- var DEFAULT_SCROLLBACK = 1000;
10313
+ var DEFAULT_SCROLLBACK = 1e4;
9973
10314
  var MIN_SCROLLBACK = 500;
9974
- var MAX_SCROLLBACK = 5000;
10315
+ var MAX_SCROLLBACK = 50000;
9975
10316
  var FOCUS_IN_SEQUENCE = "\x1B[I";
9976
10317
  var FOCUS_OUT_SEQUENCE = "\x1B[O";
9977
10318
  var MODE_SEQUENCE_TAIL_LENGTH = 16;
@@ -9991,14 +10332,14 @@ function resolveShell() {
9991
10332
  } catch {
9992
10333
  if (defaultShell)
9993
10334
  return defaultShell;
9994
- if (process7.platform === "win32") {
9995
- return process7.env.ComSpec || "cmd.exe";
10335
+ if (process8.platform === "win32") {
10336
+ return process8.env.ComSpec || "cmd.exe";
9996
10337
  }
9997
- return process7.env.SHELL || "/bin/sh";
10338
+ return process8.env.SHELL || "/bin/sh";
9998
10339
  }
9999
10340
  }
10000
10341
  function resolveShellArgs(shellPath) {
10001
- if (process7.platform === "win32") {
10342
+ if (process8.platform === "win32") {
10002
10343
  return [];
10003
10344
  }
10004
10345
  const shellName = path16.basename(shellPath);
@@ -10009,7 +10350,7 @@ function resolveShellArgs(shellPath) {
10009
10350
  }
10010
10351
  function createTerminalEnv() {
10011
10352
  return {
10012
- ...process7.env,
10353
+ ...process8.env,
10013
10354
  TERM: "xterm-256color",
10014
10355
  COLORTERM: "truecolor"
10015
10356
  };
@@ -10034,9 +10375,9 @@ function killTerminalProcessTree(subprocess) {
10034
10375
  const pid = subprocess.pid;
10035
10376
  if (typeof pid !== "number")
10036
10377
  return;
10037
- if (process7.platform !== "win32") {
10378
+ if (process8.platform !== "win32") {
10038
10379
  try {
10039
- process7.kill(-pid, "SIGKILL");
10380
+ process8.kill(-pid, "SIGKILL");
10040
10381
  return;
10041
10382
  } catch {}
10042
10383
  }
@@ -10050,9 +10391,9 @@ function signalTerminalProcessGroup(subprocess, signal) {
10050
10391
  const pid = subprocess.pid;
10051
10392
  if (typeof pid !== "number")
10052
10393
  return false;
10053
- if (process7.platform !== "win32") {
10394
+ if (process8.platform !== "win32") {
10054
10395
  try {
10055
- process7.kill(-pid, signal);
10396
+ process8.kill(-pid, signal);
10056
10397
  return true;
10057
10398
  } catch {}
10058
10399
  }
@@ -10074,7 +10415,7 @@ class TerminalManager {
10074
10415
  };
10075
10416
  }
10076
10417
  createTerminal(args) {
10077
- if (process7.platform === "win32") {
10418
+ if (process8.platform === "win32") {
10078
10419
  throw new Error("Embedded terminal is currently supported on macOS/Linux only.");
10079
10420
  }
10080
10421
  if (typeof Bun.Terminal !== "function") {
@@ -10096,7 +10437,12 @@ class TerminalManager {
10096
10437
  const rows = normalizeTerminalDimension(args.rows, DEFAULT_ROWS);
10097
10438
  const scrollback = clampScrollback(args.scrollback);
10098
10439
  const title = path16.basename(shell) || "shell";
10099
- const headless = new Terminal({ cols, rows, scrollback, allowProposedApi: true });
10440
+ const headless = new Terminal({
10441
+ cols,
10442
+ rows,
10443
+ scrollback,
10444
+ allowProposedApi: true
10445
+ });
10100
10446
  const serializeAddon = new SerializeAddon;
10101
10447
  headless.loadAddon(serializeAddon);
10102
10448
  const session = {
@@ -10241,7 +10587,9 @@ class TerminalManager {
10241
10587
  cols: session.cols,
10242
10588
  rows: session.rows,
10243
10589
  scrollback: session.scrollback,
10244
- serializedState: session.serializeAddon.serialize({ scrollback: session.scrollback }),
10590
+ serializedState: session.serializeAddon.serialize({
10591
+ scrollback: session.scrollback
10592
+ }),
10245
10593
  status: session.status,
10246
10594
  exitCode: session.exitCode
10247
10595
  };
@@ -10844,14 +11192,14 @@ function isClientEnvelope(value) {
10844
11192
  // src/server/external-open.ts
10845
11193
  import { stat as stat2 } from "fs/promises";
10846
11194
  import path18 from "path";
10847
- import process8 from "process";
11195
+ import process9 from "process";
10848
11196
  var DEFAULT_EDITOR_SETTINGS = {
10849
11197
  preset: "cursor",
10850
11198
  commandTemplate: "cursor {path}"
10851
11199
  };
10852
11200
  async function openExternal(command) {
10853
11201
  const resolvedPath = resolveLocalPath(command.localPath);
10854
- const platform = process8.platform;
11202
+ const platform = process9.platform;
10855
11203
  const info = command.action === "open_editor" || command.action === "open_finder" ? await stat2(resolvedPath).catch(() => null) : null;
10856
11204
  if (command.action === "open_editor") {
10857
11205
  if (!info) {
@@ -10921,7 +11269,7 @@ async function openExternal(command) {
10921
11269
  }
10922
11270
  }
10923
11271
  async function openUrl2(command) {
10924
- const platform = process8.platform;
11272
+ const platform = process9.platform;
10925
11273
  if (platform === "darwin") {
10926
11274
  spawnDetached("open", [command.url]);
10927
11275
  return;
@@ -12584,12 +12932,14 @@ async function serveStatic(distDir, pathname) {
12584
12932
 
12585
12933
  // src/server/cli.ts
12586
12934
  var packageRootUrl = new URL("../../", import.meta.url);
12935
+ var packageRootPath = fileURLToPath(packageRootUrl);
12587
12936
  var pkg = await Bun.file(new URL("package.json", packageRootUrl)).json();
12588
12937
  var VERSION = pkg.version ?? "0.0.0";
12589
- var ALLOW_SELF_UPDATE = !existsSync10(new URL(".git", packageRootUrl));
12590
- var argv = process9.argv.slice(2);
12938
+ var IS_NPX_RUNTIME = isNpxRuntimePath(packageRootPath);
12939
+ var ALLOW_SELF_UPDATE = !existsSync10(new URL(".git", packageRootUrl)) && !IS_NPX_RUNTIME;
12940
+ var argv = process10.argv.slice(2);
12591
12941
  var resolveExitAction = null;
12592
- var WEB_ONLY_MODE = shouldRunWebOnlyMode(process9.env[CLI_WEB_ONLY_MODE_ENV_VAR]);
12942
+ var WEB_ONLY_MODE = shouldRunWebOnlyMode(process10.env[CLI_WEB_ONLY_MODE_ENV_VAR]);
12593
12943
  var result = await runCli(argv, {
12594
12944
  version: VERSION,
12595
12945
  bunVersion: Bun.version,
@@ -12610,27 +12960,29 @@ var result = await runCli(argv, {
12610
12960
  return started;
12611
12961
  },
12612
12962
  fetchLatestVersion: fetchLatestPackageVersion,
12613
- installVersion: installPackageVersion,
12963
+ installVersion: IS_NPX_RUNTIME ? () => createUnsupportedNpxInstallResult() : installPackageVersion,
12614
12964
  openUrl,
12965
+ resolveRemoteAccessInfo,
12966
+ renderTerminalQrCode,
12615
12967
  log: console.log,
12616
12968
  warn: console.warn
12617
12969
  });
12618
12970
  if (result.kind === "exited") {
12619
- process9.exit(result.code);
12971
+ process10.exit(result.code);
12620
12972
  }
12621
12973
  if (result.kind === "restarting") {
12622
- process9.exit(result.reason === "startup_update" ? CLI_STARTUP_UPDATE_RESTART_EXIT_CODE : CLI_UI_UPDATE_RESTART_EXIT_CODE);
12974
+ process10.exit(result.reason === "startup_update" ? CLI_STARTUP_UPDATE_RESTART_EXIT_CODE : CLI_UI_UPDATE_RESTART_EXIT_CODE);
12623
12975
  }
12624
12976
  var exitAction = await new Promise((resolve2) => {
12625
12977
  resolveExitAction = resolve2;
12626
12978
  const shutdown = () => {
12627
12979
  resolve2("exit");
12628
12980
  };
12629
- process9.once("SIGINT", shutdown);
12630
- process9.once("SIGTERM", shutdown);
12981
+ process10.once("SIGINT", shutdown);
12982
+ process10.once("SIGTERM", shutdown);
12631
12983
  });
12632
12984
  await result.stop();
12633
12985
  if (exitAction === "ui_restart") {
12634
12986
  console.log(`${LOG_PREFIX} current process stopped, handing restart back to supervisor`);
12635
12987
  }
12636
- process9.exit(exitAction === "ui_restart" ? CLI_UI_UPDATE_RESTART_EXIT_CODE : 0);
12988
+ process10.exit(exitAction === "ui_restart" ? CLI_UI_UPDATE_RESTART_EXIT_CODE : 0);