happy-coder 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +330 -39
- package/dist/index.mjs +330 -39
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +30 -0
- package/dist/lib.d.mts +30 -0
- package/dist/lib.mjs +1 -1
- package/dist/{types-eN-YHsuj.cjs → types-Bkw2UUhb.cjs} +13 -2
- package/dist/{types-VkaGP8up.mjs → types-Cqy5Dx2C.mjs} +13 -2
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
|
-
var types$1 = require('./types-
|
|
4
|
+
var types$1 = require('./types-Bkw2UUhb.cjs');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
6
|
var node_child_process = require('node:child_process');
|
|
7
7
|
var node_path = require('node:path');
|
|
@@ -28,6 +28,7 @@ var child_process = require('child_process');
|
|
|
28
28
|
var util = require('util');
|
|
29
29
|
var crypto = require('crypto');
|
|
30
30
|
var qrcode = require('qrcode-terminal');
|
|
31
|
+
var open = require('open');
|
|
31
32
|
var fs = require('fs');
|
|
32
33
|
var os$1 = require('os');
|
|
33
34
|
|
|
@@ -480,6 +481,7 @@ function messageKey(message) {
|
|
|
480
481
|
}
|
|
481
482
|
async function readSessionLog(projectDir, sessionId) {
|
|
482
483
|
const expectedSessionFile = node_path.join(projectDir, `${sessionId}.jsonl`);
|
|
484
|
+
types$1.logger.debug(`[SESSION_SCANNER] Reading session file: ${expectedSessionFile}`);
|
|
483
485
|
let file;
|
|
484
486
|
try {
|
|
485
487
|
file = await promises$1.readFile(expectedSessionFile, "utf-8");
|
|
@@ -1252,6 +1254,12 @@ async function claudeRemote(opts) {
|
|
|
1252
1254
|
mcpServers: opts.mcpServers,
|
|
1253
1255
|
permissionPromptToolName: opts.permissionPromptToolName,
|
|
1254
1256
|
permissionMode: opts.permissionMode,
|
|
1257
|
+
model: opts.model,
|
|
1258
|
+
fallbackModel: opts.fallbackModel,
|
|
1259
|
+
customSystemPrompt: opts.customSystemPrompt,
|
|
1260
|
+
appendSystemPrompt: opts.appendSystemPrompt,
|
|
1261
|
+
allowedTools: opts.allowedTools,
|
|
1262
|
+
disallowedTools: opts.disallowedTools,
|
|
1255
1263
|
executable: "node",
|
|
1256
1264
|
abort: opts.signal,
|
|
1257
1265
|
pathToClaudeCodeExecutable: (() => {
|
|
@@ -1261,7 +1269,7 @@ async function claudeRemote(opts) {
|
|
|
1261
1269
|
if (opts.claudeArgs && opts.claudeArgs.length > 0) {
|
|
1262
1270
|
sdkOptions.executableArgs = [...sdkOptions.executableArgs || [], ...opts.claudeArgs];
|
|
1263
1271
|
}
|
|
1264
|
-
types$1.logger.debug(`[claudeRemote] Starting query with permission mode: ${opts.permissionMode}`);
|
|
1272
|
+
types$1.logger.debug(`[claudeRemote] Starting query with permission mode: ${opts.permissionMode}, model: ${opts.model || "default"}, fallbackModel: ${opts.fallbackModel || "none"}, customSystemPrompt: ${opts.customSystemPrompt ? "set" : "none"}, appendSystemPrompt: ${opts.appendSystemPrompt ? "set" : "none"}, allowedTools: ${opts.allowedTools ? opts.allowedTools.join(",") : "none"}, disallowedTools: ${opts.disallowedTools ? opts.disallowedTools.join(",") : "none"}`);
|
|
1265
1273
|
let message = new PushableAsyncIterable();
|
|
1266
1274
|
message.push({
|
|
1267
1275
|
type: "user",
|
|
@@ -1441,9 +1449,9 @@ async function startPermissionResolver(session) {
|
|
|
1441
1449
|
if (response.approved) {
|
|
1442
1450
|
types$1.logger.debug("Plan approved - injecting PLAN_FAKE_RESTART");
|
|
1443
1451
|
if (response.mode && ["default", "acceptEdits", "bypassPermissions"].includes(response.mode)) {
|
|
1444
|
-
session.queue.unshift(PLAN_FAKE_RESTART, response.mode);
|
|
1452
|
+
session.queue.unshift(PLAN_FAKE_RESTART, { permissionMode: response.mode });
|
|
1445
1453
|
} else {
|
|
1446
|
-
session.queue.unshift(PLAN_FAKE_RESTART, "default");
|
|
1454
|
+
session.queue.unshift(PLAN_FAKE_RESTART, { permissionMode: "default" });
|
|
1447
1455
|
}
|
|
1448
1456
|
resolve({ approved: false, reason: PLAN_FAKE_REJECT });
|
|
1449
1457
|
} else {
|
|
@@ -2152,7 +2160,13 @@ async function claudeRemoteLauncher(session) {
|
|
|
2152
2160
|
}
|
|
2153
2161
|
},
|
|
2154
2162
|
permissionPromptToolName: "mcp__permission__" + permissions.server.toolName,
|
|
2155
|
-
permissionMode: messageData.mode,
|
|
2163
|
+
permissionMode: messageData.mode.permissionMode,
|
|
2164
|
+
model: messageData.mode.model,
|
|
2165
|
+
fallbackModel: messageData.mode.fallbackModel,
|
|
2166
|
+
customSystemPrompt: messageData.mode.customSystemPrompt,
|
|
2167
|
+
appendSystemPrompt: messageData.mode.appendSystemPrompt,
|
|
2168
|
+
allowedTools: messageData.mode.allowedTools,
|
|
2169
|
+
disallowedTools: messageData.mode.disallowedTools,
|
|
2156
2170
|
onSessionFound: (sessionId) => {
|
|
2157
2171
|
sdkToLogConverter.updateSessionId(sessionId);
|
|
2158
2172
|
session.onSessionFound(sessionId);
|
|
@@ -2248,7 +2262,7 @@ async function loop(opts) {
|
|
|
2248
2262
|
}
|
|
2249
2263
|
|
|
2250
2264
|
var name = "happy-coder";
|
|
2251
|
-
var version = "0.
|
|
2265
|
+
var version = "0.6.0";
|
|
2252
2266
|
var description = "Claude Code session sharing CLI";
|
|
2253
2267
|
var author = "Kirill Dubovitskiy";
|
|
2254
2268
|
var license = "MIT";
|
|
@@ -2316,7 +2330,7 @@ var dependencies = {
|
|
|
2316
2330
|
"http-proxy": "^1.18.1",
|
|
2317
2331
|
"http-proxy-middleware": "^3.0.5",
|
|
2318
2332
|
ink: "^6.1.0",
|
|
2319
|
-
|
|
2333
|
+
open: "^10.2.0",
|
|
2320
2334
|
"qrcode-terminal": "^0.12.0",
|
|
2321
2335
|
react: "^19.1.1",
|
|
2322
2336
|
"socket.io-client": "^4.8.1",
|
|
@@ -2828,6 +2842,92 @@ class MessageQueue2 {
|
|
|
2828
2842
|
}
|
|
2829
2843
|
}
|
|
2830
2844
|
|
|
2845
|
+
function deterministicStringify(obj, options = {}) {
|
|
2846
|
+
const {
|
|
2847
|
+
undefinedBehavior = "omit",
|
|
2848
|
+
sortArrays = false,
|
|
2849
|
+
replacer,
|
|
2850
|
+
includeSymbols = false
|
|
2851
|
+
} = options;
|
|
2852
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
2853
|
+
function processValue(value, key) {
|
|
2854
|
+
if (replacer && key !== void 0) {
|
|
2855
|
+
value = replacer(key, value);
|
|
2856
|
+
}
|
|
2857
|
+
if (value === null) return null;
|
|
2858
|
+
if (value === void 0) {
|
|
2859
|
+
switch (undefinedBehavior) {
|
|
2860
|
+
case "omit":
|
|
2861
|
+
return void 0;
|
|
2862
|
+
case "null":
|
|
2863
|
+
return null;
|
|
2864
|
+
case "throw":
|
|
2865
|
+
throw new Error(`Undefined value at key: ${key}`);
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
|
|
2869
|
+
return value;
|
|
2870
|
+
}
|
|
2871
|
+
if (value instanceof Date) {
|
|
2872
|
+
return value.toISOString();
|
|
2873
|
+
}
|
|
2874
|
+
if (value instanceof RegExp) {
|
|
2875
|
+
return value.toString();
|
|
2876
|
+
}
|
|
2877
|
+
if (typeof value === "function") {
|
|
2878
|
+
return void 0;
|
|
2879
|
+
}
|
|
2880
|
+
if (typeof value === "symbol") {
|
|
2881
|
+
return includeSymbols ? value.toString() : void 0;
|
|
2882
|
+
}
|
|
2883
|
+
if (typeof value === "bigint") {
|
|
2884
|
+
return value.toString() + "n";
|
|
2885
|
+
}
|
|
2886
|
+
if (seen.has(value)) {
|
|
2887
|
+
throw new Error("Circular reference detected");
|
|
2888
|
+
}
|
|
2889
|
+
seen.add(value);
|
|
2890
|
+
if (Array.isArray(value)) {
|
|
2891
|
+
const processed2 = value.map((item, index) => processValue(item, String(index))).filter((item) => item !== void 0);
|
|
2892
|
+
if (sortArrays) {
|
|
2893
|
+
processed2.sort((a, b) => {
|
|
2894
|
+
const aStr = JSON.stringify(processValue(a));
|
|
2895
|
+
const bStr = JSON.stringify(processValue(b));
|
|
2896
|
+
return aStr.localeCompare(bStr);
|
|
2897
|
+
});
|
|
2898
|
+
}
|
|
2899
|
+
seen.delete(value);
|
|
2900
|
+
return processed2;
|
|
2901
|
+
}
|
|
2902
|
+
if (value.constructor === Object || value.constructor === void 0) {
|
|
2903
|
+
const processed2 = {};
|
|
2904
|
+
const keys = Object.keys(value).sort();
|
|
2905
|
+
for (const k of keys) {
|
|
2906
|
+
const processedValue = processValue(value[k], k);
|
|
2907
|
+
if (processedValue !== void 0) {
|
|
2908
|
+
processed2[k] = processedValue;
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
seen.delete(value);
|
|
2912
|
+
return processed2;
|
|
2913
|
+
}
|
|
2914
|
+
try {
|
|
2915
|
+
const plain = { ...value };
|
|
2916
|
+
seen.delete(value);
|
|
2917
|
+
return processValue(plain, key);
|
|
2918
|
+
} catch {
|
|
2919
|
+
seen.delete(value);
|
|
2920
|
+
return String(value);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
const processed = processValue(obj);
|
|
2924
|
+
return JSON.stringify(processed);
|
|
2925
|
+
}
|
|
2926
|
+
function hashObject(obj, options, encoding = "hex") {
|
|
2927
|
+
const jsonString = deterministicStringify(obj, options);
|
|
2928
|
+
return crypto.createHash("sha256").update(jsonString).digest(encoding);
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2831
2931
|
let caffeinateProcess = null;
|
|
2832
2932
|
function startCaffeinate() {
|
|
2833
2933
|
if (process.platform !== "darwin") {
|
|
@@ -2991,9 +3091,15 @@ async function start(credentials, options = {}) {
|
|
|
2991
3091
|
if (caffeinateStarted) {
|
|
2992
3092
|
types$1.logger.infoDeveloper("Sleep prevention enabled (macOS)");
|
|
2993
3093
|
}
|
|
2994
|
-
const messageQueue = new MessageQueue2((mode) => mode);
|
|
3094
|
+
const messageQueue = new MessageQueue2((mode) => hashObject(mode));
|
|
2995
3095
|
registerHandlers(session);
|
|
2996
3096
|
let currentPermissionMode = options.permissionMode;
|
|
3097
|
+
let currentModel = options.model;
|
|
3098
|
+
let currentFallbackModel = void 0;
|
|
3099
|
+
let currentCustomSystemPrompt = void 0;
|
|
3100
|
+
let currentAppendSystemPrompt = void 0;
|
|
3101
|
+
let currentAllowedTools = void 0;
|
|
3102
|
+
let currentDisallowedTools = void 0;
|
|
2997
3103
|
session.onUserMessage((message) => {
|
|
2998
3104
|
let messagePermissionMode = currentPermissionMode;
|
|
2999
3105
|
if (message.meta?.permissionMode) {
|
|
@@ -3008,7 +3114,64 @@ async function start(credentials, options = {}) {
|
|
|
3008
3114
|
} else {
|
|
3009
3115
|
types$1.logger.debug(`[loop] User message received with no permission mode override, using current: ${currentPermissionMode}`);
|
|
3010
3116
|
}
|
|
3011
|
-
|
|
3117
|
+
let messageModel = currentModel;
|
|
3118
|
+
if (message.meta?.hasOwnProperty("model")) {
|
|
3119
|
+
messageModel = message.meta.model || void 0;
|
|
3120
|
+
currentModel = messageModel;
|
|
3121
|
+
types$1.logger.debug(`[loop] Model updated from user message: ${messageModel || "reset to default"}`);
|
|
3122
|
+
} else {
|
|
3123
|
+
types$1.logger.debug(`[loop] User message received with no model override, using current: ${currentModel || "default"}`);
|
|
3124
|
+
}
|
|
3125
|
+
let messageCustomSystemPrompt = currentCustomSystemPrompt;
|
|
3126
|
+
if (message.meta?.hasOwnProperty("customSystemPrompt")) {
|
|
3127
|
+
messageCustomSystemPrompt = message.meta.customSystemPrompt || void 0;
|
|
3128
|
+
currentCustomSystemPrompt = messageCustomSystemPrompt;
|
|
3129
|
+
types$1.logger.debug(`[loop] Custom system prompt updated from user message: ${messageCustomSystemPrompt ? "set" : "reset to none"}`);
|
|
3130
|
+
} else {
|
|
3131
|
+
types$1.logger.debug(`[loop] User message received with no custom system prompt override, using current: ${currentCustomSystemPrompt ? "set" : "none"}`);
|
|
3132
|
+
}
|
|
3133
|
+
let messageFallbackModel = currentFallbackModel;
|
|
3134
|
+
if (message.meta?.hasOwnProperty("fallbackModel")) {
|
|
3135
|
+
messageFallbackModel = message.meta.fallbackModel || void 0;
|
|
3136
|
+
currentFallbackModel = messageFallbackModel;
|
|
3137
|
+
types$1.logger.debug(`[loop] Fallback model updated from user message: ${messageFallbackModel || "reset to none"}`);
|
|
3138
|
+
} else {
|
|
3139
|
+
types$1.logger.debug(`[loop] User message received with no fallback model override, using current: ${currentFallbackModel || "none"}`);
|
|
3140
|
+
}
|
|
3141
|
+
let messageAppendSystemPrompt = currentAppendSystemPrompt;
|
|
3142
|
+
if (message.meta?.hasOwnProperty("appendSystemPrompt")) {
|
|
3143
|
+
messageAppendSystemPrompt = message.meta.appendSystemPrompt || void 0;
|
|
3144
|
+
currentAppendSystemPrompt = messageAppendSystemPrompt;
|
|
3145
|
+
types$1.logger.debug(`[loop] Append system prompt updated from user message: ${messageAppendSystemPrompt ? "set" : "reset to none"}`);
|
|
3146
|
+
} else {
|
|
3147
|
+
types$1.logger.debug(`[loop] User message received with no append system prompt override, using current: ${currentAppendSystemPrompt ? "set" : "none"}`);
|
|
3148
|
+
}
|
|
3149
|
+
let messageAllowedTools = currentAllowedTools;
|
|
3150
|
+
if (message.meta?.hasOwnProperty("allowedTools")) {
|
|
3151
|
+
messageAllowedTools = message.meta.allowedTools || void 0;
|
|
3152
|
+
currentAllowedTools = messageAllowedTools;
|
|
3153
|
+
types$1.logger.debug(`[loop] Allowed tools updated from user message: ${messageAllowedTools ? messageAllowedTools.join(", ") : "reset to none"}`);
|
|
3154
|
+
} else {
|
|
3155
|
+
types$1.logger.debug(`[loop] User message received with no allowed tools override, using current: ${currentAllowedTools ? currentAllowedTools.join(", ") : "none"}`);
|
|
3156
|
+
}
|
|
3157
|
+
let messageDisallowedTools = currentDisallowedTools;
|
|
3158
|
+
if (message.meta?.hasOwnProperty("disallowedTools")) {
|
|
3159
|
+
messageDisallowedTools = message.meta.disallowedTools || void 0;
|
|
3160
|
+
currentDisallowedTools = messageDisallowedTools;
|
|
3161
|
+
types$1.logger.debug(`[loop] Disallowed tools updated from user message: ${messageDisallowedTools ? messageDisallowedTools.join(", ") : "reset to none"}`);
|
|
3162
|
+
} else {
|
|
3163
|
+
types$1.logger.debug(`[loop] User message received with no disallowed tools override, using current: ${currentDisallowedTools ? currentDisallowedTools.join(", ") : "none"}`);
|
|
3164
|
+
}
|
|
3165
|
+
const enhancedMode = {
|
|
3166
|
+
permissionMode: messagePermissionMode || "default",
|
|
3167
|
+
model: messageModel,
|
|
3168
|
+
fallbackModel: messageFallbackModel,
|
|
3169
|
+
customSystemPrompt: messageCustomSystemPrompt,
|
|
3170
|
+
appendSystemPrompt: messageAppendSystemPrompt,
|
|
3171
|
+
allowedTools: messageAllowedTools,
|
|
3172
|
+
disallowedTools: messageDisallowedTools
|
|
3173
|
+
};
|
|
3174
|
+
messageQueue.push(message.content.text, enhancedMode);
|
|
3012
3175
|
types$1.logger.debugLargeJson("User message pushed to queue:", message);
|
|
3013
3176
|
});
|
|
3014
3177
|
await loop({
|
|
@@ -3052,8 +3215,69 @@ function displayQRCode(url) {
|
|
|
3052
3215
|
console.log("=".repeat(80));
|
|
3053
3216
|
}
|
|
3054
3217
|
|
|
3218
|
+
function generateWebAuthUrl(publicKey) {
|
|
3219
|
+
const publicKeyBase64 = types$1.encodeBase64(publicKey, "base64url");
|
|
3220
|
+
return `https://app.happy.engineering/terminal/connect#key=${publicKeyBase64}`;
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3223
|
+
async function openBrowser(url) {
|
|
3224
|
+
try {
|
|
3225
|
+
if (!process.stdout.isTTY || process.env.CI || process.env.HEADLESS) {
|
|
3226
|
+
types$1.logger.debug("[browser] Headless environment detected, skipping browser open");
|
|
3227
|
+
return false;
|
|
3228
|
+
}
|
|
3229
|
+
types$1.logger.debug(`[browser] Attempting to open URL: ${url}`);
|
|
3230
|
+
await open(url);
|
|
3231
|
+
types$1.logger.debug("[browser] Browser opened successfully");
|
|
3232
|
+
return true;
|
|
3233
|
+
} catch (error) {
|
|
3234
|
+
types$1.logger.debug("[browser] Failed to open browser:", error);
|
|
3235
|
+
return false;
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
const AuthSelector = ({ onSelect, onCancel }) => {
|
|
3240
|
+
const [selectedIndex, setSelectedIndex] = React.useState(0);
|
|
3241
|
+
const options = [
|
|
3242
|
+
{
|
|
3243
|
+
method: "mobile",
|
|
3244
|
+
label: "Mobile App"
|
|
3245
|
+
},
|
|
3246
|
+
{
|
|
3247
|
+
method: "web",
|
|
3248
|
+
label: "Web Browser"
|
|
3249
|
+
}
|
|
3250
|
+
];
|
|
3251
|
+
ink.useInput((input, key) => {
|
|
3252
|
+
if (key.upArrow) {
|
|
3253
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
3254
|
+
} else if (key.downArrow) {
|
|
3255
|
+
setSelectedIndex((prev) => Math.min(options.length - 1, prev + 1));
|
|
3256
|
+
} else if (key.return) {
|
|
3257
|
+
onSelect(options[selectedIndex].method);
|
|
3258
|
+
} else if (key.escape || key.ctrl && input === "c") {
|
|
3259
|
+
onCancel();
|
|
3260
|
+
} else if (input === "1") {
|
|
3261
|
+
setSelectedIndex(0);
|
|
3262
|
+
onSelect("mobile");
|
|
3263
|
+
} else if (input === "2") {
|
|
3264
|
+
setSelectedIndex(1);
|
|
3265
|
+
onSelect("web");
|
|
3266
|
+
}
|
|
3267
|
+
});
|
|
3268
|
+
return /* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", paddingY: 1 }, /* @__PURE__ */ React.createElement(ink.Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(ink.Text, null, "How would you like to authenticate?")), /* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column" }, options.map((option, index) => {
|
|
3269
|
+
const isSelected = selectedIndex === index;
|
|
3270
|
+
return /* @__PURE__ */ React.createElement(ink.Box, { key: option.method, marginY: 0 }, /* @__PURE__ */ React.createElement(ink.Text, { color: isSelected ? "cyan" : "gray" }, isSelected ? "\u203A " : " ", index + 1, ". ", option.label));
|
|
3271
|
+
})), /* @__PURE__ */ React.createElement(ink.Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(ink.Text, { dimColor: true }, "Use arrows or 1-2 to select, Enter to confirm")));
|
|
3272
|
+
};
|
|
3273
|
+
|
|
3055
3274
|
async function doAuth() {
|
|
3056
|
-
console.
|
|
3275
|
+
console.clear();
|
|
3276
|
+
const authMethod = await selectAuthenticationMethod();
|
|
3277
|
+
if (!authMethod) {
|
|
3278
|
+
console.log("\nAuthentication cancelled.\n");
|
|
3279
|
+
return null;
|
|
3280
|
+
}
|
|
3057
3281
|
const secret = new Uint8Array(node_crypto.randomBytes(32));
|
|
3058
3282
|
const keypair = tweetnacl.box.keyPair.fromSecretKey(secret);
|
|
3059
3283
|
try {
|
|
@@ -3064,38 +3288,106 @@ async function doAuth() {
|
|
|
3064
3288
|
console.log("Failed to create authentication request, please try again later.");
|
|
3065
3289
|
return null;
|
|
3066
3290
|
}
|
|
3067
|
-
|
|
3291
|
+
if (authMethod === "mobile") {
|
|
3292
|
+
return await doMobileAuth(keypair);
|
|
3293
|
+
} else {
|
|
3294
|
+
return await doWebAuth(keypair);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
function selectAuthenticationMethod() {
|
|
3298
|
+
return new Promise((resolve) => {
|
|
3299
|
+
let hasResolved = false;
|
|
3300
|
+
const onSelect = (method) => {
|
|
3301
|
+
if (!hasResolved) {
|
|
3302
|
+
hasResolved = true;
|
|
3303
|
+
app.unmount();
|
|
3304
|
+
resolve(method);
|
|
3305
|
+
}
|
|
3306
|
+
};
|
|
3307
|
+
const onCancel = () => {
|
|
3308
|
+
if (!hasResolved) {
|
|
3309
|
+
hasResolved = true;
|
|
3310
|
+
app.unmount();
|
|
3311
|
+
resolve(null);
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
const app = ink.render(React.createElement(AuthSelector, { onSelect, onCancel }), {
|
|
3315
|
+
exitOnCtrlC: false,
|
|
3316
|
+
patchConsole: false
|
|
3317
|
+
});
|
|
3318
|
+
});
|
|
3319
|
+
}
|
|
3320
|
+
async function doMobileAuth(keypair) {
|
|
3321
|
+
console.clear();
|
|
3322
|
+
console.log("\nMobile Authentication\n");
|
|
3323
|
+
console.log("Scan this QR code with your Happy mobile app:\n");
|
|
3068
3324
|
const authUrl = "happy://terminal?" + types$1.encodeBase64Url(keypair.publicKey);
|
|
3069
3325
|
displayQRCode(authUrl);
|
|
3070
|
-
console.log("\
|
|
3326
|
+
console.log("\nOr manually enter this URL:");
|
|
3071
3327
|
console.log(authUrl);
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3328
|
+
console.log("");
|
|
3329
|
+
return await waitForAuthentication(keypair);
|
|
3330
|
+
}
|
|
3331
|
+
async function doWebAuth(keypair) {
|
|
3332
|
+
console.clear();
|
|
3333
|
+
console.log("\nWeb Authentication\n");
|
|
3334
|
+
const webUrl = generateWebAuthUrl(keypair.publicKey);
|
|
3335
|
+
console.log("Opening your browser...");
|
|
3336
|
+
const browserOpened = await openBrowser(webUrl);
|
|
3337
|
+
if (browserOpened) {
|
|
3338
|
+
console.log("\u2713 Browser opened\n");
|
|
3339
|
+
console.log("Complete authentication in your browser window.");
|
|
3340
|
+
} else {
|
|
3341
|
+
console.log("Could not open browser automatically.\n");
|
|
3342
|
+
console.log("Please open this URL manually:");
|
|
3343
|
+
console.log(webUrl);
|
|
3344
|
+
}
|
|
3345
|
+
console.log("");
|
|
3346
|
+
return await waitForAuthentication(keypair);
|
|
3347
|
+
}
|
|
3348
|
+
async function waitForAuthentication(keypair) {
|
|
3349
|
+
process.stdout.write("Waiting for authentication");
|
|
3350
|
+
let dots = 0;
|
|
3351
|
+
let cancelled = false;
|
|
3352
|
+
const handleInterrupt = () => {
|
|
3353
|
+
cancelled = true;
|
|
3354
|
+
console.log("\n\nAuthentication cancelled.");
|
|
3355
|
+
process.exit(0);
|
|
3356
|
+
};
|
|
3357
|
+
process.on("SIGINT", handleInterrupt);
|
|
3358
|
+
try {
|
|
3359
|
+
while (!cancelled) {
|
|
3360
|
+
try {
|
|
3361
|
+
const response = await axios.post(`${types$1.configuration.serverUrl}/v1/auth/request`, {
|
|
3362
|
+
publicKey: types$1.encodeBase64(keypair.publicKey)
|
|
3363
|
+
});
|
|
3364
|
+
if (response.data.state === "authorized") {
|
|
3365
|
+
let token = response.data.token;
|
|
3366
|
+
let r = types$1.decodeBase64(response.data.response);
|
|
3367
|
+
let decrypted = decryptWithEphemeralKey(r, keypair.secretKey);
|
|
3368
|
+
if (decrypted) {
|
|
3369
|
+
const credentials = {
|
|
3370
|
+
secret: decrypted,
|
|
3371
|
+
token
|
|
3372
|
+
};
|
|
3373
|
+
await writeCredentials(credentials);
|
|
3374
|
+
console.log("\n\n\u2713 Authentication successful\n");
|
|
3375
|
+
return credentials;
|
|
3376
|
+
} else {
|
|
3377
|
+
console.log("\n\nFailed to decrypt response. Please try again.");
|
|
3378
|
+
return null;
|
|
3379
|
+
}
|
|
3092
3380
|
}
|
|
3381
|
+
} catch (error) {
|
|
3382
|
+
console.log("\n\nFailed to check authentication status. Please try again.");
|
|
3383
|
+
return null;
|
|
3093
3384
|
}
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3385
|
+
process.stdout.write("\rWaiting for authentication" + ".".repeat(dots % 3 + 1) + " ");
|
|
3386
|
+
dots++;
|
|
3387
|
+
await types$1.delay(1e3);
|
|
3097
3388
|
}
|
|
3098
|
-
|
|
3389
|
+
} finally {
|
|
3390
|
+
process.off("SIGINT", handleInterrupt);
|
|
3099
3391
|
}
|
|
3100
3392
|
return null;
|
|
3101
3393
|
}
|
|
@@ -3326,12 +3618,11 @@ class ApiDaemonSession extends node_events.EventEmitter {
|
|
|
3326
3618
|
this.stopKeepAlive();
|
|
3327
3619
|
this.keepAliveInterval = setInterval(() => {
|
|
3328
3620
|
const payload = {
|
|
3329
|
-
type: "machine-scoped",
|
|
3330
3621
|
machineId: this.machineIdentity.machineId,
|
|
3331
3622
|
time: Date.now()
|
|
3332
3623
|
};
|
|
3333
|
-
types$1.logger.debugLargeJson(`[DAEMON SESSION] Emitting
|
|
3334
|
-
this.socket.emit("
|
|
3624
|
+
types$1.logger.debugLargeJson(`[DAEMON SESSION] Emitting machine-alive`, payload);
|
|
3625
|
+
this.socket.emit("machine-alive", payload);
|
|
3335
3626
|
}, 2e4);
|
|
3336
3627
|
}
|
|
3337
3628
|
stopKeepAlive() {
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { l as logger, d as backoff, e as delay, R as RawJSONLinesSchema, c as configuration, f as encodeBase64, A as ApiClient, g as encodeBase64Url, h as decodeBase64, j as encrypt, b as initializeConfiguration, i as initLoggerWithGlobalConfiguration } from './types-
|
|
2
|
+
import { l as logger, d as backoff, e as delay, R as RawJSONLinesSchema, c as configuration, f as encodeBase64, A as ApiClient, g as encodeBase64Url, h as decodeBase64, j as encrypt, b as initializeConfiguration, i as initLoggerWithGlobalConfiguration } from './types-Cqy5Dx2C.mjs';
|
|
3
3
|
import { randomUUID, randomBytes } from 'node:crypto';
|
|
4
4
|
import { spawn, execSync } from 'node:child_process';
|
|
5
5
|
import { resolve, join, dirname as dirname$1 } from 'node:path';
|
|
@@ -27,6 +27,7 @@ import { spawn as spawn$1, exec, execSync as execSync$1 } from 'child_process';
|
|
|
27
27
|
import { promisify } from 'util';
|
|
28
28
|
import crypto, { createHash } from 'crypto';
|
|
29
29
|
import qrcode from 'qrcode-terminal';
|
|
30
|
+
import open from 'open';
|
|
30
31
|
import { existsSync as existsSync$1, readFileSync as readFileSync$1, writeFileSync, unlinkSync, mkdirSync as mkdirSync$1, chmodSync } from 'fs';
|
|
31
32
|
import { hostname, homedir as homedir$1 } from 'os';
|
|
32
33
|
|
|
@@ -459,6 +460,7 @@ function messageKey(message) {
|
|
|
459
460
|
}
|
|
460
461
|
async function readSessionLog(projectDir, sessionId) {
|
|
461
462
|
const expectedSessionFile = join(projectDir, `${sessionId}.jsonl`);
|
|
463
|
+
logger.debug(`[SESSION_SCANNER] Reading session file: ${expectedSessionFile}`);
|
|
462
464
|
let file;
|
|
463
465
|
try {
|
|
464
466
|
file = await readFile(expectedSessionFile, "utf-8");
|
|
@@ -1231,6 +1233,12 @@ async function claudeRemote(opts) {
|
|
|
1231
1233
|
mcpServers: opts.mcpServers,
|
|
1232
1234
|
permissionPromptToolName: opts.permissionPromptToolName,
|
|
1233
1235
|
permissionMode: opts.permissionMode,
|
|
1236
|
+
model: opts.model,
|
|
1237
|
+
fallbackModel: opts.fallbackModel,
|
|
1238
|
+
customSystemPrompt: opts.customSystemPrompt,
|
|
1239
|
+
appendSystemPrompt: opts.appendSystemPrompt,
|
|
1240
|
+
allowedTools: opts.allowedTools,
|
|
1241
|
+
disallowedTools: opts.disallowedTools,
|
|
1234
1242
|
executable: "node",
|
|
1235
1243
|
abort: opts.signal,
|
|
1236
1244
|
pathToClaudeCodeExecutable: (() => {
|
|
@@ -1240,7 +1248,7 @@ async function claudeRemote(opts) {
|
|
|
1240
1248
|
if (opts.claudeArgs && opts.claudeArgs.length > 0) {
|
|
1241
1249
|
sdkOptions.executableArgs = [...sdkOptions.executableArgs || [], ...opts.claudeArgs];
|
|
1242
1250
|
}
|
|
1243
|
-
logger.debug(`[claudeRemote] Starting query with permission mode: ${opts.permissionMode}`);
|
|
1251
|
+
logger.debug(`[claudeRemote] Starting query with permission mode: ${opts.permissionMode}, model: ${opts.model || "default"}, fallbackModel: ${opts.fallbackModel || "none"}, customSystemPrompt: ${opts.customSystemPrompt ? "set" : "none"}, appendSystemPrompt: ${opts.appendSystemPrompt ? "set" : "none"}, allowedTools: ${opts.allowedTools ? opts.allowedTools.join(",") : "none"}, disallowedTools: ${opts.disallowedTools ? opts.disallowedTools.join(",") : "none"}`);
|
|
1244
1252
|
let message = new PushableAsyncIterable();
|
|
1245
1253
|
message.push({
|
|
1246
1254
|
type: "user",
|
|
@@ -1420,9 +1428,9 @@ async function startPermissionResolver(session) {
|
|
|
1420
1428
|
if (response.approved) {
|
|
1421
1429
|
logger.debug("Plan approved - injecting PLAN_FAKE_RESTART");
|
|
1422
1430
|
if (response.mode && ["default", "acceptEdits", "bypassPermissions"].includes(response.mode)) {
|
|
1423
|
-
session.queue.unshift(PLAN_FAKE_RESTART, response.mode);
|
|
1431
|
+
session.queue.unshift(PLAN_FAKE_RESTART, { permissionMode: response.mode });
|
|
1424
1432
|
} else {
|
|
1425
|
-
session.queue.unshift(PLAN_FAKE_RESTART, "default");
|
|
1433
|
+
session.queue.unshift(PLAN_FAKE_RESTART, { permissionMode: "default" });
|
|
1426
1434
|
}
|
|
1427
1435
|
resolve({ approved: false, reason: PLAN_FAKE_REJECT });
|
|
1428
1436
|
} else {
|
|
@@ -2131,7 +2139,13 @@ async function claudeRemoteLauncher(session) {
|
|
|
2131
2139
|
}
|
|
2132
2140
|
},
|
|
2133
2141
|
permissionPromptToolName: "mcp__permission__" + permissions.server.toolName,
|
|
2134
|
-
permissionMode: messageData.mode,
|
|
2142
|
+
permissionMode: messageData.mode.permissionMode,
|
|
2143
|
+
model: messageData.mode.model,
|
|
2144
|
+
fallbackModel: messageData.mode.fallbackModel,
|
|
2145
|
+
customSystemPrompt: messageData.mode.customSystemPrompt,
|
|
2146
|
+
appendSystemPrompt: messageData.mode.appendSystemPrompt,
|
|
2147
|
+
allowedTools: messageData.mode.allowedTools,
|
|
2148
|
+
disallowedTools: messageData.mode.disallowedTools,
|
|
2135
2149
|
onSessionFound: (sessionId) => {
|
|
2136
2150
|
sdkToLogConverter.updateSessionId(sessionId);
|
|
2137
2151
|
session.onSessionFound(sessionId);
|
|
@@ -2227,7 +2241,7 @@ async function loop(opts) {
|
|
|
2227
2241
|
}
|
|
2228
2242
|
|
|
2229
2243
|
var name = "happy-coder";
|
|
2230
|
-
var version = "0.
|
|
2244
|
+
var version = "0.6.0";
|
|
2231
2245
|
var description = "Claude Code session sharing CLI";
|
|
2232
2246
|
var author = "Kirill Dubovitskiy";
|
|
2233
2247
|
var license = "MIT";
|
|
@@ -2295,7 +2309,7 @@ var dependencies = {
|
|
|
2295
2309
|
"http-proxy": "^1.18.1",
|
|
2296
2310
|
"http-proxy-middleware": "^3.0.5",
|
|
2297
2311
|
ink: "^6.1.0",
|
|
2298
|
-
|
|
2312
|
+
open: "^10.2.0",
|
|
2299
2313
|
"qrcode-terminal": "^0.12.0",
|
|
2300
2314
|
react: "^19.1.1",
|
|
2301
2315
|
"socket.io-client": "^4.8.1",
|
|
@@ -2807,6 +2821,92 @@ class MessageQueue2 {
|
|
|
2807
2821
|
}
|
|
2808
2822
|
}
|
|
2809
2823
|
|
|
2824
|
+
function deterministicStringify(obj, options = {}) {
|
|
2825
|
+
const {
|
|
2826
|
+
undefinedBehavior = "omit",
|
|
2827
|
+
sortArrays = false,
|
|
2828
|
+
replacer,
|
|
2829
|
+
includeSymbols = false
|
|
2830
|
+
} = options;
|
|
2831
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
2832
|
+
function processValue(value, key) {
|
|
2833
|
+
if (replacer && key !== void 0) {
|
|
2834
|
+
value = replacer(key, value);
|
|
2835
|
+
}
|
|
2836
|
+
if (value === null) return null;
|
|
2837
|
+
if (value === void 0) {
|
|
2838
|
+
switch (undefinedBehavior) {
|
|
2839
|
+
case "omit":
|
|
2840
|
+
return void 0;
|
|
2841
|
+
case "null":
|
|
2842
|
+
return null;
|
|
2843
|
+
case "throw":
|
|
2844
|
+
throw new Error(`Undefined value at key: ${key}`);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
|
|
2848
|
+
return value;
|
|
2849
|
+
}
|
|
2850
|
+
if (value instanceof Date) {
|
|
2851
|
+
return value.toISOString();
|
|
2852
|
+
}
|
|
2853
|
+
if (value instanceof RegExp) {
|
|
2854
|
+
return value.toString();
|
|
2855
|
+
}
|
|
2856
|
+
if (typeof value === "function") {
|
|
2857
|
+
return void 0;
|
|
2858
|
+
}
|
|
2859
|
+
if (typeof value === "symbol") {
|
|
2860
|
+
return includeSymbols ? value.toString() : void 0;
|
|
2861
|
+
}
|
|
2862
|
+
if (typeof value === "bigint") {
|
|
2863
|
+
return value.toString() + "n";
|
|
2864
|
+
}
|
|
2865
|
+
if (seen.has(value)) {
|
|
2866
|
+
throw new Error("Circular reference detected");
|
|
2867
|
+
}
|
|
2868
|
+
seen.add(value);
|
|
2869
|
+
if (Array.isArray(value)) {
|
|
2870
|
+
const processed2 = value.map((item, index) => processValue(item, String(index))).filter((item) => item !== void 0);
|
|
2871
|
+
if (sortArrays) {
|
|
2872
|
+
processed2.sort((a, b) => {
|
|
2873
|
+
const aStr = JSON.stringify(processValue(a));
|
|
2874
|
+
const bStr = JSON.stringify(processValue(b));
|
|
2875
|
+
return aStr.localeCompare(bStr);
|
|
2876
|
+
});
|
|
2877
|
+
}
|
|
2878
|
+
seen.delete(value);
|
|
2879
|
+
return processed2;
|
|
2880
|
+
}
|
|
2881
|
+
if (value.constructor === Object || value.constructor === void 0) {
|
|
2882
|
+
const processed2 = {};
|
|
2883
|
+
const keys = Object.keys(value).sort();
|
|
2884
|
+
for (const k of keys) {
|
|
2885
|
+
const processedValue = processValue(value[k], k);
|
|
2886
|
+
if (processedValue !== void 0) {
|
|
2887
|
+
processed2[k] = processedValue;
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
seen.delete(value);
|
|
2891
|
+
return processed2;
|
|
2892
|
+
}
|
|
2893
|
+
try {
|
|
2894
|
+
const plain = { ...value };
|
|
2895
|
+
seen.delete(value);
|
|
2896
|
+
return processValue(plain, key);
|
|
2897
|
+
} catch {
|
|
2898
|
+
seen.delete(value);
|
|
2899
|
+
return String(value);
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
const processed = processValue(obj);
|
|
2903
|
+
return JSON.stringify(processed);
|
|
2904
|
+
}
|
|
2905
|
+
function hashObject(obj, options, encoding = "hex") {
|
|
2906
|
+
const jsonString = deterministicStringify(obj, options);
|
|
2907
|
+
return createHash("sha256").update(jsonString).digest(encoding);
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2810
2910
|
let caffeinateProcess = null;
|
|
2811
2911
|
function startCaffeinate() {
|
|
2812
2912
|
if (process.platform !== "darwin") {
|
|
@@ -2970,9 +3070,15 @@ async function start(credentials, options = {}) {
|
|
|
2970
3070
|
if (caffeinateStarted) {
|
|
2971
3071
|
logger.infoDeveloper("Sleep prevention enabled (macOS)");
|
|
2972
3072
|
}
|
|
2973
|
-
const messageQueue = new MessageQueue2((mode) => mode);
|
|
3073
|
+
const messageQueue = new MessageQueue2((mode) => hashObject(mode));
|
|
2974
3074
|
registerHandlers(session);
|
|
2975
3075
|
let currentPermissionMode = options.permissionMode;
|
|
3076
|
+
let currentModel = options.model;
|
|
3077
|
+
let currentFallbackModel = void 0;
|
|
3078
|
+
let currentCustomSystemPrompt = void 0;
|
|
3079
|
+
let currentAppendSystemPrompt = void 0;
|
|
3080
|
+
let currentAllowedTools = void 0;
|
|
3081
|
+
let currentDisallowedTools = void 0;
|
|
2976
3082
|
session.onUserMessage((message) => {
|
|
2977
3083
|
let messagePermissionMode = currentPermissionMode;
|
|
2978
3084
|
if (message.meta?.permissionMode) {
|
|
@@ -2987,7 +3093,64 @@ async function start(credentials, options = {}) {
|
|
|
2987
3093
|
} else {
|
|
2988
3094
|
logger.debug(`[loop] User message received with no permission mode override, using current: ${currentPermissionMode}`);
|
|
2989
3095
|
}
|
|
2990
|
-
|
|
3096
|
+
let messageModel = currentModel;
|
|
3097
|
+
if (message.meta?.hasOwnProperty("model")) {
|
|
3098
|
+
messageModel = message.meta.model || void 0;
|
|
3099
|
+
currentModel = messageModel;
|
|
3100
|
+
logger.debug(`[loop] Model updated from user message: ${messageModel || "reset to default"}`);
|
|
3101
|
+
} else {
|
|
3102
|
+
logger.debug(`[loop] User message received with no model override, using current: ${currentModel || "default"}`);
|
|
3103
|
+
}
|
|
3104
|
+
let messageCustomSystemPrompt = currentCustomSystemPrompt;
|
|
3105
|
+
if (message.meta?.hasOwnProperty("customSystemPrompt")) {
|
|
3106
|
+
messageCustomSystemPrompt = message.meta.customSystemPrompt || void 0;
|
|
3107
|
+
currentCustomSystemPrompt = messageCustomSystemPrompt;
|
|
3108
|
+
logger.debug(`[loop] Custom system prompt updated from user message: ${messageCustomSystemPrompt ? "set" : "reset to none"}`);
|
|
3109
|
+
} else {
|
|
3110
|
+
logger.debug(`[loop] User message received with no custom system prompt override, using current: ${currentCustomSystemPrompt ? "set" : "none"}`);
|
|
3111
|
+
}
|
|
3112
|
+
let messageFallbackModel = currentFallbackModel;
|
|
3113
|
+
if (message.meta?.hasOwnProperty("fallbackModel")) {
|
|
3114
|
+
messageFallbackModel = message.meta.fallbackModel || void 0;
|
|
3115
|
+
currentFallbackModel = messageFallbackModel;
|
|
3116
|
+
logger.debug(`[loop] Fallback model updated from user message: ${messageFallbackModel || "reset to none"}`);
|
|
3117
|
+
} else {
|
|
3118
|
+
logger.debug(`[loop] User message received with no fallback model override, using current: ${currentFallbackModel || "none"}`);
|
|
3119
|
+
}
|
|
3120
|
+
let messageAppendSystemPrompt = currentAppendSystemPrompt;
|
|
3121
|
+
if (message.meta?.hasOwnProperty("appendSystemPrompt")) {
|
|
3122
|
+
messageAppendSystemPrompt = message.meta.appendSystemPrompt || void 0;
|
|
3123
|
+
currentAppendSystemPrompt = messageAppendSystemPrompt;
|
|
3124
|
+
logger.debug(`[loop] Append system prompt updated from user message: ${messageAppendSystemPrompt ? "set" : "reset to none"}`);
|
|
3125
|
+
} else {
|
|
3126
|
+
logger.debug(`[loop] User message received with no append system prompt override, using current: ${currentAppendSystemPrompt ? "set" : "none"}`);
|
|
3127
|
+
}
|
|
3128
|
+
let messageAllowedTools = currentAllowedTools;
|
|
3129
|
+
if (message.meta?.hasOwnProperty("allowedTools")) {
|
|
3130
|
+
messageAllowedTools = message.meta.allowedTools || void 0;
|
|
3131
|
+
currentAllowedTools = messageAllowedTools;
|
|
3132
|
+
logger.debug(`[loop] Allowed tools updated from user message: ${messageAllowedTools ? messageAllowedTools.join(", ") : "reset to none"}`);
|
|
3133
|
+
} else {
|
|
3134
|
+
logger.debug(`[loop] User message received with no allowed tools override, using current: ${currentAllowedTools ? currentAllowedTools.join(", ") : "none"}`);
|
|
3135
|
+
}
|
|
3136
|
+
let messageDisallowedTools = currentDisallowedTools;
|
|
3137
|
+
if (message.meta?.hasOwnProperty("disallowedTools")) {
|
|
3138
|
+
messageDisallowedTools = message.meta.disallowedTools || void 0;
|
|
3139
|
+
currentDisallowedTools = messageDisallowedTools;
|
|
3140
|
+
logger.debug(`[loop] Disallowed tools updated from user message: ${messageDisallowedTools ? messageDisallowedTools.join(", ") : "reset to none"}`);
|
|
3141
|
+
} else {
|
|
3142
|
+
logger.debug(`[loop] User message received with no disallowed tools override, using current: ${currentDisallowedTools ? currentDisallowedTools.join(", ") : "none"}`);
|
|
3143
|
+
}
|
|
3144
|
+
const enhancedMode = {
|
|
3145
|
+
permissionMode: messagePermissionMode || "default",
|
|
3146
|
+
model: messageModel,
|
|
3147
|
+
fallbackModel: messageFallbackModel,
|
|
3148
|
+
customSystemPrompt: messageCustomSystemPrompt,
|
|
3149
|
+
appendSystemPrompt: messageAppendSystemPrompt,
|
|
3150
|
+
allowedTools: messageAllowedTools,
|
|
3151
|
+
disallowedTools: messageDisallowedTools
|
|
3152
|
+
};
|
|
3153
|
+
messageQueue.push(message.content.text, enhancedMode);
|
|
2991
3154
|
logger.debugLargeJson("User message pushed to queue:", message);
|
|
2992
3155
|
});
|
|
2993
3156
|
await loop({
|
|
@@ -3031,8 +3194,69 @@ function displayQRCode(url) {
|
|
|
3031
3194
|
console.log("=".repeat(80));
|
|
3032
3195
|
}
|
|
3033
3196
|
|
|
3197
|
+
function generateWebAuthUrl(publicKey) {
|
|
3198
|
+
const publicKeyBase64 = encodeBase64(publicKey, "base64url");
|
|
3199
|
+
return `https://app.happy.engineering/terminal/connect#key=${publicKeyBase64}`;
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
async function openBrowser(url) {
|
|
3203
|
+
try {
|
|
3204
|
+
if (!process.stdout.isTTY || process.env.CI || process.env.HEADLESS) {
|
|
3205
|
+
logger.debug("[browser] Headless environment detected, skipping browser open");
|
|
3206
|
+
return false;
|
|
3207
|
+
}
|
|
3208
|
+
logger.debug(`[browser] Attempting to open URL: ${url}`);
|
|
3209
|
+
await open(url);
|
|
3210
|
+
logger.debug("[browser] Browser opened successfully");
|
|
3211
|
+
return true;
|
|
3212
|
+
} catch (error) {
|
|
3213
|
+
logger.debug("[browser] Failed to open browser:", error);
|
|
3214
|
+
return false;
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
const AuthSelector = ({ onSelect, onCancel }) => {
|
|
3219
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
3220
|
+
const options = [
|
|
3221
|
+
{
|
|
3222
|
+
method: "mobile",
|
|
3223
|
+
label: "Mobile App"
|
|
3224
|
+
},
|
|
3225
|
+
{
|
|
3226
|
+
method: "web",
|
|
3227
|
+
label: "Web Browser"
|
|
3228
|
+
}
|
|
3229
|
+
];
|
|
3230
|
+
useInput((input, key) => {
|
|
3231
|
+
if (key.upArrow) {
|
|
3232
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
3233
|
+
} else if (key.downArrow) {
|
|
3234
|
+
setSelectedIndex((prev) => Math.min(options.length - 1, prev + 1));
|
|
3235
|
+
} else if (key.return) {
|
|
3236
|
+
onSelect(options[selectedIndex].method);
|
|
3237
|
+
} else if (key.escape || key.ctrl && input === "c") {
|
|
3238
|
+
onCancel();
|
|
3239
|
+
} else if (input === "1") {
|
|
3240
|
+
setSelectedIndex(0);
|
|
3241
|
+
onSelect("mobile");
|
|
3242
|
+
} else if (input === "2") {
|
|
3243
|
+
setSelectedIndex(1);
|
|
3244
|
+
onSelect("web");
|
|
3245
|
+
}
|
|
3246
|
+
});
|
|
3247
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, null, "How would you like to authenticate?")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, options.map((option, index) => {
|
|
3248
|
+
const isSelected = selectedIndex === index;
|
|
3249
|
+
return /* @__PURE__ */ React.createElement(Box, { key: option.method, marginY: 0 }, /* @__PURE__ */ React.createElement(Text, { color: isSelected ? "cyan" : "gray" }, isSelected ? "\u203A " : " ", index + 1, ". ", option.label));
|
|
3250
|
+
})), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Use arrows or 1-2 to select, Enter to confirm")));
|
|
3251
|
+
};
|
|
3252
|
+
|
|
3034
3253
|
async function doAuth() {
|
|
3035
|
-
console.
|
|
3254
|
+
console.clear();
|
|
3255
|
+
const authMethod = await selectAuthenticationMethod();
|
|
3256
|
+
if (!authMethod) {
|
|
3257
|
+
console.log("\nAuthentication cancelled.\n");
|
|
3258
|
+
return null;
|
|
3259
|
+
}
|
|
3036
3260
|
const secret = new Uint8Array(randomBytes(32));
|
|
3037
3261
|
const keypair = tweetnacl.box.keyPair.fromSecretKey(secret);
|
|
3038
3262
|
try {
|
|
@@ -3043,38 +3267,106 @@ async function doAuth() {
|
|
|
3043
3267
|
console.log("Failed to create authentication request, please try again later.");
|
|
3044
3268
|
return null;
|
|
3045
3269
|
}
|
|
3046
|
-
|
|
3270
|
+
if (authMethod === "mobile") {
|
|
3271
|
+
return await doMobileAuth(keypair);
|
|
3272
|
+
} else {
|
|
3273
|
+
return await doWebAuth(keypair);
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
function selectAuthenticationMethod() {
|
|
3277
|
+
return new Promise((resolve) => {
|
|
3278
|
+
let hasResolved = false;
|
|
3279
|
+
const onSelect = (method) => {
|
|
3280
|
+
if (!hasResolved) {
|
|
3281
|
+
hasResolved = true;
|
|
3282
|
+
app.unmount();
|
|
3283
|
+
resolve(method);
|
|
3284
|
+
}
|
|
3285
|
+
};
|
|
3286
|
+
const onCancel = () => {
|
|
3287
|
+
if (!hasResolved) {
|
|
3288
|
+
hasResolved = true;
|
|
3289
|
+
app.unmount();
|
|
3290
|
+
resolve(null);
|
|
3291
|
+
}
|
|
3292
|
+
};
|
|
3293
|
+
const app = render(React.createElement(AuthSelector, { onSelect, onCancel }), {
|
|
3294
|
+
exitOnCtrlC: false,
|
|
3295
|
+
patchConsole: false
|
|
3296
|
+
});
|
|
3297
|
+
});
|
|
3298
|
+
}
|
|
3299
|
+
async function doMobileAuth(keypair) {
|
|
3300
|
+
console.clear();
|
|
3301
|
+
console.log("\nMobile Authentication\n");
|
|
3302
|
+
console.log("Scan this QR code with your Happy mobile app:\n");
|
|
3047
3303
|
const authUrl = "happy://terminal?" + encodeBase64Url(keypair.publicKey);
|
|
3048
3304
|
displayQRCode(authUrl);
|
|
3049
|
-
console.log("\
|
|
3305
|
+
console.log("\nOr manually enter this URL:");
|
|
3050
3306
|
console.log(authUrl);
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3307
|
+
console.log("");
|
|
3308
|
+
return await waitForAuthentication(keypair);
|
|
3309
|
+
}
|
|
3310
|
+
async function doWebAuth(keypair) {
|
|
3311
|
+
console.clear();
|
|
3312
|
+
console.log("\nWeb Authentication\n");
|
|
3313
|
+
const webUrl = generateWebAuthUrl(keypair.publicKey);
|
|
3314
|
+
console.log("Opening your browser...");
|
|
3315
|
+
const browserOpened = await openBrowser(webUrl);
|
|
3316
|
+
if (browserOpened) {
|
|
3317
|
+
console.log("\u2713 Browser opened\n");
|
|
3318
|
+
console.log("Complete authentication in your browser window.");
|
|
3319
|
+
} else {
|
|
3320
|
+
console.log("Could not open browser automatically.\n");
|
|
3321
|
+
console.log("Please open this URL manually:");
|
|
3322
|
+
console.log(webUrl);
|
|
3323
|
+
}
|
|
3324
|
+
console.log("");
|
|
3325
|
+
return await waitForAuthentication(keypair);
|
|
3326
|
+
}
|
|
3327
|
+
async function waitForAuthentication(keypair) {
|
|
3328
|
+
process.stdout.write("Waiting for authentication");
|
|
3329
|
+
let dots = 0;
|
|
3330
|
+
let cancelled = false;
|
|
3331
|
+
const handleInterrupt = () => {
|
|
3332
|
+
cancelled = true;
|
|
3333
|
+
console.log("\n\nAuthentication cancelled.");
|
|
3334
|
+
process.exit(0);
|
|
3335
|
+
};
|
|
3336
|
+
process.on("SIGINT", handleInterrupt);
|
|
3337
|
+
try {
|
|
3338
|
+
while (!cancelled) {
|
|
3339
|
+
try {
|
|
3340
|
+
const response = await axios.post(`${configuration.serverUrl}/v1/auth/request`, {
|
|
3341
|
+
publicKey: encodeBase64(keypair.publicKey)
|
|
3342
|
+
});
|
|
3343
|
+
if (response.data.state === "authorized") {
|
|
3344
|
+
let token = response.data.token;
|
|
3345
|
+
let r = decodeBase64(response.data.response);
|
|
3346
|
+
let decrypted = decryptWithEphemeralKey(r, keypair.secretKey);
|
|
3347
|
+
if (decrypted) {
|
|
3348
|
+
const credentials = {
|
|
3349
|
+
secret: decrypted,
|
|
3350
|
+
token
|
|
3351
|
+
};
|
|
3352
|
+
await writeCredentials(credentials);
|
|
3353
|
+
console.log("\n\n\u2713 Authentication successful\n");
|
|
3354
|
+
return credentials;
|
|
3355
|
+
} else {
|
|
3356
|
+
console.log("\n\nFailed to decrypt response. Please try again.");
|
|
3357
|
+
return null;
|
|
3358
|
+
}
|
|
3071
3359
|
}
|
|
3360
|
+
} catch (error) {
|
|
3361
|
+
console.log("\n\nFailed to check authentication status. Please try again.");
|
|
3362
|
+
return null;
|
|
3072
3363
|
}
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3364
|
+
process.stdout.write("\rWaiting for authentication" + ".".repeat(dots % 3 + 1) + " ");
|
|
3365
|
+
dots++;
|
|
3366
|
+
await delay(1e3);
|
|
3076
3367
|
}
|
|
3077
|
-
|
|
3368
|
+
} finally {
|
|
3369
|
+
process.off("SIGINT", handleInterrupt);
|
|
3078
3370
|
}
|
|
3079
3371
|
return null;
|
|
3080
3372
|
}
|
|
@@ -3305,12 +3597,11 @@ class ApiDaemonSession extends EventEmitter {
|
|
|
3305
3597
|
this.stopKeepAlive();
|
|
3306
3598
|
this.keepAliveInterval = setInterval(() => {
|
|
3307
3599
|
const payload = {
|
|
3308
|
-
type: "machine-scoped",
|
|
3309
3600
|
machineId: this.machineIdentity.machineId,
|
|
3310
3601
|
time: Date.now()
|
|
3311
3602
|
};
|
|
3312
|
-
logger.debugLargeJson(`[DAEMON SESSION] Emitting
|
|
3313
|
-
this.socket.emit("
|
|
3603
|
+
logger.debugLargeJson(`[DAEMON SESSION] Emitting machine-alive`, payload);
|
|
3604
|
+
this.socket.emit("machine-alive", payload);
|
|
3314
3605
|
}, 2e4);
|
|
3315
3606
|
}
|
|
3316
3607
|
stopKeepAlive() {
|
package/dist/lib.cjs
CHANGED
package/dist/lib.d.cts
CHANGED
|
@@ -339,12 +339,30 @@ declare const UserMessageSchema: z.ZodObject<{
|
|
|
339
339
|
meta: z.ZodOptional<z.ZodObject<{
|
|
340
340
|
sentFrom: z.ZodOptional<z.ZodString>;
|
|
341
341
|
permissionMode: z.ZodOptional<z.ZodString>;
|
|
342
|
+
model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
343
|
+
fallbackModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
344
|
+
customSystemPrompt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
345
|
+
appendSystemPrompt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
346
|
+
allowedTools: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
|
|
347
|
+
disallowedTools: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
|
|
342
348
|
}, "strip", z.ZodTypeAny, {
|
|
343
349
|
sentFrom?: string | undefined;
|
|
344
350
|
permissionMode?: string | undefined;
|
|
351
|
+
model?: string | null | undefined;
|
|
352
|
+
fallbackModel?: string | null | undefined;
|
|
353
|
+
customSystemPrompt?: string | null | undefined;
|
|
354
|
+
appendSystemPrompt?: string | null | undefined;
|
|
355
|
+
allowedTools?: string[] | null | undefined;
|
|
356
|
+
disallowedTools?: string[] | null | undefined;
|
|
345
357
|
}, {
|
|
346
358
|
sentFrom?: string | undefined;
|
|
347
359
|
permissionMode?: string | undefined;
|
|
360
|
+
model?: string | null | undefined;
|
|
361
|
+
fallbackModel?: string | null | undefined;
|
|
362
|
+
customSystemPrompt?: string | null | undefined;
|
|
363
|
+
appendSystemPrompt?: string | null | undefined;
|
|
364
|
+
allowedTools?: string[] | null | undefined;
|
|
365
|
+
disallowedTools?: string[] | null | undefined;
|
|
348
366
|
}>>;
|
|
349
367
|
}, "strip", z.ZodTypeAny, {
|
|
350
368
|
content: {
|
|
@@ -356,6 +374,12 @@ declare const UserMessageSchema: z.ZodObject<{
|
|
|
356
374
|
meta?: {
|
|
357
375
|
sentFrom?: string | undefined;
|
|
358
376
|
permissionMode?: string | undefined;
|
|
377
|
+
model?: string | null | undefined;
|
|
378
|
+
fallbackModel?: string | null | undefined;
|
|
379
|
+
customSystemPrompt?: string | null | undefined;
|
|
380
|
+
appendSystemPrompt?: string | null | undefined;
|
|
381
|
+
allowedTools?: string[] | null | undefined;
|
|
382
|
+
disallowedTools?: string[] | null | undefined;
|
|
359
383
|
} | undefined;
|
|
360
384
|
}, {
|
|
361
385
|
content: {
|
|
@@ -367,6 +391,12 @@ declare const UserMessageSchema: z.ZodObject<{
|
|
|
367
391
|
meta?: {
|
|
368
392
|
sentFrom?: string | undefined;
|
|
369
393
|
permissionMode?: string | undefined;
|
|
394
|
+
model?: string | null | undefined;
|
|
395
|
+
fallbackModel?: string | null | undefined;
|
|
396
|
+
customSystemPrompt?: string | null | undefined;
|
|
397
|
+
appendSystemPrompt?: string | null | undefined;
|
|
398
|
+
allowedTools?: string[] | null | undefined;
|
|
399
|
+
disallowedTools?: string[] | null | undefined;
|
|
370
400
|
} | undefined;
|
|
371
401
|
}>;
|
|
372
402
|
type UserMessage = z.infer<typeof UserMessageSchema>;
|
package/dist/lib.d.mts
CHANGED
|
@@ -339,12 +339,30 @@ declare const UserMessageSchema: z.ZodObject<{
|
|
|
339
339
|
meta: z.ZodOptional<z.ZodObject<{
|
|
340
340
|
sentFrom: z.ZodOptional<z.ZodString>;
|
|
341
341
|
permissionMode: z.ZodOptional<z.ZodString>;
|
|
342
|
+
model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
343
|
+
fallbackModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
344
|
+
customSystemPrompt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
345
|
+
appendSystemPrompt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
346
|
+
allowedTools: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
|
|
347
|
+
disallowedTools: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
|
|
342
348
|
}, "strip", z.ZodTypeAny, {
|
|
343
349
|
sentFrom?: string | undefined;
|
|
344
350
|
permissionMode?: string | undefined;
|
|
351
|
+
model?: string | null | undefined;
|
|
352
|
+
fallbackModel?: string | null | undefined;
|
|
353
|
+
customSystemPrompt?: string | null | undefined;
|
|
354
|
+
appendSystemPrompt?: string | null | undefined;
|
|
355
|
+
allowedTools?: string[] | null | undefined;
|
|
356
|
+
disallowedTools?: string[] | null | undefined;
|
|
345
357
|
}, {
|
|
346
358
|
sentFrom?: string | undefined;
|
|
347
359
|
permissionMode?: string | undefined;
|
|
360
|
+
model?: string | null | undefined;
|
|
361
|
+
fallbackModel?: string | null | undefined;
|
|
362
|
+
customSystemPrompt?: string | null | undefined;
|
|
363
|
+
appendSystemPrompt?: string | null | undefined;
|
|
364
|
+
allowedTools?: string[] | null | undefined;
|
|
365
|
+
disallowedTools?: string[] | null | undefined;
|
|
348
366
|
}>>;
|
|
349
367
|
}, "strip", z.ZodTypeAny, {
|
|
350
368
|
content: {
|
|
@@ -356,6 +374,12 @@ declare const UserMessageSchema: z.ZodObject<{
|
|
|
356
374
|
meta?: {
|
|
357
375
|
sentFrom?: string | undefined;
|
|
358
376
|
permissionMode?: string | undefined;
|
|
377
|
+
model?: string | null | undefined;
|
|
378
|
+
fallbackModel?: string | null | undefined;
|
|
379
|
+
customSystemPrompt?: string | null | undefined;
|
|
380
|
+
appendSystemPrompt?: string | null | undefined;
|
|
381
|
+
allowedTools?: string[] | null | undefined;
|
|
382
|
+
disallowedTools?: string[] | null | undefined;
|
|
359
383
|
} | undefined;
|
|
360
384
|
}, {
|
|
361
385
|
content: {
|
|
@@ -367,6 +391,12 @@ declare const UserMessageSchema: z.ZodObject<{
|
|
|
367
391
|
meta?: {
|
|
368
392
|
sentFrom?: string | undefined;
|
|
369
393
|
permissionMode?: string | undefined;
|
|
394
|
+
model?: string | null | undefined;
|
|
395
|
+
fallbackModel?: string | null | undefined;
|
|
396
|
+
customSystemPrompt?: string | null | undefined;
|
|
397
|
+
appendSystemPrompt?: string | null | undefined;
|
|
398
|
+
allowedTools?: string[] | null | undefined;
|
|
399
|
+
disallowedTools?: string[] | null | undefined;
|
|
370
400
|
} | undefined;
|
|
371
401
|
}>;
|
|
372
402
|
type UserMessage = z.infer<typeof UserMessageSchema>;
|
package/dist/lib.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, i as initLoggerWithGlobalConfiguration, b as initializeConfiguration, l as logger } from './types-
|
|
1
|
+
export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, i as initLoggerWithGlobalConfiguration, b as initializeConfiguration, l as logger } from './types-Cqy5Dx2C.mjs';
|
|
2
2
|
import 'axios';
|
|
3
3
|
import 'chalk';
|
|
4
4
|
import 'fs';
|
|
@@ -245,8 +245,20 @@ z.z.object({
|
|
|
245
245
|
const MessageMetaSchema = z.z.object({
|
|
246
246
|
sentFrom: z.z.string().optional(),
|
|
247
247
|
// Source identifier
|
|
248
|
-
permissionMode: z.z.string().optional()
|
|
248
|
+
permissionMode: z.z.string().optional(),
|
|
249
249
|
// Permission mode for this message
|
|
250
|
+
model: z.z.string().nullable().optional(),
|
|
251
|
+
// Model name for this message (null = reset)
|
|
252
|
+
fallbackModel: z.z.string().nullable().optional(),
|
|
253
|
+
// Fallback model for this message (null = reset)
|
|
254
|
+
customSystemPrompt: z.z.string().nullable().optional(),
|
|
255
|
+
// Custom system prompt for this message (null = reset)
|
|
256
|
+
appendSystemPrompt: z.z.string().nullable().optional(),
|
|
257
|
+
// Append to system prompt for this message (null = reset)
|
|
258
|
+
allowedTools: z.z.array(z.z.string()).nullable().optional(),
|
|
259
|
+
// Allowed tools for this message (null = reset)
|
|
260
|
+
disallowedTools: z.z.array(z.z.string()).nullable().optional()
|
|
261
|
+
// Disallowed tools for this message (null = reset)
|
|
250
262
|
});
|
|
251
263
|
z.z.object({
|
|
252
264
|
session: z.z.object({
|
|
@@ -534,7 +546,6 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
534
546
|
*/
|
|
535
547
|
keepAlive(thinking, mode) {
|
|
536
548
|
this.socket.volatile.emit("session-alive", {
|
|
537
|
-
type: "session-scoped",
|
|
538
549
|
sid: this.sessionId,
|
|
539
550
|
time: Date.now(),
|
|
540
551
|
thinking,
|
|
@@ -243,8 +243,20 @@ z.object({
|
|
|
243
243
|
const MessageMetaSchema = z.object({
|
|
244
244
|
sentFrom: z.string().optional(),
|
|
245
245
|
// Source identifier
|
|
246
|
-
permissionMode: z.string().optional()
|
|
246
|
+
permissionMode: z.string().optional(),
|
|
247
247
|
// Permission mode for this message
|
|
248
|
+
model: z.string().nullable().optional(),
|
|
249
|
+
// Model name for this message (null = reset)
|
|
250
|
+
fallbackModel: z.string().nullable().optional(),
|
|
251
|
+
// Fallback model for this message (null = reset)
|
|
252
|
+
customSystemPrompt: z.string().nullable().optional(),
|
|
253
|
+
// Custom system prompt for this message (null = reset)
|
|
254
|
+
appendSystemPrompt: z.string().nullable().optional(),
|
|
255
|
+
// Append to system prompt for this message (null = reset)
|
|
256
|
+
allowedTools: z.array(z.string()).nullable().optional(),
|
|
257
|
+
// Allowed tools for this message (null = reset)
|
|
258
|
+
disallowedTools: z.array(z.string()).nullable().optional()
|
|
259
|
+
// Disallowed tools for this message (null = reset)
|
|
248
260
|
});
|
|
249
261
|
z.object({
|
|
250
262
|
session: z.object({
|
|
@@ -532,7 +544,6 @@ class ApiSessionClient extends EventEmitter {
|
|
|
532
544
|
*/
|
|
533
545
|
keepAlive(thinking, mode) {
|
|
534
546
|
this.socket.volatile.emit("session-alive", {
|
|
535
|
-
type: "session-scoped",
|
|
536
547
|
sid: this.sessionId,
|
|
537
548
|
time: Date.now(),
|
|
538
549
|
thinking,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "happy-coder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Claude Code session sharing CLI",
|
|
5
5
|
"author": "Kirill Dubovitskiy",
|
|
6
6
|
"license": "MIT",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"http-proxy": "^1.18.1",
|
|
69
69
|
"http-proxy-middleware": "^3.0.5",
|
|
70
70
|
"ink": "^6.1.0",
|
|
71
|
-
"
|
|
71
|
+
"open": "^10.2.0",
|
|
72
72
|
"qrcode-terminal": "^0.12.0",
|
|
73
73
|
"react": "^19.1.1",
|
|
74
74
|
"socket.io-client": "^4.8.1",
|