@absolutejs/absolute 0.19.0-beta.666 → 0.19.0-beta.668
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +654 -171
- package/dist/index.js +3 -2
- package/dist/index.js.map +3 -3
- package/dist/src/cli/workspaceTui.d.ts +29 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -669,8 +669,8 @@ var exports_compile = {};
|
|
|
669
669
|
__export(exports_compile, {
|
|
670
670
|
compile: () => compile
|
|
671
671
|
});
|
|
672
|
-
var {env:
|
|
673
|
-
import { existsSync as existsSync9, readdirSync, readFileSync as
|
|
672
|
+
var {env: env3 } = globalThis.Bun;
|
|
673
|
+
import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync9, unlinkSync } from "fs";
|
|
674
674
|
import { basename as basename2, join as join6, relative, resolve as resolve7 } from "path";
|
|
675
675
|
var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`, collectFiles2 = (dir) => {
|
|
676
676
|
const result = [];
|
|
@@ -686,9 +686,9 @@ var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
|
|
|
686
686
|
result.push(fullPath);
|
|
687
687
|
}
|
|
688
688
|
return result;
|
|
689
|
-
},
|
|
689
|
+
}, readPackageVersion3 = (candidate) => {
|
|
690
690
|
try {
|
|
691
|
-
const pkg = JSON.parse(
|
|
691
|
+
const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
|
|
692
692
|
if (pkg.name !== "@absolutejs/absolute")
|
|
693
693
|
return null;
|
|
694
694
|
const ver = pkg.version;
|
|
@@ -696,9 +696,9 @@ var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
|
|
|
696
696
|
} catch {
|
|
697
697
|
return null;
|
|
698
698
|
}
|
|
699
|
-
},
|
|
699
|
+
}, resolvePackageVersion3 = (candidates) => {
|
|
700
700
|
for (const candidate of candidates) {
|
|
701
|
-
const version2 =
|
|
701
|
+
const version2 = readPackageVersion3(candidate);
|
|
702
702
|
if (version2)
|
|
703
703
|
return version2;
|
|
704
704
|
}
|
|
@@ -848,12 +848,12 @@ console.log(\`
|
|
|
848
848
|
\`);
|
|
849
849
|
`;
|
|
850
850
|
}, stubPlugin, FRAMEWORK_EXTERNALS, compile = async (serverEntry, outdir, outfile, configPath2) => {
|
|
851
|
-
const prerenderPort = Number(
|
|
851
|
+
const prerenderPort = Number(env3.COMPILE_PORT) || Number(env3.PORT) || DEFAULT_PORT + 1;
|
|
852
852
|
killStaleProcesses(prerenderPort);
|
|
853
853
|
const entryName = basename2(serverEntry).replace(/\.[^.]+$/, "");
|
|
854
854
|
const resolvedOutdir = resolve7(outdir ?? "dist");
|
|
855
855
|
const resolvedOutfile = resolve7(outfile ?? "compiled-server");
|
|
856
|
-
const absoluteVersion =
|
|
856
|
+
const absoluteVersion = resolvePackageVersion3([
|
|
857
857
|
resolve7(import.meta.dir, "..", "..", "..", "package.json"),
|
|
858
858
|
resolve7(import.meta.dir, "..", "..", "package.json")
|
|
859
859
|
]);
|
|
@@ -1050,17 +1050,17 @@ var run = async (name, command) => {
|
|
|
1050
1050
|
}, shellEscape = (value) => `'${value.replaceAll("'", "'\\''")}'`, runShell = async (name, command) => run(name, ["/bin/bash", "-lc", command]), findBin = (name) => {
|
|
1051
1051
|
const local = resolve8("node_modules", ".bin", name);
|
|
1052
1052
|
return existsSync10(local) ? local : null;
|
|
1053
|
-
},
|
|
1053
|
+
}, stripAnsi2 = (str) => str.replace(/\x1b\[[0-9;]*m/g, ""), formatSvelteOutput = (output) => {
|
|
1054
1054
|
const cwd = `${process.cwd()}/`;
|
|
1055
|
-
const summaryMatch =
|
|
1055
|
+
const summaryMatch = stripAnsi2(output).match(/svelte-check found (\d+) error/);
|
|
1056
1056
|
const errorCount = summaryMatch ? parseInt(summaryMatch[1] ?? "0", 10) : 0;
|
|
1057
1057
|
const formatted = output.split(`
|
|
1058
1058
|
`).filter((line) => {
|
|
1059
|
-
const plain =
|
|
1059
|
+
const plain = stripAnsi2(line);
|
|
1060
1060
|
return !plain.startsWith("Loading svelte-check") && !plain.startsWith("Getting Svelte") && !plain.startsWith("====") && !plain.startsWith("svelte-check found") && !/^\d+ (START|COMPLETED)/.test(plain) && plain.trim() !== "";
|
|
1061
1061
|
}).flatMap((line) => {
|
|
1062
1062
|
const result = line.replaceAll(cwd, "");
|
|
1063
|
-
const plain =
|
|
1063
|
+
const plain = stripAnsi2(result);
|
|
1064
1064
|
const pathMatch = plain.match(/^(\S+\.svelte):(\d+:\d+)$/);
|
|
1065
1065
|
if (pathMatch) {
|
|
1066
1066
|
return [
|
|
@@ -1068,9 +1068,9 @@ var run = async (name, command) => {
|
|
|
1068
1068
|
];
|
|
1069
1069
|
}
|
|
1070
1070
|
if (result.includes("\x1B[35m")) {
|
|
1071
|
-
const plainLine =
|
|
1072
|
-
const before =
|
|
1073
|
-
const token =
|
|
1071
|
+
const plainLine = stripAnsi2(result);
|
|
1072
|
+
const before = stripAnsi2(result.split("\x1B[35m")[0] ?? "");
|
|
1073
|
+
const token = stripAnsi2((result.split("\x1B[35m")[1] ?? "").split(/\x1b\[3[69]m/)[0] ?? "");
|
|
1074
1074
|
if (!token)
|
|
1075
1075
|
return [result];
|
|
1076
1076
|
const expanded = before.replace(/\t/g, " ");
|
|
@@ -2379,14 +2379,568 @@ var start = async (serverEntry, outdir, configPath2) => {
|
|
|
2379
2379
|
// src/cli/scripts/workspace.ts
|
|
2380
2380
|
init_constants();
|
|
2381
2381
|
init_loadConfig();
|
|
2382
|
-
|
|
2383
|
-
var {$: $3 } = globalThis.Bun;
|
|
2384
|
-
import { existsSync as existsSync8 } from "fs";
|
|
2382
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
2385
2383
|
import { resolve as resolve6 } from "path";
|
|
2384
|
+
|
|
2385
|
+
// src/cli/workspaceTui.ts
|
|
2386
|
+
init_constants();
|
|
2387
|
+
init_getDurationString();
|
|
2388
|
+
import { openSync as openSync2 } from "fs";
|
|
2389
|
+
import { ReadStream as ReadStream2 } from "tty";
|
|
2390
|
+
var MAX_LOG_ENTRIES = 400;
|
|
2391
|
+
var ESCAPE = "\x1B";
|
|
2392
|
+
var ANSI_REGEX = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
2393
|
+
var SHORTCUTS2 = new Map([
|
|
2394
|
+
["c", "clear"],
|
|
2395
|
+
["h", "help"],
|
|
2396
|
+
["o", "open"],
|
|
2397
|
+
["p", "pause"],
|
|
2398
|
+
["q", "quit"],
|
|
2399
|
+
["r", "restart"]
|
|
2400
|
+
]);
|
|
2401
|
+
var colors = {
|
|
2402
|
+
bold: "\x1B[1m",
|
|
2403
|
+
cyan: "\x1B[36m",
|
|
2404
|
+
dim: "\x1B[2m",
|
|
2405
|
+
green: "\x1B[32m",
|
|
2406
|
+
red: "\x1B[31m",
|
|
2407
|
+
reset: "\x1B[0m",
|
|
2408
|
+
yellow: "\x1B[33m"
|
|
2409
|
+
};
|
|
2410
|
+
var helpLines = [
|
|
2411
|
+
"Hotkeys",
|
|
2412
|
+
" h Toggle help",
|
|
2413
|
+
" o Open the first public service",
|
|
2414
|
+
" r Restart the workspace",
|
|
2415
|
+
" p Pause or resume all services",
|
|
2416
|
+
" c Clear the log pane",
|
|
2417
|
+
" q Quit",
|
|
2418
|
+
" $ Enter shell mode",
|
|
2419
|
+
"",
|
|
2420
|
+
"Shell mode",
|
|
2421
|
+
' Type a shell command after "$" and press enter.',
|
|
2422
|
+
" Press Esc to exit shell mode or dismiss help.",
|
|
2423
|
+
" Use \u2191 and \u2193 to recall prior shell commands."
|
|
2424
|
+
];
|
|
2425
|
+
var trySetRawMode2 = () => {
|
|
2426
|
+
if (typeof process.stdin.setRawMode !== "function") {
|
|
2427
|
+
return null;
|
|
2428
|
+
}
|
|
2429
|
+
try {
|
|
2430
|
+
process.stdin.setRawMode(true);
|
|
2431
|
+
} catch {
|
|
2432
|
+
return null;
|
|
2433
|
+
}
|
|
2434
|
+
return process.stdin;
|
|
2435
|
+
};
|
|
2436
|
+
var openTtyStream2 = () => {
|
|
2437
|
+
const fromStdin = trySetRawMode2();
|
|
2438
|
+
if (fromStdin) {
|
|
2439
|
+
return fromStdin;
|
|
2440
|
+
}
|
|
2441
|
+
try {
|
|
2442
|
+
const ttyStream = new ReadStream2(openSync2("/dev/tty", "r"));
|
|
2443
|
+
ttyStream.setRawMode(true);
|
|
2444
|
+
return ttyStream;
|
|
2445
|
+
} catch {
|
|
2446
|
+
return null;
|
|
2447
|
+
}
|
|
2448
|
+
};
|
|
2449
|
+
var stripAnsi = (value) => value.replace(ANSI_REGEX, "");
|
|
2450
|
+
var visibleLength = (value) => stripAnsi(value).length;
|
|
2451
|
+
var truncateText = (value, width) => {
|
|
2452
|
+
if (width <= 0) {
|
|
2453
|
+
return "";
|
|
2454
|
+
}
|
|
2455
|
+
if (value.length <= width) {
|
|
2456
|
+
return value;
|
|
2457
|
+
}
|
|
2458
|
+
if (width <= 1) {
|
|
2459
|
+
return value.slice(0, width);
|
|
2460
|
+
}
|
|
2461
|
+
return `${value.slice(0, width - 1)}\u2026`;
|
|
2462
|
+
};
|
|
2463
|
+
var padLine = (value, width) => {
|
|
2464
|
+
const plainLength = visibleLength(value);
|
|
2465
|
+
if (plainLength >= width) {
|
|
2466
|
+
return value;
|
|
2467
|
+
}
|
|
2468
|
+
return `${value}${" ".repeat(width - plainLength)}`;
|
|
2469
|
+
};
|
|
2470
|
+
var wrapText = (value, width) => {
|
|
2471
|
+
if (width <= 0) {
|
|
2472
|
+
return [""];
|
|
2473
|
+
}
|
|
2474
|
+
const lines = [];
|
|
2475
|
+
for (const rawLine of value.split(`
|
|
2476
|
+
`)) {
|
|
2477
|
+
const line = rawLine.trimEnd();
|
|
2478
|
+
if (line.length === 0) {
|
|
2479
|
+
lines.push("");
|
|
2480
|
+
continue;
|
|
2481
|
+
}
|
|
2482
|
+
if (line.length <= width) {
|
|
2483
|
+
lines.push(line);
|
|
2484
|
+
continue;
|
|
2485
|
+
}
|
|
2486
|
+
const words = line.split(/\s+/);
|
|
2487
|
+
let current = "";
|
|
2488
|
+
for (const word of words) {
|
|
2489
|
+
if (current.length === 0) {
|
|
2490
|
+
if (word.length <= width) {
|
|
2491
|
+
current = word;
|
|
2492
|
+
continue;
|
|
2493
|
+
}
|
|
2494
|
+
for (let index = 0;index < word.length; index += width) {
|
|
2495
|
+
lines.push(word.slice(index, index + width));
|
|
2496
|
+
}
|
|
2497
|
+
continue;
|
|
2498
|
+
}
|
|
2499
|
+
const next = `${current} ${word}`;
|
|
2500
|
+
if (next.length <= width) {
|
|
2501
|
+
current = next;
|
|
2502
|
+
continue;
|
|
2503
|
+
}
|
|
2504
|
+
lines.push(current);
|
|
2505
|
+
if (word.length <= width) {
|
|
2506
|
+
current = word;
|
|
2507
|
+
continue;
|
|
2508
|
+
}
|
|
2509
|
+
for (let index = 0;index < word.length; index += width) {
|
|
2510
|
+
lines.push(word.slice(index, index + width));
|
|
2511
|
+
}
|
|
2512
|
+
current = "";
|
|
2513
|
+
}
|
|
2514
|
+
if (current.length > 0) {
|
|
2515
|
+
lines.push(current);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
return lines.length > 0 ? lines : [""];
|
|
2519
|
+
};
|
|
2520
|
+
var formatTimestamp2 = () => new Date().toLocaleTimeString([], {
|
|
2521
|
+
hour: "numeric",
|
|
2522
|
+
hour12: true,
|
|
2523
|
+
minute: "2-digit",
|
|
2524
|
+
second: "2-digit"
|
|
2525
|
+
});
|
|
2526
|
+
var getStatusColor = (status2) => {
|
|
2527
|
+
if (status2 === "ready")
|
|
2528
|
+
return colors.green;
|
|
2529
|
+
if (status2 === "paused")
|
|
2530
|
+
return colors.yellow;
|
|
2531
|
+
if (status2 === "starting" || status2 === "restarting")
|
|
2532
|
+
return colors.cyan;
|
|
2533
|
+
if (status2 === "error")
|
|
2534
|
+
return colors.red;
|
|
2535
|
+
return colors.dim;
|
|
2536
|
+
};
|
|
2537
|
+
var getLogColor = (level) => {
|
|
2538
|
+
if (level === "error")
|
|
2539
|
+
return colors.red;
|
|
2540
|
+
if (level === "warn")
|
|
2541
|
+
return colors.yellow;
|
|
2542
|
+
if (level === "success")
|
|
2543
|
+
return colors.green;
|
|
2544
|
+
return colors.reset;
|
|
2545
|
+
};
|
|
2546
|
+
var getSourceColor = (source) => {
|
|
2547
|
+
if (source === "workspace")
|
|
2548
|
+
return colors.cyan;
|
|
2549
|
+
if (source === "shell")
|
|
2550
|
+
return colors.yellow;
|
|
2551
|
+
return colors.reset;
|
|
2552
|
+
};
|
|
2553
|
+
var getTargetLabel = (service) => {
|
|
2554
|
+
if (service.visibility === "public" && service.url) {
|
|
2555
|
+
return service.url;
|
|
2556
|
+
}
|
|
2557
|
+
if (service.port) {
|
|
2558
|
+
return `localhost:${service.port} (internal)`;
|
|
2559
|
+
}
|
|
2560
|
+
return service.visibility === "internal" ? "internal service" : "no endpoint";
|
|
2561
|
+
};
|
|
2562
|
+
var getWorkspaceStatus = (services) => {
|
|
2563
|
+
if (services.some((service) => service.status === "error")) {
|
|
2564
|
+
return "error";
|
|
2565
|
+
}
|
|
2566
|
+
if (services.some((service) => service.status === "paused")) {
|
|
2567
|
+
return "paused";
|
|
2568
|
+
}
|
|
2569
|
+
if (services.some((service) => ["pending", "starting", "restarting"].includes(service.status))) {
|
|
2570
|
+
return "booting";
|
|
2571
|
+
}
|
|
2572
|
+
return "ready";
|
|
2573
|
+
};
|
|
2574
|
+
var createWorkspaceTui = ({
|
|
2575
|
+
actions,
|
|
2576
|
+
services,
|
|
2577
|
+
version: version2
|
|
2578
|
+
}) => {
|
|
2579
|
+
let input = null;
|
|
2580
|
+
let disposed = false;
|
|
2581
|
+
let renderTimer = null;
|
|
2582
|
+
let shellMode = false;
|
|
2583
|
+
let helpVisible = false;
|
|
2584
|
+
let promptBuffer = "";
|
|
2585
|
+
let escapeTimer = null;
|
|
2586
|
+
let escapeBuffer = "";
|
|
2587
|
+
let readyDurationMs = null;
|
|
2588
|
+
const shellHistory = [];
|
|
2589
|
+
let shellHistoryIndex = UNFOUND_INDEX;
|
|
2590
|
+
const serviceStates = new Map(services.map((service) => [
|
|
2591
|
+
service.name,
|
|
2592
|
+
{
|
|
2593
|
+
...service,
|
|
2594
|
+
status: "pending"
|
|
2595
|
+
}
|
|
2596
|
+
]));
|
|
2597
|
+
const logEntries = [];
|
|
2598
|
+
const setRawMode = (enabled) => {
|
|
2599
|
+
if (!input || typeof input.setRawMode !== "function") {
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
2602
|
+
try {
|
|
2603
|
+
input.setRawMode(enabled);
|
|
2604
|
+
} catch {}
|
|
2605
|
+
};
|
|
2606
|
+
const scheduleRender = () => {
|
|
2607
|
+
if (disposed || renderTimer) {
|
|
2608
|
+
return;
|
|
2609
|
+
}
|
|
2610
|
+
renderTimer = setTimeout(() => {
|
|
2611
|
+
renderTimer = null;
|
|
2612
|
+
render();
|
|
2613
|
+
}, 16);
|
|
2614
|
+
};
|
|
2615
|
+
const clearPendingEscape = () => {
|
|
2616
|
+
if (!escapeTimer) {
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
clearTimeout(escapeTimer);
|
|
2620
|
+
escapeTimer = null;
|
|
2621
|
+
};
|
|
2622
|
+
const armEscapeTimer = () => {
|
|
2623
|
+
clearPendingEscape();
|
|
2624
|
+
escapeTimer = setTimeout(() => {
|
|
2625
|
+
escapeTimer = null;
|
|
2626
|
+
escapeBuffer = "";
|
|
2627
|
+
exitEscapeMode();
|
|
2628
|
+
}, 30);
|
|
2629
|
+
};
|
|
2630
|
+
const resetPrompt = () => {
|
|
2631
|
+
promptBuffer = "";
|
|
2632
|
+
shellMode = false;
|
|
2633
|
+
shellHistoryIndex = UNFOUND_INDEX;
|
|
2634
|
+
scheduleRender();
|
|
2635
|
+
};
|
|
2636
|
+
const exitEscapeMode = () => {
|
|
2637
|
+
clearPendingEscape();
|
|
2638
|
+
escapeBuffer = "";
|
|
2639
|
+
if (helpVisible) {
|
|
2640
|
+
helpVisible = false;
|
|
2641
|
+
} else {
|
|
2642
|
+
resetPrompt();
|
|
2643
|
+
}
|
|
2644
|
+
scheduleRender();
|
|
2645
|
+
};
|
|
2646
|
+
const render = () => {
|
|
2647
|
+
if (disposed) {
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
const width = process.stdout.columns ?? 100;
|
|
2651
|
+
const height = process.stdout.rows ?? 28;
|
|
2652
|
+
const servicesSnapshot = [...serviceStates.values()];
|
|
2653
|
+
const workspaceStatus = getWorkspaceStatus(servicesSnapshot);
|
|
2654
|
+
const statusLabel = workspaceStatus === "ready" && readyDurationMs !== null ? `${colors.dim}ready in${colors.reset} ${colors.bold}${getDurationString(readyDurationMs)}${colors.reset}` : `${colors.dim}${workspaceStatus}${colors.reset}`;
|
|
2655
|
+
const title = `${colors.cyan}${colors.bold}ABSOLUTEJS WORKSPACE${colors.reset} ${colors.dim}v${version2}${colors.reset} ${statusLabel}`;
|
|
2656
|
+
const divider = `${colors.dim}${"\u2500".repeat(Math.max(width, 1))}${colors.reset}`;
|
|
2657
|
+
const serviceNameWidth = Math.max(7, ...servicesSnapshot.map((service) => service.name.length));
|
|
2658
|
+
const visibilityWidth = 8;
|
|
2659
|
+
const statusWidth = 10;
|
|
2660
|
+
const rows = [];
|
|
2661
|
+
rows.push(padLine(title, width));
|
|
2662
|
+
rows.push(padLine(`${colors.bold}Services${colors.reset}`, width));
|
|
2663
|
+
rows.push(divider);
|
|
2664
|
+
for (const service of servicesSnapshot) {
|
|
2665
|
+
const stateColor = getStatusColor(service.status);
|
|
2666
|
+
const detail = service.detail ? ` \xB7 ${service.detail}` : "";
|
|
2667
|
+
const targetWidth = Math.max(width - serviceNameWidth - visibilityWidth - statusWidth - 6, 8);
|
|
2668
|
+
const target = truncateText(`${getTargetLabel(service)}${detail}`, targetWidth);
|
|
2669
|
+
const row = `${colors.bold}${service.name.padEnd(serviceNameWidth)}${colors.reset} ${colors.dim}${service.visibility.padEnd(visibilityWidth)}${colors.reset} ${stateColor}${service.status.padEnd(statusWidth)}${colors.reset} ${target}`;
|
|
2670
|
+
rows.push(padLine(row, width));
|
|
2671
|
+
}
|
|
2672
|
+
rows.push(divider);
|
|
2673
|
+
const footerLines = 3;
|
|
2674
|
+
const fixedHeight = rows.length + footerLines;
|
|
2675
|
+
const logHeight = Math.max(height - fixedHeight, 3);
|
|
2676
|
+
const contentLines = helpVisible ? helpLines : logEntries.flatMap((entry) => {
|
|
2677
|
+
const prefixPlain = `${entry.timestamp} [${entry.source}] `;
|
|
2678
|
+
const prefixColor = `${colors.dim}${entry.timestamp}${colors.reset} ${getSourceColor(entry.source)}[${entry.source}]${colors.reset} `;
|
|
2679
|
+
const wrapped = wrapText(entry.message, Math.max(width - prefixPlain.length, 12));
|
|
2680
|
+
return wrapped.map((line, index) => {
|
|
2681
|
+
if (index === 0) {
|
|
2682
|
+
return `${prefixColor}${getLogColor(entry.level)}${line}${colors.reset}`;
|
|
2683
|
+
}
|
|
2684
|
+
return `${" ".repeat(prefixPlain.length)}${getLogColor(entry.level)}${line}${colors.reset}`;
|
|
2685
|
+
});
|
|
2686
|
+
});
|
|
2687
|
+
const visibleContent = contentLines.length > logHeight ? contentLines.slice(contentLines.length - logHeight) : contentLines;
|
|
2688
|
+
for (const line of visibleContent) {
|
|
2689
|
+
rows.push(padLine(line, width));
|
|
2690
|
+
}
|
|
2691
|
+
for (let index = visibleContent.length;index < logHeight; index++) {
|
|
2692
|
+
rows.push(" ".repeat(width));
|
|
2693
|
+
}
|
|
2694
|
+
rows.push(divider);
|
|
2695
|
+
const footerText = helpVisible ? "Esc or h closes help" : "Hotkeys: h help o open r restart p pause c clear logs q quit $ shell";
|
|
2696
|
+
rows.push(padLine(`${colors.dim}${truncateText(footerText, width)}${colors.reset}`, width));
|
|
2697
|
+
const promptLine = shellMode ? `${colors.yellow}$ ${colors.reset}${truncateText(promptBuffer, Math.max(width - 2, 0))}` : `${colors.dim}Press a hotkey or $ for shell mode${colors.reset}`;
|
|
2698
|
+
rows.push(padLine(promptLine, width));
|
|
2699
|
+
const screen = rows.slice(0, height).map((line) => `\x1B[2K${line}`).join(`
|
|
2700
|
+
`);
|
|
2701
|
+
process.stdout.write(`\x1B[H${screen}`);
|
|
2702
|
+
if (shellMode) {
|
|
2703
|
+
const promptColumn = Math.min(promptBuffer.length + 3, width);
|
|
2704
|
+
const promptRow = Math.min(rows.length, height);
|
|
2705
|
+
process.stdout.write(`\x1B[${promptRow};${promptColumn}H\x1B[?25h`);
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
process.stdout.write("\x1B[?25l");
|
|
2709
|
+
};
|
|
2710
|
+
const setServiceStatus = (name, status2, detail) => {
|
|
2711
|
+
const existing = serviceStates.get(name);
|
|
2712
|
+
if (!existing) {
|
|
2713
|
+
return;
|
|
2714
|
+
}
|
|
2715
|
+
existing.status = status2;
|
|
2716
|
+
existing.detail = detail;
|
|
2717
|
+
scheduleRender();
|
|
2718
|
+
};
|
|
2719
|
+
const setReadyDuration = (durationMs) => {
|
|
2720
|
+
readyDurationMs = durationMs;
|
|
2721
|
+
scheduleRender();
|
|
2722
|
+
};
|
|
2723
|
+
const addLog = (source, message, level = "info") => {
|
|
2724
|
+
const cleanMessage = stripAnsi(message).trimEnd();
|
|
2725
|
+
if (!cleanMessage) {
|
|
2726
|
+
return;
|
|
2727
|
+
}
|
|
2728
|
+
for (const line of cleanMessage.split(`
|
|
2729
|
+
`)) {
|
|
2730
|
+
logEntries.push({
|
|
2731
|
+
level,
|
|
2732
|
+
message: line,
|
|
2733
|
+
source,
|
|
2734
|
+
timestamp: formatTimestamp2()
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
if (logEntries.length > MAX_LOG_ENTRIES) {
|
|
2738
|
+
logEntries.splice(0, logEntries.length - MAX_LOG_ENTRIES);
|
|
2739
|
+
}
|
|
2740
|
+
scheduleRender();
|
|
2741
|
+
};
|
|
2742
|
+
const clearLogs = () => {
|
|
2743
|
+
logEntries.length = 0;
|
|
2744
|
+
scheduleRender();
|
|
2745
|
+
};
|
|
2746
|
+
const navigateShellHistory = (direction) => {
|
|
2747
|
+
if (!shellMode || shellHistory.length === 0) {
|
|
2748
|
+
return;
|
|
2749
|
+
}
|
|
2750
|
+
if (direction === "up") {
|
|
2751
|
+
if (shellHistoryIndex < shellHistory.length - 1) {
|
|
2752
|
+
shellHistoryIndex++;
|
|
2753
|
+
}
|
|
2754
|
+
} else if (shellHistoryIndex <= 0) {
|
|
2755
|
+
shellHistoryIndex = UNFOUND_INDEX;
|
|
2756
|
+
promptBuffer = "";
|
|
2757
|
+
scheduleRender();
|
|
2758
|
+
return;
|
|
2759
|
+
} else {
|
|
2760
|
+
shellHistoryIndex--;
|
|
2761
|
+
}
|
|
2762
|
+
promptBuffer = shellHistoryIndex === UNFOUND_INDEX ? "" : shellHistory[shellHistory.length - 1 - shellHistoryIndex] ?? "";
|
|
2763
|
+
scheduleRender();
|
|
2764
|
+
};
|
|
2765
|
+
const runShortcut = async (action) => {
|
|
2766
|
+
if (action === "clear") {
|
|
2767
|
+
clearLogs();
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2770
|
+
if (action === "help") {
|
|
2771
|
+
helpVisible = !helpVisible;
|
|
2772
|
+
scheduleRender();
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2775
|
+
await actions[action]();
|
|
2776
|
+
};
|
|
2777
|
+
const submitShellCommand = async () => {
|
|
2778
|
+
const command = promptBuffer.trim();
|
|
2779
|
+
if (!command) {
|
|
2780
|
+
resetPrompt();
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2783
|
+
shellHistory.push(command);
|
|
2784
|
+
resetPrompt();
|
|
2785
|
+
addLog("shell", `$ ${command}`, "info");
|
|
2786
|
+
await actions.shell(command);
|
|
2787
|
+
};
|
|
2788
|
+
const handleEscapeSequence = (char) => {
|
|
2789
|
+
if (!escapeBuffer) {
|
|
2790
|
+
escapeBuffer = ESCAPE;
|
|
2791
|
+
}
|
|
2792
|
+
escapeBuffer += char;
|
|
2793
|
+
if (escapeBuffer === `${ESCAPE}[`) {
|
|
2794
|
+
armEscapeTimer();
|
|
2795
|
+
return;
|
|
2796
|
+
}
|
|
2797
|
+
if (escapeBuffer === `${ESCAPE}[A`) {
|
|
2798
|
+
clearPendingEscape();
|
|
2799
|
+
escapeBuffer = "";
|
|
2800
|
+
navigateShellHistory("up");
|
|
2801
|
+
return;
|
|
2802
|
+
}
|
|
2803
|
+
if (escapeBuffer === `${ESCAPE}[B`) {
|
|
2804
|
+
clearPendingEscape();
|
|
2805
|
+
escapeBuffer = "";
|
|
2806
|
+
navigateShellHistory("down");
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2809
|
+
exitEscapeMode();
|
|
2810
|
+
};
|
|
2811
|
+
const handleChar = async (char) => {
|
|
2812
|
+
if (char === "\x03") {
|
|
2813
|
+
await actions.quit();
|
|
2814
|
+
return;
|
|
2815
|
+
}
|
|
2816
|
+
if (char === ESCAPE) {
|
|
2817
|
+
escapeBuffer = ESCAPE;
|
|
2818
|
+
armEscapeTimer();
|
|
2819
|
+
return;
|
|
2820
|
+
}
|
|
2821
|
+
if (escapeBuffer) {
|
|
2822
|
+
handleEscapeSequence(char);
|
|
2823
|
+
return;
|
|
2824
|
+
}
|
|
2825
|
+
if (char === "\x7F" || char === "\b") {
|
|
2826
|
+
if (!shellMode) {
|
|
2827
|
+
return;
|
|
2828
|
+
}
|
|
2829
|
+
if (promptBuffer.length > 0) {
|
|
2830
|
+
promptBuffer = promptBuffer.slice(0, UNFOUND_INDEX);
|
|
2831
|
+
scheduleRender();
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
resetPrompt();
|
|
2835
|
+
return;
|
|
2836
|
+
}
|
|
2837
|
+
if (char === "\r" || char === `
|
|
2838
|
+
`) {
|
|
2839
|
+
if (shellMode) {
|
|
2840
|
+
await submitShellCommand();
|
|
2841
|
+
}
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
if (char.charCodeAt(0) < ASCII_SPACE) {
|
|
2845
|
+
return;
|
|
2846
|
+
}
|
|
2847
|
+
if (!shellMode) {
|
|
2848
|
+
if (char === "$") {
|
|
2849
|
+
shellMode = true;
|
|
2850
|
+
promptBuffer = "";
|
|
2851
|
+
scheduleRender();
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
const shortcut = SHORTCUTS2.get(char.toLowerCase());
|
|
2855
|
+
if (shortcut) {
|
|
2856
|
+
await runShortcut(shortcut);
|
|
2857
|
+
}
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
promptBuffer += char;
|
|
2861
|
+
scheduleRender();
|
|
2862
|
+
};
|
|
2863
|
+
const onResize = () => {
|
|
2864
|
+
scheduleRender();
|
|
2865
|
+
};
|
|
2866
|
+
const onData = (chunk) => {
|
|
2867
|
+
const chars = chunk.toString();
|
|
2868
|
+
(async () => {
|
|
2869
|
+
for (const char of chars) {
|
|
2870
|
+
await handleChar(char);
|
|
2871
|
+
}
|
|
2872
|
+
})();
|
|
2873
|
+
};
|
|
2874
|
+
const start2 = () => {
|
|
2875
|
+
process.stdout.write("\x1B[?1049h\x1B[2J\x1B[H\x1B[?25l");
|
|
2876
|
+
input = openTtyStream2();
|
|
2877
|
+
if (input) {
|
|
2878
|
+
input.resume();
|
|
2879
|
+
input.on("data", onData);
|
|
2880
|
+
} else {
|
|
2881
|
+
addLog("workspace", "Interactive TTY input is unavailable in this terminal.", "warn");
|
|
2882
|
+
}
|
|
2883
|
+
process.stdout.on("resize", onResize);
|
|
2884
|
+
render();
|
|
2885
|
+
};
|
|
2886
|
+
const dispose = () => {
|
|
2887
|
+
if (disposed) {
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
disposed = true;
|
|
2891
|
+
clearPendingEscape();
|
|
2892
|
+
if (renderTimer) {
|
|
2893
|
+
clearTimeout(renderTimer);
|
|
2894
|
+
renderTimer = null;
|
|
2895
|
+
}
|
|
2896
|
+
process.stdout.off("resize", onResize);
|
|
2897
|
+
if (input) {
|
|
2898
|
+
input.off("data", onData);
|
|
2899
|
+
setRawMode(false);
|
|
2900
|
+
if (input !== process.stdin) {
|
|
2901
|
+
input.destroy();
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
process.stdout.write("\x1B[?25h\x1B[?1049l");
|
|
2905
|
+
};
|
|
2906
|
+
return {
|
|
2907
|
+
addLog,
|
|
2908
|
+
clearLogs,
|
|
2909
|
+
dispose,
|
|
2910
|
+
setReadyDuration,
|
|
2911
|
+
setServiceStatus,
|
|
2912
|
+
start: start2
|
|
2913
|
+
};
|
|
2914
|
+
};
|
|
2915
|
+
|
|
2916
|
+
// src/cli/scripts/workspace.ts
|
|
2386
2917
|
init_utils();
|
|
2387
|
-
var serviceTag = (name, color) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[${name}]\x1B[0m`;
|
|
2388
|
-
var workspaceTag = (color, message) => `${serviceTag("workspace", color)} ${color}${message}\x1B[0m`;
|
|
2389
2918
|
var sleep = (ms) => new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
2919
|
+
var readPackageVersion2 = (candidate) => {
|
|
2920
|
+
try {
|
|
2921
|
+
const pkg = JSON.parse(readFileSync8(candidate, "utf-8"));
|
|
2922
|
+
if (pkg.name !== "@absolutejs/absolute") {
|
|
2923
|
+
return null;
|
|
2924
|
+
}
|
|
2925
|
+
const version2 = pkg.version;
|
|
2926
|
+
return version2;
|
|
2927
|
+
} catch {
|
|
2928
|
+
return null;
|
|
2929
|
+
}
|
|
2930
|
+
};
|
|
2931
|
+
var resolvePackageVersion2 = () => {
|
|
2932
|
+
const candidates = [
|
|
2933
|
+
resolve6(import.meta.dir, "..", "..", "..", "package.json"),
|
|
2934
|
+
resolve6(import.meta.dir, "..", "..", "..", "..", "package.json")
|
|
2935
|
+
];
|
|
2936
|
+
for (const candidate of candidates) {
|
|
2937
|
+
const version2 = readPackageVersion2(candidate);
|
|
2938
|
+
if (version2) {
|
|
2939
|
+
return version2;
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
return process.env.ABSOLUTE_VERSION || "unknown";
|
|
2943
|
+
};
|
|
2390
2944
|
var getVisibility = (service) => service.visibility ?? "public";
|
|
2391
2945
|
var getServiceUrl = (service) => {
|
|
2392
2946
|
if (!service.port) {
|
|
@@ -2394,7 +2948,6 @@ var getServiceUrl = (service) => {
|
|
|
2394
2948
|
}
|
|
2395
2949
|
return `http://localhost:${service.port}/`;
|
|
2396
2950
|
};
|
|
2397
|
-
var getServicePortLabel = (service) => service.port ? `${service.port}` : "no-port";
|
|
2398
2951
|
var getHealthcheckUrl = (service) => {
|
|
2399
2952
|
if (service.healthcheck) {
|
|
2400
2953
|
return service.healthcheck;
|
|
@@ -2475,60 +3028,9 @@ var topologicallySortServices = (services) => {
|
|
|
2475
3028
|
}
|
|
2476
3029
|
return ordered;
|
|
2477
3030
|
};
|
|
2478
|
-
var
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
if (line.trim().length === 0) {
|
|
2482
|
-
return;
|
|
2483
|
-
}
|
|
2484
|
-
onBeforeWrite?.();
|
|
2485
|
-
dest.write(`${serviceTag(name, color)} ${line}
|
|
2486
|
-
`);
|
|
2487
|
-
onAfterWrite?.();
|
|
2488
|
-
};
|
|
2489
|
-
return {
|
|
2490
|
-
push: (chunk) => {
|
|
2491
|
-
buffer += Buffer.from(chunk).toString();
|
|
2492
|
-
const lines = buffer.split(`
|
|
2493
|
-
`);
|
|
2494
|
-
buffer = lines.pop() ?? "";
|
|
2495
|
-
for (const line of lines) {
|
|
2496
|
-
writeLine(line);
|
|
2497
|
-
}
|
|
2498
|
-
},
|
|
2499
|
-
flush: () => {
|
|
2500
|
-
if (!buffer || buffer.trim().length === 0) {
|
|
2501
|
-
buffer = "";
|
|
2502
|
-
return;
|
|
2503
|
-
}
|
|
2504
|
-
writeLine(buffer);
|
|
2505
|
-
buffer = "";
|
|
2506
|
-
}
|
|
2507
|
-
};
|
|
2508
|
-
};
|
|
2509
|
-
var pipeProcessLogs = (name, processHandle, onBeforeWrite, onAfterWrite) => {
|
|
2510
|
-
const stdoutForwarder = createLineForwarder(name, "\x1B[36m", process.stdout, onBeforeWrite, onAfterWrite);
|
|
2511
|
-
const stderrForwarder = createLineForwarder(name, "\x1B[31m", process.stderr, onBeforeWrite, onAfterWrite);
|
|
2512
|
-
let bufferingStartupLogs = true;
|
|
2513
|
-
const stdoutStartupChunks = [];
|
|
2514
|
-
const stderrStartupChunks = [];
|
|
2515
|
-
const releaseStartupLogs = () => {
|
|
2516
|
-
if (!bufferingStartupLogs) {
|
|
2517
|
-
return;
|
|
2518
|
-
}
|
|
2519
|
-
bufferingStartupLogs = false;
|
|
2520
|
-
for (const chunk of stdoutStartupChunks) {
|
|
2521
|
-
stdoutForwarder.push(chunk);
|
|
2522
|
-
}
|
|
2523
|
-
stdoutStartupChunks.length = 0;
|
|
2524
|
-
stdoutForwarder.flush();
|
|
2525
|
-
for (const chunk of stderrStartupChunks) {
|
|
2526
|
-
stderrForwarder.push(chunk);
|
|
2527
|
-
}
|
|
2528
|
-
stderrStartupChunks.length = 0;
|
|
2529
|
-
stderrForwarder.flush();
|
|
2530
|
-
};
|
|
2531
|
-
const forward = async (stream, forwarder, startupBuffer) => {
|
|
3031
|
+
var pipeProcessLogs = (name, processHandle, appendLog) => {
|
|
3032
|
+
const forward = async (stream, level) => {
|
|
3033
|
+
let buffer = "";
|
|
2532
3034
|
const reader = stream.getReader();
|
|
2533
3035
|
try {
|
|
2534
3036
|
while (true) {
|
|
@@ -2539,28 +3041,33 @@ var pipeProcessLogs = (name, processHandle, onBeforeWrite, onAfterWrite) => {
|
|
|
2539
3041
|
if (!value) {
|
|
2540
3042
|
continue;
|
|
2541
3043
|
}
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
3044
|
+
buffer += Buffer.from(value).toString();
|
|
3045
|
+
const lines = buffer.split(`
|
|
3046
|
+
`);
|
|
3047
|
+
buffer = lines.pop() ?? "";
|
|
3048
|
+
for (const line of lines) {
|
|
3049
|
+
if (line.trim().length === 0) {
|
|
3050
|
+
continue;
|
|
3051
|
+
}
|
|
3052
|
+
appendLog(name, line, level);
|
|
2545
3053
|
}
|
|
2546
|
-
forwarder.push(value);
|
|
2547
3054
|
}
|
|
2548
3055
|
} finally {
|
|
2549
|
-
if (
|
|
2550
|
-
|
|
3056
|
+
if (buffer.trim().length > 0) {
|
|
3057
|
+
appendLog(name, buffer, level);
|
|
2551
3058
|
}
|
|
2552
3059
|
reader.releaseLock();
|
|
2553
3060
|
}
|
|
2554
3061
|
};
|
|
2555
|
-
forward(processHandle.stdout,
|
|
2556
|
-
forward(processHandle.stderr,
|
|
2557
|
-
return { releaseStartupLogs };
|
|
3062
|
+
forward(processHandle.stdout, "info");
|
|
3063
|
+
forward(processHandle.stderr, "error");
|
|
2558
3064
|
};
|
|
2559
3065
|
var resolveService = (name, service, options) => {
|
|
2560
3066
|
const cwd = resolve6(service.cwd ?? ".");
|
|
2561
3067
|
const envVars = {
|
|
2562
3068
|
...process.env,
|
|
2563
3069
|
...service.env,
|
|
3070
|
+
ABSOLUTE_WORKSPACE_MANAGED: "1",
|
|
2564
3071
|
ABSOLUTE_WORKSPACE_SERVICE_NAME: name,
|
|
2565
3072
|
ABSOLUTE_WORKSPACE_SERVICE_VISIBILITY: getVisibility(service),
|
|
2566
3073
|
FORCE_COLOR: "1",
|
|
@@ -2599,13 +3106,6 @@ var resolveService = (name, service, options) => {
|
|
|
2599
3106
|
visibility: getVisibility(service)
|
|
2600
3107
|
};
|
|
2601
3108
|
};
|
|
2602
|
-
var renderWorkspaceStepStart = (message) => {
|
|
2603
|
-
process.stdout.write(`${workspaceTag("\x1B[36m", message)}\r`);
|
|
2604
|
-
};
|
|
2605
|
-
var renderWorkspaceStepDone = (message, color = "\x1B[32m") => {
|
|
2606
|
-
process.stdout.write(`\r\x1B[2K${workspaceTag(color, message)}
|
|
2607
|
-
`);
|
|
2608
|
-
};
|
|
2609
3109
|
var workspace = async (subcommand, options) => {
|
|
2610
3110
|
if (subcommand !== "dev") {
|
|
2611
3111
|
throw new Error(subcommand ? `Unknown workspace command: ${subcommand}` : "No workspace subcommand specified. Use `absolute workspace dev`.");
|
|
@@ -2617,20 +3117,31 @@ var workspace = async (subcommand, options) => {
|
|
|
2617
3117
|
let shuttingDown = false;
|
|
2618
3118
|
let restarting = false;
|
|
2619
3119
|
let paused = false;
|
|
2620
|
-
let
|
|
2621
|
-
|
|
2622
|
-
const
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
3120
|
+
let workspaceBootStartedAt = performance.now();
|
|
3121
|
+
const absoluteVersion = resolvePackageVersion2();
|
|
3122
|
+
const tui = createWorkspaceTui({
|
|
3123
|
+
actions: {
|
|
3124
|
+
open: () => openInBrowser(),
|
|
3125
|
+
pause: () => {
|
|
3126
|
+
togglePause();
|
|
3127
|
+
},
|
|
3128
|
+
quit: () => {
|
|
3129
|
+
shutdown(0);
|
|
3130
|
+
},
|
|
3131
|
+
restart: () => restartWorkspace(),
|
|
3132
|
+
shell: (command) => runShellCommand2(command)
|
|
3133
|
+
},
|
|
3134
|
+
services: orderedNames.map((name) => {
|
|
3135
|
+
const service = workspaceConfig.services[name];
|
|
3136
|
+
return {
|
|
3137
|
+
name,
|
|
3138
|
+
port: service?.port,
|
|
3139
|
+
url: service ? getServiceUrl(service) : null,
|
|
3140
|
+
visibility: service ? getVisibility(service) : "public"
|
|
3141
|
+
};
|
|
3142
|
+
}),
|
|
3143
|
+
version: absoluteVersion
|
|
3144
|
+
});
|
|
2634
3145
|
const killProcesses = async () => {
|
|
2635
3146
|
const snapshot = [...running];
|
|
2636
3147
|
running.length = 0;
|
|
@@ -2655,9 +3166,7 @@ var workspace = async (subcommand, options) => {
|
|
|
2655
3166
|
return;
|
|
2656
3167
|
}
|
|
2657
3168
|
shuttingDown = true;
|
|
2658
|
-
|
|
2659
|
-
interactive.dispose();
|
|
2660
|
-
}
|
|
3169
|
+
tui.dispose();
|
|
2661
3170
|
if (paused) {
|
|
2662
3171
|
for (const service of running) {
|
|
2663
3172
|
sendSignalToService(service.process, "SIGCONT");
|
|
@@ -2667,33 +3176,8 @@ var workspace = async (subcommand, options) => {
|
|
|
2667
3176
|
await killProcesses();
|
|
2668
3177
|
process.exit(exitCode);
|
|
2669
3178
|
};
|
|
2670
|
-
const buildWorkspaceReadySummary = () => {
|
|
2671
|
-
const publicServices = orderedNames.map((name) => {
|
|
2672
|
-
const service = workspaceConfig.services[name];
|
|
2673
|
-
if (!service || getVisibility(service) !== "public") {
|
|
2674
|
-
return null;
|
|
2675
|
-
}
|
|
2676
|
-
const url = getServiceUrl(service);
|
|
2677
|
-
return url ? `${name} ${url}` : name;
|
|
2678
|
-
}).filter((value) => Boolean(value));
|
|
2679
|
-
const internalServices = orderedNames.map((name) => {
|
|
2680
|
-
const service = workspaceConfig.services[name];
|
|
2681
|
-
if (!service || getVisibility(service) !== "internal") {
|
|
2682
|
-
return null;
|
|
2683
|
-
}
|
|
2684
|
-
return service.port ? `${name} on ${service.port}` : name;
|
|
2685
|
-
}).filter((value) => Boolean(value));
|
|
2686
|
-
const segments = [];
|
|
2687
|
-
if (publicServices.length > 0) {
|
|
2688
|
-
segments.push(...publicServices);
|
|
2689
|
-
}
|
|
2690
|
-
if (internalServices.length > 0) {
|
|
2691
|
-
segments.push(`internal: ${internalServices.join(", ")}`);
|
|
2692
|
-
}
|
|
2693
|
-
return segments.join(" \xB7 ");
|
|
2694
|
-
};
|
|
2695
3179
|
const startServices = async () => {
|
|
2696
|
-
|
|
3180
|
+
tui.setReadyDuration(null);
|
|
2697
3181
|
for (const name of orderedNames) {
|
|
2698
3182
|
const service = workspaceConfig.services[name];
|
|
2699
3183
|
if (!service) {
|
|
@@ -2707,7 +3191,7 @@ var workspace = async (subcommand, options) => {
|
|
|
2707
3191
|
if (resolved.service.kind === "absolute" && resolved.configPath && !existsSync8(resolved.configPath)) {
|
|
2708
3192
|
throw new Error(`${name} references missing config "${resolved.configPath}"`);
|
|
2709
3193
|
}
|
|
2710
|
-
|
|
3194
|
+
tui.setServiceStatus(name, restarting ? "restarting" : "starting");
|
|
2711
3195
|
const processHandle = Bun.spawn(resolved.command, {
|
|
2712
3196
|
cwd: resolved.cwd,
|
|
2713
3197
|
env: resolved.env,
|
|
@@ -2715,12 +3199,11 @@ var workspace = async (subcommand, options) => {
|
|
|
2715
3199
|
stdin: "ignore",
|
|
2716
3200
|
stdout: "pipe"
|
|
2717
3201
|
});
|
|
2718
|
-
|
|
3202
|
+
pipeProcessLogs(name, processHandle, tui.addLog);
|
|
2719
3203
|
const runningService = {
|
|
2720
3204
|
name,
|
|
2721
3205
|
process: processHandle,
|
|
2722
|
-
resolved
|
|
2723
|
-
releaseStartupLogs
|
|
3206
|
+
resolved
|
|
2724
3207
|
};
|
|
2725
3208
|
running.push(runningService);
|
|
2726
3209
|
processHandle.exited.then((exitCode) => {
|
|
@@ -2730,59 +3213,72 @@ var workspace = async (subcommand, options) => {
|
|
|
2730
3213
|
if (!running.includes(runningService)) {
|
|
2731
3214
|
return;
|
|
2732
3215
|
}
|
|
2733
|
-
|
|
3216
|
+
tui.setServiceStatus(name, "error", `exit code ${exitCode || 1}`);
|
|
3217
|
+
tui.addLog("workspace", `${name} exited with code ${exitCode || 1}. Shutting down workspace.`, "error");
|
|
2734
3218
|
shutdown(exitCode || 1);
|
|
2735
3219
|
});
|
|
2736
3220
|
await waitForHealthcheck(getHealthcheckUrl(resolved.service));
|
|
2737
|
-
|
|
2738
|
-
releaseStartupLogs();
|
|
3221
|
+
tui.setServiceStatus(name, "ready");
|
|
2739
3222
|
}
|
|
2740
|
-
workspaceReady = true;
|
|
2741
|
-
console.log(workspaceTag("\x1B[32m", `Ready \xB7 ${buildWorkspaceReadySummary()}`));
|
|
2742
|
-
printHint();
|
|
2743
|
-
interactive?.showPrompt();
|
|
2744
3223
|
};
|
|
2745
3224
|
const restartWorkspace = async () => {
|
|
2746
3225
|
if (shuttingDown || restarting) {
|
|
2747
3226
|
return;
|
|
2748
3227
|
}
|
|
2749
3228
|
restarting = true;
|
|
2750
|
-
workspaceReady = false;
|
|
2751
3229
|
if (paused) {
|
|
2752
3230
|
for (const service of running) {
|
|
2753
3231
|
sendSignalToService(service.process, "SIGCONT");
|
|
2754
3232
|
}
|
|
2755
3233
|
paused = false;
|
|
2756
3234
|
}
|
|
2757
|
-
|
|
2758
|
-
|
|
3235
|
+
tui.addLog("workspace", "Restarting workspace...", "info");
|
|
3236
|
+
for (const name of orderedNames) {
|
|
3237
|
+
tui.setServiceStatus(name, "restarting");
|
|
3238
|
+
}
|
|
2759
3239
|
await killProcesses();
|
|
2760
3240
|
restarting = false;
|
|
3241
|
+
workspaceBootStartedAt = performance.now();
|
|
2761
3242
|
await startServices();
|
|
3243
|
+
tui.setReadyDuration(performance.now() - workspaceBootStartedAt);
|
|
3244
|
+
tui.addLog("workspace", "Workspace ready.", "success");
|
|
2762
3245
|
};
|
|
2763
3246
|
const togglePause = () => {
|
|
2764
3247
|
if (paused) {
|
|
2765
3248
|
for (const service of running) {
|
|
2766
3249
|
sendSignalToService(service.process, "SIGCONT");
|
|
3250
|
+
tui.setServiceStatus(service.name, "ready");
|
|
2767
3251
|
}
|
|
2768
3252
|
paused = false;
|
|
2769
|
-
|
|
3253
|
+
tui.addLog("workspace", "Workspace resumed.", "success");
|
|
2770
3254
|
} else {
|
|
2771
3255
|
for (const service of running) {
|
|
2772
3256
|
sendSignalToService(service.process, "SIGSTOP");
|
|
3257
|
+
tui.setServiceStatus(service.name, "paused");
|
|
2773
3258
|
}
|
|
2774
3259
|
paused = true;
|
|
2775
|
-
|
|
3260
|
+
tui.addLog("workspace", "Workspace paused.", "warn");
|
|
2776
3261
|
}
|
|
2777
3262
|
};
|
|
2778
3263
|
const runShellCommand2 = async (command) => {
|
|
2779
|
-
|
|
3264
|
+
const processHandle = Bun.spawn(["bash", "-lc", command], {
|
|
3265
|
+
env: { ...process.env, FORCE_COLOR: "1" },
|
|
3266
|
+
stderr: "pipe",
|
|
3267
|
+
stdout: "pipe"
|
|
3268
|
+
});
|
|
3269
|
+
pipeProcessLogs("shell", processHandle, tui.addLog);
|
|
3270
|
+
const exitCode = await processHandle.exited;
|
|
3271
|
+
if (exitCode === 0) {
|
|
3272
|
+
tui.addLog("workspace", `Shell command finished: ${command}`, "success");
|
|
3273
|
+
return;
|
|
3274
|
+
}
|
|
3275
|
+
tui.addLog("workspace", `Shell command failed with exit code ${exitCode}: ${command}`, "error");
|
|
2780
3276
|
};
|
|
2781
3277
|
const openInBrowser = async () => {
|
|
2782
3278
|
const publicService = orderedNames.map((name) => workspaceConfig.services[name]).find((service) => service && getVisibility(service) === "public");
|
|
2783
3279
|
const url = publicService ? getServiceUrl(publicService) : null;
|
|
2784
3280
|
if (!url) {
|
|
2785
|
-
|
|
3281
|
+
tui.addLog("workspace", "No public service to open.", "warn");
|
|
2786
3282
|
return;
|
|
2787
3283
|
}
|
|
2788
3284
|
const { platform: platform4 } = process;
|
|
@@ -2803,35 +3299,22 @@ var workspace = async (subcommand, options) => {
|
|
|
2803
3299
|
stderr: "ignore",
|
|
2804
3300
|
stdout: "ignore"
|
|
2805
3301
|
});
|
|
2806
|
-
|
|
3302
|
+
tui.addLog("workspace", `Opening ${url}`, "info");
|
|
2807
3303
|
} catch {
|
|
2808
|
-
|
|
3304
|
+
tui.addLog("workspace", `Could not open browser automatically. Visit ${url}`, "warn");
|
|
2809
3305
|
}
|
|
2810
3306
|
};
|
|
2811
|
-
interactive = createInteractiveHandler({
|
|
2812
|
-
shell: runShellCommand2,
|
|
2813
|
-
clear: () => {
|
|
2814
|
-
process.stdout.write("\x1Bc");
|
|
2815
|
-
},
|
|
2816
|
-
help: () => {
|
|
2817
|
-
printHelp("workspace");
|
|
2818
|
-
},
|
|
2819
|
-
open: () => openInBrowser(),
|
|
2820
|
-
pause: () => {
|
|
2821
|
-
togglePause();
|
|
2822
|
-
},
|
|
2823
|
-
quit: () => {
|
|
2824
|
-
shutdown(0);
|
|
2825
|
-
},
|
|
2826
|
-
restart: () => restartWorkspace()
|
|
2827
|
-
});
|
|
2828
3307
|
process.on("SIGINT", () => {
|
|
2829
3308
|
shutdown(0);
|
|
2830
3309
|
});
|
|
2831
3310
|
process.on("SIGTERM", () => {
|
|
2832
3311
|
shutdown(0);
|
|
2833
3312
|
});
|
|
3313
|
+
tui.start();
|
|
3314
|
+
tui.addLog("workspace", `Workspace booting ${orderedNames.length} services.`, "info");
|
|
2834
3315
|
await startServices();
|
|
3316
|
+
tui.setReadyDuration(performance.now() - workspaceBootStartedAt);
|
|
3317
|
+
tui.addLog("workspace", "Workspace ready.", "success");
|
|
2835
3318
|
await new Promise(() => {});
|
|
2836
3319
|
};
|
|
2837
3320
|
|