kaizenai 0.4.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.
- package/README.md +3 -5
- package/bin/kaizen +49 -9
- package/dist/client/assets/index-DYFTsr9U.js +648 -0
- package/dist/client/assets/index-u00ojO6u.css +32 -0
- package/dist/client/index.html +14 -6
- package/dist/client/manifest-dark.webmanifest +6 -0
- package/dist/client/manifest.webmanifest +6 -0
- package/dist/server/cli-supervisor.js +6 -6
- package/dist/server/cli.js +692 -75
- package/package.json +6 -6
- package/dist/client/assets/index-BR0jGwm_.css +0 -32
- package/dist/client/assets/index-CYqy6l6r.js +0 -628
package/dist/server/cli.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/server/cli.ts
|
|
3
3
|
import { existsSync as existsSync10 } from "fs";
|
|
4
|
-
import
|
|
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.
|
|
10
|
+
version: "0.6.0",
|
|
10
11
|
description: "Local web UI for coding agents",
|
|
11
|
-
license: "MIT",
|
|
12
12
|
keywords: [
|
|
13
13
|
"claude",
|
|
14
14
|
"claude-code",
|
|
@@ -44,15 +44,15 @@ var package_default = {
|
|
|
44
44
|
bun: ">=1.3.5"
|
|
45
45
|
},
|
|
46
46
|
scripts: {
|
|
47
|
-
build: "vite build && bun
|
|
47
|
+
build: "vite build && bun ./scripts/build-server.ts",
|
|
48
48
|
check: "tsc --noEmit && bun run build",
|
|
49
|
-
dev: "bun
|
|
49
|
+
dev: "bun ./scripts/dev.ts",
|
|
50
50
|
"dev:client": "vite --host 0.0.0.0 --port 5174",
|
|
51
|
-
"dev:server": "bun
|
|
51
|
+
"dev:server": "bun ./scripts/dev-server.ts --no-open --port 5175",
|
|
52
52
|
fmt: "prettier package.json README.md --check",
|
|
53
53
|
"install:dev": "bun install && bun run build && bun install -g .",
|
|
54
54
|
lint: "tsc --noEmit",
|
|
55
|
-
start: "bun
|
|
55
|
+
start: "bun ./dist/server/cli.js",
|
|
56
56
|
test: "bun test",
|
|
57
57
|
typecheck: "tsc --noEmit",
|
|
58
58
|
prepublishOnly: "bun run build"
|
|
@@ -68,6 +68,7 @@ var package_default = {
|
|
|
68
68
|
"@xterm/xterm": "^6.0.0",
|
|
69
69
|
"default-shell": "^2.2.0",
|
|
70
70
|
"puppeteer-core": "^24.40.0",
|
|
71
|
+
qrcode: "^1.5.4",
|
|
71
72
|
"react-resizable-panels": "^4.7.3",
|
|
72
73
|
sonner: "^2.0.7"
|
|
73
74
|
},
|
|
@@ -159,8 +160,8 @@ function getProviderSettingsFilePath(homeDir, env = getRuntimeEnv()) {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
// src/server/cli-runtime.ts
|
|
162
|
-
import
|
|
163
|
-
import { spawnSync as
|
|
163
|
+
import process3 from "process";
|
|
164
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
164
165
|
|
|
165
166
|
// src/server/process-utils.ts
|
|
166
167
|
import { spawn, spawnSync } from "child_process";
|
|
@@ -185,6 +186,277 @@ function canOpenMacApp(appName) {
|
|
|
185
186
|
|
|
186
187
|
// src/shared/ports.ts
|
|
187
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
|
+
}
|
|
188
460
|
|
|
189
461
|
// src/server/restart.ts
|
|
190
462
|
var CLI_CHILD_MODE_ENV_VAR = "KAIZEN_CLI_MODE";
|
|
@@ -241,6 +513,7 @@ function parseChildArgsEnv(value) {
|
|
|
241
513
|
|
|
242
514
|
// src/server/cli-runtime.ts
|
|
243
515
|
var MINIMUM_BUN_VERSION = "1.3.5";
|
|
516
|
+
var SUPPRESS_REMOTE_STARTUP_OUTPUT_ENV_VAR = "KAIZEN_SUPPRESS_REMOTE_STARTUP_OUTPUT";
|
|
244
517
|
function printHelp() {
|
|
245
518
|
console.log(`${APP_NAME} \u2014 local-only project chat UI
|
|
246
519
|
|
|
@@ -250,7 +523,7 @@ Usage:
|
|
|
250
523
|
Options:
|
|
251
524
|
--port <number> Port to listen on (default: ${PROD_SERVER_PORT})
|
|
252
525
|
--host <host> Bind to a specific host or IP
|
|
253
|
-
--remote
|
|
526
|
+
--remote Share to your Tailscale devices and print a QR handoff
|
|
254
527
|
--strict-port Fail instead of trying another port
|
|
255
528
|
--no-open Don't open browser automatically
|
|
256
529
|
--version Print version and exit
|
|
@@ -261,6 +534,7 @@ function parseArgs(argv) {
|
|
|
261
534
|
let host = "127.0.0.1";
|
|
262
535
|
let openBrowser = true;
|
|
263
536
|
let strictPort = false;
|
|
537
|
+
let remoteMode = false;
|
|
264
538
|
for (let index = 0;index < argv.length; index += 1) {
|
|
265
539
|
const arg = argv[index];
|
|
266
540
|
if (arg === "--version" || arg === "-v") {
|
|
@@ -287,6 +561,7 @@ function parseArgs(argv) {
|
|
|
287
561
|
}
|
|
288
562
|
if (arg === "--remote") {
|
|
289
563
|
host = "0.0.0.0";
|
|
564
|
+
remoteMode = true;
|
|
290
565
|
continue;
|
|
291
566
|
}
|
|
292
567
|
if (arg === "--no-open") {
|
|
@@ -306,7 +581,8 @@ function parseArgs(argv) {
|
|
|
306
581
|
port,
|
|
307
582
|
host,
|
|
308
583
|
openBrowser,
|
|
309
|
-
strictPort
|
|
584
|
+
strictPort,
|
|
585
|
+
remoteMode
|
|
310
586
|
}
|
|
311
587
|
};
|
|
312
588
|
}
|
|
@@ -323,14 +599,25 @@ function compareVersions(currentVersion, latestVersion) {
|
|
|
323
599
|
}
|
|
324
600
|
return 0;
|
|
325
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
|
+
}
|
|
326
613
|
function normalizeVersion(version) {
|
|
327
614
|
return version.trim().replace(/^v/i, "").split("-")[0].split(".").map((part) => Number.parseInt(part, 10)).filter((part) => Number.isFinite(part));
|
|
328
615
|
}
|
|
329
616
|
async function maybeSelfUpdate(_argv, deps) {
|
|
330
|
-
if (
|
|
617
|
+
if (process3.env[DISABLE_SELF_UPDATE_ENV_VAR] === "1") {
|
|
331
618
|
return null;
|
|
332
619
|
}
|
|
333
|
-
if (shouldSkipStartupUpdateOnce(
|
|
620
|
+
if (shouldSkipStartupUpdateOnce(process3.env[CLI_SKIP_STARTUP_UPDATE_ONCE_ENV_VAR])) {
|
|
334
621
|
deps.warn(`${LOG_PREFIX} skipping startup update after restart to avoid an update loop`);
|
|
335
622
|
return null;
|
|
336
623
|
}
|
|
@@ -395,8 +682,34 @@ async function runCli(argv, deps) {
|
|
|
395
682
|
const displayHost = bindHost === "127.0.0.1" || bindHost === "0.0.0.0" ? "localhost" : bindHost;
|
|
396
683
|
const launchUrl = `http://${displayHost}:${port}`;
|
|
397
684
|
deps.log(`${LOG_PREFIX} listening on http://${bindHost}:${port}`);
|
|
685
|
+
deps.log(`${LOG_PREFIX} local URL: ${launchUrl}`);
|
|
398
686
|
deps.log(`${LOG_PREFIX} data dir: ${getDataDirDisplay()}`);
|
|
399
|
-
|
|
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";
|
|
400
713
|
if (parsedArgs.options.openBrowser && !suppressOpenBrowser) {
|
|
401
714
|
deps.openUrl(launchUrl);
|
|
402
715
|
}
|
|
@@ -406,7 +719,7 @@ async function runCli(argv, deps) {
|
|
|
406
719
|
};
|
|
407
720
|
}
|
|
408
721
|
function openUrl(url) {
|
|
409
|
-
const platform =
|
|
722
|
+
const platform = process3.platform;
|
|
410
723
|
if (platform === "darwin") {
|
|
411
724
|
spawnDetached("open", [url]);
|
|
412
725
|
} else if (platform === "win32") {
|
|
@@ -453,16 +766,16 @@ function installPackageVersion(packageName, version) {
|
|
|
453
766
|
userMessage: "Kaizen could not find Bun to install the update."
|
|
454
767
|
};
|
|
455
768
|
}
|
|
456
|
-
const result =
|
|
769
|
+
const result = spawnSync3("bun", ["install", "-g", `${packageName}@${version}`], {
|
|
457
770
|
stdio: ["ignore", "pipe", "pipe"],
|
|
458
771
|
encoding: "utf8"
|
|
459
772
|
});
|
|
460
773
|
const stdout = result.stdout ?? "";
|
|
461
774
|
const stderr = result.stderr ?? "";
|
|
462
775
|
if (stdout)
|
|
463
|
-
|
|
776
|
+
process3.stdout.write(stdout);
|
|
464
777
|
if (stderr)
|
|
465
|
-
|
|
778
|
+
process3.stderr.write(stderr);
|
|
466
779
|
if (result.status === 0) {
|
|
467
780
|
return {
|
|
468
781
|
ok: true,
|
|
@@ -500,6 +813,11 @@ var CODEX_REASONING_OPTIONS = [
|
|
|
500
813
|
{ id: "high", label: "High" },
|
|
501
814
|
{ id: "xhigh", label: "XHigh" }
|
|
502
815
|
];
|
|
816
|
+
var CLAUDE_CONTEXT_WINDOW_FALLBACKS = {
|
|
817
|
+
sonnet: 1e6,
|
|
818
|
+
opus: 1e6,
|
|
819
|
+
haiku: 200000
|
|
820
|
+
};
|
|
503
821
|
var GEMINI_THINKING_OPTIONS = [
|
|
504
822
|
{ id: "off", label: "Off" },
|
|
505
823
|
{ id: "standard", label: "Standard" },
|
|
@@ -1085,6 +1403,7 @@ class EventStore {
|
|
|
1085
1403
|
await this.ensureFile(this.turnsLogPath);
|
|
1086
1404
|
await this.loadSnapshot();
|
|
1087
1405
|
await this.replayLogs();
|
|
1406
|
+
await this.repairDuplicateSessionTokens();
|
|
1088
1407
|
this.hydrateProjectWorktrees();
|
|
1089
1408
|
await this.reconcileAllProjectFeatureState();
|
|
1090
1409
|
if (!await this.hasLegacyTranscriptData() && await this.shouldCompact()) {
|
|
@@ -1552,6 +1871,29 @@ class EventStore {
|
|
|
1552
1871
|
}
|
|
1553
1872
|
return entries;
|
|
1554
1873
|
}
|
|
1874
|
+
async repairDuplicateSessionTokens() {
|
|
1875
|
+
const chatsBySessionKey = new Map;
|
|
1876
|
+
for (const chat of this.state.chatsById.values()) {
|
|
1877
|
+
if (chat.deletedAt)
|
|
1878
|
+
continue;
|
|
1879
|
+
const sessionKey = this.getChatPersistenceKey(chat);
|
|
1880
|
+
if (!sessionKey)
|
|
1881
|
+
continue;
|
|
1882
|
+
const existing = chatsBySessionKey.get(sessionKey) ?? [];
|
|
1883
|
+
existing.push(chat);
|
|
1884
|
+
chatsBySessionKey.set(sessionKey, existing);
|
|
1885
|
+
}
|
|
1886
|
+
for (const chats of chatsBySessionKey.values()) {
|
|
1887
|
+
if (chats.length < 2)
|
|
1888
|
+
continue;
|
|
1889
|
+
const [keeper, ...duplicates] = [...chats].sort((left, right) => right.updatedAt - left.updatedAt || (right.lastMessageAt ?? 0) - (left.lastMessageAt ?? 0) || right.createdAt - left.createdAt);
|
|
1890
|
+
for (const duplicate of duplicates) {
|
|
1891
|
+
if (!duplicate.sessionToken)
|
|
1892
|
+
continue;
|
|
1893
|
+
await this.setSessionToken(duplicate.id, null);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1555
1897
|
async openProject(localPath, title) {
|
|
1556
1898
|
const identity = resolveProjectRepositoryIdentity(localPath);
|
|
1557
1899
|
const worktreePaths = identity.isGitRepo ? resolveProjectWorktreePaths(identity.worktreePath) : [identity.worktreePath];
|
|
@@ -2019,15 +2361,33 @@ class EventStore {
|
|
|
2019
2361
|
const chat = this.requireChat(chatId);
|
|
2020
2362
|
if (chat.sessionToken === sessionToken)
|
|
2021
2363
|
return;
|
|
2022
|
-
const
|
|
2364
|
+
const affectedProjectIds = new Set([chat.projectId]);
|
|
2365
|
+
if (sessionToken && chat.provider) {
|
|
2366
|
+
for (const candidate of this.state.chatsById.values()) {
|
|
2367
|
+
if (candidate.id === chatId || candidate.deletedAt)
|
|
2368
|
+
continue;
|
|
2369
|
+
if (candidate.provider !== chat.provider || candidate.sessionToken !== sessionToken)
|
|
2370
|
+
continue;
|
|
2371
|
+
affectedProjectIds.add(candidate.projectId);
|
|
2372
|
+
await this.append(this.turnsLogPath, {
|
|
2373
|
+
v: STORE_VERSION,
|
|
2374
|
+
type: "session_token_set",
|
|
2375
|
+
timestamp: Date.now(),
|
|
2376
|
+
chatId: candidate.id,
|
|
2377
|
+
sessionToken: null
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
await this.append(this.turnsLogPath, {
|
|
2023
2382
|
v: STORE_VERSION,
|
|
2024
2383
|
type: "session_token_set",
|
|
2025
2384
|
timestamp: Date.now(),
|
|
2026
2385
|
chatId,
|
|
2027
2386
|
sessionToken
|
|
2028
|
-
};
|
|
2029
|
-
|
|
2030
|
-
|
|
2387
|
+
});
|
|
2388
|
+
for (const projectId of affectedProjectIds) {
|
|
2389
|
+
await this.syncProjectFeatureState(projectId);
|
|
2390
|
+
}
|
|
2031
2391
|
}
|
|
2032
2392
|
async reconcileProjectFeatureState(projectId) {
|
|
2033
2393
|
const project = this.getProject(projectId);
|
|
@@ -2391,7 +2751,9 @@ class EventStore {
|
|
|
2391
2751
|
}
|
|
2392
2752
|
|
|
2393
2753
|
// src/server/agent.ts
|
|
2394
|
-
import {
|
|
2754
|
+
import {
|
|
2755
|
+
query as query2
|
|
2756
|
+
} from "@anthropic-ai/claude-agent-sdk";
|
|
2395
2757
|
|
|
2396
2758
|
// src/shared/tools.ts
|
|
2397
2759
|
function normalizeToolCall(args) {
|
|
@@ -3169,7 +3531,7 @@ class CodexAppServerManager {
|
|
|
3169
3531
|
}
|
|
3170
3532
|
async startSession(args) {
|
|
3171
3533
|
const existing = this.sessions.get(args.chatId);
|
|
3172
|
-
if (existing && !existing.closed && existing.cwd === args.cwd) {
|
|
3534
|
+
if (existing && !existing.closed && existing.cwd === args.cwd && existing.sessionToken === args.sessionToken) {
|
|
3173
3535
|
return;
|
|
3174
3536
|
}
|
|
3175
3537
|
if (existing) {
|
|
@@ -5576,7 +5938,7 @@ function codexServiceTierFromModelOptions(modelOptions) {
|
|
|
5576
5938
|
import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
5577
5939
|
import { homedir as homedir4 } from "os";
|
|
5578
5940
|
import path7 from "path";
|
|
5579
|
-
import
|
|
5941
|
+
import process4 from "process";
|
|
5580
5942
|
|
|
5581
5943
|
// src/server/usage/base-provider-usage.ts
|
|
5582
5944
|
import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
|
|
@@ -5672,6 +6034,69 @@ function usageWarnings(args) {
|
|
|
5672
6034
|
}
|
|
5673
6035
|
return warnings;
|
|
5674
6036
|
}
|
|
6037
|
+
function relevantMessagesForCurrentContext(messages) {
|
|
6038
|
+
let lastContextClearedIndex = -1;
|
|
6039
|
+
for (let index = messages.length - 1;index >= 0; index -= 1) {
|
|
6040
|
+
if (messages[index]?.kind === "context_cleared") {
|
|
6041
|
+
lastContextClearedIndex = index;
|
|
6042
|
+
break;
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
return lastContextClearedIndex >= 0 ? messages.slice(lastContextClearedIndex + 1) : messages;
|
|
6046
|
+
}
|
|
6047
|
+
function estimateTokenCountFromText(text) {
|
|
6048
|
+
if (!text)
|
|
6049
|
+
return 0;
|
|
6050
|
+
const trimmed = text.trim();
|
|
6051
|
+
if (!trimmed)
|
|
6052
|
+
return 0;
|
|
6053
|
+
return Math.ceil(trimmed.length / 4);
|
|
6054
|
+
}
|
|
6055
|
+
function estimateTokenCountFromUnknown(value) {
|
|
6056
|
+
if (typeof value === "string") {
|
|
6057
|
+
return estimateTokenCountFromText(value);
|
|
6058
|
+
}
|
|
6059
|
+
try {
|
|
6060
|
+
return estimateTokenCountFromText(JSON.stringify(value));
|
|
6061
|
+
} catch {
|
|
6062
|
+
return 0;
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
function estimateCurrentThreadTokens(messages) {
|
|
6066
|
+
let total = 0;
|
|
6067
|
+
for (const entry of relevantMessagesForCurrentContext(messages)) {
|
|
6068
|
+
switch (entry.kind) {
|
|
6069
|
+
case "user_prompt":
|
|
6070
|
+
total += estimateTokenCountFromText(entry.content);
|
|
6071
|
+
if (entry.attachments?.length) {
|
|
6072
|
+
total += entry.attachments.length * 256;
|
|
6073
|
+
}
|
|
6074
|
+
break;
|
|
6075
|
+
case "assistant_text":
|
|
6076
|
+
total += estimateTokenCountFromText(entry.text);
|
|
6077
|
+
break;
|
|
6078
|
+
case "compact_summary":
|
|
6079
|
+
total += estimateTokenCountFromText(entry.summary);
|
|
6080
|
+
break;
|
|
6081
|
+
case "result":
|
|
6082
|
+
total += estimateTokenCountFromText(entry.result);
|
|
6083
|
+
break;
|
|
6084
|
+
case "tool_call":
|
|
6085
|
+
total += estimateTokenCountFromText(entry.tool.toolName);
|
|
6086
|
+
total += estimateTokenCountFromUnknown(entry.tool.input);
|
|
6087
|
+
break;
|
|
6088
|
+
case "tool_result":
|
|
6089
|
+
total += estimateTokenCountFromUnknown(entry.content);
|
|
6090
|
+
break;
|
|
6091
|
+
case "status":
|
|
6092
|
+
total += estimateTokenCountFromText(entry.status);
|
|
6093
|
+
break;
|
|
6094
|
+
default:
|
|
6095
|
+
break;
|
|
6096
|
+
}
|
|
6097
|
+
}
|
|
6098
|
+
return total;
|
|
6099
|
+
}
|
|
5675
6100
|
function buildSnapshot(args) {
|
|
5676
6101
|
const contextUsedPercent = args.threadTokens !== null && args.contextWindowTokens && args.contextWindowTokens > 0 ? toPercent(args.threadTokens / args.contextWindowTokens * 100) : null;
|
|
5677
6102
|
const snapshot = {
|
|
@@ -5706,6 +6131,65 @@ function buildSnapshot(args) {
|
|
|
5706
6131
|
].some((value) => value !== null);
|
|
5707
6132
|
return hasAnyData ? snapshot : null;
|
|
5708
6133
|
}
|
|
6134
|
+
function usageTotals(usage) {
|
|
6135
|
+
const inputTokens = toNumber(usage.input_tokens) ?? 0;
|
|
6136
|
+
const outputTokens = toNumber(usage.output_tokens) ?? 0;
|
|
6137
|
+
const cachedInputTokens = (toNumber(usage.cache_read_input_tokens) ?? 0) + (toNumber(usage.cache_creation_input_tokens) ?? 0);
|
|
6138
|
+
const reasoningOutputTokens = toNumber(usage.reasoning_output_tokens) ?? 0;
|
|
6139
|
+
return {
|
|
6140
|
+
inputTokens,
|
|
6141
|
+
outputTokens,
|
|
6142
|
+
cachedInputTokens,
|
|
6143
|
+
reasoningOutputTokens,
|
|
6144
|
+
totalTokens: inputTokens + outputTokens + cachedInputTokens + reasoningOutputTokens
|
|
6145
|
+
};
|
|
6146
|
+
}
|
|
6147
|
+
function mergeUsageSnapshots(reconstructed, live) {
|
|
6148
|
+
if (!reconstructed)
|
|
6149
|
+
return live;
|
|
6150
|
+
if (!live)
|
|
6151
|
+
return reconstructed;
|
|
6152
|
+
const merged = {
|
|
6153
|
+
...reconstructed,
|
|
6154
|
+
...live,
|
|
6155
|
+
provider: live.provider,
|
|
6156
|
+
threadTokens: live.threadTokens ?? reconstructed.threadTokens,
|
|
6157
|
+
contextWindowTokens: live.contextWindowTokens ?? reconstructed.contextWindowTokens,
|
|
6158
|
+
contextUsedPercent: live.contextUsedPercent ?? reconstructed.contextUsedPercent,
|
|
6159
|
+
lastTurnTokens: live.lastTurnTokens ?? reconstructed.lastTurnTokens,
|
|
6160
|
+
inputTokens: live.inputTokens ?? reconstructed.inputTokens,
|
|
6161
|
+
outputTokens: live.outputTokens ?? reconstructed.outputTokens,
|
|
6162
|
+
cachedInputTokens: live.cachedInputTokens ?? reconstructed.cachedInputTokens,
|
|
6163
|
+
reasoningOutputTokens: live.reasoningOutputTokens ?? reconstructed.reasoningOutputTokens,
|
|
6164
|
+
sessionLimitUsedPercent: live.sessionLimitUsedPercent ?? reconstructed.sessionLimitUsedPercent,
|
|
6165
|
+
rateLimitResetAt: live.rateLimitResetAt ?? reconstructed.rateLimitResetAt,
|
|
6166
|
+
source: live.source === "live" ? "live" : reconstructed.source,
|
|
6167
|
+
updatedAt: live.updatedAt ?? reconstructed.updatedAt,
|
|
6168
|
+
warnings: []
|
|
6169
|
+
};
|
|
6170
|
+
merged.warnings = usageWarnings({
|
|
6171
|
+
contextUsedPercent: merged.contextUsedPercent,
|
|
6172
|
+
sessionLimitUsedPercent: merged.sessionLimitUsedPercent,
|
|
6173
|
+
updatedAt: merged.updatedAt
|
|
6174
|
+
});
|
|
6175
|
+
return merged;
|
|
6176
|
+
}
|
|
6177
|
+
function applyThreadEstimate(snapshot, messages) {
|
|
6178
|
+
const estimatedThreadTokens = estimateCurrentThreadTokens(messages);
|
|
6179
|
+
if (!snapshot)
|
|
6180
|
+
return null;
|
|
6181
|
+
const contextUsedPercent = snapshot.contextWindowTokens && snapshot.contextWindowTokens > 0 ? toPercent(estimatedThreadTokens / snapshot.contextWindowTokens * 100) : null;
|
|
6182
|
+
return {
|
|
6183
|
+
...snapshot,
|
|
6184
|
+
threadTokens: estimatedThreadTokens,
|
|
6185
|
+
contextUsedPercent,
|
|
6186
|
+
warnings: usageWarnings({
|
|
6187
|
+
contextUsedPercent,
|
|
6188
|
+
sessionLimitUsedPercent: snapshot.sessionLimitUsedPercent,
|
|
6189
|
+
updatedAt: snapshot.updatedAt
|
|
6190
|
+
})
|
|
6191
|
+
};
|
|
6192
|
+
}
|
|
5709
6193
|
function deriveAvailability(updatedAt) {
|
|
5710
6194
|
if (updatedAt === null)
|
|
5711
6195
|
return "available";
|
|
@@ -5773,6 +6257,77 @@ function unavailableEntry(provider) {
|
|
|
5773
6257
|
// src/server/usage/claude-usage.ts
|
|
5774
6258
|
var PROVIDER_CACHE_TTL_MS = 30000;
|
|
5775
6259
|
var PROVIDER_USAGE_REQUEST_MIN_INTERVAL_MS = 30 * 60 * 1000;
|
|
6260
|
+
function extractClaudeUsageRecord(entry) {
|
|
6261
|
+
if (!entry.debugRaw)
|
|
6262
|
+
return null;
|
|
6263
|
+
const raw = parseJsonLine3(entry.debugRaw);
|
|
6264
|
+
if (!raw)
|
|
6265
|
+
return null;
|
|
6266
|
+
const type = raw.type;
|
|
6267
|
+
if (type !== "assistant" && type !== "result")
|
|
6268
|
+
return null;
|
|
6269
|
+
if (type === "assistant") {
|
|
6270
|
+
const message = asRecord3(raw.message);
|
|
6271
|
+
const usage2 = asRecord3(message?.usage);
|
|
6272
|
+
if (!usage2)
|
|
6273
|
+
return null;
|
|
6274
|
+
const usageTotalsRecord2 = usageTotals(usage2);
|
|
6275
|
+
return {
|
|
6276
|
+
key: typeof raw.uuid === "string" ? raw.uuid : entry.messageId ?? entry._id,
|
|
6277
|
+
updatedAt: entry.createdAt,
|
|
6278
|
+
totals: usageTotalsRecord2,
|
|
6279
|
+
contextWindowTokens: null
|
|
6280
|
+
};
|
|
6281
|
+
}
|
|
6282
|
+
const usage = asRecord3(raw.usage);
|
|
6283
|
+
const modelUsage = asRecord3(raw.modelUsage);
|
|
6284
|
+
const firstModel = modelUsage ? Object.values(modelUsage).map((value) => asRecord3(value)).find(Boolean) ?? null : null;
|
|
6285
|
+
if (!usage && !firstModel)
|
|
6286
|
+
return null;
|
|
6287
|
+
const usageTotalsRecord = usage ? usageTotals(usage) : {
|
|
6288
|
+
inputTokens: toNumber(firstModel?.inputTokens) ?? 0,
|
|
6289
|
+
outputTokens: toNumber(firstModel?.outputTokens) ?? 0,
|
|
6290
|
+
cachedInputTokens: (toNumber(firstModel?.cacheReadInputTokens) ?? 0) + (toNumber(firstModel?.cacheCreationInputTokens) ?? 0),
|
|
6291
|
+
reasoningOutputTokens: 0,
|
|
6292
|
+
totalTokens: (toNumber(firstModel?.inputTokens) ?? 0) + (toNumber(firstModel?.outputTokens) ?? 0) + (toNumber(firstModel?.cacheReadInputTokens) ?? 0) + (toNumber(firstModel?.cacheCreationInputTokens) ?? 0)
|
|
6293
|
+
};
|
|
6294
|
+
return {
|
|
6295
|
+
key: typeof raw.uuid === "string" ? raw.uuid : entry.messageId ?? entry._id,
|
|
6296
|
+
updatedAt: entry.createdAt,
|
|
6297
|
+
totals: usageTotalsRecord,
|
|
6298
|
+
contextWindowTokens: toNumber(firstModel?.contextWindow)
|
|
6299
|
+
};
|
|
6300
|
+
}
|
|
6301
|
+
function reconstructClaudeUsage(messages, liveRateLimit) {
|
|
6302
|
+
const relevantMessages = relevantMessagesForCurrentContext(messages);
|
|
6303
|
+
const deduped = new Map;
|
|
6304
|
+
for (const entry of relevantMessages) {
|
|
6305
|
+
const usageRecord = extractClaudeUsageRecord(entry);
|
|
6306
|
+
if (!usageRecord)
|
|
6307
|
+
continue;
|
|
6308
|
+
deduped.set(usageRecord.key, usageRecord);
|
|
6309
|
+
}
|
|
6310
|
+
const latest = [...deduped.values()].filter((value) => Boolean(value)).sort((a, b) => b.updatedAt - a.updatedAt)[0];
|
|
6311
|
+
if (!latest && liveRateLimit?.percent == null) {
|
|
6312
|
+
return null;
|
|
6313
|
+
}
|
|
6314
|
+
const latestModel = [...relevantMessages].reverse().find((entry) => entry.kind === "system_init" && entry.provider === "claude");
|
|
6315
|
+
const fallbackContextWindow = latestModel ? CLAUDE_CONTEXT_WINDOW_FALLBACKS[latestModel.model.toLowerCase()] ?? null : null;
|
|
6316
|
+
return buildSnapshot({
|
|
6317
|
+
provider: "claude",
|
|
6318
|
+
threadTokens: estimateCurrentThreadTokens(messages),
|
|
6319
|
+
contextWindowTokens: latest?.contextWindowTokens ?? fallbackContextWindow,
|
|
6320
|
+
lastTurnTokens: latest?.totals.totalTokens ?? null,
|
|
6321
|
+
inputTokens: latest?.totals.inputTokens ?? null,
|
|
6322
|
+
outputTokens: latest?.totals.outputTokens ?? null,
|
|
6323
|
+
cachedInputTokens: latest?.totals.cachedInputTokens ?? null,
|
|
6324
|
+
reasoningOutputTokens: latest?.totals.reasoningOutputTokens ?? null,
|
|
6325
|
+
sessionLimitUsedPercent: liveRateLimit?.percent ?? null,
|
|
6326
|
+
rateLimitResetAt: liveRateLimit?.resetsAt ?? null,
|
|
6327
|
+
source: latest ? "reconstructed" : "live",
|
|
6328
|
+
updatedAt: latest?.updatedAt ?? null
|
|
6329
|
+
});
|
|
6330
|
+
}
|
|
5776
6331
|
function createClaudeRateLimitSnapshot(percent, resetsAt) {
|
|
5777
6332
|
return buildSnapshot({
|
|
5778
6333
|
provider: "claude",
|
|
@@ -5915,7 +6470,7 @@ function collectClaudeUsageScreen(args) {
|
|
|
5915
6470
|
stderr: "pipe",
|
|
5916
6471
|
cwd: args?.cwd,
|
|
5917
6472
|
env: {
|
|
5918
|
-
...
|
|
6473
|
+
...process4.env,
|
|
5919
6474
|
...args?.cwd ? { CLAUDE_USAGE_CWD: args.cwd } : {},
|
|
5920
6475
|
CLAUDE_USAGE_CONTINUE: args?.continueSession ? "1" : "0"
|
|
5921
6476
|
}
|
|
@@ -5924,7 +6479,7 @@ function collectClaudeUsageScreen(args) {
|
|
|
5924
6479
|
}
|
|
5925
6480
|
function isRunningPid(pid) {
|
|
5926
6481
|
try {
|
|
5927
|
-
|
|
6482
|
+
process4.kill(pid, 0);
|
|
5928
6483
|
return true;
|
|
5929
6484
|
} catch {
|
|
5930
6485
|
return false;
|
|
@@ -6272,7 +6827,7 @@ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync
|
|
|
6272
6827
|
import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
|
|
6273
6828
|
import { tmpdir as tmpdir3 } from "os";
|
|
6274
6829
|
import path10 from "path";
|
|
6275
|
-
import
|
|
6830
|
+
import process6 from "process";
|
|
6276
6831
|
import puppeteer from "puppeteer-core";
|
|
6277
6832
|
|
|
6278
6833
|
// src/server/usage/cursor-cookies.ts
|
|
@@ -6281,7 +6836,7 @@ import { copyFileSync, existsSync as existsSync5, mkdtempSync, readdirSync as re
|
|
|
6281
6836
|
import { createDecipheriv, pbkdf2Sync } from "crypto";
|
|
6282
6837
|
import { homedir as homedir6, tmpdir as tmpdir2 } from "os";
|
|
6283
6838
|
import path9 from "path";
|
|
6284
|
-
import
|
|
6839
|
+
import process5 from "process";
|
|
6285
6840
|
var CHROME_EPOCH_OFFSET_MS = Date.UTC(1601, 0, 1);
|
|
6286
6841
|
var CURSOR_SESSION_COOKIE_NAME = "WorkosCursorSessionToken";
|
|
6287
6842
|
function normalizeCookieDomain(domain) {
|
|
@@ -6578,7 +7133,7 @@ function decryptChromiumCookieValue(encryptedValue, key, platform) {
|
|
|
6578
7133
|
return null;
|
|
6579
7134
|
}
|
|
6580
7135
|
}
|
|
6581
|
-
function bootstrapCursorSessionFromBrowser(platform =
|
|
7136
|
+
function bootstrapCursorSessionFromBrowser(platform = process5.platform) {
|
|
6582
7137
|
if (platform !== "linux" && platform !== "darwin")
|
|
6583
7138
|
return null;
|
|
6584
7139
|
for (const source of discoverChromiumCookieSources(platform)) {
|
|
@@ -6912,7 +7467,7 @@ class CursorUsage extends BaseProviderUsage {
|
|
|
6912
7467
|
return null;
|
|
6913
7468
|
}
|
|
6914
7469
|
}
|
|
6915
|
-
async refresh(platform =
|
|
7470
|
+
async refresh(platform = process6.platform, force = false) {
|
|
6916
7471
|
const now = Date.now();
|
|
6917
7472
|
const persistedEntry = this.loadPersistedEntry();
|
|
6918
7473
|
const lastRequestedAt = persistedEntry?.lastRequestedAt ?? this.readLastRequestedAt(this.provider);
|
|
@@ -6977,7 +7532,7 @@ class CursorUsage extends BaseProviderUsage {
|
|
|
6977
7532
|
this.persistUsageEntry(entry);
|
|
6978
7533
|
return entry;
|
|
6979
7534
|
}
|
|
6980
|
-
async importFromCurl(curlCommand, platform =
|
|
7535
|
+
async importFromCurl(curlCommand, platform = process6.platform) {
|
|
6981
7536
|
const imported = importCursorSessionFromCurl(curlCommand);
|
|
6982
7537
|
if (!imported) {
|
|
6983
7538
|
return cursorStatusEntry({
|
|
@@ -6995,7 +7550,7 @@ class CursorUsage extends BaseProviderUsage {
|
|
|
6995
7550
|
this.persistSession(session);
|
|
6996
7551
|
return this.refresh(platform);
|
|
6997
7552
|
}
|
|
6998
|
-
async signInWithBrowser(platform =
|
|
7553
|
+
async signInWithBrowser(platform = process6.platform) {
|
|
6999
7554
|
const executablePath = resolveBrowserExecutable(platform);
|
|
7000
7555
|
if (!executablePath) {
|
|
7001
7556
|
return cursorStatusEntry({
|
|
@@ -7068,13 +7623,13 @@ function getCursorUsage(dataDir) {
|
|
|
7068
7623
|
}
|
|
7069
7624
|
return _instances3.get(dataDir);
|
|
7070
7625
|
}
|
|
7071
|
-
async function refreshCursorUsage(dataDir, platform =
|
|
7626
|
+
async function refreshCursorUsage(dataDir, platform = process6.platform, force = false) {
|
|
7072
7627
|
return getCursorUsage(dataDir).refresh(platform, force);
|
|
7073
7628
|
}
|
|
7074
|
-
async function importCursorUsageFromCurl(dataDir, curlCommand, platform =
|
|
7629
|
+
async function importCursorUsageFromCurl(dataDir, curlCommand, platform = process6.platform) {
|
|
7075
7630
|
return getCursorUsage(dataDir).importFromCurl(curlCommand, platform);
|
|
7076
7631
|
}
|
|
7077
|
-
async function signInToCursorWithBrowser(dataDir, platform =
|
|
7632
|
+
async function signInToCursorWithBrowser(dataDir, platform = process6.platform) {
|
|
7078
7633
|
return getCursorUsage(dataDir).signInWithBrowser(platform);
|
|
7079
7634
|
}
|
|
7080
7635
|
|
|
@@ -7222,10 +7777,21 @@ function formatRecoveredAskUserQuestionFollowUp(tool, result) {
|
|
|
7222
7777
|
].join(`
|
|
7223
7778
|
`);
|
|
7224
7779
|
}
|
|
7225
|
-
function planModeFollowUp(result) {
|
|
7780
|
+
function planModeFollowUp(result, plan) {
|
|
7226
7781
|
let text = "";
|
|
7227
7782
|
if (result.confirmed) {
|
|
7228
|
-
|
|
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
|
+
`);
|
|
7229
7795
|
} else {
|
|
7230
7796
|
text = result.message ? `Revise the plan using this feedback: ${result.message}` : "Revise the plan using this feedback.";
|
|
7231
7797
|
}
|
|
@@ -7321,7 +7887,14 @@ function normalizeClaudeStreamMessage(message) {
|
|
|
7321
7887
|
];
|
|
7322
7888
|
}
|
|
7323
7889
|
if (message.type === "system" && message.subtype === "status" && typeof message.status === "string") {
|
|
7324
|
-
return [
|
|
7890
|
+
return [
|
|
7891
|
+
timestamped3({
|
|
7892
|
+
kind: "status",
|
|
7893
|
+
messageId,
|
|
7894
|
+
status: message.status,
|
|
7895
|
+
debugRaw
|
|
7896
|
+
})
|
|
7897
|
+
];
|
|
7325
7898
|
}
|
|
7326
7899
|
if (message.type === "system" && message.subtype === "compact_boundary") {
|
|
7327
7900
|
return [timestamped3({ kind: "compact_boundary", messageId, debugRaw })];
|
|
@@ -7330,7 +7903,14 @@ function normalizeClaudeStreamMessage(message) {
|
|
|
7330
7903
|
return [timestamped3({ kind: "context_cleared", messageId, debugRaw })];
|
|
7331
7904
|
}
|
|
7332
7905
|
if (message.type === "user" && message.message?.role === "user" && typeof message.message.content === "string" && message.message.content.startsWith("This session is being continued")) {
|
|
7333
|
-
return [
|
|
7906
|
+
return [
|
|
7907
|
+
timestamped3({
|
|
7908
|
+
kind: "compact_summary",
|
|
7909
|
+
messageId,
|
|
7910
|
+
summary: message.message.content,
|
|
7911
|
+
debugRaw
|
|
7912
|
+
})
|
|
7913
|
+
];
|
|
7334
7914
|
}
|
|
7335
7915
|
return [];
|
|
7336
7916
|
}
|
|
@@ -7968,7 +8548,7 @@ class AgentCoordinator {
|
|
|
7968
8548
|
await this.store.setSessionToken(command.chatId, null);
|
|
7969
8549
|
await this.store.appendMessage(command.chatId, timestamped3({ kind: "context_cleared" }));
|
|
7970
8550
|
}
|
|
7971
|
-
const followUp = planModeFollowUp(result);
|
|
8551
|
+
const followUp = planModeFollowUp(result, recoveredPending.input.plan);
|
|
7972
8552
|
const messageEntry = timestamped3({
|
|
7973
8553
|
kind: "user_prompt",
|
|
7974
8554
|
content: followUp.content
|
|
@@ -8016,7 +8596,7 @@ class AgentCoordinator {
|
|
|
8016
8596
|
await this.store.appendMessage(command.chatId, timestamped3({ kind: "context_cleared" }));
|
|
8017
8597
|
}
|
|
8018
8598
|
if (shouldUseSyntheticPlanFollowUp(active.provider, pending.tool)) {
|
|
8019
|
-
active.postToolFollowUp = planModeFollowUp(result);
|
|
8599
|
+
active.postToolFollowUp = planModeFollowUp(result, pending.tool.input.plan);
|
|
8020
8600
|
}
|
|
8021
8601
|
}
|
|
8022
8602
|
pending.resolve(command.result);
|
|
@@ -9702,17 +10282,17 @@ function formatDisplayPath3(filePath) {
|
|
|
9702
10282
|
|
|
9703
10283
|
// src/server/machine-name.ts
|
|
9704
10284
|
import { hostname } from "os";
|
|
9705
|
-
import
|
|
9706
|
-
import { spawnSync as
|
|
10285
|
+
import process7 from "process";
|
|
10286
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
9707
10287
|
function runAndRead(command, args) {
|
|
9708
|
-
const result =
|
|
10288
|
+
const result = spawnSync4(command, args, { encoding: "utf8" });
|
|
9709
10289
|
if (result.status !== 0)
|
|
9710
10290
|
return null;
|
|
9711
10291
|
const value = result.stdout.trim();
|
|
9712
10292
|
return value || null;
|
|
9713
10293
|
}
|
|
9714
10294
|
function getMachineDisplayName() {
|
|
9715
|
-
if (
|
|
10295
|
+
if (process7.platform === "darwin") {
|
|
9716
10296
|
const computerName = runAndRead("scutil", ["--get", "ComputerName"]);
|
|
9717
10297
|
if (computerName) {
|
|
9718
10298
|
return computerName;
|
|
@@ -9724,15 +10304,15 @@ function getMachineDisplayName() {
|
|
|
9724
10304
|
|
|
9725
10305
|
// src/server/terminal-manager.ts
|
|
9726
10306
|
import path16 from "path";
|
|
9727
|
-
import
|
|
10307
|
+
import process8 from "process";
|
|
9728
10308
|
import defaultShell, { detectDefaultShell } from "default-shell";
|
|
9729
10309
|
import { Terminal } from "@xterm/headless";
|
|
9730
10310
|
import { SerializeAddon } from "@xterm/addon-serialize";
|
|
9731
10311
|
var DEFAULT_COLS = 80;
|
|
9732
10312
|
var DEFAULT_ROWS = 24;
|
|
9733
|
-
var DEFAULT_SCROLLBACK =
|
|
10313
|
+
var DEFAULT_SCROLLBACK = 1e4;
|
|
9734
10314
|
var MIN_SCROLLBACK = 500;
|
|
9735
|
-
var MAX_SCROLLBACK =
|
|
10315
|
+
var MAX_SCROLLBACK = 50000;
|
|
9736
10316
|
var FOCUS_IN_SEQUENCE = "\x1B[I";
|
|
9737
10317
|
var FOCUS_OUT_SEQUENCE = "\x1B[O";
|
|
9738
10318
|
var MODE_SEQUENCE_TAIL_LENGTH = 16;
|
|
@@ -9752,14 +10332,14 @@ function resolveShell() {
|
|
|
9752
10332
|
} catch {
|
|
9753
10333
|
if (defaultShell)
|
|
9754
10334
|
return defaultShell;
|
|
9755
|
-
if (
|
|
9756
|
-
return
|
|
10335
|
+
if (process8.platform === "win32") {
|
|
10336
|
+
return process8.env.ComSpec || "cmd.exe";
|
|
9757
10337
|
}
|
|
9758
|
-
return
|
|
10338
|
+
return process8.env.SHELL || "/bin/sh";
|
|
9759
10339
|
}
|
|
9760
10340
|
}
|
|
9761
10341
|
function resolveShellArgs(shellPath) {
|
|
9762
|
-
if (
|
|
10342
|
+
if (process8.platform === "win32") {
|
|
9763
10343
|
return [];
|
|
9764
10344
|
}
|
|
9765
10345
|
const shellName = path16.basename(shellPath);
|
|
@@ -9770,7 +10350,7 @@ function resolveShellArgs(shellPath) {
|
|
|
9770
10350
|
}
|
|
9771
10351
|
function createTerminalEnv() {
|
|
9772
10352
|
return {
|
|
9773
|
-
...
|
|
10353
|
+
...process8.env,
|
|
9774
10354
|
TERM: "xterm-256color",
|
|
9775
10355
|
COLORTERM: "truecolor"
|
|
9776
10356
|
};
|
|
@@ -9795,9 +10375,9 @@ function killTerminalProcessTree(subprocess) {
|
|
|
9795
10375
|
const pid = subprocess.pid;
|
|
9796
10376
|
if (typeof pid !== "number")
|
|
9797
10377
|
return;
|
|
9798
|
-
if (
|
|
10378
|
+
if (process8.platform !== "win32") {
|
|
9799
10379
|
try {
|
|
9800
|
-
|
|
10380
|
+
process8.kill(-pid, "SIGKILL");
|
|
9801
10381
|
return;
|
|
9802
10382
|
} catch {}
|
|
9803
10383
|
}
|
|
@@ -9811,9 +10391,9 @@ function signalTerminalProcessGroup(subprocess, signal) {
|
|
|
9811
10391
|
const pid = subprocess.pid;
|
|
9812
10392
|
if (typeof pid !== "number")
|
|
9813
10393
|
return false;
|
|
9814
|
-
if (
|
|
10394
|
+
if (process8.platform !== "win32") {
|
|
9815
10395
|
try {
|
|
9816
|
-
|
|
10396
|
+
process8.kill(-pid, signal);
|
|
9817
10397
|
return true;
|
|
9818
10398
|
} catch {}
|
|
9819
10399
|
}
|
|
@@ -9835,7 +10415,7 @@ class TerminalManager {
|
|
|
9835
10415
|
};
|
|
9836
10416
|
}
|
|
9837
10417
|
createTerminal(args) {
|
|
9838
|
-
if (
|
|
10418
|
+
if (process8.platform === "win32") {
|
|
9839
10419
|
throw new Error("Embedded terminal is currently supported on macOS/Linux only.");
|
|
9840
10420
|
}
|
|
9841
10421
|
if (typeof Bun.Terminal !== "function") {
|
|
@@ -9857,7 +10437,12 @@ class TerminalManager {
|
|
|
9857
10437
|
const rows = normalizeTerminalDimension(args.rows, DEFAULT_ROWS);
|
|
9858
10438
|
const scrollback = clampScrollback(args.scrollback);
|
|
9859
10439
|
const title = path16.basename(shell) || "shell";
|
|
9860
|
-
const headless = new Terminal({
|
|
10440
|
+
const headless = new Terminal({
|
|
10441
|
+
cols,
|
|
10442
|
+
rows,
|
|
10443
|
+
scrollback,
|
|
10444
|
+
allowProposedApi: true
|
|
10445
|
+
});
|
|
9861
10446
|
const serializeAddon = new SerializeAddon;
|
|
9862
10447
|
headless.loadAddon(serializeAddon);
|
|
9863
10448
|
const session = {
|
|
@@ -10002,7 +10587,9 @@ class TerminalManager {
|
|
|
10002
10587
|
cols: session.cols,
|
|
10003
10588
|
rows: session.rows,
|
|
10004
10589
|
scrollback: session.scrollback,
|
|
10005
|
-
serializedState: session.serializeAddon.serialize({
|
|
10590
|
+
serializedState: session.serializeAddon.serialize({
|
|
10591
|
+
scrollback: session.scrollback
|
|
10592
|
+
}),
|
|
10006
10593
|
status: session.status,
|
|
10007
10594
|
exitCode: session.exitCode
|
|
10008
10595
|
};
|
|
@@ -10605,14 +11192,14 @@ function isClientEnvelope(value) {
|
|
|
10605
11192
|
// src/server/external-open.ts
|
|
10606
11193
|
import { stat as stat2 } from "fs/promises";
|
|
10607
11194
|
import path18 from "path";
|
|
10608
|
-
import
|
|
11195
|
+
import process9 from "process";
|
|
10609
11196
|
var DEFAULT_EDITOR_SETTINGS = {
|
|
10610
11197
|
preset: "cursor",
|
|
10611
11198
|
commandTemplate: "cursor {path}"
|
|
10612
11199
|
};
|
|
10613
11200
|
async function openExternal(command) {
|
|
10614
11201
|
const resolvedPath = resolveLocalPath(command.localPath);
|
|
10615
|
-
const platform =
|
|
11202
|
+
const platform = process9.platform;
|
|
10616
11203
|
const info = command.action === "open_editor" || command.action === "open_finder" ? await stat2(resolvedPath).catch(() => null) : null;
|
|
10617
11204
|
if (command.action === "open_editor") {
|
|
10618
11205
|
if (!info) {
|
|
@@ -10682,7 +11269,7 @@ async function openExternal(command) {
|
|
|
10682
11269
|
}
|
|
10683
11270
|
}
|
|
10684
11271
|
async function openUrl2(command) {
|
|
10685
|
-
const platform =
|
|
11272
|
+
const platform = process9.platform;
|
|
10686
11273
|
if (platform === "darwin") {
|
|
10687
11274
|
spawnDetached("open", [command.url]);
|
|
10688
11275
|
return;
|
|
@@ -11347,6 +11934,25 @@ function deriveChatSnapshot(state, activeStatuses, chatId, getMessages, pendingT
|
|
|
11347
11934
|
};
|
|
11348
11935
|
}
|
|
11349
11936
|
|
|
11937
|
+
// src/server/usage/chat-usage.ts
|
|
11938
|
+
function resolveChatUsage({
|
|
11939
|
+
chat,
|
|
11940
|
+
messages,
|
|
11941
|
+
liveUsage,
|
|
11942
|
+
codexSessionsDir
|
|
11943
|
+
}) {
|
|
11944
|
+
if (!chat?.provider)
|
|
11945
|
+
return liveUsage;
|
|
11946
|
+
if (chat.provider === "claude") {
|
|
11947
|
+
return mergeUsageSnapshots(reconstructClaudeUsage(messages), liveUsage);
|
|
11948
|
+
}
|
|
11949
|
+
if (chat.provider === "codex") {
|
|
11950
|
+
const reconstructed = chat.sessionToken ? applyThreadEstimate(reconstructCodexUsageFromFile(chat.sessionToken, codexSessionsDir), messages) : null;
|
|
11951
|
+
return mergeUsageSnapshots(reconstructed, liveUsage);
|
|
11952
|
+
}
|
|
11953
|
+
return liveUsage;
|
|
11954
|
+
}
|
|
11955
|
+
|
|
11350
11956
|
// src/server/ws-router.ts
|
|
11351
11957
|
var PROVIDER_USAGE_POLL_INTERVAL_MS = 30 * 60 * 1000;
|
|
11352
11958
|
var PROVIDER_USAGE_POLL_MAX_INTERVAL_MS = 31 * 60 * 1000;
|
|
@@ -11375,7 +11981,8 @@ function createWsRouter({
|
|
|
11375
11981
|
await refreshCursorUsage(store.dataDir, undefined, force).then(() => {});
|
|
11376
11982
|
}
|
|
11377
11983
|
},
|
|
11378
|
-
openUrlCommand = openUrl2
|
|
11984
|
+
openUrlCommand = openUrl2,
|
|
11985
|
+
resolveChatUsageForSnapshot = resolveChatUsage
|
|
11379
11986
|
}) {
|
|
11380
11987
|
const sockets = new Set;
|
|
11381
11988
|
let providerUsageRefreshInFlight = null;
|
|
@@ -11490,7 +12097,13 @@ function createWsRouter({
|
|
|
11490
12097
|
snapshot: {
|
|
11491
12098
|
type: "chat",
|
|
11492
12099
|
data: (() => {
|
|
11493
|
-
|
|
12100
|
+
const chat = store.getChat(topic.chatId);
|
|
12101
|
+
const messages = store.getMessages(topic.chatId);
|
|
12102
|
+
return deriveChatSnapshot(store.state, agent.getActiveStatuses(), topic.chatId, () => messages, agent.getChatPendingTool(topic.chatId), resolveChatUsageForSnapshot({
|
|
12103
|
+
chat,
|
|
12104
|
+
messages,
|
|
12105
|
+
liveUsage: agent.getLiveUsage(topic.chatId)
|
|
12106
|
+
}), providerSettings?.getSnapshot().settings ?? DEFAULT_PROVIDER_SETTINGS);
|
|
11494
12107
|
})()
|
|
11495
12108
|
}
|
|
11496
12109
|
};
|
|
@@ -12319,12 +12932,14 @@ async function serveStatic(distDir, pathname) {
|
|
|
12319
12932
|
|
|
12320
12933
|
// src/server/cli.ts
|
|
12321
12934
|
var packageRootUrl = new URL("../../", import.meta.url);
|
|
12935
|
+
var packageRootPath = fileURLToPath(packageRootUrl);
|
|
12322
12936
|
var pkg = await Bun.file(new URL("package.json", packageRootUrl)).json();
|
|
12323
12937
|
var VERSION = pkg.version ?? "0.0.0";
|
|
12324
|
-
var
|
|
12325
|
-
var
|
|
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);
|
|
12326
12941
|
var resolveExitAction = null;
|
|
12327
|
-
var WEB_ONLY_MODE = shouldRunWebOnlyMode(
|
|
12942
|
+
var WEB_ONLY_MODE = shouldRunWebOnlyMode(process10.env[CLI_WEB_ONLY_MODE_ENV_VAR]);
|
|
12328
12943
|
var result = await runCli(argv, {
|
|
12329
12944
|
version: VERSION,
|
|
12330
12945
|
bunVersion: Bun.version,
|
|
@@ -12345,27 +12960,29 @@ var result = await runCli(argv, {
|
|
|
12345
12960
|
return started;
|
|
12346
12961
|
},
|
|
12347
12962
|
fetchLatestVersion: fetchLatestPackageVersion,
|
|
12348
|
-
installVersion: installPackageVersion,
|
|
12963
|
+
installVersion: IS_NPX_RUNTIME ? () => createUnsupportedNpxInstallResult() : installPackageVersion,
|
|
12349
12964
|
openUrl,
|
|
12965
|
+
resolveRemoteAccessInfo,
|
|
12966
|
+
renderTerminalQrCode,
|
|
12350
12967
|
log: console.log,
|
|
12351
12968
|
warn: console.warn
|
|
12352
12969
|
});
|
|
12353
12970
|
if (result.kind === "exited") {
|
|
12354
|
-
|
|
12971
|
+
process10.exit(result.code);
|
|
12355
12972
|
}
|
|
12356
12973
|
if (result.kind === "restarting") {
|
|
12357
|
-
|
|
12974
|
+
process10.exit(result.reason === "startup_update" ? CLI_STARTUP_UPDATE_RESTART_EXIT_CODE : CLI_UI_UPDATE_RESTART_EXIT_CODE);
|
|
12358
12975
|
}
|
|
12359
12976
|
var exitAction = await new Promise((resolve2) => {
|
|
12360
12977
|
resolveExitAction = resolve2;
|
|
12361
12978
|
const shutdown = () => {
|
|
12362
12979
|
resolve2("exit");
|
|
12363
12980
|
};
|
|
12364
|
-
|
|
12365
|
-
|
|
12981
|
+
process10.once("SIGINT", shutdown);
|
|
12982
|
+
process10.once("SIGTERM", shutdown);
|
|
12366
12983
|
});
|
|
12367
12984
|
await result.stop();
|
|
12368
12985
|
if (exitAction === "ui_restart") {
|
|
12369
12986
|
console.log(`${LOG_PREFIX} current process stopped, handing restart back to supervisor`);
|
|
12370
12987
|
}
|
|
12371
|
-
|
|
12988
|
+
process10.exit(exitAction === "ui_restart" ? CLI_UI_UPDATE_RESTART_EXIT_CODE : 0);
|