very-happy-cli 0.2.159 → 0.2.161
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-VsfkZiVF.mjs → AcpBackend-6U6pQgx1.mjs} +4 -2
- package/dist/{AcpBackend-DUYFlyfN.cjs → AcpBackend-Ba98QDif.cjs} +78 -76
- package/dist/{AcpSessionManager-Bdv8LWXh.cjs → AcpSessionManager-BAY1Pggx.cjs} +9 -9
- package/dist/{AcpSessionManager-rYtVb0kN.mjs → AcpSessionManager-CoMWFVw3.mjs} +1 -1
- package/dist/BasePermissionHandler-2R0uH2CI.cjs +157 -0
- package/dist/BasePermissionHandler-BvvhBTja.mjs +155 -0
- package/dist/BaseReasoningProcessor-DyhO1uoU.mjs +198 -0
- package/dist/BaseReasoningProcessor-fsVNmCEF.cjs +200 -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-CvsJyeQB.mjs → agentHome-Ck1i1TQB.mjs} +8 -22
- package/dist/{agentHome-CHh4lmjM.cjs → agentHome-DvNnGphb.cjs} +14 -28
- package/dist/api-B2vLMS-z.cjs +3346 -0
- package/dist/api-SffjRQ1b.mjs +3339 -0
- package/dist/{types-Dh-s4Lqs.mjs → apiMachine-BpW9t8MO.mjs} +9116 -16205
- package/dist/{types-BbvK1Tr_.cjs → apiMachine-gFA54X7y.cjs} +9104 -16247
- package/dist/assistantTools-BfA0R3_m.cjs +380 -0
- package/dist/assistantTools-CgiTbnWb.mjs +376 -0
- package/dist/attachmentContent-CSGxr71v.mjs +82 -0
- package/dist/attachmentContent-DMMZzevR.cjs +88 -0
- package/dist/attachmentLimits-CzVrqgLR.cjs +354 -0
- package/dist/attachmentLimits-yba1uHFO.mjs +348 -0
- package/dist/auth-Bt4ceBBy.cjs +227 -0
- package/dist/auth-CYHjQ8oQ.mjs +225 -0
- package/dist/auth-DdyVZSo6.mjs +218 -0
- package/dist/auth-x4CEoysH.cjs +220 -0
- package/dist/authRelay-BdYkC1Po.cjs +156 -0
- package/dist/authRelay-DSAvg1uv.mjs +143 -0
- package/dist/{auto-DKmEls8Z.cjs → auto-BDXDUnjB.cjs} +70 -79
- package/dist/{auto-nb8GhRRy.mjs → auto-CXLlRi3a.mjs} +27 -36
- package/dist/backgroundTaskActivity-8PdPxrvy.mjs +149 -0
- package/dist/backgroundTaskActivity-CBvUdj39.cjs +157 -0
- package/dist/bootstrap-BNQq05jR.mjs +112 -0
- package/dist/bootstrap-CkgIq8N3.cjs +116 -0
- package/dist/browser-9pyhQr1R.mjs +20 -0
- package/dist/browser-BDOlKWYe.cjs +22 -0
- package/dist/claudeCliPath-BqkaH2oR.mjs +8 -0
- package/dist/claudeCliPath-U4HHmV4r.cjs +10 -0
- package/dist/claudeLocal-BDuGFnjJ.cjs +343 -0
- package/dist/claudeLocal-CQa8qnwq.mjs +339 -0
- package/dist/claudeLocalLauncher-Bd5I2-j6.cjs +154 -0
- package/dist/claudeLocalLauncher-CDqQI9ak.mjs +152 -0
- package/dist/cliUpdate-DsKXPFYp.cjs +120 -0
- package/dist/cliUpdate-Y34VGe9Y.mjs +112 -0
- package/dist/client-Brn-UwVk.mjs +100 -0
- package/dist/client-CXb1VJui.mjs +112 -0
- package/dist/client-C_axwRry.cjs +104 -0
- package/dist/client-DOBWJL3u.cjs +119 -0
- package/dist/codex/happyMcpStdioBridge.cjs +33 -14
- package/dist/codex/happyMcpStdioBridge.mjs +26 -7
- package/dist/codexAppServerClient-ZzhUTWPo.cjs +1090 -0
- package/dist/codexAppServerClient-oeQKD6Qf.mjs +1088 -0
- package/dist/codexCommand-C7H0NpBe.cjs +2088 -0
- package/dist/codexCommand-CJ82bl3l.mjs +2086 -0
- package/dist/codexThreadFork-BCaEMBiM.mjs +338 -0
- package/dist/codexThreadFork-BZqF-9bX.cjs +350 -0
- package/dist/{config-ClLtM6F3.mjs → config-BglnktIo.mjs} +7 -40
- package/dist/{config-DLx-vW3I.cjs → config-CYpL1Ahd.cjs} +26 -59
- package/dist/configuration-DoWFJUPm.mjs +170 -0
- package/dist/configuration-v9f8LM9T.cjs +181 -0
- package/dist/connect-hgcNjyAB.mjs +682 -0
- package/dist/connect-yEvjdRvx.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-B45vMtSZ.cjs +69 -0
- package/dist/context-C9GHPLEm.mjs +65 -0
- package/dist/controlClient-DjoKTtgK.mjs +260 -0
- package/dist/controlClient-fEHE85bH.cjs +279 -0
- package/dist/createSessionMetadata-BRLB9CNO.cjs +58 -0
- package/dist/createSessionMetadata-mIeehclp.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-DKDP_mM-.cjs +507 -0
- package/dist/deterministicJson-p835AdAU.mjs +502 -0
- package/dist/doctor-BPkYWSuq.mjs +75 -0
- package/dist/doctor-BeUuQXFs.mjs +391 -0
- package/dist/doctor-hVNnSZJn.cjs +395 -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-CO7d1gOC.mjs +61 -0
- package/dist/ensureDaemonRunning-Dg9aZfjs.cjs +64 -0
- package/dist/handleResumeCommand-BiTPK0qd.mjs +240 -0
- package/dist/handleResumeCommand-DnVmy7a3.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-2mCaeKyf.mjs → index-CAFwetLk.mjs} +68 -38
- package/dist/index-Cqx-Raq6.mjs +68 -0
- package/dist/index-D3kI0hDR.cjs +107 -0
- package/dist/index-DA7yINuk.cjs +994 -0
- package/dist/{index-BEOMECtE.cjs → index-LIn6nCcq.cjs} +121 -91
- package/dist/index-Pf2cV9cn.cjs +182 -0
- package/dist/index-SPGPenK5.mjs +158 -0
- package/dist/index-gNTWtSKN.cjs +71 -0
- package/dist/index-qdS1friN.mjs +949 -0
- package/dist/index.cjs +5 -46
- package/dist/index.mjs +5 -46
- package/dist/{install-BPhJppvd.mjs → install-BO_W7VhL.mjs} +37 -37
- package/dist/install-Bu85vlHD.cjs +88 -0
- package/dist/install-ClUUl3yp.mjs +86 -0
- package/dist/{install-BOHrUaFM.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-Cou3WUf3.cjs → installTerminalHooks-BQZbvxsZ.cjs} +5 -40
- package/dist/{installTerminalHooks-WeznjkuC.mjs → installTerminalHooks-By7gBMEF.mjs} +3 -38
- package/dist/lib.cjs +28 -19
- package/dist/lib.d.cts +12 -2
- package/dist/lib.d.mts +12 -2
- package/dist/lib.mjs +23 -14
- package/dist/lidState-C2dI0FAN.mjs +297 -0
- package/dist/lidState-KHHu1015.cjs +305 -0
- package/dist/limits-C-a7gr04.cjs +43 -0
- package/dist/limits-DIo7rEBR.mjs +36 -0
- package/dist/localHappyAgentAuth-BXiHPXdC.cjs +86 -0
- package/dist/localHappyAgentAuth-DCsetiyG.mjs +82 -0
- package/dist/logger-Bc8-u1w0.cjs +275 -0
- package/dist/logger-fXoNzNuB.mjs +271 -0
- package/dist/loginShellEnv-BkuYtExZ.cjs +182 -0
- package/dist/loginShellEnv-Dw8M7tkI.mjs +167 -0
- package/dist/machineMetadata-BHjehxJY.mjs +38 -0
- package/dist/machineMetadata-BibDMFVx.cjs +41 -0
- package/dist/main-BktM4A1K.mjs +939 -0
- package/dist/main-CLaX-t-b.cjs +941 -0
- package/dist/manager-BQ4flvQZ.mjs +101 -0
- package/dist/manager-Cly8xVIR.cjs +105 -0
- package/dist/{mcp-Dy3f_K0h.mjs → mcp-CeUd9KDr.mjs} +27 -33
- package/dist/{mcp-CPYbtsYG.cjs → mcp-GBfE70nk.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-Cl5BEP5x.cjs +173 -0
- package/dist/package-uOg9SCKi.mjs +171 -0
- package/dist/path-B7zW-WwE.cjs +18 -0
- package/dist/path-D2wdaNSk.mjs +15 -0
- package/dist/peerTools-CIeiy9wn.cjs +283 -0
- package/dist/peerTools-Dz3_7Bkk.mjs +272 -0
- package/dist/permissionMode-CH4FoZfU.mjs +80 -0
- package/dist/permissionMode-CrV-XuMs.cjs +87 -0
- package/dist/persistence-CBNMzSa8.cjs +414 -0
- package/dist/persistence-oET5n61f.mjs +372 -0
- package/dist/piExtension.mjs +2 -1
- package/dist/piRunnerArgs-Bf7RUF__.cjs +66 -0
- package/dist/piRunnerArgs-Dk1ncwHF.mjs +59 -0
- package/dist/{piRuntime-NtF0YqjH.cjs → piRuntime-B_6TE9Eb.cjs} +4 -4
- package/dist/{piRuntime-kLZNpAAl.mjs → piRuntime-BwRdhK_N.mjs} +2 -2
- package/dist/{piTerminal-B0fEOV_2.mjs → piTerminal-Bap3R8Re.mjs} +32 -35
- package/dist/{piTerminal-9J0GAKzb.cjs → piTerminal-CagmEoez.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-BjSV2Skz.cjs +538 -0
- package/dist/registerCommonHandlers-CrgvJsOy.mjs +536 -0
- package/dist/registerKillSessionHandler-6TC2S3lf.cjs +366 -0
- package/dist/registerKillSessionHandler-Cbc8Rgtd.mjs +363 -0
- package/dist/remoteSessionClient-BNx78NLb.cjs +634 -0
- package/dist/remoteSessionClient-CO7qOjR0.mjs +612 -0
- package/dist/resources-BdFBQi8r.mjs +117 -0
- package/dist/resources-JDPugcDq.cjs +120 -0
- package/dist/run-2l5vSt2h.mjs +5743 -0
- package/dist/run-D5xAEWSB.cjs +5766 -0
- package/dist/runClaude-BX6ixjtW.cjs +894 -0
- package/dist/runClaude-BgN6xo05.mjs +892 -0
- package/dist/runClaudeDeps-Bp20MScE.cjs +4168 -0
- package/dist/runClaudeDeps-COBBhD9o.mjs +4129 -0
- package/dist/{runGemini-F-mqx3_K.cjs → runGemini-CSMPkt0p.cjs} +202 -175
- package/dist/{runGemini-DV6Ouadt.mjs → runGemini-Cc2jq-uh.mjs} +58 -31
- package/dist/{runOpenClaw-D7MlPS5n.mjs → runOpenClaw-CIlCwdW2.mjs} +31 -32
- package/dist/{runOpenClaw-Qqo9j63M.cjs → runOpenClaw-YBwua4Rf.cjs} +64 -65
- package/dist/sandbox-C4gXLP1p.cjs +187 -0
- package/dist/sandbox-CNkwOtHI.mjs +180 -0
- package/dist/selfCheck-CU8xSDsl.cjs +87 -0
- package/dist/selfCheck-hJ6PCh7v.mjs +84 -0
- package/dist/{send-CLhYXNgy.mjs → send-CGHcKZ-w.mjs} +12 -5
- package/dist/{send-4qJTCAYW.cjs → send-DY9Nr-x4.cjs} +16 -9
- package/dist/server-II51Mimf.cjs +407 -0
- package/dist/server-m2b5pamZ.mjs +404 -0
- package/dist/sessionLock-7ePAYDOg.cjs +205 -0
- package/dist/sessionLock-Cw6FmbMv.mjs +197 -0
- package/dist/sessionModeFile-BVCV5f7E.cjs +36 -0
- package/dist/sessionModeFile-CDmDyRkD.mjs +32 -0
- package/dist/sessionOps-C_f1wKOr.mjs +760 -0
- package/dist/sessionOps-DJdExmBy.cjs +783 -0
- package/dist/sessionScanner-CaAHLyNV.mjs +206 -0
- package/dist/sessionScanner-D1goKdd3.cjs +209 -0
- package/dist/{sessions-Bh1pyddi.cjs → sessions-DGs6ALNo.cjs} +37 -28
- package/dist/{sessions-CzIH2rGp.mjs → sessions-DnvmWUel.mjs} +17 -8
- package/dist/setupOfflineReconnection-CVViqZft.mjs +72 -0
- package/dist/setupOfflineReconnection-R7XuR_sj.cjs +74 -0
- package/dist/smallFastModel-BLNgMrwR.mjs +18 -0
- package/dist/smallFastModel-D_uxGMo3.cjs +21 -0
- package/dist/{spawn-CmFOSVdW.cjs → spawn-CXTH7qg1.cjs} +61 -65
- package/dist/{spawn-CPIWlPOM.mjs → spawn-DNYSvFtC.mjs} +35 -40
- package/dist/spawnAgents-BGQiEF-w.cjs +52 -0
- package/dist/spawnAgents-DZzZ9jMI.mjs +45 -0
- package/dist/spawnHappyCLI-BhYj84rv.cjs +66 -0
- package/dist/spawnHappyCLI-cqWQbwsb.mjs +64 -0
- package/dist/specialCommands-BSqhYg5X.mjs +52 -0
- package/dist/specialCommands-DSsTeRej.cjs +54 -0
- package/dist/startFileWatcher-CKKx-8ZO.cjs +71 -0
- package/dist/startFileWatcher-CdegMbJj.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-CVk4QERO.cjs +71 -0
- package/dist/sync-CxD-w_40.mjs +69 -0
- package/dist/systemPrompt-BygJcmbi.cjs +97 -0
- package/dist/systemPrompt-DmtypUc7.mjs +94 -0
- package/dist/{teams-CEahj4cm.mjs → teams-BOC6nIPL.mjs} +9 -40
- package/dist/{teams-CB1R9-Z9.cjs → teams-BsvpOBfn.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-BZNdNiIV.mjs +38 -0
- package/dist/time-CNRE4ChD.cjs +42 -0
- package/dist/titleGenerator-CFRhU-xq.cjs +172 -0
- package/dist/titleGenerator-ox6ptCsp.mjs +166 -0
- package/dist/tmuxSocket-B8Yx20sT.mjs +19 -0
- package/dist/tmuxSocket-D-Xt6Vn-.cjs +24 -0
- package/dist/{todo-DqyRI4wP.cjs → todo-B-KB7oAB.cjs} +15 -12
- package/dist/{todo-Cknp5D4U.mjs → todo-C5KBVbUw.mjs} +11 -8
- package/dist/tools-BZabNIid.mjs +177 -0
- package/dist/tools-BmQQUATY.mjs +60 -0
- package/dist/tools-CsSovyNp.cjs +180 -0
- package/dist/tools-lMDra4Pv.cjs +63 -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-B1WLtC1d.mjs +47 -0
- package/dist/uninstall-fvA5aWpU.cjs +49 -0
- package/package.json +7 -7
- package/scripts/session_hook_forwarder.cjs +9 -2
- package/dist/agentGuidance-BRDN1NoZ.cjs +0 -2217
- package/dist/agentGuidance-KgvYuF3R.mjs +0 -2145
- package/dist/index-BF6WfgV5.mjs +0 -19624
- package/dist/index-C-JazrMB.mjs +0 -52
- package/dist/index-blDSadUR.cjs +0 -58
- package/dist/index-rH3DrQ5B.cjs +0 -19688
- package/dist/limits-BH5CV5vR.cjs +0 -2683
- package/dist/limits-CjRj8IX5.mjs +0 -2558
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chalk = require('chalk');
|
|
4
|
+
var persistence = require('./persistence-CBNMzSa8.cjs');
|
|
5
|
+
var auth = require('./auth-Bt4ceBBy.cjs');
|
|
6
|
+
var configuration = require('./configuration-v9f8LM9T.cjs');
|
|
7
|
+
var node_fs = require('node:fs');
|
|
8
|
+
var node_readline = require('node:readline');
|
|
9
|
+
var controlClient = require('./controlClient-fEHE85bH.cjs');
|
|
10
|
+
var logger = require('./logger-Bc8-u1w0.cjs');
|
|
11
|
+
var os = require('node:os');
|
|
12
|
+
var authRelay = require('./authRelay-BdYkC1Po.cjs');
|
|
13
|
+
require('node:fs/promises');
|
|
14
|
+
require('zod');
|
|
15
|
+
require('node:path');
|
|
16
|
+
require('./package-Cl5BEP5x.cjs');
|
|
17
|
+
require('node:crypto');
|
|
18
|
+
require('tweetnacl');
|
|
19
|
+
require('node:util');
|
|
20
|
+
require('axios');
|
|
21
|
+
require('./time-CNRE4ChD.cjs');
|
|
22
|
+
require('./browser-BDOlKWYe.cjs');
|
|
23
|
+
require('open');
|
|
24
|
+
|
|
25
|
+
function localMachineIdentityStatus(machineId) {
|
|
26
|
+
if (machineId) {
|
|
27
|
+
return {
|
|
28
|
+
configured: true,
|
|
29
|
+
label: "Local machine identity configured (relay registration not checked)"
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
configured: false,
|
|
34
|
+
label: "Local machine identity missing",
|
|
35
|
+
nextStep: 'Run "very-happy auth login --force" to create and register one'
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DEFAULT_SERVER_URL = "https://veryhappy.dev";
|
|
40
|
+
async function handleAuthCommand(args) {
|
|
41
|
+
const subcommand = args[0];
|
|
42
|
+
if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
43
|
+
showAuthHelp();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
switch (subcommand) {
|
|
47
|
+
case "login":
|
|
48
|
+
await handleAuthLogin(args.slice(1));
|
|
49
|
+
break;
|
|
50
|
+
case "logout":
|
|
51
|
+
await handleAuthLogout();
|
|
52
|
+
break;
|
|
53
|
+
case "status":
|
|
54
|
+
await handleAuthStatus();
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
console.error(chalk.red(`Unknown auth subcommand: ${subcommand}`));
|
|
58
|
+
showAuthHelp();
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function showAuthHelp() {
|
|
63
|
+
console.log(`
|
|
64
|
+
${chalk.bold("very-happy auth")} - Authentication management
|
|
65
|
+
|
|
66
|
+
${chalk.bold("Usage:")}
|
|
67
|
+
very-happy auth login [--force] Authenticate with Very Happy
|
|
68
|
+
very-happy auth logout Remove authentication and machine data
|
|
69
|
+
very-happy auth status Show authentication status
|
|
70
|
+
very-happy auth help Show this help message
|
|
71
|
+
|
|
72
|
+
${chalk.bold("Options:")}
|
|
73
|
+
--force Clear credentials, machine ID, and stop daemon before re-auth
|
|
74
|
+
|
|
75
|
+
${chalk.gray("Security: the configured Very Happy server is a trusted relay and account service.")}
|
|
76
|
+
${chalk.gray("It can recover account material, access relayed content, and act through capabilities")}
|
|
77
|
+
${chalk.gray("exposed by an online daemon. Use a server you trust.")}
|
|
78
|
+
`);
|
|
79
|
+
}
|
|
80
|
+
function printDaemonNextStep() {
|
|
81
|
+
console.log(chalk.bold("\nNext: start the machine daemon"));
|
|
82
|
+
if (process.env.HAPPY_SERVER_URL || process.env.HAPPY_WEBAPP_URL) {
|
|
83
|
+
console.log(chalk.yellow(" Keep the same HAPPY_SERVER_URL and HAPPY_WEBAPP_URL environment when starting it."));
|
|
84
|
+
}
|
|
85
|
+
console.log(` ${chalk.cyan("very-happy daemon start")}`);
|
|
86
|
+
console.log(chalk.gray(" It starts in the background and keeps this machine available in Web."));
|
|
87
|
+
}
|
|
88
|
+
async function handleAuthLogin(args) {
|
|
89
|
+
const forceAuth = args.includes("--force") || args.includes("-f");
|
|
90
|
+
if (forceAuth) {
|
|
91
|
+
console.log(chalk.yellow("Force authentication requested."));
|
|
92
|
+
console.log(chalk.gray("This will:"));
|
|
93
|
+
console.log(chalk.gray(" \u2022 Clear existing credentials"));
|
|
94
|
+
console.log(chalk.gray(" \u2022 Clear machine ID"));
|
|
95
|
+
console.log(chalk.gray(" \u2022 Stop daemon if running"));
|
|
96
|
+
console.log(chalk.gray(" \u2022 Re-authenticate and register machine\n"));
|
|
97
|
+
try {
|
|
98
|
+
logger.logger.debug("Stopping daemon for force auth...");
|
|
99
|
+
await controlClient.stopDaemon();
|
|
100
|
+
console.log(chalk.gray("\u2713 Stopped daemon"));
|
|
101
|
+
} catch (error) {
|
|
102
|
+
logger.logger.debug("Daemon was not running or failed to stop:", error);
|
|
103
|
+
}
|
|
104
|
+
await persistence.clearCredentials();
|
|
105
|
+
console.log(chalk.gray("\u2713 Cleared credentials"));
|
|
106
|
+
await persistence.clearMachineId();
|
|
107
|
+
console.log(chalk.gray("\u2713 Cleared machine ID"));
|
|
108
|
+
console.log("");
|
|
109
|
+
}
|
|
110
|
+
if (!forceAuth) {
|
|
111
|
+
const existingCreds = await persistence.readCredentials();
|
|
112
|
+
const settings = await persistence.readSettings();
|
|
113
|
+
if (existingCreds && settings?.machineId) {
|
|
114
|
+
const relayProblem = authRelay.credentialRelayProblem(existingCreds.authServerUrl, configuration.configuration.serverUrl);
|
|
115
|
+
if (relayProblem) {
|
|
116
|
+
console.log(chalk.red(`\u2717 ${relayProblem}`));
|
|
117
|
+
console.log(chalk.yellow(" Use a separate HAPPY_HOME_DIR for each relay (recommended),"));
|
|
118
|
+
console.log(chalk.yellow(" or run 'very-happy auth login --force' to replace this home's credentials."));
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
console.log(chalk.green("\u2713 Already authenticated"));
|
|
123
|
+
console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
|
|
124
|
+
console.log(chalk.gray(` Host: ${os.hostname()}`));
|
|
125
|
+
console.log(chalk.gray(` Use 'very-happy auth login --force' to re-authenticate`));
|
|
126
|
+
printDaemonNextStep();
|
|
127
|
+
return;
|
|
128
|
+
} else if (existingCreds && !settings?.machineId) {
|
|
129
|
+
console.log(chalk.yellow("\u26A0\uFE0F Credentials exist but machine ID is missing"));
|
|
130
|
+
console.log(chalk.gray(" This can happen if --auth flag was used previously"));
|
|
131
|
+
console.log(chalk.gray(" Fixing by setting up machine...\n"));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const result = await auth.authAndSetupMachineIfNeeded();
|
|
136
|
+
console.log(chalk.green("\n\u2713 Authentication successful"));
|
|
137
|
+
console.log(chalk.gray(` Machine ID: ${result.machineId}`));
|
|
138
|
+
printDaemonNextStep();
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error(chalk.red("Authentication failed:"), error instanceof Error ? error.message : "Unknown error");
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function handleAuthLogout() {
|
|
145
|
+
const happyDir = configuration.configuration.happyHomeDir;
|
|
146
|
+
const credentials = await persistence.readCredentials();
|
|
147
|
+
if (!credentials) {
|
|
148
|
+
console.log(chalk.yellow("Not currently authenticated"));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
console.log(chalk.blue("This will log you out of Happy"));
|
|
152
|
+
console.log(chalk.yellow("\u26A0\uFE0F You will need to re-authenticate to use Happy again"));
|
|
153
|
+
const rl = node_readline.createInterface({
|
|
154
|
+
input: process.stdin,
|
|
155
|
+
output: process.stdout
|
|
156
|
+
});
|
|
157
|
+
const answer = await new Promise((resolve) => {
|
|
158
|
+
rl.question(chalk.yellow("Are you sure you want to log out? (y/N): "), resolve);
|
|
159
|
+
});
|
|
160
|
+
rl.close();
|
|
161
|
+
if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
|
162
|
+
try {
|
|
163
|
+
try {
|
|
164
|
+
await controlClient.stopDaemon();
|
|
165
|
+
console.log(chalk.gray("Stopped daemon"));
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
if (node_fs.existsSync(happyDir)) {
|
|
169
|
+
node_fs.rmSync(happyDir, { recursive: true, force: true });
|
|
170
|
+
}
|
|
171
|
+
console.log(chalk.green("\u2713 Successfully logged out"));
|
|
172
|
+
console.log(chalk.gray(' Run "very-happy auth login" to authenticate again'));
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throw new Error(`Failed to logout: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
console.log(chalk.blue("Logout cancelled"));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function handleAuthStatus() {
|
|
181
|
+
const credentials = await persistence.readCredentials();
|
|
182
|
+
const settings = await persistence.readSettings();
|
|
183
|
+
console.log(chalk.bold("\nAuthentication Status\n"));
|
|
184
|
+
if (!credentials) {
|
|
185
|
+
console.log(chalk.red("\u2717 Not authenticated"));
|
|
186
|
+
console.log(chalk.gray(' Run "very-happy auth login" to authenticate'));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const relayProblem = authRelay.credentialRelayProblem(credentials.authServerUrl, configuration.configuration.serverUrl);
|
|
190
|
+
if (relayProblem) {
|
|
191
|
+
console.log(chalk.red(`\u2717 Credentials found, but not valid for the configured relay: ${relayProblem}`));
|
|
192
|
+
console.log(chalk.gray(" Use another HAPPY_HOME_DIR or run 'very-happy auth login --force'."));
|
|
193
|
+
} else {
|
|
194
|
+
console.log(chalk.green("\u2713 Authenticated to the configured relay"));
|
|
195
|
+
console.log(chalk.gray(` Relay: ${credentials.authServerUrl ?? DEFAULT_SERVER_URL}`));
|
|
196
|
+
}
|
|
197
|
+
const machineIdentity = localMachineIdentityStatus(settings?.machineId);
|
|
198
|
+
if (machineIdentity.configured) {
|
|
199
|
+
console.log(chalk.green(`\u2713 ${machineIdentity.label}`));
|
|
200
|
+
console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
|
|
201
|
+
console.log(chalk.gray(` Host: ${os.hostname()}`));
|
|
202
|
+
} else {
|
|
203
|
+
console.log(chalk.yellow(`\u26A0\uFE0F ${machineIdentity.label}`));
|
|
204
|
+
console.log(chalk.gray(` ${machineIdentity.nextStep}`));
|
|
205
|
+
}
|
|
206
|
+
console.log(chalk.gray(`
|
|
207
|
+
Data directory: ${configuration.configuration.happyHomeDir}`));
|
|
208
|
+
try {
|
|
209
|
+
const running = await controlClient.checkIfDaemonRunningAndCleanupStaleState();
|
|
210
|
+
if (running) {
|
|
211
|
+
console.log(chalk.green("\u2713 Daemon running"));
|
|
212
|
+
} else {
|
|
213
|
+
console.log(chalk.gray("\u2717 Daemon not running"));
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
console.log(chalk.gray("\u2717 Daemon not running"));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
exports.handleAuthCommand = handleAuthCommand;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var node_crypto = require('node:crypto');
|
|
4
|
+
var tweetnacl = require('tweetnacl');
|
|
5
|
+
|
|
6
|
+
function encodeBase64(buffer, variant = "base64") {
|
|
7
|
+
if (variant === "base64url") {
|
|
8
|
+
return encodeBase64Url(buffer);
|
|
9
|
+
}
|
|
10
|
+
return Buffer.from(buffer).toString("base64");
|
|
11
|
+
}
|
|
12
|
+
function encodeBase64Url(buffer) {
|
|
13
|
+
return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
14
|
+
}
|
|
15
|
+
function decodeBase64(base64, variant = "base64") {
|
|
16
|
+
if (variant === "base64url") {
|
|
17
|
+
const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
|
|
18
|
+
return new Uint8Array(Buffer.from(base64Standard, "base64"));
|
|
19
|
+
}
|
|
20
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
21
|
+
}
|
|
22
|
+
function getRandomBytes(size) {
|
|
23
|
+
return new Uint8Array(node_crypto.randomBytes(size));
|
|
24
|
+
}
|
|
25
|
+
function libsodiumPublicKeyFromSecretKey(seed) {
|
|
26
|
+
const hashedSeed = new Uint8Array(node_crypto.createHash("sha512").update(seed).digest());
|
|
27
|
+
const secretKey = hashedSeed.slice(0, 32);
|
|
28
|
+
return new Uint8Array(tweetnacl.box.keyPair.fromSecretKey(secretKey).publicKey);
|
|
29
|
+
}
|
|
30
|
+
function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
|
|
31
|
+
const ephemeralKeyPair = tweetnacl.box.keyPair();
|
|
32
|
+
const nonce = getRandomBytes(tweetnacl.box.nonceLength);
|
|
33
|
+
const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
|
|
34
|
+
const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
|
|
35
|
+
result.set(ephemeralKeyPair.publicKey, 0);
|
|
36
|
+
result.set(nonce, ephemeralKeyPair.publicKey.length);
|
|
37
|
+
result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
function encryptLegacy(data, secret) {
|
|
41
|
+
const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
|
|
42
|
+
const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
|
|
43
|
+
const result = new Uint8Array(nonce.length + encrypted.length);
|
|
44
|
+
result.set(nonce);
|
|
45
|
+
result.set(encrypted, nonce.length);
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function decryptLegacy(data, secret) {
|
|
49
|
+
const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
|
|
50
|
+
const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
|
|
51
|
+
const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
|
|
52
|
+
if (!decrypted) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
56
|
+
}
|
|
57
|
+
function decryptBlob(bundle, key) {
|
|
58
|
+
if (bundle.length < tweetnacl.secretbox.nonceLength + 16) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const nonce = bundle.slice(0, tweetnacl.secretbox.nonceLength);
|
|
62
|
+
const ciphertext = bundle.slice(tweetnacl.secretbox.nonceLength);
|
|
63
|
+
const decrypted = tweetnacl.secretbox.open(ciphertext, nonce, key);
|
|
64
|
+
if (!decrypted) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return new Uint8Array(decrypted);
|
|
68
|
+
}
|
|
69
|
+
function encryptWithDataKey(data, dataKey) {
|
|
70
|
+
const nonce = getRandomBytes(12);
|
|
71
|
+
const cipher = node_crypto.createCipheriv("aes-256-gcm", dataKey, nonce);
|
|
72
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(data));
|
|
73
|
+
const encrypted = Buffer.concat([
|
|
74
|
+
cipher.update(plaintext),
|
|
75
|
+
cipher.final()
|
|
76
|
+
]);
|
|
77
|
+
const authTag = cipher.getAuthTag();
|
|
78
|
+
const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
|
|
79
|
+
bundle.set([0], 0);
|
|
80
|
+
bundle.set(nonce, 1);
|
|
81
|
+
bundle.set(new Uint8Array(encrypted), 13);
|
|
82
|
+
bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
|
|
83
|
+
return bundle;
|
|
84
|
+
}
|
|
85
|
+
function decryptWithDataKey(bundle, dataKey) {
|
|
86
|
+
if (bundle.length < 1) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (bundle[0] !== 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (bundle.length < 12 + 16 + 1) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const nonce = bundle.slice(1, 13);
|
|
96
|
+
const authTag = bundle.slice(bundle.length - 16);
|
|
97
|
+
const ciphertext = bundle.slice(13, bundle.length - 16);
|
|
98
|
+
try {
|
|
99
|
+
const decipher = node_crypto.createDecipheriv("aes-256-gcm", dataKey, nonce);
|
|
100
|
+
decipher.setAuthTag(authTag);
|
|
101
|
+
const decrypted = Buffer.concat([
|
|
102
|
+
decipher.update(ciphertext),
|
|
103
|
+
decipher.final()
|
|
104
|
+
]);
|
|
105
|
+
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function encrypt(key, variant, data) {
|
|
111
|
+
if (variant === "legacy") {
|
|
112
|
+
return encryptLegacy(data, key);
|
|
113
|
+
} else {
|
|
114
|
+
return encryptWithDataKey(data, key);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function decrypt(key, variant, data) {
|
|
118
|
+
if (variant === "legacy") {
|
|
119
|
+
return decryptLegacy(data, key);
|
|
120
|
+
} else {
|
|
121
|
+
return decryptWithDataKey(data, key);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const DEFAULT_SERVER_URL = "https://veryhappy.dev";
|
|
126
|
+
const LEGACY_CLOUD_SERVER_URL = "https://happy.mereith.com";
|
|
127
|
+
const CLOUD_SERVER_URLS = [DEFAULT_SERVER_URL, LEGACY_CLOUD_SERVER_URL];
|
|
128
|
+
function sameRelay(left, right) {
|
|
129
|
+
try {
|
|
130
|
+
return new URL(left).origin === new URL(right).origin;
|
|
131
|
+
} catch {
|
|
132
|
+
return left.replace(/\/+$/, "") === right.replace(/\/+$/, "");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function credentialRelayProblem(credentialServerUrl, configuredServerUrl) {
|
|
136
|
+
if (credentialServerUrl) {
|
|
137
|
+
return sameRelay(credentialServerUrl, configuredServerUrl) ? void 0 : `Credentials belong to ${credentialServerUrl}, but the active relay is ${configuredServerUrl}.`;
|
|
138
|
+
}
|
|
139
|
+
if (CLOUD_SERVER_URLS.some((cloudUrl) => sameRelay(configuredServerUrl, cloudUrl))) {
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
142
|
+
return `These credentials predate relay tracking, so they cannot be safely reused with ${configuredServerUrl}.`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
exports.credentialRelayProblem = credentialRelayProblem;
|
|
146
|
+
exports.decodeBase64 = decodeBase64;
|
|
147
|
+
exports.decrypt = decrypt;
|
|
148
|
+
exports.decryptBlob = decryptBlob;
|
|
149
|
+
exports.decryptLegacy = decryptLegacy;
|
|
150
|
+
exports.decryptWithDataKey = decryptWithDataKey;
|
|
151
|
+
exports.encodeBase64 = encodeBase64;
|
|
152
|
+
exports.encodeBase64Url = encodeBase64Url;
|
|
153
|
+
exports.encrypt = encrypt;
|
|
154
|
+
exports.getRandomBytes = getRandomBytes;
|
|
155
|
+
exports.libsodiumEncryptForPublicKey = libsodiumEncryptForPublicKey;
|
|
156
|
+
exports.libsodiumPublicKeyFromSecretKey = libsodiumPublicKeyFromSecretKey;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { randomBytes, createCipheriv, createDecipheriv, createHash } from 'node:crypto';
|
|
2
|
+
import tweetnacl from 'tweetnacl';
|
|
3
|
+
|
|
4
|
+
function encodeBase64(buffer, variant = "base64") {
|
|
5
|
+
if (variant === "base64url") {
|
|
6
|
+
return encodeBase64Url(buffer);
|
|
7
|
+
}
|
|
8
|
+
return Buffer.from(buffer).toString("base64");
|
|
9
|
+
}
|
|
10
|
+
function encodeBase64Url(buffer) {
|
|
11
|
+
return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
12
|
+
}
|
|
13
|
+
function decodeBase64(base64, variant = "base64") {
|
|
14
|
+
if (variant === "base64url") {
|
|
15
|
+
const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
|
|
16
|
+
return new Uint8Array(Buffer.from(base64Standard, "base64"));
|
|
17
|
+
}
|
|
18
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
19
|
+
}
|
|
20
|
+
function getRandomBytes(size) {
|
|
21
|
+
return new Uint8Array(randomBytes(size));
|
|
22
|
+
}
|
|
23
|
+
function libsodiumPublicKeyFromSecretKey(seed) {
|
|
24
|
+
const hashedSeed = new Uint8Array(createHash("sha512").update(seed).digest());
|
|
25
|
+
const secretKey = hashedSeed.slice(0, 32);
|
|
26
|
+
return new Uint8Array(tweetnacl.box.keyPair.fromSecretKey(secretKey).publicKey);
|
|
27
|
+
}
|
|
28
|
+
function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
|
|
29
|
+
const ephemeralKeyPair = tweetnacl.box.keyPair();
|
|
30
|
+
const nonce = getRandomBytes(tweetnacl.box.nonceLength);
|
|
31
|
+
const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
|
|
32
|
+
const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
|
|
33
|
+
result.set(ephemeralKeyPair.publicKey, 0);
|
|
34
|
+
result.set(nonce, ephemeralKeyPair.publicKey.length);
|
|
35
|
+
result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
function encryptLegacy(data, secret) {
|
|
39
|
+
const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
|
|
40
|
+
const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
|
|
41
|
+
const result = new Uint8Array(nonce.length + encrypted.length);
|
|
42
|
+
result.set(nonce);
|
|
43
|
+
result.set(encrypted, nonce.length);
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
function decryptLegacy(data, secret) {
|
|
47
|
+
const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
|
|
48
|
+
const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
|
|
49
|
+
const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
|
|
50
|
+
if (!decrypted) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
54
|
+
}
|
|
55
|
+
function decryptBlob(bundle, key) {
|
|
56
|
+
if (bundle.length < tweetnacl.secretbox.nonceLength + 16) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const nonce = bundle.slice(0, tweetnacl.secretbox.nonceLength);
|
|
60
|
+
const ciphertext = bundle.slice(tweetnacl.secretbox.nonceLength);
|
|
61
|
+
const decrypted = tweetnacl.secretbox.open(ciphertext, nonce, key);
|
|
62
|
+
if (!decrypted) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return new Uint8Array(decrypted);
|
|
66
|
+
}
|
|
67
|
+
function encryptWithDataKey(data, dataKey) {
|
|
68
|
+
const nonce = getRandomBytes(12);
|
|
69
|
+
const cipher = createCipheriv("aes-256-gcm", dataKey, nonce);
|
|
70
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(data));
|
|
71
|
+
const encrypted = Buffer.concat([
|
|
72
|
+
cipher.update(plaintext),
|
|
73
|
+
cipher.final()
|
|
74
|
+
]);
|
|
75
|
+
const authTag = cipher.getAuthTag();
|
|
76
|
+
const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
|
|
77
|
+
bundle.set([0], 0);
|
|
78
|
+
bundle.set(nonce, 1);
|
|
79
|
+
bundle.set(new Uint8Array(encrypted), 13);
|
|
80
|
+
bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
|
|
81
|
+
return bundle;
|
|
82
|
+
}
|
|
83
|
+
function decryptWithDataKey(bundle, dataKey) {
|
|
84
|
+
if (bundle.length < 1) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (bundle[0] !== 0) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
if (bundle.length < 12 + 16 + 1) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const nonce = bundle.slice(1, 13);
|
|
94
|
+
const authTag = bundle.slice(bundle.length - 16);
|
|
95
|
+
const ciphertext = bundle.slice(13, bundle.length - 16);
|
|
96
|
+
try {
|
|
97
|
+
const decipher = createDecipheriv("aes-256-gcm", dataKey, nonce);
|
|
98
|
+
decipher.setAuthTag(authTag);
|
|
99
|
+
const decrypted = Buffer.concat([
|
|
100
|
+
decipher.update(ciphertext),
|
|
101
|
+
decipher.final()
|
|
102
|
+
]);
|
|
103
|
+
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function encrypt(key, variant, data) {
|
|
109
|
+
if (variant === "legacy") {
|
|
110
|
+
return encryptLegacy(data, key);
|
|
111
|
+
} else {
|
|
112
|
+
return encryptWithDataKey(data, key);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function decrypt(key, variant, data) {
|
|
116
|
+
if (variant === "legacy") {
|
|
117
|
+
return decryptLegacy(data, key);
|
|
118
|
+
} else {
|
|
119
|
+
return decryptWithDataKey(data, key);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const DEFAULT_SERVER_URL = "https://veryhappy.dev";
|
|
124
|
+
const LEGACY_CLOUD_SERVER_URL = "https://happy.mereith.com";
|
|
125
|
+
const CLOUD_SERVER_URLS = [DEFAULT_SERVER_URL, LEGACY_CLOUD_SERVER_URL];
|
|
126
|
+
function sameRelay(left, right) {
|
|
127
|
+
try {
|
|
128
|
+
return new URL(left).origin === new URL(right).origin;
|
|
129
|
+
} catch {
|
|
130
|
+
return left.replace(/\/+$/, "") === right.replace(/\/+$/, "");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function credentialRelayProblem(credentialServerUrl, configuredServerUrl) {
|
|
134
|
+
if (credentialServerUrl) {
|
|
135
|
+
return sameRelay(credentialServerUrl, configuredServerUrl) ? void 0 : `Credentials belong to ${credentialServerUrl}, but the active relay is ${configuredServerUrl}.`;
|
|
136
|
+
}
|
|
137
|
+
if (CLOUD_SERVER_URLS.some((cloudUrl) => sameRelay(configuredServerUrl, cloudUrl))) {
|
|
138
|
+
return void 0;
|
|
139
|
+
}
|
|
140
|
+
return `These credentials predate relay tracking, so they cannot be safely reused with ${configuredServerUrl}.`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export { decryptWithDataKey as a, decryptLegacy as b, credentialRelayProblem as c, decodeBase64 as d, encodeBase64 as e, encodeBase64Url as f, encrypt as g, decrypt as h, decryptBlob as i, libsodiumPublicKeyFromSecretKey as j, getRandomBytes as k, libsodiumEncryptForPublicKey as l };
|