happy-coder 0.10.0-3 → 0.10.0-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/{index-tqOLc1Il.cjs → index-67rskwL7.cjs} +61 -84
- package/dist/{index-DPVbp4Yx.mjs → index-Dw96QD4T.mjs} +60 -83
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +42 -110
- package/dist/lib.d.mts +42 -110
- package/dist/lib.mjs +1 -1
- package/dist/{runCodex-BxLD6H6G.cjs → runCodex-BLNf5zb1.cjs} +3 -3
- package/dist/{runCodex-C07HQlsW.mjs → runCodex-BNH8w4O9.mjs} +3 -3
- package/dist/{types-xds_c-JJ.mjs → types-2wHnX7UW.mjs} +208 -164
- package/dist/{types-CsJGQvQ3.cjs → types-BcDnTXMg.cjs} +209 -164
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -21,6 +21,15 @@ This will:
|
|
|
21
21
|
2. Display a QR code to connect from your mobile device
|
|
22
22
|
3. Allow real-time session sharing between Claude Code and your mobile app
|
|
23
23
|
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
- `happy auth` – Manage authentication
|
|
27
|
+
- `happy codex` – Start Codex mode
|
|
28
|
+
- `happy connect` – Store AI vendor API keys in Happy cloud
|
|
29
|
+
- `happy notify` – Send a push notification to your devices
|
|
30
|
+
- `happy daemon` – Manage background service
|
|
31
|
+
- `happy doctor` – System diagnostics & troubleshooting
|
|
32
|
+
|
|
24
33
|
## Options
|
|
25
34
|
|
|
26
35
|
- `-h, --help` - Show help
|
|
@@ -40,4 +49,4 @@ This will:
|
|
|
40
49
|
|
|
41
50
|
## License
|
|
42
51
|
|
|
43
|
-
MIT
|
|
52
|
+
MIT
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
4
|
var os = require('node:os');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
|
-
var types = require('./types-
|
|
6
|
+
var types = require('./types-BcDnTXMg.cjs');
|
|
7
7
|
var node_child_process = require('node:child_process');
|
|
8
8
|
var node_path = require('node:path');
|
|
9
9
|
var node_readline = require('node:readline');
|
|
@@ -923,7 +923,7 @@ class AbortError extends Error {
|
|
|
923
923
|
}
|
|
924
924
|
}
|
|
925
925
|
|
|
926
|
-
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
926
|
+
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-67rskwL7.cjs', document.baseURI).href)));
|
|
927
927
|
const __dirname$1 = node_path.join(__filename$1, "..");
|
|
928
928
|
function getDefaultClaudeCodePath() {
|
|
929
929
|
return node_path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
|
|
@@ -3684,7 +3684,7 @@ function displayQRCode(url) {
|
|
|
3684
3684
|
|
|
3685
3685
|
function generateWebAuthUrl(publicKey) {
|
|
3686
3686
|
const publicKeyBase64 = types.encodeBase64(publicKey, "base64url");
|
|
3687
|
-
return
|
|
3687
|
+
return `${types.configuration.webappUrl}/terminal/connect#key=${publicKeyBase64}`;
|
|
3688
3688
|
}
|
|
3689
3689
|
|
|
3690
3690
|
async function openBrowser(url) {
|
|
@@ -3751,7 +3751,8 @@ async function doAuth() {
|
|
|
3751
3751
|
console.log(`[AUTH DEBUG] Sending auth request to: ${types.configuration.serverUrl}/v1/auth/request`);
|
|
3752
3752
|
console.log(`[AUTH DEBUG] Public key: ${types.encodeBase64(keypair.publicKey).substring(0, 20)}...`);
|
|
3753
3753
|
await axios.post(`${types.configuration.serverUrl}/v1/auth/request`, {
|
|
3754
|
-
publicKey: types.encodeBase64(keypair.publicKey)
|
|
3754
|
+
publicKey: types.encodeBase64(keypair.publicKey),
|
|
3755
|
+
supportsV2: true
|
|
3755
3756
|
});
|
|
3756
3757
|
console.log(`[AUTH DEBUG] Auth request sent successfully`);
|
|
3757
3758
|
} catch (error) {
|
|
@@ -3830,20 +3831,50 @@ async function waitForAuthentication(keypair) {
|
|
|
3830
3831
|
while (!cancelled) {
|
|
3831
3832
|
try {
|
|
3832
3833
|
const response = await axios.post(`${types.configuration.serverUrl}/v1/auth/request`, {
|
|
3833
|
-
publicKey: types.encodeBase64(keypair.publicKey)
|
|
3834
|
+
publicKey: types.encodeBase64(keypair.publicKey),
|
|
3835
|
+
supportsV2: true
|
|
3834
3836
|
});
|
|
3835
3837
|
if (response.data.state === "authorized") {
|
|
3836
3838
|
let token = response.data.token;
|
|
3837
3839
|
let r = types.decodeBase64(response.data.response);
|
|
3838
3840
|
let decrypted = decryptWithEphemeralKey(r, keypair.secretKey);
|
|
3839
3841
|
if (decrypted) {
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3842
|
+
if (decrypted.length === 32) {
|
|
3843
|
+
const credentials = {
|
|
3844
|
+
secret: decrypted,
|
|
3845
|
+
token
|
|
3846
|
+
};
|
|
3847
|
+
await types.writeCredentialsLegacy(credentials);
|
|
3848
|
+
console.log("\n\n\u2713 Authentication successful\n");
|
|
3849
|
+
return {
|
|
3850
|
+
encryption: {
|
|
3851
|
+
type: "legacy",
|
|
3852
|
+
secret: decrypted
|
|
3853
|
+
},
|
|
3854
|
+
token
|
|
3855
|
+
};
|
|
3856
|
+
} else {
|
|
3857
|
+
if (decrypted[0] === 0) {
|
|
3858
|
+
const credentials = {
|
|
3859
|
+
publicKey: decrypted.slice(1, 33),
|
|
3860
|
+
machineKey: node_crypto.randomBytes(32),
|
|
3861
|
+
token
|
|
3862
|
+
};
|
|
3863
|
+
await types.writeCredentialsDataKey(credentials);
|
|
3864
|
+
console.log("\n\n\u2713 Authentication successful\n");
|
|
3865
|
+
return {
|
|
3866
|
+
encryption: {
|
|
3867
|
+
type: "dataKey",
|
|
3868
|
+
publicKey: credentials.publicKey,
|
|
3869
|
+
machineKey: credentials.machineKey
|
|
3870
|
+
},
|
|
3871
|
+
token
|
|
3872
|
+
};
|
|
3873
|
+
} else {
|
|
3874
|
+
console.log("\n\nFailed to decrypt response. Please try again.");
|
|
3875
|
+
return null;
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3847
3878
|
} else {
|
|
3848
3879
|
console.log("\n\nFailed to decrypt response. Please try again.");
|
|
3849
3880
|
return null;
|
|
@@ -3875,6 +3906,7 @@ function decryptWithEphemeralKey(encryptedBundle, recipientSecretKey) {
|
|
|
3875
3906
|
async function authAndSetupMachineIfNeeded() {
|
|
3876
3907
|
types.logger.debug("[AUTH] Starting auth and machine setup...");
|
|
3877
3908
|
let credentials = await types.readCredentials();
|
|
3909
|
+
let newAuth = false;
|
|
3878
3910
|
if (!credentials) {
|
|
3879
3911
|
types.logger.debug("[AUTH] No credentials found, starting authentication flow...");
|
|
3880
3912
|
const authResult = await doAuth();
|
|
@@ -3882,13 +3914,12 @@ async function authAndSetupMachineIfNeeded() {
|
|
|
3882
3914
|
throw new Error("Authentication failed or was cancelled");
|
|
3883
3915
|
}
|
|
3884
3916
|
credentials = authResult;
|
|
3917
|
+
newAuth = true;
|
|
3885
3918
|
} else {
|
|
3886
3919
|
types.logger.debug("[AUTH] Using existing credentials");
|
|
3887
3920
|
}
|
|
3888
3921
|
const settings = await types.updateSettings(async (s) => {
|
|
3889
|
-
if (!s.machineId) {
|
|
3890
|
-
const newMachineId = node_crypto.randomUUID();
|
|
3891
|
-
types.logger.debug(`[AUTH] No machine ID found, generating new one: ${newMachineId}; We will not create machine on startup since we don't have api client intialized`);
|
|
3922
|
+
if (newAuth || !s.machineId) {
|
|
3892
3923
|
return {
|
|
3893
3924
|
...s,
|
|
3894
3925
|
machineId: node_crypto.randomUUID()
|
|
@@ -4368,7 +4399,7 @@ async function startDaemon() {
|
|
|
4368
4399
|
httpPort: controlPort,
|
|
4369
4400
|
startedAt: Date.now()
|
|
4370
4401
|
};
|
|
4371
|
-
const api =
|
|
4402
|
+
const api = await types.ApiClient.create(credentials);
|
|
4372
4403
|
const machine = await api.getOrCreateMachine({
|
|
4373
4404
|
machineId,
|
|
4374
4405
|
metadata: initialMachineMetadata,
|
|
@@ -4572,7 +4603,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
4572
4603
|
types.logger.debug("Daemon spawn requested with local mode - forcing remote mode");
|
|
4573
4604
|
options.startingMode = "remote";
|
|
4574
4605
|
}
|
|
4575
|
-
const api =
|
|
4606
|
+
const api = await types.ApiClient.create(credentials);
|
|
4576
4607
|
let state = {};
|
|
4577
4608
|
const settings = await types.readSettings();
|
|
4578
4609
|
let machineId = settings?.machineId;
|
|
@@ -4942,33 +4973,6 @@ async function uninstall() {
|
|
|
4942
4973
|
await uninstall$1();
|
|
4943
4974
|
}
|
|
4944
4975
|
|
|
4945
|
-
const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
4946
|
-
function bytesToBase32(bytes) {
|
|
4947
|
-
let result = "";
|
|
4948
|
-
let buffer = 0;
|
|
4949
|
-
let bufferLength = 0;
|
|
4950
|
-
for (const byte of bytes) {
|
|
4951
|
-
buffer = buffer << 8 | byte;
|
|
4952
|
-
bufferLength += 8;
|
|
4953
|
-
while (bufferLength >= 5) {
|
|
4954
|
-
bufferLength -= 5;
|
|
4955
|
-
result += BASE32_ALPHABET[buffer >> bufferLength & 31];
|
|
4956
|
-
}
|
|
4957
|
-
}
|
|
4958
|
-
if (bufferLength > 0) {
|
|
4959
|
-
result += BASE32_ALPHABET[buffer << 5 - bufferLength & 31];
|
|
4960
|
-
}
|
|
4961
|
-
return result;
|
|
4962
|
-
}
|
|
4963
|
-
function formatSecretKeyForBackup(secretBytes) {
|
|
4964
|
-
const base32 = bytesToBase32(secretBytes);
|
|
4965
|
-
const groups = [];
|
|
4966
|
-
for (let i = 0; i < base32.length; i += 5) {
|
|
4967
|
-
groups.push(base32.slice(i, i + 5));
|
|
4968
|
-
}
|
|
4969
|
-
return groups.join("-");
|
|
4970
|
-
}
|
|
4971
|
-
|
|
4972
4976
|
async function handleAuthCommand(args) {
|
|
4973
4977
|
const subcommand = args[0];
|
|
4974
4978
|
if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
@@ -4982,9 +4986,9 @@ async function handleAuthCommand(args) {
|
|
|
4982
4986
|
case "logout":
|
|
4983
4987
|
await handleAuthLogout();
|
|
4984
4988
|
break;
|
|
4985
|
-
case
|
|
4986
|
-
|
|
4987
|
-
|
|
4989
|
+
// case 'backup':
|
|
4990
|
+
// await handleAuthShowBackup();
|
|
4991
|
+
// break;
|
|
4988
4992
|
case "status":
|
|
4989
4993
|
await handleAuthStatus();
|
|
4990
4994
|
break;
|
|
@@ -5007,11 +5011,6 @@ ${chalk.bold("Usage:")}
|
|
|
5007
5011
|
|
|
5008
5012
|
${chalk.bold("Options:")}
|
|
5009
5013
|
--force Clear credentials, machine ID, and stop daemon before re-auth
|
|
5010
|
-
|
|
5011
|
-
${chalk.bold("Notes:")}
|
|
5012
|
-
\u2022 Use 'auth login --force' when you need to re-register your machine
|
|
5013
|
-
\u2022 'auth show-backup' displays the key format expected by mobile/web clients
|
|
5014
|
-
\u2022 The backup key allows linking multiple devices to the same account
|
|
5015
5014
|
`);
|
|
5016
5015
|
}
|
|
5017
5016
|
async function handleAuthLogin(args) {
|
|
@@ -5096,31 +5095,6 @@ async function handleAuthLogout() {
|
|
|
5096
5095
|
console.log(chalk.blue("Logout cancelled"));
|
|
5097
5096
|
}
|
|
5098
5097
|
}
|
|
5099
|
-
async function handleAuthShowBackup() {
|
|
5100
|
-
const credentials = await types.readCredentials();
|
|
5101
|
-
const settings = await types.readSettings();
|
|
5102
|
-
if (!credentials) {
|
|
5103
|
-
console.log(chalk.yellow("Not authenticated"));
|
|
5104
|
-
console.log(chalk.gray('Run "happy auth login" to authenticate first'));
|
|
5105
|
-
return;
|
|
5106
|
-
}
|
|
5107
|
-
const formattedBackupKey = formatSecretKeyForBackup(credentials.secret);
|
|
5108
|
-
console.log(chalk.bold("\n\u{1F4F1} Backup Key\n"));
|
|
5109
|
-
console.log(chalk.cyan("Your backup key:"));
|
|
5110
|
-
console.log(chalk.bold(formattedBackupKey));
|
|
5111
|
-
console.log("");
|
|
5112
|
-
console.log(chalk.cyan("Machine Information:"));
|
|
5113
|
-
console.log(` Machine ID: ${settings?.machineId || "not set"}`);
|
|
5114
|
-
console.log(` Host: ${os.hostname()}`);
|
|
5115
|
-
console.log("");
|
|
5116
|
-
console.log(chalk.bold("How to use this backup key:"));
|
|
5117
|
-
console.log(chalk.gray("\u2022 In Happy mobile app: Go to restore/link device and enter this key"));
|
|
5118
|
-
console.log(chalk.gray("\u2022 This key format matches what the mobile app expects"));
|
|
5119
|
-
console.log(chalk.gray("\u2022 You can type it with or without dashes - the app will normalize it"));
|
|
5120
|
-
console.log(chalk.gray("\u2022 Common typos (0\u2192O, 1\u2192I) are automatically corrected"));
|
|
5121
|
-
console.log("");
|
|
5122
|
-
console.log(chalk.yellow("\u26A0\uFE0F Keep this key secure - it provides full access to your account"));
|
|
5123
|
-
}
|
|
5124
5098
|
async function handleAuthStatus() {
|
|
5125
5099
|
const credentials = await types.readCredentials();
|
|
5126
5100
|
const settings = await types.readSettings();
|
|
@@ -5657,7 +5631,7 @@ ${chalk.bold("happy connect")} - Connect AI vendor API keys to Happy cloud
|
|
|
5657
5631
|
|
|
5658
5632
|
${chalk.bold("Usage:")}
|
|
5659
5633
|
happy connect codex Store your Codex API key in Happy cloud
|
|
5660
|
-
happy connect
|
|
5634
|
+
happy connect claude Store your Anthropic API key in Happy cloud
|
|
5661
5635
|
happy connect gemini Store your Gemini API key in Happy cloud
|
|
5662
5636
|
happy connect help Show this help message
|
|
5663
5637
|
|
|
@@ -5668,7 +5642,7 @@ ${chalk.bold("Description:")}
|
|
|
5668
5642
|
|
|
5669
5643
|
${chalk.bold("Examples:")}
|
|
5670
5644
|
happy connect codex
|
|
5671
|
-
happy connect
|
|
5645
|
+
happy connect claude
|
|
5672
5646
|
happy connect gemini
|
|
5673
5647
|
|
|
5674
5648
|
${chalk.bold("Notes:")}
|
|
@@ -5687,7 +5661,7 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
5687
5661
|
console.log(chalk.gray(' Please run "happy auth login" first'));
|
|
5688
5662
|
process.exit(1);
|
|
5689
5663
|
}
|
|
5690
|
-
const api =
|
|
5664
|
+
const api = await types.ApiClient.create(credentials);
|
|
5691
5665
|
if (vendor === "codex") {
|
|
5692
5666
|
console.log("\u{1F680} Registering Codex token with server");
|
|
5693
5667
|
const codexAuthTokens = await authenticateCodex();
|
|
@@ -5752,11 +5726,11 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
5752
5726
|
return;
|
|
5753
5727
|
} else if (subcommand === "codex") {
|
|
5754
5728
|
try {
|
|
5755
|
-
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-
|
|
5729
|
+
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-BLNf5zb1.cjs'); });
|
|
5756
5730
|
const {
|
|
5757
5731
|
credentials
|
|
5758
5732
|
} = await authAndSetupMachineIfNeeded();
|
|
5759
|
-
await runCodex(credentials);
|
|
5733
|
+
await runCodex({ credentials });
|
|
5760
5734
|
} catch (error) {
|
|
5761
5735
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
5762
5736
|
if (process.env.DEBUG) {
|
|
@@ -5924,6 +5898,9 @@ ${chalk.bold("happy")} - Claude Code On the Go
|
|
|
5924
5898
|
${chalk.bold("Usage:")}
|
|
5925
5899
|
happy [options] Start Claude with mobile control
|
|
5926
5900
|
happy auth Manage authentication
|
|
5901
|
+
happy codex Start Codex mode
|
|
5902
|
+
happy connect Connect AI vendor API keys
|
|
5903
|
+
happy notify Send push notification
|
|
5927
5904
|
happy daemon Manage background service that allows
|
|
5928
5905
|
to spawn new sessions away from your computer
|
|
5929
5906
|
happy doctor System diagnostics & troubleshooting
|
|
@@ -6022,12 +5999,12 @@ ${chalk.bold("Examples:")}
|
|
|
6022
5999
|
}
|
|
6023
6000
|
let credentials = await types.readCredentials();
|
|
6024
6001
|
if (!credentials) {
|
|
6025
|
-
console.error(chalk.red('Error: Not authenticated. Please run "happy
|
|
6002
|
+
console.error(chalk.red('Error: Not authenticated. Please run "happy auth login" first.'));
|
|
6026
6003
|
process.exit(1);
|
|
6027
6004
|
}
|
|
6028
6005
|
console.log(chalk.blue("\u{1F4F1} Sending push notification..."));
|
|
6029
6006
|
try {
|
|
6030
|
-
const api =
|
|
6007
|
+
const api = await types.ApiClient.create(credentials);
|
|
6031
6008
|
const notificationTitle = title || "Happy";
|
|
6032
6009
|
api.push().sendToAllDevices(
|
|
6033
6010
|
notificationTitle,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import os$1, { homedir } from 'node:os';
|
|
3
3
|
import { randomUUID, randomBytes } from 'node:crypto';
|
|
4
|
-
import { l as logger, p as projectPath, d as backoff, e as delay, R as RawJSONLinesSchema, f as AsyncLock, g as readDaemonState, h as clearDaemonState, b as packageJson, c as configuration, r as readSettings, i as readCredentials, j as encodeBase64, u as updateSettings, k as encodeBase64Url, m as decodeBase64, w as
|
|
4
|
+
import { l as logger, p as projectPath, d as backoff, e as delay, R as RawJSONLinesSchema, f as AsyncLock, g as readDaemonState, h as clearDaemonState, b as packageJson, c as configuration, r as readSettings, i as readCredentials, j as encodeBase64, u as updateSettings, k as encodeBase64Url, m as decodeBase64, w as writeCredentialsLegacy, n as writeCredentialsDataKey, o as acquireDaemonLock, q as writeDaemonState, A as ApiClient, s as releaseDaemonLock, t as clearCredentials, v as clearMachineId, x as getLatestDaemonLog } from './types-2wHnX7UW.mjs';
|
|
5
5
|
import { spawn, execSync, execFileSync } from 'node:child_process';
|
|
6
6
|
import { resolve, join } from 'node:path';
|
|
7
7
|
import { createInterface } from 'node:readline';
|
|
@@ -3681,7 +3681,7 @@ function displayQRCode(url) {
|
|
|
3681
3681
|
|
|
3682
3682
|
function generateWebAuthUrl(publicKey) {
|
|
3683
3683
|
const publicKeyBase64 = encodeBase64(publicKey, "base64url");
|
|
3684
|
-
return
|
|
3684
|
+
return `${configuration.webappUrl}/terminal/connect#key=${publicKeyBase64}`;
|
|
3685
3685
|
}
|
|
3686
3686
|
|
|
3687
3687
|
async function openBrowser(url) {
|
|
@@ -3748,7 +3748,8 @@ async function doAuth() {
|
|
|
3748
3748
|
console.log(`[AUTH DEBUG] Sending auth request to: ${configuration.serverUrl}/v1/auth/request`);
|
|
3749
3749
|
console.log(`[AUTH DEBUG] Public key: ${encodeBase64(keypair.publicKey).substring(0, 20)}...`);
|
|
3750
3750
|
await axios.post(`${configuration.serverUrl}/v1/auth/request`, {
|
|
3751
|
-
publicKey: encodeBase64(keypair.publicKey)
|
|
3751
|
+
publicKey: encodeBase64(keypair.publicKey),
|
|
3752
|
+
supportsV2: true
|
|
3752
3753
|
});
|
|
3753
3754
|
console.log(`[AUTH DEBUG] Auth request sent successfully`);
|
|
3754
3755
|
} catch (error) {
|
|
@@ -3827,20 +3828,50 @@ async function waitForAuthentication(keypair) {
|
|
|
3827
3828
|
while (!cancelled) {
|
|
3828
3829
|
try {
|
|
3829
3830
|
const response = await axios.post(`${configuration.serverUrl}/v1/auth/request`, {
|
|
3830
|
-
publicKey: encodeBase64(keypair.publicKey)
|
|
3831
|
+
publicKey: encodeBase64(keypair.publicKey),
|
|
3832
|
+
supportsV2: true
|
|
3831
3833
|
});
|
|
3832
3834
|
if (response.data.state === "authorized") {
|
|
3833
3835
|
let token = response.data.token;
|
|
3834
3836
|
let r = decodeBase64(response.data.response);
|
|
3835
3837
|
let decrypted = decryptWithEphemeralKey(r, keypair.secretKey);
|
|
3836
3838
|
if (decrypted) {
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3839
|
+
if (decrypted.length === 32) {
|
|
3840
|
+
const credentials = {
|
|
3841
|
+
secret: decrypted,
|
|
3842
|
+
token
|
|
3843
|
+
};
|
|
3844
|
+
await writeCredentialsLegacy(credentials);
|
|
3845
|
+
console.log("\n\n\u2713 Authentication successful\n");
|
|
3846
|
+
return {
|
|
3847
|
+
encryption: {
|
|
3848
|
+
type: "legacy",
|
|
3849
|
+
secret: decrypted
|
|
3850
|
+
},
|
|
3851
|
+
token
|
|
3852
|
+
};
|
|
3853
|
+
} else {
|
|
3854
|
+
if (decrypted[0] === 0) {
|
|
3855
|
+
const credentials = {
|
|
3856
|
+
publicKey: decrypted.slice(1, 33),
|
|
3857
|
+
machineKey: randomBytes(32),
|
|
3858
|
+
token
|
|
3859
|
+
};
|
|
3860
|
+
await writeCredentialsDataKey(credentials);
|
|
3861
|
+
console.log("\n\n\u2713 Authentication successful\n");
|
|
3862
|
+
return {
|
|
3863
|
+
encryption: {
|
|
3864
|
+
type: "dataKey",
|
|
3865
|
+
publicKey: credentials.publicKey,
|
|
3866
|
+
machineKey: credentials.machineKey
|
|
3867
|
+
},
|
|
3868
|
+
token
|
|
3869
|
+
};
|
|
3870
|
+
} else {
|
|
3871
|
+
console.log("\n\nFailed to decrypt response. Please try again.");
|
|
3872
|
+
return null;
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3844
3875
|
} else {
|
|
3845
3876
|
console.log("\n\nFailed to decrypt response. Please try again.");
|
|
3846
3877
|
return null;
|
|
@@ -3872,6 +3903,7 @@ function decryptWithEphemeralKey(encryptedBundle, recipientSecretKey) {
|
|
|
3872
3903
|
async function authAndSetupMachineIfNeeded() {
|
|
3873
3904
|
logger.debug("[AUTH] Starting auth and machine setup...");
|
|
3874
3905
|
let credentials = await readCredentials();
|
|
3906
|
+
let newAuth = false;
|
|
3875
3907
|
if (!credentials) {
|
|
3876
3908
|
logger.debug("[AUTH] No credentials found, starting authentication flow...");
|
|
3877
3909
|
const authResult = await doAuth();
|
|
@@ -3879,13 +3911,12 @@ async function authAndSetupMachineIfNeeded() {
|
|
|
3879
3911
|
throw new Error("Authentication failed or was cancelled");
|
|
3880
3912
|
}
|
|
3881
3913
|
credentials = authResult;
|
|
3914
|
+
newAuth = true;
|
|
3882
3915
|
} else {
|
|
3883
3916
|
logger.debug("[AUTH] Using existing credentials");
|
|
3884
3917
|
}
|
|
3885
3918
|
const settings = await updateSettings(async (s) => {
|
|
3886
|
-
if (!s.machineId) {
|
|
3887
|
-
const newMachineId = randomUUID();
|
|
3888
|
-
logger.debug(`[AUTH] No machine ID found, generating new one: ${newMachineId}; We will not create machine on startup since we don't have api client intialized`);
|
|
3919
|
+
if (newAuth || !s.machineId) {
|
|
3889
3920
|
return {
|
|
3890
3921
|
...s,
|
|
3891
3922
|
machineId: randomUUID()
|
|
@@ -4365,7 +4396,7 @@ async function startDaemon() {
|
|
|
4365
4396
|
httpPort: controlPort,
|
|
4366
4397
|
startedAt: Date.now()
|
|
4367
4398
|
};
|
|
4368
|
-
const api =
|
|
4399
|
+
const api = await ApiClient.create(credentials);
|
|
4369
4400
|
const machine = await api.getOrCreateMachine({
|
|
4370
4401
|
machineId,
|
|
4371
4402
|
metadata: initialMachineMetadata,
|
|
@@ -4569,7 +4600,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
4569
4600
|
logger.debug("Daemon spawn requested with local mode - forcing remote mode");
|
|
4570
4601
|
options.startingMode = "remote";
|
|
4571
4602
|
}
|
|
4572
|
-
const api =
|
|
4603
|
+
const api = await ApiClient.create(credentials);
|
|
4573
4604
|
let state = {};
|
|
4574
4605
|
const settings = await readSettings();
|
|
4575
4606
|
let machineId = settings?.machineId;
|
|
@@ -4939,33 +4970,6 @@ async function uninstall() {
|
|
|
4939
4970
|
await uninstall$1();
|
|
4940
4971
|
}
|
|
4941
4972
|
|
|
4942
|
-
const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
4943
|
-
function bytesToBase32(bytes) {
|
|
4944
|
-
let result = "";
|
|
4945
|
-
let buffer = 0;
|
|
4946
|
-
let bufferLength = 0;
|
|
4947
|
-
for (const byte of bytes) {
|
|
4948
|
-
buffer = buffer << 8 | byte;
|
|
4949
|
-
bufferLength += 8;
|
|
4950
|
-
while (bufferLength >= 5) {
|
|
4951
|
-
bufferLength -= 5;
|
|
4952
|
-
result += BASE32_ALPHABET[buffer >> bufferLength & 31];
|
|
4953
|
-
}
|
|
4954
|
-
}
|
|
4955
|
-
if (bufferLength > 0) {
|
|
4956
|
-
result += BASE32_ALPHABET[buffer << 5 - bufferLength & 31];
|
|
4957
|
-
}
|
|
4958
|
-
return result;
|
|
4959
|
-
}
|
|
4960
|
-
function formatSecretKeyForBackup(secretBytes) {
|
|
4961
|
-
const base32 = bytesToBase32(secretBytes);
|
|
4962
|
-
const groups = [];
|
|
4963
|
-
for (let i = 0; i < base32.length; i += 5) {
|
|
4964
|
-
groups.push(base32.slice(i, i + 5));
|
|
4965
|
-
}
|
|
4966
|
-
return groups.join("-");
|
|
4967
|
-
}
|
|
4968
|
-
|
|
4969
4973
|
async function handleAuthCommand(args) {
|
|
4970
4974
|
const subcommand = args[0];
|
|
4971
4975
|
if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
@@ -4979,9 +4983,9 @@ async function handleAuthCommand(args) {
|
|
|
4979
4983
|
case "logout":
|
|
4980
4984
|
await handleAuthLogout();
|
|
4981
4985
|
break;
|
|
4982
|
-
case
|
|
4983
|
-
|
|
4984
|
-
|
|
4986
|
+
// case 'backup':
|
|
4987
|
+
// await handleAuthShowBackup();
|
|
4988
|
+
// break;
|
|
4985
4989
|
case "status":
|
|
4986
4990
|
await handleAuthStatus();
|
|
4987
4991
|
break;
|
|
@@ -5004,11 +5008,6 @@ ${chalk.bold("Usage:")}
|
|
|
5004
5008
|
|
|
5005
5009
|
${chalk.bold("Options:")}
|
|
5006
5010
|
--force Clear credentials, machine ID, and stop daemon before re-auth
|
|
5007
|
-
|
|
5008
|
-
${chalk.bold("Notes:")}
|
|
5009
|
-
\u2022 Use 'auth login --force' when you need to re-register your machine
|
|
5010
|
-
\u2022 'auth show-backup' displays the key format expected by mobile/web clients
|
|
5011
|
-
\u2022 The backup key allows linking multiple devices to the same account
|
|
5012
5011
|
`);
|
|
5013
5012
|
}
|
|
5014
5013
|
async function handleAuthLogin(args) {
|
|
@@ -5093,31 +5092,6 @@ async function handleAuthLogout() {
|
|
|
5093
5092
|
console.log(chalk.blue("Logout cancelled"));
|
|
5094
5093
|
}
|
|
5095
5094
|
}
|
|
5096
|
-
async function handleAuthShowBackup() {
|
|
5097
|
-
const credentials = await readCredentials();
|
|
5098
|
-
const settings = await readSettings();
|
|
5099
|
-
if (!credentials) {
|
|
5100
|
-
console.log(chalk.yellow("Not authenticated"));
|
|
5101
|
-
console.log(chalk.gray('Run "happy auth login" to authenticate first'));
|
|
5102
|
-
return;
|
|
5103
|
-
}
|
|
5104
|
-
const formattedBackupKey = formatSecretKeyForBackup(credentials.secret);
|
|
5105
|
-
console.log(chalk.bold("\n\u{1F4F1} Backup Key\n"));
|
|
5106
|
-
console.log(chalk.cyan("Your backup key:"));
|
|
5107
|
-
console.log(chalk.bold(formattedBackupKey));
|
|
5108
|
-
console.log("");
|
|
5109
|
-
console.log(chalk.cyan("Machine Information:"));
|
|
5110
|
-
console.log(` Machine ID: ${settings?.machineId || "not set"}`);
|
|
5111
|
-
console.log(` Host: ${os$1.hostname()}`);
|
|
5112
|
-
console.log("");
|
|
5113
|
-
console.log(chalk.bold("How to use this backup key:"));
|
|
5114
|
-
console.log(chalk.gray("\u2022 In Happy mobile app: Go to restore/link device and enter this key"));
|
|
5115
|
-
console.log(chalk.gray("\u2022 This key format matches what the mobile app expects"));
|
|
5116
|
-
console.log(chalk.gray("\u2022 You can type it with or without dashes - the app will normalize it"));
|
|
5117
|
-
console.log(chalk.gray("\u2022 Common typos (0\u2192O, 1\u2192I) are automatically corrected"));
|
|
5118
|
-
console.log("");
|
|
5119
|
-
console.log(chalk.yellow("\u26A0\uFE0F Keep this key secure - it provides full access to your account"));
|
|
5120
|
-
}
|
|
5121
5095
|
async function handleAuthStatus() {
|
|
5122
5096
|
const credentials = await readCredentials();
|
|
5123
5097
|
const settings = await readSettings();
|
|
@@ -5654,7 +5628,7 @@ ${chalk.bold("happy connect")} - Connect AI vendor API keys to Happy cloud
|
|
|
5654
5628
|
|
|
5655
5629
|
${chalk.bold("Usage:")}
|
|
5656
5630
|
happy connect codex Store your Codex API key in Happy cloud
|
|
5657
|
-
happy connect
|
|
5631
|
+
happy connect claude Store your Anthropic API key in Happy cloud
|
|
5658
5632
|
happy connect gemini Store your Gemini API key in Happy cloud
|
|
5659
5633
|
happy connect help Show this help message
|
|
5660
5634
|
|
|
@@ -5665,7 +5639,7 @@ ${chalk.bold("Description:")}
|
|
|
5665
5639
|
|
|
5666
5640
|
${chalk.bold("Examples:")}
|
|
5667
5641
|
happy connect codex
|
|
5668
|
-
happy connect
|
|
5642
|
+
happy connect claude
|
|
5669
5643
|
happy connect gemini
|
|
5670
5644
|
|
|
5671
5645
|
${chalk.bold("Notes:")}
|
|
@@ -5684,7 +5658,7 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
5684
5658
|
console.log(chalk.gray(' Please run "happy auth login" first'));
|
|
5685
5659
|
process.exit(1);
|
|
5686
5660
|
}
|
|
5687
|
-
const api =
|
|
5661
|
+
const api = await ApiClient.create(credentials);
|
|
5688
5662
|
if (vendor === "codex") {
|
|
5689
5663
|
console.log("\u{1F680} Registering Codex token with server");
|
|
5690
5664
|
const codexAuthTokens = await authenticateCodex();
|
|
@@ -5749,11 +5723,11 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
5749
5723
|
return;
|
|
5750
5724
|
} else if (subcommand === "codex") {
|
|
5751
5725
|
try {
|
|
5752
|
-
const { runCodex } = await import('./runCodex-
|
|
5726
|
+
const { runCodex } = await import('./runCodex-BNH8w4O9.mjs');
|
|
5753
5727
|
const {
|
|
5754
5728
|
credentials
|
|
5755
5729
|
} = await authAndSetupMachineIfNeeded();
|
|
5756
|
-
await runCodex(credentials);
|
|
5730
|
+
await runCodex({ credentials });
|
|
5757
5731
|
} catch (error) {
|
|
5758
5732
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
5759
5733
|
if (process.env.DEBUG) {
|
|
@@ -5921,6 +5895,9 @@ ${chalk.bold("happy")} - Claude Code On the Go
|
|
|
5921
5895
|
${chalk.bold("Usage:")}
|
|
5922
5896
|
happy [options] Start Claude with mobile control
|
|
5923
5897
|
happy auth Manage authentication
|
|
5898
|
+
happy codex Start Codex mode
|
|
5899
|
+
happy connect Connect AI vendor API keys
|
|
5900
|
+
happy notify Send push notification
|
|
5924
5901
|
happy daemon Manage background service that allows
|
|
5925
5902
|
to spawn new sessions away from your computer
|
|
5926
5903
|
happy doctor System diagnostics & troubleshooting
|
|
@@ -6019,12 +5996,12 @@ ${chalk.bold("Examples:")}
|
|
|
6019
5996
|
}
|
|
6020
5997
|
let credentials = await readCredentials();
|
|
6021
5998
|
if (!credentials) {
|
|
6022
|
-
console.error(chalk.red('Error: Not authenticated. Please run "happy
|
|
5999
|
+
console.error(chalk.red('Error: Not authenticated. Please run "happy auth login" first.'));
|
|
6023
6000
|
process.exit(1);
|
|
6024
6001
|
}
|
|
6025
6002
|
console.log(chalk.blue("\u{1F4F1} Sending push notification..."));
|
|
6026
6003
|
try {
|
|
6027
|
-
const api =
|
|
6004
|
+
const api = await ApiClient.create(credentials);
|
|
6028
6005
|
const notificationTitle = title || "Happy";
|
|
6029
6006
|
api.push().sendToAllDevices(
|
|
6030
6007
|
notificationTitle,
|
package/dist/index.cjs
CHANGED
package/dist/index.mjs
CHANGED