very-happy-cli 0.2.158 → 0.2.160
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/{AcpBackend-DIDRig5G.cjs → AcpBackend-Bm2rdke8.cjs} +78 -76
- package/dist/{AcpBackend-vDVLHuYJ.mjs → AcpBackend-mzv8Dhba.mjs} +4 -2
- package/dist/{AcpSessionManager-C44U99CC.cjs → AcpSessionManager-BAY1Pggx.cjs} +9 -9
- package/dist/{AcpSessionManager-CmRjfhn3.mjs → AcpSessionManager-CoMWFVw3.mjs} +1 -1
- package/dist/BasePermissionHandler-CFHAxumV.cjs +157 -0
- package/dist/BasePermissionHandler-m3WVholL.mjs +155 -0
- package/dist/BaseReasoningProcessor-BFa9_rZA.cjs +200 -0
- package/dist/BaseReasoningProcessor-CYrKBXGm.mjs +198 -0
- package/dist/RemoteModeDisplay-D-HewyXg.mjs +150 -0
- package/dist/RemoteModeDisplay-NC1gPHtj.cjs +153 -0
- package/dist/agentAttachments-Dbw8iMCZ.cjs +20 -0
- package/dist/agentAttachments-M_B5qEF2.mjs +18 -0
- package/dist/agentGuidance-B5NATuFa.cjs +28 -0
- package/dist/agentGuidance-CQuNlNx8.mjs +20 -0
- package/dist/{agentHome-Dw8Jx_CJ.cjs → agentHome-CboCeKWq.cjs} +14 -28
- package/dist/{agentHome-CyLmGH5L.mjs → agentHome-Dj1C43aJ.mjs} +8 -22
- package/dist/api-CLMUr-SX.mjs +3339 -0
- package/dist/api-CgundNNX.cjs +3346 -0
- package/dist/{types-DIBF4U0Y.mjs → apiMachine-B9dLW_p5.mjs} +9116 -16015
- package/dist/{types-DnrPeN80.cjs → apiMachine-Bn8482VM.cjs} +9104 -16057
- package/dist/assistantTools-BTqPnlFl.cjs +380 -0
- package/dist/assistantTools-D6tuc6o4.mjs +376 -0
- package/dist/attachmentContent-DpiTJZb4.cjs +88 -0
- package/dist/attachmentContent-_kxRznNf.mjs +82 -0
- package/dist/attachmentLimits-CzVrqgLR.cjs +354 -0
- package/dist/attachmentLimits-yba1uHFO.mjs +348 -0
- package/dist/auth-BDOVh5h5.cjs +220 -0
- package/dist/auth-Cwutmdsa.mjs +225 -0
- package/dist/auth-PYHZQNPp.mjs +218 -0
- package/dist/auth-ba4WMYrN.cjs +227 -0
- package/dist/authRelay-BdYkC1Po.cjs +156 -0
- package/dist/authRelay-DSAvg1uv.mjs +143 -0
- package/dist/{auto-DZbNcTXx.cjs → auto-BOX_fgmR.cjs} +70 -79
- package/dist/{auto-BlMZPwOk.mjs → auto-BXu14yP_.mjs} +27 -36
- package/dist/backgroundTaskActivity-8PdPxrvy.mjs +149 -0
- package/dist/backgroundTaskActivity-CBvUdj39.cjs +157 -0
- package/dist/bootstrap-B5G1TijC.cjs +116 -0
- package/dist/bootstrap-hxXPCFMx.mjs +112 -0
- package/dist/browser-Y6Bl8L_O.cjs +22 -0
- package/dist/browser-jh2P0UVZ.mjs +20 -0
- package/dist/claudeCliPath-BqkaH2oR.mjs +8 -0
- package/dist/claudeCliPath-U4HHmV4r.cjs +10 -0
- package/dist/claudeLocal-BaY4fZKi.mjs +339 -0
- package/dist/claudeLocal-CRGG4Pea.cjs +343 -0
- package/dist/claudeLocalLauncher-D4T7MTqd.mjs +152 -0
- package/dist/claudeLocalLauncher-DyEbVzUN.cjs +154 -0
- package/dist/cliUpdate-DsKXPFYp.cjs +120 -0
- package/dist/cliUpdate-Y34VGe9Y.mjs +112 -0
- package/dist/client-3Ac-O4pw.cjs +104 -0
- package/dist/client-BPNbmtvO.cjs +119 -0
- package/dist/client-CeGKqqcJ.mjs +100 -0
- package/dist/client-ClR0K8zX.mjs +112 -0
- package/dist/codex/happyMcpStdioBridge.cjs +33 -14
- package/dist/codex/happyMcpStdioBridge.mjs +26 -7
- package/dist/codexAppServerClient-I3teiV-N.mjs +1088 -0
- package/dist/codexAppServerClient-q2FjnRcK.cjs +1090 -0
- package/dist/codexCommand-4zcccIoq.cjs +2088 -0
- package/dist/codexCommand-Cc9gcbdX.mjs +2086 -0
- package/dist/codexThreadFork-BH9IZPqD.cjs +350 -0
- package/dist/codexThreadFork-slzAseLx.mjs +338 -0
- package/dist/{config-BtPArqRH.mjs → config-CTx_jb-C.mjs} +7 -40
- package/dist/{config-DG84bjwF.cjs → config-Ci9Kpgwa.cjs} +26 -59
- package/dist/configuration-BSYpcPwE.cjs +181 -0
- package/dist/configuration-RfeNjFmd.mjs +170 -0
- package/dist/connect-BpOdzNbE.mjs +682 -0
- package/dist/connect-C2CiOUFs.cjs +684 -0
- package/dist/constants-BC4St0xd.mjs +11 -0
- package/dist/constants-VJ0N6WuG.cjs +17 -0
- package/dist/contentLogMetadata-BlWI0egu.mjs +51 -0
- package/dist/contentLogMetadata-ZSFk_NjQ.cjs +54 -0
- package/dist/context-DsDr6E2t.cjs +69 -0
- package/dist/context-H0Bcb9S_.mjs +65 -0
- package/dist/controlClient-BGbDcgWS.mjs +260 -0
- package/dist/controlClient-Bcwv3D21.cjs +279 -0
- package/dist/createSessionMetadata-BJxEnM-o.cjs +58 -0
- package/dist/createSessionMetadata-CVVkGjxV.mjs +54 -0
- package/dist/defaultPermissionMode-BnxJPVHT.mjs +4 -0
- package/dist/defaultPermissionMode-CxmTXUQR.cjs +7 -0
- package/dist/detectCLI-CSGMlV51.mjs +58 -0
- package/dist/detectCLI-DR9ZaN6B.cjs +61 -0
- package/dist/deterministicJson-C5CYTVpm.cjs +507 -0
- package/dist/deterministicJson-CzkFJWw1.mjs +502 -0
- package/dist/doctor-BPkYWSuq.mjs +75 -0
- package/dist/doctor-D2YJ-Yn2.cjs +395 -0
- package/dist/doctor-DOvByjbk.mjs +391 -0
- package/dist/doctor-u46qYYKX.cjs +79 -0
- package/dist/doctorReadiness-BCZGcN0a.cjs +151 -0
- package/dist/doctorReadiness-C208SMN9.mjs +142 -0
- package/dist/editPaths-BVfS_FFL.mjs +68 -0
- package/dist/editPaths-Bqp4OFPw.cjs +73 -0
- package/dist/ensureDaemonRunning-BCwNrMyv.cjs +64 -0
- package/dist/ensureDaemonRunning-Dqa2pOjY.mjs +61 -0
- package/dist/handleResumeCommand-BvXYf5El.mjs +240 -0
- package/dist/handleResumeCommand-C2cKchpX.cjs +245 -0
- package/dist/ids-B6ZC0HGt.cjs +13 -0
- package/dist/ids-DU6sLx4C.mjs +10 -0
- package/dist/index-B6UGRQ7h.mjs +102 -0
- package/dist/{index-DPCsWlWK.cjs → index-B6pAdvJx.cjs} +131 -91
- package/dist/index-Bu5Zi_a9.cjs +182 -0
- package/dist/index-D3kI0hDR.cjs +107 -0
- package/dist/index-DA7yINuk.cjs +994 -0
- package/dist/index-DDeHXrIX.cjs +71 -0
- package/dist/index-DKLU_fD0.mjs +158 -0
- package/dist/{index-ZChGsuB7.mjs → index-DMZqWwsP.mjs} +79 -39
- package/dist/index-DVkMGPyS.mjs +68 -0
- package/dist/index-qdS1friN.mjs +949 -0
- package/dist/index.cjs +5 -46
- package/dist/index.mjs +5 -46
- package/dist/{install-DarS_Wco.mjs → install-BO_W7VhL.mjs} +37 -37
- package/dist/install-BgoC2e_2.cjs +88 -0
- package/dist/install-CFY3tYtJ.mjs +86 -0
- package/dist/{install-wKQGgXvN.cjs → install-Cs5qAPrB.cjs} +38 -38
- package/dist/{installPiTools-DuZ6jZvX.cjs → installPiTools-BKnXjrVu.cjs} +2 -3
- package/dist/{installPiTools-Cj7yU9Fc.mjs → installPiTools-B_CdM5_2.mjs} +1 -2
- package/dist/{installTerminalHooks-DcQfee_O.cjs → installTerminalHooks-BQZbvxsZ.cjs} +5 -40
- package/dist/{installTerminalHooks-CpNbRFPz.mjs → installTerminalHooks-By7gBMEF.mjs} +3 -38
- package/dist/lib.cjs +28 -19
- package/dist/lib.d.cts +126 -2
- package/dist/lib.d.mts +126 -2
- package/dist/lib.mjs +23 -14
- package/dist/lidState-BeDfxxEX.cjs +305 -0
- package/dist/lidState-CeEt442o.mjs +297 -0
- package/dist/limits-C-a7gr04.cjs +43 -0
- package/dist/limits-DIo7rEBR.mjs +36 -0
- package/dist/localHappyAgentAuth-C-HQyiv6.mjs +82 -0
- package/dist/localHappyAgentAuth-D6rFhaoc.cjs +86 -0
- package/dist/logger-6HYUEXNk.mjs +271 -0
- package/dist/logger-CS0mw3fa.cjs +275 -0
- package/dist/loginShellEnv-BkuYtExZ.cjs +182 -0
- package/dist/loginShellEnv-Dw8M7tkI.mjs +167 -0
- package/dist/machineMetadata-DyDbODYd.cjs +41 -0
- package/dist/machineMetadata-oOzBfDfs.mjs +38 -0
- package/dist/main-BkxJP6JQ.cjs +941 -0
- package/dist/main-BwNIAI1A.mjs +939 -0
- package/dist/manager-BQ4flvQZ.mjs +101 -0
- package/dist/manager-Cly8xVIR.cjs +105 -0
- package/dist/{mcp-BN6bKIGb.mjs → mcp-B1A4I3TK.mjs} +27 -33
- package/dist/{mcp-DGdeUUDJ.cjs → mcp-Hszd7qpS.cjs} +42 -48
- package/dist/messageBuffer-BWlGgR3A.mjs +71 -0
- package/dist/messageBuffer-fJMH6uY8.cjs +73 -0
- package/dist/mirrorProtocol-BNLtoWjO.mjs +170 -0
- package/dist/mirrorProtocol-BUtmg1ws.cjs +186 -0
- package/dist/oneShotPrompts-CL0dip9C.cjs +16 -0
- package/dist/oneShotPrompts-DT4p6RNd.mjs +12 -0
- package/dist/package-BhaKlnxz.mjs +171 -0
- package/dist/package-BwMrPwqH.cjs +173 -0
- package/dist/path-B7zW-WwE.cjs +18 -0
- package/dist/path-D2wdaNSk.mjs +15 -0
- package/dist/peerTools-BTqw97Gb.mjs +272 -0
- package/dist/peerTools-BvKtHXj8.cjs +283 -0
- package/dist/permissionMode-Cr5JCbB7.mjs +80 -0
- package/dist/permissionMode-CrV-XuMs.cjs +87 -0
- package/dist/persistence-HGW_ZVs9.mjs +372 -0
- package/dist/persistence-yKTH3-5X.cjs +414 -0
- package/dist/piExtension.mjs +2 -1
- package/dist/piRunnerArgs-BNloJCCg.cjs +66 -0
- package/dist/piRunnerArgs-BuX1UFWy.mjs +59 -0
- package/dist/{piRuntime-bkw2NAZU.mjs → piRuntime-CSGkwCKQ.mjs} +2 -2
- package/dist/{piRuntime-3qtbKujX.cjs → piRuntime-C_H7MJCM.cjs} +4 -4
- package/dist/{piTerminal-DilKwM7B.mjs → piTerminal-C8a1OrNQ.mjs} +32 -35
- package/dist/{piTerminal-BYxp3LMr.cjs → piTerminal-DNCZ6yTM.cjs} +32 -35
- package/dist/previewPath-B0rBdhy1.cjs +83 -0
- package/dist/previewPath-CXN4Vag0.mjs +81 -0
- package/dist/projectPath-BTBIMB57.mjs +10 -0
- package/dist/projectPath-ChLhdOyt.cjs +13 -0
- package/dist/registerCommonHandlers-Ccvsz7Ou.mjs +536 -0
- package/dist/registerCommonHandlers-DskirN-_.cjs +538 -0
- package/dist/registerKillSessionHandler-DZeLXWcB.cjs +361 -0
- package/dist/registerKillSessionHandler-GYvHdipX.mjs +358 -0
- package/dist/remoteSessionClient-B0LraSB9.cjs +634 -0
- package/dist/remoteSessionClient-CYnXRynE.mjs +612 -0
- package/dist/resources-BdFBQi8r.mjs +117 -0
- package/dist/resources-JDPugcDq.cjs +120 -0
- package/dist/run-CJzxoHVh.cjs +5762 -0
- package/dist/run-D3ifupU3.mjs +5739 -0
- package/dist/runClaude-Ba01qT-u.mjs +848 -0
- package/dist/runClaude-Da3RirzE.cjs +850 -0
- package/dist/runClaudeDeps-DOt79DjU.mjs +3624 -0
- package/dist/runClaudeDeps-DOzEBMls.cjs +3649 -0
- package/dist/{runGemini-Cjii069p.mjs → runGemini-BGeTz57m.mjs} +58 -31
- package/dist/{runGemini-28O2sqyI.cjs → runGemini-DSr9LIxL.cjs} +202 -175
- package/dist/{runOpenClaw-CiMZad-H.cjs → runOpenClaw-B1mTxN35.cjs} +64 -65
- package/dist/{runOpenClaw-jaLr7KF1.mjs → runOpenClaw-CogSedIc.mjs} +31 -32
- package/dist/sandbox-CG7IyDTU.cjs +187 -0
- package/dist/sandbox-DTe6d54R.mjs +180 -0
- package/dist/selfCheck-C9mVrQwf.mjs +84 -0
- package/dist/selfCheck-Dc-mNSLb.cjs +87 -0
- package/dist/{send-D5ejHPXG.cjs → send-B7J_eaG3.cjs} +16 -9
- package/dist/{send-C9mOtChi.mjs → send-CBXJKc6O.mjs} +12 -5
- package/dist/server-CZTP_iOn.cjs +407 -0
- package/dist/server-DcVBv04l.mjs +404 -0
- package/dist/sessionLock-Bys1PNSo.cjs +205 -0
- package/dist/sessionLock-D-EG2CfG.mjs +197 -0
- package/dist/sessionModeFile-DMl3dw3r.mjs +32 -0
- package/dist/sessionModeFile-RSm0NQkH.cjs +36 -0
- package/dist/sessionOps-Dn8z85h_.cjs +783 -0
- package/dist/sessionOps-SuHW8Dfe.mjs +760 -0
- package/dist/sessionScanner-BOrGjMXL.cjs +209 -0
- package/dist/sessionScanner-CX3onqXh.mjs +206 -0
- package/dist/{sessions-W6771g3X.mjs → sessions-BEG_HTSi.mjs} +17 -8
- package/dist/{sessions-BNMPQoLE.cjs → sessions-D74aJbhL.cjs} +37 -28
- package/dist/setupOfflineReconnection-ByElY60m.cjs +74 -0
- package/dist/setupOfflineReconnection-DpvWTt69.mjs +72 -0
- package/dist/smallFastModel-BLNgMrwR.mjs +18 -0
- package/dist/smallFastModel-D_uxGMo3.cjs +21 -0
- package/dist/{spawn-D0QVhzRb.mjs → spawn-B7yro0b2.mjs} +26 -38
- package/dist/{spawn-Ddtom1cb.cjs → spawn-DgR0RKRs.cjs} +52 -64
- package/dist/spawnAgents-BGQiEF-w.cjs +52 -0
- package/dist/spawnAgents-DZzZ9jMI.mjs +45 -0
- package/dist/spawnHappyCLI-B2H5s4B_.mjs +64 -0
- package/dist/spawnHappyCLI-DBhwZT7O.cjs +66 -0
- package/dist/specialCommands-BSqhYg5X.mjs +52 -0
- package/dist/specialCommands-DSsTeRej.cjs +54 -0
- package/dist/startFileWatcher-B4VsLgjD.cjs +71 -0
- package/dist/startFileWatcher-DErVWZE4.mjs +69 -0
- package/dist/startupPlan-BLMbfQIH.cjs +12 -0
- package/dist/startupPlan-DFMyNgAs.mjs +10 -0
- package/dist/streamRelay-BiKw59Cc.cjs +233 -0
- package/dist/streamRelay-FPVbHpbM.mjs +231 -0
- package/dist/sync-C8gyQFMc.mjs +69 -0
- package/dist/sync-DccIKZ5_.cjs +71 -0
- package/dist/systemPrompt-BwTPWfMu.cjs +97 -0
- package/dist/systemPrompt-DX521JJz.mjs +94 -0
- package/dist/{teams-CqSwtw2O.mjs → teams-C32F1hqd.mjs} +9 -40
- package/dist/{teams-BZ1tayg_.cjs → teams-Dl8g8kTI.cjs} +14 -45
- package/dist/template-BaHAmH2K.cjs +129 -0
- package/dist/template-CiOPa2rd.mjs +121 -0
- package/dist/{terminalTitleSuggest-DKgX3osW.mjs → terminalTitleSuggest-CeKsX_V6.mjs} +1 -9
- package/dist/{terminalTitleSuggest-Dji8I0Jy.cjs → terminalTitleSuggest-DhvRFX3H.cjs} +0 -10
- package/dist/terminals-B_bfC9c2.cjs +250 -0
- package/dist/terminals-Dlg0x3nY.mjs +235 -0
- package/dist/time-D0XyHOid.mjs +38 -0
- package/dist/time-DCuTyook.cjs +42 -0
- package/dist/titleGenerator-CQ8CoGhg.mjs +166 -0
- package/dist/titleGenerator-ChzMJsyg.cjs +172 -0
- package/dist/tmuxSocket-B8Yx20sT.mjs +19 -0
- package/dist/tmuxSocket-D-Xt6Vn-.cjs +24 -0
- package/dist/{todo-BYMg0fR0.cjs → todo-BvUOTa8G.cjs} +15 -12
- package/dist/{todo-BbusB1zM.mjs → todo-CZ6cFrrn.mjs} +11 -8
- package/dist/tools-BZtHF_qQ.mjs +177 -0
- package/dist/tools-Be3AoAwu.cjs +63 -0
- package/dist/tools-DQj0AeLk.cjs +180 -0
- package/dist/tools-DkluDcG4.mjs +60 -0
- package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
- package/dist/trimIdent-BAjmv_TG.mjs +20 -0
- package/dist/types-CRS-srZK.cjs +66 -0
- package/dist/types-CkqAACXx.mjs +64 -0
- package/dist/uninstall-BkLzaJ6I.mjs +47 -0
- package/dist/uninstall-x-TUtf5H.cjs +49 -0
- package/package.json +7 -7
- package/dist/agentGuidance-CrR9l62U.mjs +0 -2145
- package/dist/agentGuidance-DwOWbX55.cjs +0 -2217
- package/dist/index-BG932eCh.cjs +0 -19655
- package/dist/index-CVCt3kSt.mjs +0 -19591
- package/dist/index-D5ScEErM.cjs +0 -58
- package/dist/index-DgO0FHOx.mjs +0 -52
- package/dist/limits-D2uN090G.mjs +0 -2528
- package/dist/limits-kqFqhfsh.cjs +0 -2652
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var logger = require('./logger-CS0mw3fa.cjs');
|
|
4
|
+
var persistence = require('./persistence-yKTH3-5X.cjs');
|
|
5
|
+
var configuration = require('./configuration-BSYpcPwE.cjs');
|
|
6
|
+
|
|
7
|
+
function daemonControlHeaders(controlToken) {
|
|
8
|
+
return {
|
|
9
|
+
"Content-Type": "application/json",
|
|
10
|
+
...controlToken ? { Authorization: `Bearer ${controlToken}` } : {}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
async function daemonPost(path, body) {
|
|
14
|
+
const state = await persistence.readDaemonState();
|
|
15
|
+
if (!state?.httpPort) {
|
|
16
|
+
const errorMessage = "No daemon running, no state file found";
|
|
17
|
+
logger.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
18
|
+
return {
|
|
19
|
+
error: errorMessage
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
process.kill(state.pid, 0);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
const errorMessage = "Daemon is not running, file is stale";
|
|
26
|
+
logger.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
27
|
+
return {
|
|
28
|
+
error: errorMessage
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const timeout = process.env.HAPPY_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.HAPPY_DAEMON_HTTP_TIMEOUT) : 1e4;
|
|
33
|
+
const response = await fetch(`http://127.0.0.1:${state.httpPort}${path}`, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
// A state written by an old daemon has no token and must remain callable;
|
|
36
|
+
// a new daemon always writes one and rejects requests without it.
|
|
37
|
+
headers: daemonControlHeaders(state.controlToken),
|
|
38
|
+
body: JSON.stringify(body || {}),
|
|
39
|
+
// Mostly increased for stress test
|
|
40
|
+
signal: AbortSignal.timeout(timeout)
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const failure = await response.json().catch(() => null);
|
|
44
|
+
const reason = typeof failure?.error === "string" && failure.error ? failure.error : `HTTP ${response.status}`;
|
|
45
|
+
const errorMessage = `Request failed: ${path}, ${reason}`;
|
|
46
|
+
logger.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
47
|
+
return {
|
|
48
|
+
error: errorMessage
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return await response.json();
|
|
52
|
+
} catch (error) {
|
|
53
|
+
const errorMessage = `Request failed: ${path}, ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
54
|
+
logger.logger.debug(`[CONTROL CLIENT] ${errorMessage}`);
|
|
55
|
+
return {
|
|
56
|
+
error: errorMessage
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const SESSION_STARTED_RETRY_TIMEOUT_MS = 3e3;
|
|
61
|
+
const SESSION_STARTED_RETRY_INTERVAL_MS = 100;
|
|
62
|
+
async function notifyDaemonSessionStarted(sessionId, metadata, encryption) {
|
|
63
|
+
const payload = { sessionId, metadata, encryption };
|
|
64
|
+
const deadline = Date.now() + SESSION_STARTED_RETRY_TIMEOUT_MS;
|
|
65
|
+
let result;
|
|
66
|
+
while (true) {
|
|
67
|
+
result = await daemonPost("/session-started", payload);
|
|
68
|
+
if (!result?.error) {
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
if (Date.now() >= deadline) {
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
await new Promise((resolve) => setTimeout(resolve, SESSION_STARTED_RETRY_INTERVAL_MS));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function listDaemonSessions() {
|
|
78
|
+
const result = await daemonPost("/list");
|
|
79
|
+
return result.children || [];
|
|
80
|
+
}
|
|
81
|
+
async function stopDaemonSession(sessionId) {
|
|
82
|
+
const result = await daemonPost("/stop-session", { sessionId });
|
|
83
|
+
return result.success || false;
|
|
84
|
+
}
|
|
85
|
+
async function spawnDaemonSession(directory, sessionId, opts) {
|
|
86
|
+
const result = await daemonPost("/spawn-session", {
|
|
87
|
+
directory,
|
|
88
|
+
sessionId,
|
|
89
|
+
spawnedBy: opts?.spawnedBy,
|
|
90
|
+
permissionMode: opts?.permissionMode,
|
|
91
|
+
agent: opts?.agent,
|
|
92
|
+
environmentVariables: opts?.environmentVariables,
|
|
93
|
+
resumeClaudeSessionId: opts?.resumeClaudeSessionId,
|
|
94
|
+
resumeCodexThreadId: opts?.resumeCodexThreadId,
|
|
95
|
+
parentSessionId: opts?.parentSessionId
|
|
96
|
+
});
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
const ROUTE_NOT_FOUND_RE = /, (Not Found|HTTP 404)$/;
|
|
100
|
+
async function resumeDaemonSession(sessionId, opts) {
|
|
101
|
+
const result = await daemonPost("/resume-session", {
|
|
102
|
+
sessionId,
|
|
103
|
+
...opts?.model !== void 0 ? { model: opts.model } : {},
|
|
104
|
+
...opts?.permissionMode !== void 0 ? { permissionMode: opts.permissionMode } : {}
|
|
105
|
+
});
|
|
106
|
+
if (result?.error) {
|
|
107
|
+
const raw = String(result.error);
|
|
108
|
+
const error = ROUTE_NOT_FOUND_RE.test(raw) ? "daemon too old to resume sessions (no /resume-session route); upgrade very-happy-cli and run `very-happy daemon start`" : raw.replace(/^Request failed: \/resume-session, /, "");
|
|
109
|
+
return { success: false, error };
|
|
110
|
+
}
|
|
111
|
+
if (result?.success === true && typeof result.sessionId === "string") {
|
|
112
|
+
return { success: true, sessionId: result.sessionId };
|
|
113
|
+
}
|
|
114
|
+
return { success: false, error: typeof result?.error === "string" ? result.error : "unexpected daemon response" };
|
|
115
|
+
}
|
|
116
|
+
async function notifyDaemonSessionEvent(sessionId, event, spawnedBy) {
|
|
117
|
+
return daemonPost("/session-event", { sessionId, event, spawnedBy });
|
|
118
|
+
}
|
|
119
|
+
async function notifyDaemonClaudeAuthFailed(sessionId) {
|
|
120
|
+
return daemonPost("/session-event", { sessionId, event: "auth_failed" });
|
|
121
|
+
}
|
|
122
|
+
async function notifyDaemonTurnEvent(sessionId, event) {
|
|
123
|
+
return daemonPost("/session-event", { sessionId, event });
|
|
124
|
+
}
|
|
125
|
+
async function notifyDaemonBackgroundTasks(sessionId, backgroundTasks) {
|
|
126
|
+
return daemonPost("/session-event", { sessionId, event: "background_tasks", backgroundTasks });
|
|
127
|
+
}
|
|
128
|
+
function reportSessionEditToDaemon(edit) {
|
|
129
|
+
void daemonPost("/session-edit", edit).then((result) => {
|
|
130
|
+
if (result?.error) logger.logger.debug(`[CONTROL CLIENT] session-edit not recorded: ${result.error}`);
|
|
131
|
+
}).catch((error) => {
|
|
132
|
+
logger.logger.debug("[CONTROL CLIENT] session-edit failed:", error);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
async function listDaemonPeers() {
|
|
136
|
+
const result = await daemonPost("/peers");
|
|
137
|
+
if (result?.error) {
|
|
138
|
+
const reason = String(result.error);
|
|
139
|
+
if (/404|Not Found/i.test(reason)) {
|
|
140
|
+
throw new Error("The running daemon does not support session peers; restart it on a CLI that does (`very-happy daemon start` after updating)");
|
|
141
|
+
}
|
|
142
|
+
throw new Error(reason);
|
|
143
|
+
}
|
|
144
|
+
return Array.isArray(result?.sessions) ? result.sessions : [];
|
|
145
|
+
}
|
|
146
|
+
async function stopDaemonHttp() {
|
|
147
|
+
await daemonPost("/stop");
|
|
148
|
+
}
|
|
149
|
+
async function pushClipboardViaDaemon(text, terminalId) {
|
|
150
|
+
const result = await daemonPost("/clipboard", { text, ...terminalId ? { terminalId } : {} });
|
|
151
|
+
if (result?.error && result.delivered === void 0) {
|
|
152
|
+
return { delivered: false, error: result.error };
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
async function pushFilePreviewViaDaemon(terminalId, path, mode = "file") {
|
|
157
|
+
const result = await daemonPost("/file-preview", { terminalId, path, mode });
|
|
158
|
+
if (result?.error) return { delivered: false, error: result.error };
|
|
159
|
+
return { delivered: result?.delivered === true, ...result?.delivered === true ? {} : { error: "preview was not delivered" } };
|
|
160
|
+
}
|
|
161
|
+
async function setTerminalTitleViaDaemon(terminalId, title, ifAbsent = false) {
|
|
162
|
+
const result = await daemonPost("/terminal-title", { terminalId, title, ifAbsent });
|
|
163
|
+
if (result?.error) {
|
|
164
|
+
return { ok: false, error: result.error };
|
|
165
|
+
}
|
|
166
|
+
return { ok: result?.status === "ok", error: result?.status === "ok" ? void 0 : "unexpected daemon response" };
|
|
167
|
+
}
|
|
168
|
+
async function checkIfDaemonRunningAndCleanupStaleState() {
|
|
169
|
+
const state = await persistence.readDaemonState();
|
|
170
|
+
if (!state) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
process.kill(state.pid, 0);
|
|
175
|
+
} catch {
|
|
176
|
+
logger.logger.debug("[DAEMON RUN] Daemon PID not running, cleaning up state");
|
|
177
|
+
await cleanupDaemonState();
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
if (state.httpPort) {
|
|
181
|
+
try {
|
|
182
|
+
const response = await fetch(`http://127.0.0.1:${state.httpPort}/list`, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: daemonControlHeaders(state.controlToken),
|
|
185
|
+
body: "{}",
|
|
186
|
+
signal: AbortSignal.timeout(2e3)
|
|
187
|
+
});
|
|
188
|
+
if (response.ok) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
logger.logger.debug(`[DAEMON RUN] PID ${state.pid} is alive but HTTP health check failed on port ${state.httpPort}, cleaning up stale state`);
|
|
193
|
+
await cleanupDaemonState();
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
async function isDaemonRunningCurrentlyInstalledHappyVersion() {
|
|
200
|
+
logger.logger.debug("[DAEMON CONTROL] Checking if daemon is running same version");
|
|
201
|
+
const runningDaemon = await checkIfDaemonRunningAndCleanupStaleState();
|
|
202
|
+
if (!runningDaemon) {
|
|
203
|
+
logger.logger.debug("[DAEMON CONTROL] No daemon running, returning false");
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const state = await persistence.readDaemonState();
|
|
207
|
+
if (!state) {
|
|
208
|
+
logger.logger.debug("[DAEMON CONTROL] No daemon state found, returning false");
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
const currentCliVersion = configuration.configuration.currentCliVersion;
|
|
212
|
+
logger.logger.debug(`[DAEMON CONTROL] Current CLI version: ${currentCliVersion}, Daemon started with version: ${state.startedWithCliVersion}`);
|
|
213
|
+
return currentCliVersion === state.startedWithCliVersion;
|
|
214
|
+
}
|
|
215
|
+
async function cleanupDaemonState() {
|
|
216
|
+
try {
|
|
217
|
+
await persistence.clearDaemonState();
|
|
218
|
+
logger.logger.debug("[DAEMON RUN] Daemon state file removed");
|
|
219
|
+
} catch (error) {
|
|
220
|
+
logger.logger.debug("[DAEMON RUN] Error cleaning up daemon metadata", error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function stopDaemon() {
|
|
224
|
+
try {
|
|
225
|
+
const state = await persistence.readDaemonState();
|
|
226
|
+
if (!state) {
|
|
227
|
+
logger.logger.debug("No daemon state found");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
logger.logger.debug(`Stopping daemon with PID ${state.pid}`);
|
|
231
|
+
try {
|
|
232
|
+
await stopDaemonHttp();
|
|
233
|
+
await waitForProcessDeath(state.pid, 2e3);
|
|
234
|
+
logger.logger.debug("Daemon stopped gracefully via HTTP");
|
|
235
|
+
return;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
logger.logger.debug("HTTP stop failed, will force kill", error);
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
process.kill(state.pid, "SIGKILL");
|
|
241
|
+
logger.logger.debug("Force killed daemon");
|
|
242
|
+
} catch (error) {
|
|
243
|
+
logger.logger.debug("Daemon already dead");
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
logger.logger.debug("Error stopping daemon", error);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async function waitForProcessDeath(pid, timeout) {
|
|
250
|
+
const start = Date.now();
|
|
251
|
+
while (Date.now() - start < timeout) {
|
|
252
|
+
try {
|
|
253
|
+
process.kill(pid, 0);
|
|
254
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
255
|
+
} catch {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
throw new Error("Process did not die within timeout");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
exports.checkIfDaemonRunningAndCleanupStaleState = checkIfDaemonRunningAndCleanupStaleState;
|
|
263
|
+
exports.cleanupDaemonState = cleanupDaemonState;
|
|
264
|
+
exports.isDaemonRunningCurrentlyInstalledHappyVersion = isDaemonRunningCurrentlyInstalledHappyVersion;
|
|
265
|
+
exports.listDaemonPeers = listDaemonPeers;
|
|
266
|
+
exports.listDaemonSessions = listDaemonSessions;
|
|
267
|
+
exports.notifyDaemonBackgroundTasks = notifyDaemonBackgroundTasks;
|
|
268
|
+
exports.notifyDaemonClaudeAuthFailed = notifyDaemonClaudeAuthFailed;
|
|
269
|
+
exports.notifyDaemonSessionEvent = notifyDaemonSessionEvent;
|
|
270
|
+
exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
|
|
271
|
+
exports.notifyDaemonTurnEvent = notifyDaemonTurnEvent;
|
|
272
|
+
exports.pushClipboardViaDaemon = pushClipboardViaDaemon;
|
|
273
|
+
exports.pushFilePreviewViaDaemon = pushFilePreviewViaDaemon;
|
|
274
|
+
exports.reportSessionEditToDaemon = reportSessionEditToDaemon;
|
|
275
|
+
exports.resumeDaemonSession = resumeDaemonSession;
|
|
276
|
+
exports.setTerminalTitleViaDaemon = setTerminalTitleViaDaemon;
|
|
277
|
+
exports.spawnDaemonSession = spawnDaemonSession;
|
|
278
|
+
exports.stopDaemon = stopDaemon;
|
|
279
|
+
exports.stopDaemonSession = stopDaemonSession;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var os = require('node:os');
|
|
4
|
+
var path = require('node:path');
|
|
5
|
+
var configuration = require('./configuration-BSYpcPwE.cjs');
|
|
6
|
+
var projectPath = require('./projectPath-ChLhdOyt.cjs');
|
|
7
|
+
var _package = require('./package-BwMrPwqH.cjs');
|
|
8
|
+
|
|
9
|
+
const SPAWN_ORIGIN_TAG_MAX = 24;
|
|
10
|
+
function spawnOriginTags(env = process.env, flavor = void 0) {
|
|
11
|
+
if (env.HAPPY_SESSION_VARIANT === "assistant" && flavor === "claude") return void 0;
|
|
12
|
+
const origin = (env.HAPPY_SPAWNED_BY ?? "").trim();
|
|
13
|
+
if (!origin) return void 0;
|
|
14
|
+
if (origin.length > SPAWN_ORIGIN_TAG_MAX) return void 0;
|
|
15
|
+
if (!/^[a-z0-9][a-z0-9_-]*$/.test(origin)) return void 0;
|
|
16
|
+
return [origin];
|
|
17
|
+
}
|
|
18
|
+
function isValidSpawnOrigin(value) {
|
|
19
|
+
return spawnOriginTags({ HAPPY_SPAWNED_BY: value }) !== void 0;
|
|
20
|
+
}
|
|
21
|
+
function createSessionMetadata(opts) {
|
|
22
|
+
const state = {
|
|
23
|
+
controlledByUser: false
|
|
24
|
+
};
|
|
25
|
+
const originTags = spawnOriginTags(process.env, opts.flavor);
|
|
26
|
+
const metadata = {
|
|
27
|
+
path: process.cwd(),
|
|
28
|
+
host: os.hostname(),
|
|
29
|
+
version: _package.packageJson.version,
|
|
30
|
+
os: os.platform(),
|
|
31
|
+
machineId: opts.machineId,
|
|
32
|
+
...opts.teamOperationId ? { teamOperationId: opts.teamOperationId } : {},
|
|
33
|
+
homeDir: os.homedir(),
|
|
34
|
+
happyHomeDir: configuration.configuration.happyHomeDir,
|
|
35
|
+
happyLibDir: projectPath.projectPath(),
|
|
36
|
+
happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
|
|
37
|
+
startedFromDaemon: opts.startedBy === "daemon",
|
|
38
|
+
hostPid: process.pid,
|
|
39
|
+
startedBy: opts.startedBy || "terminal",
|
|
40
|
+
lifecycleState: "running",
|
|
41
|
+
lifecycleStateSince: Date.now(),
|
|
42
|
+
flavor: opts.flavor,
|
|
43
|
+
sandbox: opts.sandbox?.enabled ? opts.sandbox : null,
|
|
44
|
+
dangerouslySkipPermissions: opts.dangerouslySkipPermissions ?? null,
|
|
45
|
+
...opts.parentSessionId ? { parentSessionId: opts.parentSessionId } : {},
|
|
46
|
+
...opts.forkedFromMessageId ? { forkedFromMessageId: opts.forkedFromMessageId } : {},
|
|
47
|
+
...opts.capabilities ? { capabilities: [...opts.capabilities] } : {},
|
|
48
|
+
// B-091/B-303: dispatched sessions carry their spawn origin as a tag
|
|
49
|
+
// from birth (see spawnOriginTags) — applies to every flavor the daemon
|
|
50
|
+
// can spawn, since HAPPY_SPAWNED_BY is exported flavor-independently.
|
|
51
|
+
...originTags ? { tags: originTags } : {}
|
|
52
|
+
};
|
|
53
|
+
return { state, metadata };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
exports.createSessionMetadata = createSessionMetadata;
|
|
57
|
+
exports.isValidSpawnOrigin = isValidSpawnOrigin;
|
|
58
|
+
exports.spawnOriginTags = spawnOriginTags;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { c as configuration } from './configuration-RfeNjFmd.mjs';
|
|
4
|
+
import { p as projectPath } from './projectPath-BTBIMB57.mjs';
|
|
5
|
+
import { p as packageJson } from './package-BhaKlnxz.mjs';
|
|
6
|
+
|
|
7
|
+
const SPAWN_ORIGIN_TAG_MAX = 24;
|
|
8
|
+
function spawnOriginTags(env = process.env, flavor = void 0) {
|
|
9
|
+
if (env.HAPPY_SESSION_VARIANT === "assistant" && flavor === "claude") return void 0;
|
|
10
|
+
const origin = (env.HAPPY_SPAWNED_BY ?? "").trim();
|
|
11
|
+
if (!origin) return void 0;
|
|
12
|
+
if (origin.length > SPAWN_ORIGIN_TAG_MAX) return void 0;
|
|
13
|
+
if (!/^[a-z0-9][a-z0-9_-]*$/.test(origin)) return void 0;
|
|
14
|
+
return [origin];
|
|
15
|
+
}
|
|
16
|
+
function isValidSpawnOrigin(value) {
|
|
17
|
+
return spawnOriginTags({ HAPPY_SPAWNED_BY: value }) !== void 0;
|
|
18
|
+
}
|
|
19
|
+
function createSessionMetadata(opts) {
|
|
20
|
+
const state = {
|
|
21
|
+
controlledByUser: false
|
|
22
|
+
};
|
|
23
|
+
const originTags = spawnOriginTags(process.env, opts.flavor);
|
|
24
|
+
const metadata = {
|
|
25
|
+
path: process.cwd(),
|
|
26
|
+
host: os.hostname(),
|
|
27
|
+
version: packageJson.version,
|
|
28
|
+
os: os.platform(),
|
|
29
|
+
machineId: opts.machineId,
|
|
30
|
+
...opts.teamOperationId ? { teamOperationId: opts.teamOperationId } : {},
|
|
31
|
+
homeDir: os.homedir(),
|
|
32
|
+
happyHomeDir: configuration.happyHomeDir,
|
|
33
|
+
happyLibDir: projectPath(),
|
|
34
|
+
happyToolsDir: resolve(projectPath(), "tools", "unpacked"),
|
|
35
|
+
startedFromDaemon: opts.startedBy === "daemon",
|
|
36
|
+
hostPid: process.pid,
|
|
37
|
+
startedBy: opts.startedBy || "terminal",
|
|
38
|
+
lifecycleState: "running",
|
|
39
|
+
lifecycleStateSince: Date.now(),
|
|
40
|
+
flavor: opts.flavor,
|
|
41
|
+
sandbox: opts.sandbox?.enabled ? opts.sandbox : null,
|
|
42
|
+
dangerouslySkipPermissions: opts.dangerouslySkipPermissions ?? null,
|
|
43
|
+
...opts.parentSessionId ? { parentSessionId: opts.parentSessionId } : {},
|
|
44
|
+
...opts.forkedFromMessageId ? { forkedFromMessageId: opts.forkedFromMessageId } : {},
|
|
45
|
+
...opts.capabilities ? { capabilities: [...opts.capabilities] } : {},
|
|
46
|
+
// B-091/B-303: dispatched sessions carry their spawn origin as a tag
|
|
47
|
+
// from birth (see spawnOriginTags) — applies to every flavor the daemon
|
|
48
|
+
// can spawn, since HAPPY_SPAWNED_BY is exported flavor-independently.
|
|
49
|
+
...originTags ? { tags: originTags } : {}
|
|
50
|
+
};
|
|
51
|
+
return { state, metadata };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { createSessionMetadata as c, isValidSpawnOrigin as i, spawnOriginTags as s };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_CLAUDE_PERMISSION_MODE = "default";
|
|
4
|
+
const DEFAULT_CODEX_PERMISSION_MODE = "default";
|
|
5
|
+
|
|
6
|
+
exports.DEFAULT_CLAUDE_PERMISSION_MODE = DEFAULT_CLAUDE_PERMISSION_MODE;
|
|
7
|
+
exports.DEFAULT_CODEX_PERMISSION_MODE = DEFAULT_CODEX_PERMISSION_MODE;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
|
|
6
|
+
function detectCLIAvailability() {
|
|
7
|
+
const isWindows = os.platform() === "win32";
|
|
8
|
+
if (isWindows) {
|
|
9
|
+
return detectWindows();
|
|
10
|
+
}
|
|
11
|
+
return detectPosix();
|
|
12
|
+
}
|
|
13
|
+
function commandExists(command) {
|
|
14
|
+
try {
|
|
15
|
+
execSync(`command -v ${command} >/dev/null 2>&1`, { stdio: "ignore" });
|
|
16
|
+
return true;
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function commandExistsWindows(name) {
|
|
22
|
+
try {
|
|
23
|
+
execSync(`powershell -NoProfile -Command "Get-Command ${name} -ErrorAction SilentlyContinue"`, { stdio: "ignore", windowsHide: true });
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const SAFE_COMMAND_NAME = /^[A-Za-z0-9._-]+$/;
|
|
30
|
+
function commandOnPath(name) {
|
|
31
|
+
if (!SAFE_COMMAND_NAME.test(name)) return false;
|
|
32
|
+
return os.platform() === "win32" ? commandExistsWindows(name) : commandExists(name);
|
|
33
|
+
}
|
|
34
|
+
function detectPosix() {
|
|
35
|
+
const claude = commandExists("claude");
|
|
36
|
+
const codex = commandExists("codex");
|
|
37
|
+
const gemini = commandExists("gemini");
|
|
38
|
+
const openclawCommand = commandExists("openclaw");
|
|
39
|
+
const openclawConfig = existsSync(join(os.homedir(), ".openclaw", "openclaw.json"));
|
|
40
|
+
const openclawEnv = !!process.env.OPENCLAW_GATEWAY_URL;
|
|
41
|
+
const openclaw = openclawCommand || openclawConfig || openclawEnv;
|
|
42
|
+
const pi = commandExists("pi") && commandExists("pi-acp");
|
|
43
|
+
return { claude, codex, gemini, openclaw, pi, detectedAt: Date.now() };
|
|
44
|
+
}
|
|
45
|
+
function detectWindows() {
|
|
46
|
+
const checkCommand = commandExistsWindows;
|
|
47
|
+
const claude = checkCommand("claude");
|
|
48
|
+
const codex = checkCommand("codex");
|
|
49
|
+
const gemini = checkCommand("gemini");
|
|
50
|
+
const openclawCommand = checkCommand("openclaw");
|
|
51
|
+
const openclawConfig = existsSync(join(process.env.USERPROFILE || os.homedir(), ".openclaw", "openclaw.json"));
|
|
52
|
+
const openclawEnv = !!process.env.OPENCLAW_GATEWAY_URL;
|
|
53
|
+
const openclaw = openclawCommand || openclawConfig || openclawEnv;
|
|
54
|
+
const pi = checkCommand("pi") && checkCommand("pi-acp");
|
|
55
|
+
return { claude, codex, gemini, openclaw, pi, detectedAt: Date.now() };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { commandOnPath as c, detectCLIAvailability as d };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var child_process = require('child_process');
|
|
4
|
+
var os = require('os');
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
var path = require('path');
|
|
7
|
+
|
|
8
|
+
function detectCLIAvailability() {
|
|
9
|
+
const isWindows = os.platform() === "win32";
|
|
10
|
+
if (isWindows) {
|
|
11
|
+
return detectWindows();
|
|
12
|
+
}
|
|
13
|
+
return detectPosix();
|
|
14
|
+
}
|
|
15
|
+
function commandExists(command) {
|
|
16
|
+
try {
|
|
17
|
+
child_process.execSync(`command -v ${command} >/dev/null 2>&1`, { stdio: "ignore" });
|
|
18
|
+
return true;
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function commandExistsWindows(name) {
|
|
24
|
+
try {
|
|
25
|
+
child_process.execSync(`powershell -NoProfile -Command "Get-Command ${name} -ErrorAction SilentlyContinue"`, { stdio: "ignore", windowsHide: true });
|
|
26
|
+
return true;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const SAFE_COMMAND_NAME = /^[A-Za-z0-9._-]+$/;
|
|
32
|
+
function commandOnPath(name) {
|
|
33
|
+
if (!SAFE_COMMAND_NAME.test(name)) return false;
|
|
34
|
+
return os.platform() === "win32" ? commandExistsWindows(name) : commandExists(name);
|
|
35
|
+
}
|
|
36
|
+
function detectPosix() {
|
|
37
|
+
const claude = commandExists("claude");
|
|
38
|
+
const codex = commandExists("codex");
|
|
39
|
+
const gemini = commandExists("gemini");
|
|
40
|
+
const openclawCommand = commandExists("openclaw");
|
|
41
|
+
const openclawConfig = fs.existsSync(path.join(os.homedir(), ".openclaw", "openclaw.json"));
|
|
42
|
+
const openclawEnv = !!process.env.OPENCLAW_GATEWAY_URL;
|
|
43
|
+
const openclaw = openclawCommand || openclawConfig || openclawEnv;
|
|
44
|
+
const pi = commandExists("pi") && commandExists("pi-acp");
|
|
45
|
+
return { claude, codex, gemini, openclaw, pi, detectedAt: Date.now() };
|
|
46
|
+
}
|
|
47
|
+
function detectWindows() {
|
|
48
|
+
const checkCommand = commandExistsWindows;
|
|
49
|
+
const claude = checkCommand("claude");
|
|
50
|
+
const codex = checkCommand("codex");
|
|
51
|
+
const gemini = checkCommand("gemini");
|
|
52
|
+
const openclawCommand = checkCommand("openclaw");
|
|
53
|
+
const openclawConfig = fs.existsSync(path.join(process.env.USERPROFILE || os.homedir(), ".openclaw", "openclaw.json"));
|
|
54
|
+
const openclawEnv = !!process.env.OPENCLAW_GATEWAY_URL;
|
|
55
|
+
const openclaw = openclawCommand || openclawConfig || openclawEnv;
|
|
56
|
+
const pi = checkCommand("pi") && checkCommand("pi-acp");
|
|
57
|
+
return { claude, codex, gemini, openclaw, pi, detectedAt: Date.now() };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
exports.commandOnPath = commandOnPath;
|
|
61
|
+
exports.detectCLIAvailability = detectCLIAvailability;
|