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,305 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var logger = require('./logger-CS0mw3fa.cjs');
|
|
4
|
+
var authRelay = require('./authRelay-BdYkC1Po.cjs');
|
|
5
|
+
var os = require('os');
|
|
6
|
+
var child_process = require('child_process');
|
|
7
|
+
|
|
8
|
+
function isRateQuotaCode(code) {
|
|
9
|
+
return typeof code === "string" && code.endsWith("_rate_quota_exceeded");
|
|
10
|
+
}
|
|
11
|
+
const RATE_RETRY_MIN_DELAY_MS = 2e3;
|
|
12
|
+
const RATE_RETRY_MAX_DELAY_MS = 6e4;
|
|
13
|
+
function rateRetryDelayMs(attempt, random = Math.random()) {
|
|
14
|
+
const exponent = Math.max(0, Math.min(attempt, 32) - 1);
|
|
15
|
+
const ceiling = Math.min(RATE_RETRY_MIN_DELAY_MS * 2 ** exponent, RATE_RETRY_MAX_DELAY_MS);
|
|
16
|
+
return Math.round(RATE_RETRY_MIN_DELAY_MS + random * (ceiling - RATE_RETRY_MIN_DELAY_MS));
|
|
17
|
+
}
|
|
18
|
+
async function pauseForRateQuota(opts) {
|
|
19
|
+
const ms = rateRetryDelayMs(opts.attempt);
|
|
20
|
+
opts.log?.(`[RATE] ${opts.label} write refused (${opts.code}), attempt ${opts.attempt} \u2014 waiting ${ms}ms`);
|
|
21
|
+
await opts.sleep(ms);
|
|
22
|
+
return ms;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class RpcHandlerManager {
|
|
26
|
+
handlers = /* @__PURE__ */ new Map();
|
|
27
|
+
/**
|
|
28
|
+
* B-506: methods whose params/result are PLAINTEXT JSON strings instead of
|
|
29
|
+
* machine-key ciphertext. The caller is a CLI on another machine of the
|
|
30
|
+
* same account, which holds neither this machine's key nor the account
|
|
31
|
+
* content key; the trusted relay sees the payload either way (this fork
|
|
32
|
+
* is server-trusted, not e2e). Only `registerPlainHandler` puts a method
|
|
33
|
+
* here; everything else keeps decrypting.
|
|
34
|
+
*/
|
|
35
|
+
plainMethods = /* @__PURE__ */ new Set();
|
|
36
|
+
scopePrefix;
|
|
37
|
+
encryptionKey;
|
|
38
|
+
encryptionVariant;
|
|
39
|
+
logger;
|
|
40
|
+
sockets = /* @__PURE__ */ new Set();
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.scopePrefix = config.scopePrefix;
|
|
43
|
+
this.encryptionKey = config.encryptionKey;
|
|
44
|
+
this.encryptionVariant = config.encryptionVariant;
|
|
45
|
+
this.logger = config.logger || ((msg, data) => logger.logger.debug(msg, data));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Register an RPC handler for a specific method
|
|
49
|
+
* @param method - The method name (without prefix)
|
|
50
|
+
* @param handler - The handler function
|
|
51
|
+
*/
|
|
52
|
+
registerHandler(method, handler) {
|
|
53
|
+
const prefixedMethod = this.getPrefixedMethod(method);
|
|
54
|
+
this.handlers.set(prefixedMethod, handler);
|
|
55
|
+
for (const socket of this.sockets) socket.emit("rpc-register", { method: prefixedMethod });
|
|
56
|
+
}
|
|
57
|
+
unregisterHandler(method) {
|
|
58
|
+
const prefixedMethod = this.getPrefixedMethod(method);
|
|
59
|
+
this.handlers.delete(prefixedMethod);
|
|
60
|
+
this.plainMethods.delete(prefixedMethod);
|
|
61
|
+
for (const socket of this.sockets) socket.emit("rpc-unregister", { method: prefixedMethod });
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* B-506: register a method whose request is a plaintext JSON string and
|
|
65
|
+
* whose response is returned as a plaintext JSON string (see
|
|
66
|
+
* `plainMethods`). The handler receives the parsed params; a throw is
|
|
67
|
+
* answered with `{ error }` like the encrypted path.
|
|
68
|
+
*/
|
|
69
|
+
registerPlainHandler(method, handler) {
|
|
70
|
+
this.plainMethods.add(this.getPrefixedMethod(method));
|
|
71
|
+
this.registerHandler(method, handler);
|
|
72
|
+
}
|
|
73
|
+
isPlainMethod(prefixedMethod) {
|
|
74
|
+
return this.plainMethods.has(prefixedMethod);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Handle an incoming RPC request
|
|
78
|
+
* @param request - The RPC request data
|
|
79
|
+
* @param callback - The response callback
|
|
80
|
+
*/
|
|
81
|
+
async handleRequest(request) {
|
|
82
|
+
if (this.plainMethods.has(request.method)) {
|
|
83
|
+
return this.handlePlainRequest(request);
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const handler = this.handlers.get(request.method);
|
|
87
|
+
if (!handler) {
|
|
88
|
+
this.logger("[RPC] [ERROR] Method not found", { method: request.method });
|
|
89
|
+
const errorResponse = { error: "Method not found" };
|
|
90
|
+
const encryptedError = authRelay.encodeBase64(authRelay.encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
|
|
91
|
+
return encryptedError;
|
|
92
|
+
}
|
|
93
|
+
const decryptedParams = authRelay.decrypt(this.encryptionKey, this.encryptionVariant, authRelay.decodeBase64(request.params));
|
|
94
|
+
this.logger("[RPC] Calling handler", { method: request.method });
|
|
95
|
+
const result = await handler(decryptedParams);
|
|
96
|
+
this.logger("[RPC] Handler returned", { method: request.method, hasResult: result !== void 0 });
|
|
97
|
+
const encryptedResponse = authRelay.encodeBase64(authRelay.encrypt(this.encryptionKey, this.encryptionVariant, result));
|
|
98
|
+
this.logger("[RPC] Sending encrypted response", { method: request.method, responseLength: encryptedResponse.length });
|
|
99
|
+
return encryptedResponse;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
this.logger("[RPC] [ERROR] Error handling request", { error });
|
|
102
|
+
const errorResponse = {
|
|
103
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
104
|
+
};
|
|
105
|
+
return authRelay.encodeBase64(authRelay.encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Plaintext variant of handleRequest (B-506). Never throws; never encrypts. */
|
|
109
|
+
async handlePlainRequest(request) {
|
|
110
|
+
const handler = this.handlers.get(request.method);
|
|
111
|
+
if (!handler) return JSON.stringify({ error: "Method not found" });
|
|
112
|
+
let params;
|
|
113
|
+
try {
|
|
114
|
+
params = typeof request.params === "string" && request.params.length > 0 ? JSON.parse(request.params) : {};
|
|
115
|
+
} catch {
|
|
116
|
+
return JSON.stringify({ error: "Invalid JSON params" });
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
this.logger("[RPC] Calling plain handler", { method: request.method });
|
|
120
|
+
const result = await handler(params);
|
|
121
|
+
return JSON.stringify(result === void 0 ? null : result);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
this.logger("[RPC] [ERROR] Error handling plain request", { error });
|
|
124
|
+
return JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error" });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
onSocketConnect(socket) {
|
|
128
|
+
this.sockets.add(socket);
|
|
129
|
+
for (const [prefixedMethod] of this.handlers) {
|
|
130
|
+
socket.emit("rpc-register", { method: prefixedMethod });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Attach a candidate transport and wait until the server confirms every
|
|
135
|
+
* method before a release handover closes the old transport.
|
|
136
|
+
*/
|
|
137
|
+
async onSocketConnectAndWait(socket, timeoutMs = 1e4) {
|
|
138
|
+
this.sockets.add(socket);
|
|
139
|
+
const pending = new Set(this.handlers.keys());
|
|
140
|
+
const acknowledged = /* @__PURE__ */ new Set();
|
|
141
|
+
await new Promise((resolve, reject) => {
|
|
142
|
+
let settleTimer = null;
|
|
143
|
+
const timer = setTimeout(() => {
|
|
144
|
+
cleanup();
|
|
145
|
+
reject(new Error(`RPC registration timed out (${pending.size} pending)`));
|
|
146
|
+
}, timeoutMs);
|
|
147
|
+
timer.unref?.();
|
|
148
|
+
const maybeSettle = () => {
|
|
149
|
+
if (pending.size > 0 || settleTimer) return;
|
|
150
|
+
settleTimer = setTimeout(() => {
|
|
151
|
+
settleTimer = null;
|
|
152
|
+
for (const prefixedMethod of this.handlers.keys()) {
|
|
153
|
+
if (acknowledged.has(prefixedMethod) || pending.has(prefixedMethod)) continue;
|
|
154
|
+
pending.add(prefixedMethod);
|
|
155
|
+
socket.emit("rpc-register", { method: prefixedMethod });
|
|
156
|
+
}
|
|
157
|
+
if (pending.size === 0) {
|
|
158
|
+
cleanup();
|
|
159
|
+
resolve();
|
|
160
|
+
}
|
|
161
|
+
}, 0);
|
|
162
|
+
settleTimer.unref?.();
|
|
163
|
+
};
|
|
164
|
+
const onRegistered = (data) => {
|
|
165
|
+
if (typeof data?.method !== "string") return;
|
|
166
|
+
acknowledged.add(data.method);
|
|
167
|
+
pending.delete(data.method);
|
|
168
|
+
maybeSettle();
|
|
169
|
+
};
|
|
170
|
+
const cleanup = () => {
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
if (settleTimer) clearTimeout(settleTimer);
|
|
173
|
+
socket.off("rpc-registered", onRegistered);
|
|
174
|
+
};
|
|
175
|
+
socket.on("rpc-registered", onRegistered);
|
|
176
|
+
for (const prefixedMethod of pending) socket.emit("rpc-register", { method: prefixedMethod });
|
|
177
|
+
maybeSettle();
|
|
178
|
+
}).catch((error) => {
|
|
179
|
+
this.sockets.delete(socket);
|
|
180
|
+
throw error;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
onSocketDisconnect(socket) {
|
|
184
|
+
if (socket) this.sockets.delete(socket);
|
|
185
|
+
else this.sockets.clear();
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Get the number of registered handlers
|
|
189
|
+
*/
|
|
190
|
+
getHandlerCount() {
|
|
191
|
+
return this.handlers.size;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Check if a handler is registered
|
|
195
|
+
* @param method - The method name (without prefix)
|
|
196
|
+
*/
|
|
197
|
+
hasHandler(method) {
|
|
198
|
+
const prefixedMethod = this.getPrefixedMethod(method);
|
|
199
|
+
return this.handlers.has(prefixedMethod);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Clear all handlers
|
|
203
|
+
*/
|
|
204
|
+
clearHandlers() {
|
|
205
|
+
this.handlers.clear();
|
|
206
|
+
this.logger("Cleared all RPC handlers");
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get the prefixed method name
|
|
210
|
+
* @param method - The method name
|
|
211
|
+
*/
|
|
212
|
+
getPrefixedMethod(method) {
|
|
213
|
+
return `${this.scopePrefix}:${method}`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function describeSocketAckDrop(drop) {
|
|
218
|
+
switch (drop.reason) {
|
|
219
|
+
case "no-ack":
|
|
220
|
+
return "no ack callback on the frame (re-delivered after a transport close?); response dropped";
|
|
221
|
+
case "handler-threw":
|
|
222
|
+
return `handler threw: ${drop.error instanceof Error ? drop.error.message : String(drop.error)}`;
|
|
223
|
+
case "ack-threw":
|
|
224
|
+
return `ack callback threw: ${drop.error instanceof Error ? drop.error.message : String(drop.error)}`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function safeAck(ack, onDrop) {
|
|
228
|
+
return (value) => {
|
|
229
|
+
if (typeof ack !== "function") {
|
|
230
|
+
onDrop({ reason: "no-ack" });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
ack(value);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
onDrop({ reason: "ack-threw", error });
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
async function answerSocketRequest(ack, run, onDrop) {
|
|
241
|
+
let value;
|
|
242
|
+
try {
|
|
243
|
+
value = await run();
|
|
244
|
+
} catch (error) {
|
|
245
|
+
onDrop({ reason: "handler-threw", error });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
safeAck(ack, onDrop)(value);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function hasNetworkConnectivity() {
|
|
252
|
+
const interfaces = os.networkInterfaces();
|
|
253
|
+
for (const name of Object.keys(interfaces)) {
|
|
254
|
+
for (const iface of interfaces[name] || []) {
|
|
255
|
+
if (!iface.internal && iface.family === "IPv4") return true;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
function isLidClosed() {
|
|
261
|
+
if (process.platform !== "darwin") return false;
|
|
262
|
+
try {
|
|
263
|
+
const output = child_process.execSync("ioreg -r -k AppleClamshellState -d 4", {
|
|
264
|
+
timeout: 5e3,
|
|
265
|
+
encoding: "utf-8"
|
|
266
|
+
});
|
|
267
|
+
return output.includes('"AppleClamshellState" = Yes');
|
|
268
|
+
} catch {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function hasExternalDisplay() {
|
|
273
|
+
if (process.platform !== "darwin") return false;
|
|
274
|
+
try {
|
|
275
|
+
const output = child_process.execSync("system_profiler SPDisplaysDataType -json 2>/dev/null", {
|
|
276
|
+
timeout: 1e4,
|
|
277
|
+
encoding: "utf-8"
|
|
278
|
+
});
|
|
279
|
+
const data = JSON.parse(output);
|
|
280
|
+
const gpus = data.SPDisplaysDataType || [];
|
|
281
|
+
for (const gpu of gpus) {
|
|
282
|
+
const displays = gpu.spdisplays_ndrvs || [];
|
|
283
|
+
for (const display of displays) {
|
|
284
|
+
const isBuiltIn = display.spdisplays_builtin === "spdisplays_yes" || display.spdisplays_connection_type === "spdisplays_internal";
|
|
285
|
+
if (!isBuiltIn) return true;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
} catch {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function shouldReconnect() {
|
|
294
|
+
if (!hasNetworkConnectivity()) return false;
|
|
295
|
+
if (isLidClosed() && !hasExternalDisplay()) return false;
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
exports.RpcHandlerManager = RpcHandlerManager;
|
|
300
|
+
exports.answerSocketRequest = answerSocketRequest;
|
|
301
|
+
exports.describeSocketAckDrop = describeSocketAckDrop;
|
|
302
|
+
exports.isRateQuotaCode = isRateQuotaCode;
|
|
303
|
+
exports.pauseForRateQuota = pauseForRateQuota;
|
|
304
|
+
exports.safeAck = safeAck;
|
|
305
|
+
exports.shouldReconnect = shouldReconnect;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { l as logger } from './logger-6HYUEXNk.mjs';
|
|
2
|
+
import { e as encodeBase64, g as encrypt, h as decrypt, d as decodeBase64 } from './authRelay-DSAvg1uv.mjs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
|
|
6
|
+
function isRateQuotaCode(code) {
|
|
7
|
+
return typeof code === "string" && code.endsWith("_rate_quota_exceeded");
|
|
8
|
+
}
|
|
9
|
+
const RATE_RETRY_MIN_DELAY_MS = 2e3;
|
|
10
|
+
const RATE_RETRY_MAX_DELAY_MS = 6e4;
|
|
11
|
+
function rateRetryDelayMs(attempt, random = Math.random()) {
|
|
12
|
+
const exponent = Math.max(0, Math.min(attempt, 32) - 1);
|
|
13
|
+
const ceiling = Math.min(RATE_RETRY_MIN_DELAY_MS * 2 ** exponent, RATE_RETRY_MAX_DELAY_MS);
|
|
14
|
+
return Math.round(RATE_RETRY_MIN_DELAY_MS + random * (ceiling - RATE_RETRY_MIN_DELAY_MS));
|
|
15
|
+
}
|
|
16
|
+
async function pauseForRateQuota(opts) {
|
|
17
|
+
const ms = rateRetryDelayMs(opts.attempt);
|
|
18
|
+
opts.log?.(`[RATE] ${opts.label} write refused (${opts.code}), attempt ${opts.attempt} \u2014 waiting ${ms}ms`);
|
|
19
|
+
await opts.sleep(ms);
|
|
20
|
+
return ms;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class RpcHandlerManager {
|
|
24
|
+
handlers = /* @__PURE__ */ new Map();
|
|
25
|
+
/**
|
|
26
|
+
* B-506: methods whose params/result are PLAINTEXT JSON strings instead of
|
|
27
|
+
* machine-key ciphertext. The caller is a CLI on another machine of the
|
|
28
|
+
* same account, which holds neither this machine's key nor the account
|
|
29
|
+
* content key; the trusted relay sees the payload either way (this fork
|
|
30
|
+
* is server-trusted, not e2e). Only `registerPlainHandler` puts a method
|
|
31
|
+
* here; everything else keeps decrypting.
|
|
32
|
+
*/
|
|
33
|
+
plainMethods = /* @__PURE__ */ new Set();
|
|
34
|
+
scopePrefix;
|
|
35
|
+
encryptionKey;
|
|
36
|
+
encryptionVariant;
|
|
37
|
+
logger;
|
|
38
|
+
sockets = /* @__PURE__ */ new Set();
|
|
39
|
+
constructor(config) {
|
|
40
|
+
this.scopePrefix = config.scopePrefix;
|
|
41
|
+
this.encryptionKey = config.encryptionKey;
|
|
42
|
+
this.encryptionVariant = config.encryptionVariant;
|
|
43
|
+
this.logger = config.logger || ((msg, data) => logger.debug(msg, data));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Register an RPC handler for a specific method
|
|
47
|
+
* @param method - The method name (without prefix)
|
|
48
|
+
* @param handler - The handler function
|
|
49
|
+
*/
|
|
50
|
+
registerHandler(method, handler) {
|
|
51
|
+
const prefixedMethod = this.getPrefixedMethod(method);
|
|
52
|
+
this.handlers.set(prefixedMethod, handler);
|
|
53
|
+
for (const socket of this.sockets) socket.emit("rpc-register", { method: prefixedMethod });
|
|
54
|
+
}
|
|
55
|
+
unregisterHandler(method) {
|
|
56
|
+
const prefixedMethod = this.getPrefixedMethod(method);
|
|
57
|
+
this.handlers.delete(prefixedMethod);
|
|
58
|
+
this.plainMethods.delete(prefixedMethod);
|
|
59
|
+
for (const socket of this.sockets) socket.emit("rpc-unregister", { method: prefixedMethod });
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* B-506: register a method whose request is a plaintext JSON string and
|
|
63
|
+
* whose response is returned as a plaintext JSON string (see
|
|
64
|
+
* `plainMethods`). The handler receives the parsed params; a throw is
|
|
65
|
+
* answered with `{ error }` like the encrypted path.
|
|
66
|
+
*/
|
|
67
|
+
registerPlainHandler(method, handler) {
|
|
68
|
+
this.plainMethods.add(this.getPrefixedMethod(method));
|
|
69
|
+
this.registerHandler(method, handler);
|
|
70
|
+
}
|
|
71
|
+
isPlainMethod(prefixedMethod) {
|
|
72
|
+
return this.plainMethods.has(prefixedMethod);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Handle an incoming RPC request
|
|
76
|
+
* @param request - The RPC request data
|
|
77
|
+
* @param callback - The response callback
|
|
78
|
+
*/
|
|
79
|
+
async handleRequest(request) {
|
|
80
|
+
if (this.plainMethods.has(request.method)) {
|
|
81
|
+
return this.handlePlainRequest(request);
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const handler = this.handlers.get(request.method);
|
|
85
|
+
if (!handler) {
|
|
86
|
+
this.logger("[RPC] [ERROR] Method not found", { method: request.method });
|
|
87
|
+
const errorResponse = { error: "Method not found" };
|
|
88
|
+
const encryptedError = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
|
|
89
|
+
return encryptedError;
|
|
90
|
+
}
|
|
91
|
+
const decryptedParams = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(request.params));
|
|
92
|
+
this.logger("[RPC] Calling handler", { method: request.method });
|
|
93
|
+
const result = await handler(decryptedParams);
|
|
94
|
+
this.logger("[RPC] Handler returned", { method: request.method, hasResult: result !== void 0 });
|
|
95
|
+
const encryptedResponse = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, result));
|
|
96
|
+
this.logger("[RPC] Sending encrypted response", { method: request.method, responseLength: encryptedResponse.length });
|
|
97
|
+
return encryptedResponse;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
this.logger("[RPC] [ERROR] Error handling request", { error });
|
|
100
|
+
const errorResponse = {
|
|
101
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
102
|
+
};
|
|
103
|
+
return encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Plaintext variant of handleRequest (B-506). Never throws; never encrypts. */
|
|
107
|
+
async handlePlainRequest(request) {
|
|
108
|
+
const handler = this.handlers.get(request.method);
|
|
109
|
+
if (!handler) return JSON.stringify({ error: "Method not found" });
|
|
110
|
+
let params;
|
|
111
|
+
try {
|
|
112
|
+
params = typeof request.params === "string" && request.params.length > 0 ? JSON.parse(request.params) : {};
|
|
113
|
+
} catch {
|
|
114
|
+
return JSON.stringify({ error: "Invalid JSON params" });
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
this.logger("[RPC] Calling plain handler", { method: request.method });
|
|
118
|
+
const result = await handler(params);
|
|
119
|
+
return JSON.stringify(result === void 0 ? null : result);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.logger("[RPC] [ERROR] Error handling plain request", { error });
|
|
122
|
+
return JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error" });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
onSocketConnect(socket) {
|
|
126
|
+
this.sockets.add(socket);
|
|
127
|
+
for (const [prefixedMethod] of this.handlers) {
|
|
128
|
+
socket.emit("rpc-register", { method: prefixedMethod });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Attach a candidate transport and wait until the server confirms every
|
|
133
|
+
* method before a release handover closes the old transport.
|
|
134
|
+
*/
|
|
135
|
+
async onSocketConnectAndWait(socket, timeoutMs = 1e4) {
|
|
136
|
+
this.sockets.add(socket);
|
|
137
|
+
const pending = new Set(this.handlers.keys());
|
|
138
|
+
const acknowledged = /* @__PURE__ */ new Set();
|
|
139
|
+
await new Promise((resolve, reject) => {
|
|
140
|
+
let settleTimer = null;
|
|
141
|
+
const timer = setTimeout(() => {
|
|
142
|
+
cleanup();
|
|
143
|
+
reject(new Error(`RPC registration timed out (${pending.size} pending)`));
|
|
144
|
+
}, timeoutMs);
|
|
145
|
+
timer.unref?.();
|
|
146
|
+
const maybeSettle = () => {
|
|
147
|
+
if (pending.size > 0 || settleTimer) return;
|
|
148
|
+
settleTimer = setTimeout(() => {
|
|
149
|
+
settleTimer = null;
|
|
150
|
+
for (const prefixedMethod of this.handlers.keys()) {
|
|
151
|
+
if (acknowledged.has(prefixedMethod) || pending.has(prefixedMethod)) continue;
|
|
152
|
+
pending.add(prefixedMethod);
|
|
153
|
+
socket.emit("rpc-register", { method: prefixedMethod });
|
|
154
|
+
}
|
|
155
|
+
if (pending.size === 0) {
|
|
156
|
+
cleanup();
|
|
157
|
+
resolve();
|
|
158
|
+
}
|
|
159
|
+
}, 0);
|
|
160
|
+
settleTimer.unref?.();
|
|
161
|
+
};
|
|
162
|
+
const onRegistered = (data) => {
|
|
163
|
+
if (typeof data?.method !== "string") return;
|
|
164
|
+
acknowledged.add(data.method);
|
|
165
|
+
pending.delete(data.method);
|
|
166
|
+
maybeSettle();
|
|
167
|
+
};
|
|
168
|
+
const cleanup = () => {
|
|
169
|
+
clearTimeout(timer);
|
|
170
|
+
if (settleTimer) clearTimeout(settleTimer);
|
|
171
|
+
socket.off("rpc-registered", onRegistered);
|
|
172
|
+
};
|
|
173
|
+
socket.on("rpc-registered", onRegistered);
|
|
174
|
+
for (const prefixedMethod of pending) socket.emit("rpc-register", { method: prefixedMethod });
|
|
175
|
+
maybeSettle();
|
|
176
|
+
}).catch((error) => {
|
|
177
|
+
this.sockets.delete(socket);
|
|
178
|
+
throw error;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
onSocketDisconnect(socket) {
|
|
182
|
+
if (socket) this.sockets.delete(socket);
|
|
183
|
+
else this.sockets.clear();
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Get the number of registered handlers
|
|
187
|
+
*/
|
|
188
|
+
getHandlerCount() {
|
|
189
|
+
return this.handlers.size;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Check if a handler is registered
|
|
193
|
+
* @param method - The method name (without prefix)
|
|
194
|
+
*/
|
|
195
|
+
hasHandler(method) {
|
|
196
|
+
const prefixedMethod = this.getPrefixedMethod(method);
|
|
197
|
+
return this.handlers.has(prefixedMethod);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Clear all handlers
|
|
201
|
+
*/
|
|
202
|
+
clearHandlers() {
|
|
203
|
+
this.handlers.clear();
|
|
204
|
+
this.logger("Cleared all RPC handlers");
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get the prefixed method name
|
|
208
|
+
* @param method - The method name
|
|
209
|
+
*/
|
|
210
|
+
getPrefixedMethod(method) {
|
|
211
|
+
return `${this.scopePrefix}:${method}`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function describeSocketAckDrop(drop) {
|
|
216
|
+
switch (drop.reason) {
|
|
217
|
+
case "no-ack":
|
|
218
|
+
return "no ack callback on the frame (re-delivered after a transport close?); response dropped";
|
|
219
|
+
case "handler-threw":
|
|
220
|
+
return `handler threw: ${drop.error instanceof Error ? drop.error.message : String(drop.error)}`;
|
|
221
|
+
case "ack-threw":
|
|
222
|
+
return `ack callback threw: ${drop.error instanceof Error ? drop.error.message : String(drop.error)}`;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function safeAck(ack, onDrop) {
|
|
226
|
+
return (value) => {
|
|
227
|
+
if (typeof ack !== "function") {
|
|
228
|
+
onDrop({ reason: "no-ack" });
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
ack(value);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
onDrop({ reason: "ack-threw", error });
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async function answerSocketRequest(ack, run, onDrop) {
|
|
239
|
+
let value;
|
|
240
|
+
try {
|
|
241
|
+
value = await run();
|
|
242
|
+
} catch (error) {
|
|
243
|
+
onDrop({ reason: "handler-threw", error });
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
safeAck(ack, onDrop)(value);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function hasNetworkConnectivity() {
|
|
250
|
+
const interfaces = os.networkInterfaces();
|
|
251
|
+
for (const name of Object.keys(interfaces)) {
|
|
252
|
+
for (const iface of interfaces[name] || []) {
|
|
253
|
+
if (!iface.internal && iface.family === "IPv4") return true;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
function isLidClosed() {
|
|
259
|
+
if (process.platform !== "darwin") return false;
|
|
260
|
+
try {
|
|
261
|
+
const output = execSync("ioreg -r -k AppleClamshellState -d 4", {
|
|
262
|
+
timeout: 5e3,
|
|
263
|
+
encoding: "utf-8"
|
|
264
|
+
});
|
|
265
|
+
return output.includes('"AppleClamshellState" = Yes');
|
|
266
|
+
} catch {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function hasExternalDisplay() {
|
|
271
|
+
if (process.platform !== "darwin") return false;
|
|
272
|
+
try {
|
|
273
|
+
const output = execSync("system_profiler SPDisplaysDataType -json 2>/dev/null", {
|
|
274
|
+
timeout: 1e4,
|
|
275
|
+
encoding: "utf-8"
|
|
276
|
+
});
|
|
277
|
+
const data = JSON.parse(output);
|
|
278
|
+
const gpus = data.SPDisplaysDataType || [];
|
|
279
|
+
for (const gpu of gpus) {
|
|
280
|
+
const displays = gpu.spdisplays_ndrvs || [];
|
|
281
|
+
for (const display of displays) {
|
|
282
|
+
const isBuiltIn = display.spdisplays_builtin === "spdisplays_yes" || display.spdisplays_connection_type === "spdisplays_internal";
|
|
283
|
+
if (!isBuiltIn) return true;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
} catch {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function shouldReconnect() {
|
|
292
|
+
if (!hasNetworkConnectivity()) return false;
|
|
293
|
+
if (isLidClosed() && !hasExternalDisplay()) return false;
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export { RpcHandlerManager as R, answerSocketRequest as a, shouldReconnect as b, describeSocketAckDrop as d, isRateQuotaCode as i, pauseForRateQuota as p, safeAck as s };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const CLIPBOARD_MAX_BYTES = 256 * 1024;
|
|
4
|
+
const CLIPBOARD_HISTORY_MAX_BYTES = 32 * 1024;
|
|
5
|
+
const CLIPBOARD_TOOL_NAME = "copy_to_clipboard";
|
|
6
|
+
const CLIPBOARD_TOOL_TITLE = "Copy to User Clipboard";
|
|
7
|
+
const CLIPBOARD_TOOL_DESCRIPTION = `Copy text to the clipboard of the device the user is currently viewing this session from (their web browser, possibly a phone). Use this whenever the user wants a piece of text handed to them \u2014 not only when they literally say "clipboard". Trigger phrases include: "copy X", "copy that for me", "give me X", "send me X", "I need X on my phone", "\u590D\u5236\u7ED9\u6211", "\u628A X \u590D\u5236\u4E00\u4E0B", "\u53D1\u6211", "\u7ED9\u6211\u4E00\u4EFD", "\u590D\u5236\u5230\u526A\u5207\u677F/\u526A\u8D34\u677F". Typical payloads: command output, code snippets, tokens/keys, URLs, file contents, commit messages. The text is delivered to every device where the user has the web client open. Payloads larger than ${CLIPBOARD_MAX_BYTES / 1024}KB are truncated.`;
|
|
8
|
+
function prepareClipboardText(input, maxBytes = CLIPBOARD_MAX_BYTES) {
|
|
9
|
+
const totalBytes = Buffer.byteLength(input, "utf8");
|
|
10
|
+
if (totalBytes <= maxBytes) {
|
|
11
|
+
return { text: input, truncated: false, totalBytes };
|
|
12
|
+
}
|
|
13
|
+
const sliced = Buffer.from(input, "utf8").subarray(0, maxBytes).toString("utf8");
|
|
14
|
+
let text = sliced;
|
|
15
|
+
if (text.endsWith("\uFFFD")) {
|
|
16
|
+
text = text.slice(0, -1);
|
|
17
|
+
}
|
|
18
|
+
const lastCode = text.charCodeAt(text.length - 1);
|
|
19
|
+
if (lastCode >= 55296 && lastCode <= 56319) {
|
|
20
|
+
text = text.slice(0, -1);
|
|
21
|
+
}
|
|
22
|
+
return { text, truncated: true, totalBytes };
|
|
23
|
+
}
|
|
24
|
+
function prepareClipboardHistoryText(input) {
|
|
25
|
+
const prepared = prepareClipboardText(input, CLIPBOARD_HISTORY_MAX_BYTES);
|
|
26
|
+
if (Buffer.byteLength(JSON.stringify(prepared.text), "utf8") <= CLIPBOARD_HISTORY_MAX_BYTES + 2) return prepared;
|
|
27
|
+
let encodedBytes = 0;
|
|
28
|
+
let text = "";
|
|
29
|
+
for (const character of prepared.text) {
|
|
30
|
+
const cost = Buffer.byteLength(JSON.stringify(character), "utf8") - 2;
|
|
31
|
+
if (encodedBytes + cost > CLIPBOARD_HISTORY_MAX_BYTES) break;
|
|
32
|
+
encodedBytes += cost;
|
|
33
|
+
text += character;
|
|
34
|
+
}
|
|
35
|
+
return { text, truncated: true, totalBytes: prepared.totalBytes };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
exports.CLIPBOARD_MAX_BYTES = CLIPBOARD_MAX_BYTES;
|
|
39
|
+
exports.CLIPBOARD_TOOL_DESCRIPTION = CLIPBOARD_TOOL_DESCRIPTION;
|
|
40
|
+
exports.CLIPBOARD_TOOL_NAME = CLIPBOARD_TOOL_NAME;
|
|
41
|
+
exports.CLIPBOARD_TOOL_TITLE = CLIPBOARD_TOOL_TITLE;
|
|
42
|
+
exports.prepareClipboardHistoryText = prepareClipboardHistoryText;
|
|
43
|
+
exports.prepareClipboardText = prepareClipboardText;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const CLIPBOARD_MAX_BYTES = 256 * 1024;
|
|
2
|
+
const CLIPBOARD_HISTORY_MAX_BYTES = 32 * 1024;
|
|
3
|
+
const CLIPBOARD_TOOL_NAME = "copy_to_clipboard";
|
|
4
|
+
const CLIPBOARD_TOOL_TITLE = "Copy to User Clipboard";
|
|
5
|
+
const CLIPBOARD_TOOL_DESCRIPTION = `Copy text to the clipboard of the device the user is currently viewing this session from (their web browser, possibly a phone). Use this whenever the user wants a piece of text handed to them \u2014 not only when they literally say "clipboard". Trigger phrases include: "copy X", "copy that for me", "give me X", "send me X", "I need X on my phone", "\u590D\u5236\u7ED9\u6211", "\u628A X \u590D\u5236\u4E00\u4E0B", "\u53D1\u6211", "\u7ED9\u6211\u4E00\u4EFD", "\u590D\u5236\u5230\u526A\u5207\u677F/\u526A\u8D34\u677F". Typical payloads: command output, code snippets, tokens/keys, URLs, file contents, commit messages. The text is delivered to every device where the user has the web client open. Payloads larger than ${CLIPBOARD_MAX_BYTES / 1024}KB are truncated.`;
|
|
6
|
+
function prepareClipboardText(input, maxBytes = CLIPBOARD_MAX_BYTES) {
|
|
7
|
+
const totalBytes = Buffer.byteLength(input, "utf8");
|
|
8
|
+
if (totalBytes <= maxBytes) {
|
|
9
|
+
return { text: input, truncated: false, totalBytes };
|
|
10
|
+
}
|
|
11
|
+
const sliced = Buffer.from(input, "utf8").subarray(0, maxBytes).toString("utf8");
|
|
12
|
+
let text = sliced;
|
|
13
|
+
if (text.endsWith("\uFFFD")) {
|
|
14
|
+
text = text.slice(0, -1);
|
|
15
|
+
}
|
|
16
|
+
const lastCode = text.charCodeAt(text.length - 1);
|
|
17
|
+
if (lastCode >= 55296 && lastCode <= 56319) {
|
|
18
|
+
text = text.slice(0, -1);
|
|
19
|
+
}
|
|
20
|
+
return { text, truncated: true, totalBytes };
|
|
21
|
+
}
|
|
22
|
+
function prepareClipboardHistoryText(input) {
|
|
23
|
+
const prepared = prepareClipboardText(input, CLIPBOARD_HISTORY_MAX_BYTES);
|
|
24
|
+
if (Buffer.byteLength(JSON.stringify(prepared.text), "utf8") <= CLIPBOARD_HISTORY_MAX_BYTES + 2) return prepared;
|
|
25
|
+
let encodedBytes = 0;
|
|
26
|
+
let text = "";
|
|
27
|
+
for (const character of prepared.text) {
|
|
28
|
+
const cost = Buffer.byteLength(JSON.stringify(character), "utf8") - 2;
|
|
29
|
+
if (encodedBytes + cost > CLIPBOARD_HISTORY_MAX_BYTES) break;
|
|
30
|
+
encodedBytes += cost;
|
|
31
|
+
text += character;
|
|
32
|
+
}
|
|
33
|
+
return { text, truncated: true, totalBytes: prepared.totalBytes };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { CLIPBOARD_TOOL_NAME as C, CLIPBOARD_TOOL_TITLE as a, CLIPBOARD_TOOL_DESCRIPTION as b, CLIPBOARD_MAX_BYTES as c, prepareClipboardHistoryText as d, prepareClipboardText as p };
|