@slopus/happy-terminal 0.3.1 → 0.3.3
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 +2 -1
- package/dist/app/runApp.d.ts +2 -0
- package/dist/index.js +16 -71
- package/dist/main.js +78 -95
- package/package.json +2 -2
- package/dist/app/renderHappyTerminalVersion.d.ts +0 -1
package/README.md
CHANGED
|
@@ -43,7 +43,8 @@ await runHappyTerminal({
|
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
Embedded sessions use `happy` in resume instructions by default. A different host command can be
|
|
46
|
-
provided with `commandName`.
|
|
46
|
+
provided with `commandName`. An embedded host may also provide `version` to replace the installed
|
|
47
|
+
Happy Terminal package version in the startup UI.
|
|
47
48
|
|
|
48
49
|
Run only one inline terminal at a time because each instance owns the process terminal while it is
|
|
49
50
|
active. Happy Terminal requires Node.js 24 or newer.
|
package/dist/app/runApp.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface RunAppOptions {
|
|
|
17
17
|
sessionSelection?: StartupSessionSelection;
|
|
18
18
|
showReasoning?: boolean;
|
|
19
19
|
showUsage?: boolean;
|
|
20
|
+
/** Version displayed by the host, defaulting to the installed Happy Terminal package. */
|
|
21
|
+
version?: string;
|
|
20
22
|
}
|
|
21
23
|
export type RunAppResult = {
|
|
22
24
|
action: "exit";
|
package/dist/index.js
CHANGED
|
@@ -889,6 +889,9 @@ function delay2(milliseconds) {
|
|
|
889
889
|
}
|
|
890
890
|
|
|
891
891
|
// sources/daemon/runDaemonCommand.ts
|
|
892
|
+
import {
|
|
893
|
+
HappyAgentApiError
|
|
894
|
+
} from "@slopus/happy-agent-client";
|
|
892
895
|
import { createRootContext as createRootContext2 } from "@steve.kite/stdlib";
|
|
893
896
|
|
|
894
897
|
// sources/processes/killProcessTree.ts
|
|
@@ -1806,7 +1809,7 @@ async function delay3(ms, signal) {
|
|
|
1806
1809
|
import { resolve } from "node:path";
|
|
1807
1810
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
1808
1811
|
import {
|
|
1809
|
-
HappyAgentApiError
|
|
1812
|
+
HappyAgentApiError as HappyAgentApiError2
|
|
1810
1813
|
} from "@slopus/happy-agent-client";
|
|
1811
1814
|
var WORKSPACE_INITIALIZATION_TIMEOUT_MS = 12e4;
|
|
1812
1815
|
var WORKSPACE_INITIALIZATION_POLL_MS = 50;
|
|
@@ -1876,7 +1879,7 @@ async function waitForWorkspaceReady(client, workspaceId) {
|
|
|
1876
1879
|
try {
|
|
1877
1880
|
return (await client.getWorkspace(workspaceId)).workspace;
|
|
1878
1881
|
} catch (error) {
|
|
1879
|
-
if (!(error instanceof
|
|
1882
|
+
if (!(error instanceof HappyAgentApiError2) || error.code !== "not_initialized") {
|
|
1880
1883
|
throw error;
|
|
1881
1884
|
}
|
|
1882
1885
|
if (Date.now() >= deadline) {
|
|
@@ -9346,76 +9349,26 @@ function renderPendingSteeringMessages(messages, width) {
|
|
|
9346
9349
|
|
|
9347
9350
|
// sources/app/renderHappyTerminalBanner.ts
|
|
9348
9351
|
import { truncateToWidth as truncateToWidth20 } from "@earendil-works/pi-tui";
|
|
9349
|
-
|
|
9350
|
-
// sources/app/renderHappyTerminalVersion.ts
|
|
9351
|
-
var VERSION_GLYPHS = {
|
|
9352
|
-
"0": [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
9353
|
-
"1": [" \u2588\u2588\u2557", "\u2588\u2588\u2588\u2551", "\u255A\u2588\u2588\u2551", " \u2588\u2588\u2551", " \u2588\u2588\u2551", " \u255A\u2550\u255D"],
|
|
9354
|
-
"2": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2557", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
|
|
9355
|
-
"3": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2557", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
9356
|
-
"4": ["\u2588\u2588\u2557 \u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2551", " \u255A\u2550\u255D"],
|
|
9357
|
-
"5": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
|
|
9358
|
-
"6": [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
9359
|
-
"7": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2554\u255D", " \u2588\u2588\u2554\u255D ", " \u2588\u2588\u2551 ", " \u255A\u2550\u255D "],
|
|
9360
|
-
"8": [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u255D "],
|
|
9361
|
-
"9": [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2551", " \u255A\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u255D "],
|
|
9362
|
-
".": [" ", " ", " ", " ", "\u2588\u2588\u2557", "\u255A\u2550\u255D"]
|
|
9363
|
-
};
|
|
9364
|
-
function renderHappyTerminalVersion(version, width) {
|
|
9365
|
-
const glyphs = [...version].map((character) => VERSION_GLYPHS[character]);
|
|
9366
|
-
if (width < 40 || glyphs.some((glyph) => glyph === void 0)) {
|
|
9367
|
-
return [version.slice(0, width)];
|
|
9368
|
-
}
|
|
9369
|
-
const chunks = [];
|
|
9370
|
-
let chunk = [];
|
|
9371
|
-
for (const glyph of glyphs) {
|
|
9372
|
-
const candidate = [...chunk, glyph];
|
|
9373
|
-
const candidateWidth = Math.max(
|
|
9374
|
-
...Array.from(
|
|
9375
|
-
{ length: 6 },
|
|
9376
|
-
(_, rowIndex) => candidate.reduce((rowWidth, item) => rowWidth + (item[rowIndex]?.length ?? 0), 0)
|
|
9377
|
-
)
|
|
9378
|
-
);
|
|
9379
|
-
if (chunk.length > 0 && candidateWidth > width) {
|
|
9380
|
-
chunks.push(chunk);
|
|
9381
|
-
chunk = [glyph];
|
|
9382
|
-
} else {
|
|
9383
|
-
chunk = candidate;
|
|
9384
|
-
}
|
|
9385
|
-
}
|
|
9386
|
-
if (chunk.length > 0) chunks.push(chunk);
|
|
9387
|
-
return chunks.flatMap((items) => {
|
|
9388
|
-
const rows = Array.from(
|
|
9389
|
-
{ length: 6 },
|
|
9390
|
-
(_, rowIndex) => items.map((glyph) => glyph[rowIndex] ?? "").join("").trimEnd()
|
|
9391
|
-
);
|
|
9392
|
-
while (rows[0] === "") rows.shift();
|
|
9393
|
-
while (rows.at(-1) === "") rows.pop();
|
|
9394
|
-
return rows;
|
|
9395
|
-
});
|
|
9396
|
-
}
|
|
9397
|
-
|
|
9398
|
-
// sources/app/renderHappyTerminalBanner.ts
|
|
9399
9352
|
var RESET19 = "\x1B[0m";
|
|
9400
|
-
var
|
|
9353
|
+
var HAPPY_LOGO = [
|
|
9401
9354
|
"\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
9402
9355
|
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D",
|
|
9403
9356
|
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2588\u2588\u2554\u255D ",
|
|
9404
9357
|
"\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u255A\u2588\u2588\u2554\u255D ",
|
|
9405
|
-
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
9406
|
-
"\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
9358
|
+
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
|
|
9359
|
+
"\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D "
|
|
9407
9360
|
];
|
|
9408
|
-
var
|
|
9361
|
+
var HAPPY_LOGO_WIDTH = Math.max(...HAPPY_LOGO.map((line) => line.length));
|
|
9409
9362
|
var BANNER_GAP = " ";
|
|
9410
9363
|
var BANNER_PADDING = " ";
|
|
9411
9364
|
function renderHappyTerminalBanner(options) {
|
|
9412
9365
|
const width = Math.max(1, options.width);
|
|
9413
9366
|
const contentWidth = width - BANNER_PADDING.length * 2;
|
|
9414
9367
|
if (contentWidth <= 0) return [" ".repeat(width)];
|
|
9415
|
-
if (contentWidth <
|
|
9368
|
+
if (contentWidth < HAPPY_LOGO_WIDTH + BANNER_GAP.length + options.version.length) {
|
|
9416
9369
|
const lines3 = [
|
|
9417
9370
|
truncateToWidth20(
|
|
9418
|
-
`${options.brand}
|
|
9371
|
+
`${options.brand}HAPPY${RESET19} ${options.secondary}${options.version}${RESET19}`,
|
|
9419
9372
|
contentWidth,
|
|
9420
9373
|
"",
|
|
9421
9374
|
false
|
|
@@ -9423,17 +9376,9 @@ function renderHappyTerminalBanner(options) {
|
|
|
9423
9376
|
];
|
|
9424
9377
|
return padBannerLines(lines3);
|
|
9425
9378
|
}
|
|
9426
|
-
const
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
const lines3 = HAPPY_TERMINAL_LOGO.map(
|
|
9430
|
-
(line, index) => `${options.brand}${line.padEnd(HAPPY_TERMINAL_LOGO_WIDTH)}${RESET19}${BANNER_GAP}${options.secondary}${versionLines[index]}${RESET19}`
|
|
9431
|
-
);
|
|
9432
|
-
return padBannerLines(lines3);
|
|
9433
|
-
}
|
|
9434
|
-
const lines2 = HAPPY_TERMINAL_LOGO.map((line, index) => {
|
|
9435
|
-
const version = index === HAPPY_TERMINAL_LOGO.length - 1 ? `${BANNER_GAP}${options.secondary}${versionLines[0]}${RESET19}` : "";
|
|
9436
|
-
return `${options.brand}${line.padEnd(HAPPY_TERMINAL_LOGO_WIDTH)}${RESET19}${version}`;
|
|
9379
|
+
const lines2 = HAPPY_LOGO.map((line, index) => {
|
|
9380
|
+
const version = index === HAPPY_LOGO.length - 1 ? `${BANNER_GAP}${options.secondary}${options.version}${RESET19}` : "";
|
|
9381
|
+
return `${options.brand}${line.padEnd(HAPPY_LOGO_WIDTH)}${RESET19}${version}`;
|
|
9437
9382
|
});
|
|
9438
9383
|
return padBannerLines(lines2);
|
|
9439
9384
|
}
|
|
@@ -16604,6 +16549,7 @@ var StartupStatusApp = class {
|
|
|
16604
16549
|
var INITIAL_TUI_MESSAGE_LIMIT = 30;
|
|
16605
16550
|
async function runApp(ctx, options = {}) {
|
|
16606
16551
|
const requestedCwd = options.cwd ?? process.cwd();
|
|
16552
|
+
const version = options.version ?? readPackageVersion();
|
|
16607
16553
|
const loadedConfig = await loadConfig({ cwd: requestedCwd });
|
|
16608
16554
|
let compactCompletedTurns = options.compactCompletedTurns ?? loadedConfig.config.settings.compactCompletedTurns;
|
|
16609
16555
|
let completionChime = loadedConfig.config.settings.completionChime;
|
|
@@ -16620,7 +16566,7 @@ async function runApp(ctx, options = {}) {
|
|
|
16620
16566
|
rows: () => terminal.rows,
|
|
16621
16567
|
theme: startupTheme,
|
|
16622
16568
|
tui,
|
|
16623
|
-
version
|
|
16569
|
+
version
|
|
16624
16570
|
});
|
|
16625
16571
|
const terminalCrashCleanup = installTerminalCrashCleanup({ terminal, tui });
|
|
16626
16572
|
let terminalAppearance;
|
|
@@ -16743,7 +16689,6 @@ async function runApp(ctx, options = {}) {
|
|
|
16743
16689
|
if (loadedConfig.config.defaults.serviceTier !== void 0) {
|
|
16744
16690
|
agent.setServiceTier(loadedConfig.config.defaults.serviceTier);
|
|
16745
16691
|
}
|
|
16746
|
-
const version = readPackageVersion();
|
|
16747
16692
|
const tuiInspectorUrl = getNodeInspectorUrl();
|
|
16748
16693
|
const startupQuotas = await resolveStartupProviderQuota(() => agent.providerQuotas());
|
|
16749
16694
|
const startupUsage = providerQuotaToStartupStatusUsage(
|
package/dist/main.js
CHANGED
|
@@ -1799,6 +1799,9 @@ function delay2(milliseconds) {
|
|
|
1799
1799
|
// sources/daemon/runDaemonCommand.ts
|
|
1800
1800
|
init_HappyTerminalUserError();
|
|
1801
1801
|
import { lstat as lstat2 } from "node:fs/promises";
|
|
1802
|
+
import {
|
|
1803
|
+
HappyAgentApiError
|
|
1804
|
+
} from "@slopus/happy-agent-client";
|
|
1802
1805
|
import { createRootContext as createRootContext2 } from "@steve.kite/stdlib";
|
|
1803
1806
|
|
|
1804
1807
|
// sources/processes/killProcessTree.ts
|
|
@@ -2768,7 +2771,7 @@ function errorCode(error) {
|
|
|
2768
2771
|
|
|
2769
2772
|
// sources/daemon/runDaemonCommand.ts
|
|
2770
2773
|
var DAEMON_SHUTDOWN_TIMEOUT_MS = 3e4;
|
|
2771
|
-
var
|
|
2774
|
+
var DRAIN_POLL_INTERVAL_MS = 100;
|
|
2772
2775
|
async function runDaemonCommand(command, log = console.log, ctx = createRootContext2().named("daemon-command")) {
|
|
2773
2776
|
const paths = getHappyDaemonPaths();
|
|
2774
2777
|
if (command === "start") {
|
|
@@ -2791,10 +2794,7 @@ async function runDaemonCommand(command, log = console.log, ctx = createRootCont
|
|
|
2791
2794
|
const observed = await observeLocalProtocolServer(paths);
|
|
2792
2795
|
if (command === "reload") {
|
|
2793
2796
|
if (observed !== void 0) {
|
|
2794
|
-
await stopLocalProtocolServer(ctx, observed.client, paths,
|
|
2795
|
-
forceAfterMs: DAEMON_RELOAD_GRACE_TIMEOUT_MS,
|
|
2796
|
-
onForce: log
|
|
2797
|
-
});
|
|
2797
|
+
await stopLocalProtocolServer(ctx, observed.client, paths, log);
|
|
2798
2798
|
} else await assertNoUnresponsiveDaemon(paths.pidPath);
|
|
2799
2799
|
const connection = await ensureLocalProtocolServer();
|
|
2800
2800
|
log(`Daemon is running at ${connection.paths.socketPath}`);
|
|
@@ -2812,8 +2812,13 @@ async function runDaemonCommand(command, log = console.log, ctx = createRootCont
|
|
|
2812
2812
|
log("Daemon is not running.");
|
|
2813
2813
|
}
|
|
2814
2814
|
} else if (observed.health.ready) {
|
|
2815
|
-
log(
|
|
2815
|
+
log(
|
|
2816
|
+
observed.health.draining === true ? `Daemon is draining at ${paths.socketPath}` : `Daemon is running at ${paths.socketPath}`
|
|
2817
|
+
);
|
|
2816
2818
|
log(`Daemon PID: ${String(pid ?? "unknown")}`);
|
|
2819
|
+
if (observed.health.draining === true) {
|
|
2820
|
+
log(formatDrainProgress(observed.health.drainWaitingFor ?? []));
|
|
2821
|
+
}
|
|
2817
2822
|
} else {
|
|
2818
2823
|
log(`Daemon is starting at ${paths.socketPath}`);
|
|
2819
2824
|
log(`Daemon PID: ${String(pid ?? "unknown")}`);
|
|
@@ -2828,28 +2833,25 @@ async function runDaemonCommand(command, log = console.log, ctx = createRootCont
|
|
|
2828
2833
|
return;
|
|
2829
2834
|
}
|
|
2830
2835
|
log("Daemon is stopping.");
|
|
2831
|
-
await stopLocalProtocolServer(ctx, observed.client, paths);
|
|
2836
|
+
await stopLocalProtocolServer(ctx, observed.client, paths, log);
|
|
2832
2837
|
log("Daemon stopped.");
|
|
2833
2838
|
}
|
|
2834
|
-
async function stopLocalProtocolServer(ctx, client, paths,
|
|
2839
|
+
async function stopLocalProtocolServer(ctx, client, paths, report) {
|
|
2835
2840
|
let pid = await readDaemonPid(paths.pidPath);
|
|
2841
|
+
await drainLocalProtocolServer(client, report);
|
|
2836
2842
|
let requestError;
|
|
2837
2843
|
try {
|
|
2838
2844
|
pid = (await client.shutdown()).pid;
|
|
2839
2845
|
} catch (error) {
|
|
2840
2846
|
requestError = error;
|
|
2841
2847
|
}
|
|
2842
|
-
const
|
|
2843
|
-
|
|
2848
|
+
const stopped = await waitForDaemonShutdown(
|
|
2849
|
+
ctx,
|
|
2850
|
+
paths.socketPath,
|
|
2851
|
+
pid,
|
|
2852
|
+
DAEMON_SHUTDOWN_TIMEOUT_MS
|
|
2853
|
+
);
|
|
2844
2854
|
if (stopped.socketRemoved && stopped.processExited) return;
|
|
2845
|
-
if (options.forceAfterMs !== void 0 && pid !== void 0) {
|
|
2846
|
-
options.onForce?.(
|
|
2847
|
-
`Daemon did not stop gracefully; forcing process ${String(pid)} to exit.`
|
|
2848
|
-
);
|
|
2849
|
-
if (!stopped.processExited) await killDaemonProcess(ctx, pid);
|
|
2850
|
-
await removeDaemonPid(paths.pidPath, pid);
|
|
2851
|
-
return;
|
|
2852
|
-
}
|
|
2853
2855
|
if (requestError !== void 0) {
|
|
2854
2856
|
throw new Error(
|
|
2855
2857
|
`Could not stop the existing local daemon: ${requestError instanceof Error ? requestError.message : String(requestError)}`
|
|
@@ -2864,6 +2866,45 @@ async function stopLocalProtocolServer(ctx, client, paths, options = {}) {
|
|
|
2864
2866
|
);
|
|
2865
2867
|
}
|
|
2866
2868
|
}
|
|
2869
|
+
async function drainLocalProtocolServer(client, report) {
|
|
2870
|
+
try {
|
|
2871
|
+
await client.drain();
|
|
2872
|
+
} catch (error) {
|
|
2873
|
+
if (error instanceof HappyAgentApiError && (error.status === 403 || error.status === 404)) {
|
|
2874
|
+
report("This daemon does not support draining; stopping it directly.");
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
throw error;
|
|
2878
|
+
}
|
|
2879
|
+
let previous;
|
|
2880
|
+
for (; ; ) {
|
|
2881
|
+
const health = await client.getHealth();
|
|
2882
|
+
const waiting = health.drainWaitingFor ?? [];
|
|
2883
|
+
const current = JSON.stringify(waiting);
|
|
2884
|
+
if (current !== previous) {
|
|
2885
|
+
report(formatDrainProgress(waiting));
|
|
2886
|
+
previous = current;
|
|
2887
|
+
}
|
|
2888
|
+
if (waiting.length === 0) return;
|
|
2889
|
+
await new Promise((resolve4) => setTimeout(resolve4, DRAIN_POLL_INTERVAL_MS));
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
function formatDrainProgress(waiting) {
|
|
2893
|
+
if (waiting.length === 0) return "Daemon drain is complete.";
|
|
2894
|
+
return `Draining: ${waiting.map(formatDrainWait).join("; ")}.`;
|
|
2895
|
+
}
|
|
2896
|
+
function formatDrainWait(wait) {
|
|
2897
|
+
const noun = wait.name === "api-mutations" ? plural(wait.count, "API mutation") : wait.name === "agent-system" ? plural(wait.count, "agent") : wait.name === "auto-agent-system" ? plural(wait.count, "permission reviewer") : `${String(wait.count)} ${wait.name.replaceAll("-", " ")}`;
|
|
2898
|
+
if (wait.agents === void 0 || wait.agents.length === 0) return noun;
|
|
2899
|
+
const agents = wait.agents.map((agent) => `${agent.id}: ${stageLabel(agent.stage)}`).join(", ");
|
|
2900
|
+
return `${noun} (${agents}${wait.truncated === true ? ", \u2026" : ""})`;
|
|
2901
|
+
}
|
|
2902
|
+
function stageLabel(stage) {
|
|
2903
|
+
return stage === "tools" ? "tool calls" : stage;
|
|
2904
|
+
}
|
|
2905
|
+
function plural(count2, noun) {
|
|
2906
|
+
return `${String(count2)} ${noun}${count2 === 1 ? "" : "s"}`;
|
|
2907
|
+
}
|
|
2867
2908
|
async function waitForDaemonShutdown(ctx, socketPath, pid, timeoutMs) {
|
|
2868
2909
|
const [socketRemoved, processExited] = await Promise.all([
|
|
2869
2910
|
waitForSocketRemoval(socketPath, timeoutMs),
|
|
@@ -3017,7 +3058,7 @@ async function delay4(ms, signal) {
|
|
|
3017
3058
|
import { resolve } from "node:path";
|
|
3018
3059
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
3019
3060
|
import {
|
|
3020
|
-
HappyAgentApiError
|
|
3061
|
+
HappyAgentApiError as HappyAgentApiError2
|
|
3021
3062
|
} from "@slopus/happy-agent-client";
|
|
3022
3063
|
var WORKSPACE_INITIALIZATION_TIMEOUT_MS = 12e4;
|
|
3023
3064
|
var WORKSPACE_INITIALIZATION_POLL_MS = 50;
|
|
@@ -3087,7 +3128,7 @@ async function waitForWorkspaceReady(client, workspaceId) {
|
|
|
3087
3128
|
try {
|
|
3088
3129
|
return (await client.getWorkspace(workspaceId)).workspace;
|
|
3089
3130
|
} catch (error) {
|
|
3090
|
-
if (!(error instanceof
|
|
3131
|
+
if (!(error instanceof HappyAgentApiError2) || error.code !== "not_initialized") {
|
|
3091
3132
|
throw error;
|
|
3092
3133
|
}
|
|
3093
3134
|
if (Date.now() >= deadline) {
|
|
@@ -9937,8 +9978,8 @@ var RESET13 = "\x1B[0m";
|
|
|
9937
9978
|
var DIM10 = "\x1B[2m";
|
|
9938
9979
|
function renderBackgroundTerminalSummary(count2, width) {
|
|
9939
9980
|
if (count2 === 0) return void 0;
|
|
9940
|
-
const
|
|
9941
|
-
const summary = ` ${String(count2)} background terminal${
|
|
9981
|
+
const plural2 = count2 === 1 ? "" : "s";
|
|
9982
|
+
const summary = ` ${String(count2)} background terminal${plural2} running \xB7 /ps to view \xB7 /stop to close`;
|
|
9942
9983
|
return truncateToWidth11(`${DIM10}${summary}${RESET13}`, Math.max(1, width), "", true);
|
|
9943
9984
|
}
|
|
9944
9985
|
|
|
@@ -11020,76 +11061,26 @@ function renderPendingSteeringMessages(messages, width) {
|
|
|
11020
11061
|
|
|
11021
11062
|
// sources/app/renderHappyTerminalBanner.ts
|
|
11022
11063
|
import { truncateToWidth as truncateToWidth20 } from "@earendil-works/pi-tui";
|
|
11023
|
-
|
|
11024
|
-
// sources/app/renderHappyTerminalVersion.ts
|
|
11025
|
-
var VERSION_GLYPHS = {
|
|
11026
|
-
"0": [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
11027
|
-
"1": [" \u2588\u2588\u2557", "\u2588\u2588\u2588\u2551", "\u255A\u2588\u2588\u2551", " \u2588\u2588\u2551", " \u2588\u2588\u2551", " \u255A\u2550\u255D"],
|
|
11028
|
-
"2": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2557", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
|
|
11029
|
-
"3": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2557", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
11030
|
-
"4": ["\u2588\u2588\u2557 \u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2551", " \u255A\u2550\u255D"],
|
|
11031
|
-
"5": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
|
|
11032
|
-
"6": [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
11033
|
-
"7": ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2554\u255D", " \u2588\u2588\u2554\u255D ", " \u2588\u2588\u2551 ", " \u255A\u2550\u255D "],
|
|
11034
|
-
"8": [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u255D "],
|
|
11035
|
-
"9": [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2551", " \u255A\u2550\u2550\u2550\u2588\u2588\u2551", " \u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u255D "],
|
|
11036
|
-
".": [" ", " ", " ", " ", "\u2588\u2588\u2557", "\u255A\u2550\u255D"]
|
|
11037
|
-
};
|
|
11038
|
-
function renderHappyTerminalVersion(version, width) {
|
|
11039
|
-
const glyphs = [...version].map((character) => VERSION_GLYPHS[character]);
|
|
11040
|
-
if (width < 40 || glyphs.some((glyph) => glyph === void 0)) {
|
|
11041
|
-
return [version.slice(0, width)];
|
|
11042
|
-
}
|
|
11043
|
-
const chunks = [];
|
|
11044
|
-
let chunk = [];
|
|
11045
|
-
for (const glyph of glyphs) {
|
|
11046
|
-
const candidate = [...chunk, glyph];
|
|
11047
|
-
const candidateWidth = Math.max(
|
|
11048
|
-
...Array.from(
|
|
11049
|
-
{ length: 6 },
|
|
11050
|
-
(_, rowIndex) => candidate.reduce((rowWidth, item) => rowWidth + (item[rowIndex]?.length ?? 0), 0)
|
|
11051
|
-
)
|
|
11052
|
-
);
|
|
11053
|
-
if (chunk.length > 0 && candidateWidth > width) {
|
|
11054
|
-
chunks.push(chunk);
|
|
11055
|
-
chunk = [glyph];
|
|
11056
|
-
} else {
|
|
11057
|
-
chunk = candidate;
|
|
11058
|
-
}
|
|
11059
|
-
}
|
|
11060
|
-
if (chunk.length > 0) chunks.push(chunk);
|
|
11061
|
-
return chunks.flatMap((items) => {
|
|
11062
|
-
const rows = Array.from(
|
|
11063
|
-
{ length: 6 },
|
|
11064
|
-
(_, rowIndex) => items.map((glyph) => glyph[rowIndex] ?? "").join("").trimEnd()
|
|
11065
|
-
);
|
|
11066
|
-
while (rows[0] === "") rows.shift();
|
|
11067
|
-
while (rows.at(-1) === "") rows.pop();
|
|
11068
|
-
return rows;
|
|
11069
|
-
});
|
|
11070
|
-
}
|
|
11071
|
-
|
|
11072
|
-
// sources/app/renderHappyTerminalBanner.ts
|
|
11073
11064
|
var RESET19 = "\x1B[0m";
|
|
11074
|
-
var
|
|
11065
|
+
var HAPPY_LOGO = [
|
|
11075
11066
|
"\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
11076
11067
|
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D",
|
|
11077
11068
|
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2588\u2588\u2554\u255D ",
|
|
11078
11069
|
"\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u255A\u2588\u2588\u2554\u255D ",
|
|
11079
|
-
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
11080
|
-
"\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
11070
|
+
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
|
|
11071
|
+
"\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D "
|
|
11081
11072
|
];
|
|
11082
|
-
var
|
|
11073
|
+
var HAPPY_LOGO_WIDTH = Math.max(...HAPPY_LOGO.map((line) => line.length));
|
|
11083
11074
|
var BANNER_GAP = " ";
|
|
11084
11075
|
var BANNER_PADDING = " ";
|
|
11085
11076
|
function renderHappyTerminalBanner(options) {
|
|
11086
11077
|
const width = Math.max(1, options.width);
|
|
11087
11078
|
const contentWidth = width - BANNER_PADDING.length * 2;
|
|
11088
11079
|
if (contentWidth <= 0) return [" ".repeat(width)];
|
|
11089
|
-
if (contentWidth <
|
|
11080
|
+
if (contentWidth < HAPPY_LOGO_WIDTH + BANNER_GAP.length + options.version.length) {
|
|
11090
11081
|
const lines3 = [
|
|
11091
11082
|
truncateToWidth20(
|
|
11092
|
-
`${options.brand}
|
|
11083
|
+
`${options.brand}HAPPY${RESET19} ${options.secondary}${options.version}${RESET19}`,
|
|
11093
11084
|
contentWidth,
|
|
11094
11085
|
"",
|
|
11095
11086
|
false
|
|
@@ -11097,17 +11088,9 @@ function renderHappyTerminalBanner(options) {
|
|
|
11097
11088
|
];
|
|
11098
11089
|
return padBannerLines(lines3);
|
|
11099
11090
|
}
|
|
11100
|
-
const
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
const lines3 = HAPPY_TERMINAL_LOGO.map(
|
|
11104
|
-
(line, index) => `${options.brand}${line.padEnd(HAPPY_TERMINAL_LOGO_WIDTH)}${RESET19}${BANNER_GAP}${options.secondary}${versionLines[index]}${RESET19}`
|
|
11105
|
-
);
|
|
11106
|
-
return padBannerLines(lines3);
|
|
11107
|
-
}
|
|
11108
|
-
const lines2 = HAPPY_TERMINAL_LOGO.map((line, index) => {
|
|
11109
|
-
const version = index === HAPPY_TERMINAL_LOGO.length - 1 ? `${BANNER_GAP}${options.secondary}${versionLines[0]}${RESET19}` : "";
|
|
11110
|
-
return `${options.brand}${line.padEnd(HAPPY_TERMINAL_LOGO_WIDTH)}${RESET19}${version}`;
|
|
11091
|
+
const lines2 = HAPPY_LOGO.map((line, index) => {
|
|
11092
|
+
const version = index === HAPPY_LOGO.length - 1 ? `${BANNER_GAP}${options.secondary}${options.version}${RESET19}` : "";
|
|
11093
|
+
return `${options.brand}${line.padEnd(HAPPY_LOGO_WIDTH)}${RESET19}${version}`;
|
|
11111
11094
|
});
|
|
11112
11095
|
return padBannerLines(lines2);
|
|
11113
11096
|
}
|
|
@@ -11247,8 +11230,8 @@ var RESET21 = "\x1B[0m";
|
|
|
11247
11230
|
var DIM18 = "\x1B[2m";
|
|
11248
11231
|
function renderSubagentSummary(options) {
|
|
11249
11232
|
if (options.count === 0) return void 0;
|
|
11250
|
-
const
|
|
11251
|
-
const summary = ` ${String(options.count)} agent${
|
|
11233
|
+
const plural2 = options.count === 1 ? "" : "s";
|
|
11234
|
+
const summary = ` ${String(options.count)} agent${plural2} running \xB7 /agents to view \xB7 ${formatActivityElapsedTime(options.elapsedMs)} \xB7 ${formatCompactTokens(options.totalTokens)} context tokens`;
|
|
11252
11235
|
return truncateToWidth23(`${DIM18}${summary}${RESET21}`, Math.max(1, options.width), "", true);
|
|
11253
11236
|
}
|
|
11254
11237
|
|
|
@@ -11271,8 +11254,8 @@ var RESET23 = "\x1B[0m";
|
|
|
11271
11254
|
var DIM20 = "\x1B[2m";
|
|
11272
11255
|
function renderWorkflowSummary(count2, width) {
|
|
11273
11256
|
if (count2 === 0) return void 0;
|
|
11274
|
-
const
|
|
11275
|
-
const summary = ` ${String(count2)} workflow${
|
|
11257
|
+
const plural2 = count2 === 1 ? "" : "s";
|
|
11258
|
+
const summary = ` ${String(count2)} workflow${plural2} running \xB7 /workflows to view`;
|
|
11276
11259
|
return truncateToWidth25(`${DIM20}${summary}${RESET23}`, Math.max(1, width), "", true);
|
|
11277
11260
|
}
|
|
11278
11261
|
|
|
@@ -18281,6 +18264,7 @@ var StartupStatusApp = class {
|
|
|
18281
18264
|
var INITIAL_TUI_MESSAGE_LIMIT = 30;
|
|
18282
18265
|
async function runApp(ctx, options = {}) {
|
|
18283
18266
|
const requestedCwd = options.cwd ?? process.cwd();
|
|
18267
|
+
const version = options.version ?? readPackageVersion();
|
|
18284
18268
|
const loadedConfig = await loadConfig({ cwd: requestedCwd });
|
|
18285
18269
|
let compactCompletedTurns = options.compactCompletedTurns ?? loadedConfig.config.settings.compactCompletedTurns;
|
|
18286
18270
|
let completionChime = loadedConfig.config.settings.completionChime;
|
|
@@ -18297,7 +18281,7 @@ async function runApp(ctx, options = {}) {
|
|
|
18297
18281
|
rows: () => terminal.rows,
|
|
18298
18282
|
theme: startupTheme,
|
|
18299
18283
|
tui,
|
|
18300
|
-
version
|
|
18284
|
+
version
|
|
18301
18285
|
});
|
|
18302
18286
|
const terminalCrashCleanup = installTerminalCrashCleanup({ terminal, tui });
|
|
18303
18287
|
let terminalAppearance;
|
|
@@ -18420,7 +18404,6 @@ async function runApp(ctx, options = {}) {
|
|
|
18420
18404
|
if (loadedConfig.config.defaults.serviceTier !== void 0) {
|
|
18421
18405
|
agent.setServiceTier(loadedConfig.config.defaults.serviceTier);
|
|
18422
18406
|
}
|
|
18423
|
-
const version = readPackageVersion();
|
|
18424
18407
|
const tuiInspectorUrl = getNodeInspectorUrl();
|
|
18425
18408
|
const startupQuotas = await resolveStartupProviderQuota(() => agent.providerQuotas());
|
|
18426
18409
|
const startupUsage = providerQuotaToStartupStatusUsage(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slopus/happy-terminal",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Happy Terminal, the embeddable terminal client for Happy Agent.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"coding-agent",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@sinclair/typebox": "^0.34.49",
|
|
64
64
|
"@slopus/ghostty-wasm": "1.3.6",
|
|
65
65
|
"@slopus/ghostty-web": "0.1.3",
|
|
66
|
-
"@slopus/happy-agent-client": "0.0.
|
|
66
|
+
"@slopus/happy-agent-client": "0.0.20",
|
|
67
67
|
"@slopus/happy-providers": "0.0.11",
|
|
68
68
|
"@slopus/murmur": "0.4.4",
|
|
69
69
|
"@steve.kite/stdlib": "0.0.12",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function renderHappyTerminalVersion(version: string, width: number): string[];
|