@slopus/happy-terminal 0.3.2 → 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/dist/index.js +5 -2
- package/dist/main.js +67 -26
- package/package.json +2 -2
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) {
|
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
|
|
|
@@ -11189,8 +11230,8 @@ var RESET21 = "\x1B[0m";
|
|
|
11189
11230
|
var DIM18 = "\x1B[2m";
|
|
11190
11231
|
function renderSubagentSummary(options) {
|
|
11191
11232
|
if (options.count === 0) return void 0;
|
|
11192
|
-
const
|
|
11193
|
-
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`;
|
|
11194
11235
|
return truncateToWidth23(`${DIM18}${summary}${RESET21}`, Math.max(1, options.width), "", true);
|
|
11195
11236
|
}
|
|
11196
11237
|
|
|
@@ -11213,8 +11254,8 @@ var RESET23 = "\x1B[0m";
|
|
|
11213
11254
|
var DIM20 = "\x1B[2m";
|
|
11214
11255
|
function renderWorkflowSummary(count2, width) {
|
|
11215
11256
|
if (count2 === 0) return void 0;
|
|
11216
|
-
const
|
|
11217
|
-
const summary = ` ${String(count2)} workflow${
|
|
11257
|
+
const plural2 = count2 === 1 ? "" : "s";
|
|
11258
|
+
const summary = ` ${String(count2)} workflow${plural2} running \xB7 /workflows to view`;
|
|
11218
11259
|
return truncateToWidth25(`${DIM20}${summary}${RESET23}`, Math.max(1, width), "", true);
|
|
11219
11260
|
}
|
|
11220
11261
|
|
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",
|