@zhigang1992/happy-cli 0.12.1
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 +60 -0
- package/bin/happy-mcp.mjs +32 -0
- package/bin/happy.mjs +35 -0
- package/dist/codex/happyMcpStdioBridge.cjs +80 -0
- package/dist/codex/happyMcpStdioBridge.d.cts +2 -0
- package/dist/codex/happyMcpStdioBridge.d.mts +2 -0
- package/dist/codex/happyMcpStdioBridge.mjs +78 -0
- package/dist/index-BOBrKhX5.cjs +6655 -0
- package/dist/index-DsHtmQqP.mjs +6624 -0
- package/dist/index.cjs +42 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +39 -0
- package/dist/lib.cjs +31 -0
- package/dist/lib.d.cts +817 -0
- package/dist/lib.d.mts +817 -0
- package/dist/lib.mjs +21 -0
- package/dist/list-BW6QBLa1.cjs +328 -0
- package/dist/list-hET5tyMc.mjs +326 -0
- package/dist/prompt-DXkgjktW.cjs +203 -0
- package/dist/prompt-Dz7G8yGx.mjs +201 -0
- package/dist/runCodex-CLGYMNs2.mjs +1335 -0
- package/dist/runCodex-CylcX5Ug.cjs +1338 -0
- package/dist/types-BsjUgWOx.cjs +2264 -0
- package/dist/types-CGco5Y-r.mjs +2213 -0
- package/package.json +126 -0
- package/scripts/claude_local_launcher.cjs +98 -0
- package/scripts/claude_remote_launcher.cjs +13 -0
- package/scripts/ripgrep_launcher.cjs +33 -0
- package/scripts/unpack-tools.cjs +163 -0
- package/tools/archives/difftastic-LICENSE +21 -0
- package/tools/archives/difftastic-arm64-darwin.tar.gz +0 -0
- package/tools/archives/difftastic-arm64-linux.tar.gz +0 -0
- package/tools/archives/difftastic-x64-darwin.tar.gz +0 -0
- package/tools/archives/difftastic-x64-linux.tar.gz +0 -0
- package/tools/archives/difftastic-x64-win32.tar.gz +0 -0
- package/tools/archives/ripgrep-LICENSE +3 -0
- package/tools/archives/ripgrep-arm64-darwin.tar.gz +0 -0
- package/tools/archives/ripgrep-arm64-linux.tar.gz +0 -0
- package/tools/archives/ripgrep-x64-darwin.tar.gz +0 -0
- package/tools/archives/ripgrep-x64-linux.tar.gz +0 -0
- package/tools/archives/ripgrep-x64-win32.tar.gz +0 -0
- package/tools/licenses/difftastic-LICENSE +21 -0
- package/tools/licenses/ripgrep-LICENSE +3 -0
- package/tools/unpacked/difft +0 -0
- package/tools/unpacked/rg +0 -0
- package/tools/unpacked/ripgrep.node +0 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var types = require('./types-BsjUgWOx.cjs');
|
|
4
|
+
var axios = require('axios');
|
|
5
|
+
var socket_ioClient = require('socket.io-client');
|
|
6
|
+
require('chalk');
|
|
7
|
+
require('fs');
|
|
8
|
+
require('node:fs');
|
|
9
|
+
require('node:os');
|
|
10
|
+
require('node:path');
|
|
11
|
+
require('node:fs/promises');
|
|
12
|
+
require('zod');
|
|
13
|
+
require('node:crypto');
|
|
14
|
+
require('tweetnacl');
|
|
15
|
+
require('node:events');
|
|
16
|
+
require('child_process');
|
|
17
|
+
require('util');
|
|
18
|
+
require('fs/promises');
|
|
19
|
+
require('crypto');
|
|
20
|
+
require('path');
|
|
21
|
+
require('url');
|
|
22
|
+
require('os');
|
|
23
|
+
require('expo-server-sdk');
|
|
24
|
+
|
|
25
|
+
const DEFAULT_TIMEOUT_MINUTES = 20;
|
|
26
|
+
async function promptSession(credentials, sessionId, promptText, timeoutMinutes = DEFAULT_TIMEOUT_MINUTES) {
|
|
27
|
+
try {
|
|
28
|
+
const serverUrl = types.configuration.serverUrl;
|
|
29
|
+
const response = await axios.get(`${serverUrl}/v2/sessions/active`, {
|
|
30
|
+
headers: {
|
|
31
|
+
"Authorization": `Bearer ${credentials.token}`
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const session = response.data.sessions.find((s) => s.id === sessionId);
|
|
35
|
+
if (!session) {
|
|
36
|
+
console.error(`Session ${sessionId} not found or not active.`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
let agentState = {};
|
|
40
|
+
if (session.agentState) {
|
|
41
|
+
try {
|
|
42
|
+
const key2 = credentials.encryption.type === "legacy" ? credentials.encryption.secret : credentials.encryption.machineKey;
|
|
43
|
+
const decrypted = types.decrypt(
|
|
44
|
+
key2,
|
|
45
|
+
credentials.encryption.type,
|
|
46
|
+
types.decodeBase64(session.agentState)
|
|
47
|
+
);
|
|
48
|
+
if (decrypted) {
|
|
49
|
+
agentState = decrypted;
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
types.logger.debug(`Failed to decrypt agent state for session ${sessionId}:`, error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (agentState.thinking) {
|
|
56
|
+
console.error("Claude is already working on a task. Please wait for it to finish.");
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const wsUrl = serverUrl.replace(/^http/, "ws");
|
|
60
|
+
const socket = socket_ioClient.io(wsUrl, {
|
|
61
|
+
path: "/v1/updates",
|
|
62
|
+
auth: {
|
|
63
|
+
token: credentials.token,
|
|
64
|
+
clientType: "session-scoped",
|
|
65
|
+
sessionId
|
|
66
|
+
},
|
|
67
|
+
transports: ["websocket", "polling"]
|
|
68
|
+
});
|
|
69
|
+
let isThinking = false;
|
|
70
|
+
let hasStartedThinking = false;
|
|
71
|
+
let completed = false;
|
|
72
|
+
await new Promise((resolve, reject) => {
|
|
73
|
+
const timeout = setTimeout(() => {
|
|
74
|
+
reject(new Error("Connection timeout"));
|
|
75
|
+
}, 1e4);
|
|
76
|
+
socket.on("connect", () => {
|
|
77
|
+
clearTimeout(timeout);
|
|
78
|
+
types.logger.debug(`Connected to WebSocket for session ${sessionId}`);
|
|
79
|
+
resolve();
|
|
80
|
+
});
|
|
81
|
+
socket.on("connect_error", (error) => {
|
|
82
|
+
clearTimeout(timeout);
|
|
83
|
+
reject(error);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
socket.on("update", (data) => {
|
|
87
|
+
types.logger.debugLargeJson("[prompt] Received update:", data);
|
|
88
|
+
if (data.body.t === "new-message") {
|
|
89
|
+
try {
|
|
90
|
+
const key2 = credentials.encryption.type === "legacy" ? credentials.encryption.secret : credentials.encryption.machineKey;
|
|
91
|
+
const content = types.decrypt(
|
|
92
|
+
key2,
|
|
93
|
+
credentials.encryption.type,
|
|
94
|
+
types.decodeBase64(data.body.message.content.c)
|
|
95
|
+
);
|
|
96
|
+
if (!content) {
|
|
97
|
+
types.logger.debug("Failed to decrypt message content");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (content.role === "agent" && content.content?.type === "output") {
|
|
101
|
+
const output = content.content.data;
|
|
102
|
+
if (output.type === "user" && output.message?.content) {
|
|
103
|
+
console.log("[User]:", output.message.content);
|
|
104
|
+
} else if (output.type === "assistant" && output.message?.content) {
|
|
105
|
+
if (Array.isArray(output.message.content)) {
|
|
106
|
+
for (const block of output.message.content) {
|
|
107
|
+
if (block.type === "text") {
|
|
108
|
+
console.log(block.text);
|
|
109
|
+
} else if (block.type === "tool_use") {
|
|
110
|
+
console.log(`
|
|
111
|
+
[Tool: ${block.name}]`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
types.logger.debug("Failed to decrypt or process message:", error);
|
|
119
|
+
}
|
|
120
|
+
} else if (data.body.t === "update-session" && data.body.agentState) {
|
|
121
|
+
try {
|
|
122
|
+
if (data.body.agentState.value) {
|
|
123
|
+
const key2 = credentials.encryption.type === "legacy" ? credentials.encryption.secret : credentials.encryption.machineKey;
|
|
124
|
+
const updatedState = types.decrypt(
|
|
125
|
+
key2,
|
|
126
|
+
credentials.encryption.type,
|
|
127
|
+
types.decodeBase64(data.body.agentState.value)
|
|
128
|
+
);
|
|
129
|
+
if (updatedState) {
|
|
130
|
+
types.logger.debug(`[prompt] Agent state updated: thinking=${updatedState.thinking}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
types.logger.debug("Failed to decrypt agent state:", error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
socket.on("ephemeral", (data) => {
|
|
139
|
+
if (data.type === "activity" && data.id === sessionId) {
|
|
140
|
+
types.logger.debug(`[prompt] Activity update: thinking=${data.thinking}, active=${data.active}`);
|
|
141
|
+
if (data.thinking && !hasStartedThinking) {
|
|
142
|
+
hasStartedThinking = true;
|
|
143
|
+
console.log("[Claude is thinking...]");
|
|
144
|
+
}
|
|
145
|
+
isThinking = data.thinking;
|
|
146
|
+
if (hasStartedThinking && !data.thinking) {
|
|
147
|
+
completed = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
const messageContent = {
|
|
152
|
+
role: "user",
|
|
153
|
+
content: {
|
|
154
|
+
type: "text",
|
|
155
|
+
text: promptText
|
|
156
|
+
},
|
|
157
|
+
meta: {
|
|
158
|
+
sentFrom: "cli"
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const key = credentials.encryption.type === "legacy" ? credentials.encryption.secret : credentials.encryption.machineKey;
|
|
162
|
+
const encryptedMessage = types.encodeBase64(types.encrypt(
|
|
163
|
+
key,
|
|
164
|
+
credentials.encryption.type,
|
|
165
|
+
messageContent
|
|
166
|
+
));
|
|
167
|
+
types.logger.debug("[prompt] Sending message to session...");
|
|
168
|
+
socket.emit("message", {
|
|
169
|
+
sid: sessionId,
|
|
170
|
+
message: encryptedMessage
|
|
171
|
+
});
|
|
172
|
+
console.log(`[Sent prompt to session ${sessionId}]`);
|
|
173
|
+
const checkInterval = setInterval(() => {
|
|
174
|
+
if (completed) {
|
|
175
|
+
clearInterval(checkInterval);
|
|
176
|
+
socket.close();
|
|
177
|
+
console.log("\n[Claude has finished]");
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
}, 500);
|
|
181
|
+
const timeoutMs = timeoutMinutes * 60 * 1e3;
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
clearInterval(checkInterval);
|
|
184
|
+
socket.close();
|
|
185
|
+
console.error(`
|
|
186
|
+
Timeout waiting for Claude to complete (${timeoutMinutes} minutes).`);
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}, timeoutMs);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (axios.isAxiosError(error)) {
|
|
191
|
+
if (error.response?.status === 401) {
|
|
192
|
+
console.error("Authentication failed. Please run `happy auth` to authenticate.");
|
|
193
|
+
} else {
|
|
194
|
+
console.error(`Failed to send prompt: ${error.response?.data?.message || error.message}`);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
console.error(`Failed to send prompt: ${error}`);
|
|
198
|
+
}
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
exports.promptSession = promptSession;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { c as configuration, b as decrypt, d as decodeBase64, l as logger, g as encodeBase64, h as encrypt } from './types-CGco5Y-r.mjs';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { io } from 'socket.io-client';
|
|
4
|
+
import 'chalk';
|
|
5
|
+
import 'fs';
|
|
6
|
+
import 'node:fs';
|
|
7
|
+
import 'node:os';
|
|
8
|
+
import 'node:path';
|
|
9
|
+
import 'node:fs/promises';
|
|
10
|
+
import 'zod';
|
|
11
|
+
import 'node:crypto';
|
|
12
|
+
import 'tweetnacl';
|
|
13
|
+
import 'node:events';
|
|
14
|
+
import 'child_process';
|
|
15
|
+
import 'util';
|
|
16
|
+
import 'fs/promises';
|
|
17
|
+
import 'crypto';
|
|
18
|
+
import 'path';
|
|
19
|
+
import 'url';
|
|
20
|
+
import 'os';
|
|
21
|
+
import 'expo-server-sdk';
|
|
22
|
+
|
|
23
|
+
const DEFAULT_TIMEOUT_MINUTES = 20;
|
|
24
|
+
async function promptSession(credentials, sessionId, promptText, timeoutMinutes = DEFAULT_TIMEOUT_MINUTES) {
|
|
25
|
+
try {
|
|
26
|
+
const serverUrl = configuration.serverUrl;
|
|
27
|
+
const response = await axios.get(`${serverUrl}/v2/sessions/active`, {
|
|
28
|
+
headers: {
|
|
29
|
+
"Authorization": `Bearer ${credentials.token}`
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
const session = response.data.sessions.find((s) => s.id === sessionId);
|
|
33
|
+
if (!session) {
|
|
34
|
+
console.error(`Session ${sessionId} not found or not active.`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
let agentState = {};
|
|
38
|
+
if (session.agentState) {
|
|
39
|
+
try {
|
|
40
|
+
const key2 = credentials.encryption.type === "legacy" ? credentials.encryption.secret : credentials.encryption.machineKey;
|
|
41
|
+
const decrypted = decrypt(
|
|
42
|
+
key2,
|
|
43
|
+
credentials.encryption.type,
|
|
44
|
+
decodeBase64(session.agentState)
|
|
45
|
+
);
|
|
46
|
+
if (decrypted) {
|
|
47
|
+
agentState = decrypted;
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
logger.debug(`Failed to decrypt agent state for session ${sessionId}:`, error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (agentState.thinking) {
|
|
54
|
+
console.error("Claude is already working on a task. Please wait for it to finish.");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const wsUrl = serverUrl.replace(/^http/, "ws");
|
|
58
|
+
const socket = io(wsUrl, {
|
|
59
|
+
path: "/v1/updates",
|
|
60
|
+
auth: {
|
|
61
|
+
token: credentials.token,
|
|
62
|
+
clientType: "session-scoped",
|
|
63
|
+
sessionId
|
|
64
|
+
},
|
|
65
|
+
transports: ["websocket", "polling"]
|
|
66
|
+
});
|
|
67
|
+
let isThinking = false;
|
|
68
|
+
let hasStartedThinking = false;
|
|
69
|
+
let completed = false;
|
|
70
|
+
await new Promise((resolve, reject) => {
|
|
71
|
+
const timeout = setTimeout(() => {
|
|
72
|
+
reject(new Error("Connection timeout"));
|
|
73
|
+
}, 1e4);
|
|
74
|
+
socket.on("connect", () => {
|
|
75
|
+
clearTimeout(timeout);
|
|
76
|
+
logger.debug(`Connected to WebSocket for session ${sessionId}`);
|
|
77
|
+
resolve();
|
|
78
|
+
});
|
|
79
|
+
socket.on("connect_error", (error) => {
|
|
80
|
+
clearTimeout(timeout);
|
|
81
|
+
reject(error);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
socket.on("update", (data) => {
|
|
85
|
+
logger.debugLargeJson("[prompt] Received update:", data);
|
|
86
|
+
if (data.body.t === "new-message") {
|
|
87
|
+
try {
|
|
88
|
+
const key2 = credentials.encryption.type === "legacy" ? credentials.encryption.secret : credentials.encryption.machineKey;
|
|
89
|
+
const content = decrypt(
|
|
90
|
+
key2,
|
|
91
|
+
credentials.encryption.type,
|
|
92
|
+
decodeBase64(data.body.message.content.c)
|
|
93
|
+
);
|
|
94
|
+
if (!content) {
|
|
95
|
+
logger.debug("Failed to decrypt message content");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (content.role === "agent" && content.content?.type === "output") {
|
|
99
|
+
const output = content.content.data;
|
|
100
|
+
if (output.type === "user" && output.message?.content) {
|
|
101
|
+
console.log("[User]:", output.message.content);
|
|
102
|
+
} else if (output.type === "assistant" && output.message?.content) {
|
|
103
|
+
if (Array.isArray(output.message.content)) {
|
|
104
|
+
for (const block of output.message.content) {
|
|
105
|
+
if (block.type === "text") {
|
|
106
|
+
console.log(block.text);
|
|
107
|
+
} else if (block.type === "tool_use") {
|
|
108
|
+
console.log(`
|
|
109
|
+
[Tool: ${block.name}]`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
logger.debug("Failed to decrypt or process message:", error);
|
|
117
|
+
}
|
|
118
|
+
} else if (data.body.t === "update-session" && data.body.agentState) {
|
|
119
|
+
try {
|
|
120
|
+
if (data.body.agentState.value) {
|
|
121
|
+
const key2 = credentials.encryption.type === "legacy" ? credentials.encryption.secret : credentials.encryption.machineKey;
|
|
122
|
+
const updatedState = decrypt(
|
|
123
|
+
key2,
|
|
124
|
+
credentials.encryption.type,
|
|
125
|
+
decodeBase64(data.body.agentState.value)
|
|
126
|
+
);
|
|
127
|
+
if (updatedState) {
|
|
128
|
+
logger.debug(`[prompt] Agent state updated: thinking=${updatedState.thinking}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logger.debug("Failed to decrypt agent state:", error);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
socket.on("ephemeral", (data) => {
|
|
137
|
+
if (data.type === "activity" && data.id === sessionId) {
|
|
138
|
+
logger.debug(`[prompt] Activity update: thinking=${data.thinking}, active=${data.active}`);
|
|
139
|
+
if (data.thinking && !hasStartedThinking) {
|
|
140
|
+
hasStartedThinking = true;
|
|
141
|
+
console.log("[Claude is thinking...]");
|
|
142
|
+
}
|
|
143
|
+
isThinking = data.thinking;
|
|
144
|
+
if (hasStartedThinking && !data.thinking) {
|
|
145
|
+
completed = true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
const messageContent = {
|
|
150
|
+
role: "user",
|
|
151
|
+
content: {
|
|
152
|
+
type: "text",
|
|
153
|
+
text: promptText
|
|
154
|
+
},
|
|
155
|
+
meta: {
|
|
156
|
+
sentFrom: "cli"
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const key = credentials.encryption.type === "legacy" ? credentials.encryption.secret : credentials.encryption.machineKey;
|
|
160
|
+
const encryptedMessage = encodeBase64(encrypt(
|
|
161
|
+
key,
|
|
162
|
+
credentials.encryption.type,
|
|
163
|
+
messageContent
|
|
164
|
+
));
|
|
165
|
+
logger.debug("[prompt] Sending message to session...");
|
|
166
|
+
socket.emit("message", {
|
|
167
|
+
sid: sessionId,
|
|
168
|
+
message: encryptedMessage
|
|
169
|
+
});
|
|
170
|
+
console.log(`[Sent prompt to session ${sessionId}]`);
|
|
171
|
+
const checkInterval = setInterval(() => {
|
|
172
|
+
if (completed) {
|
|
173
|
+
clearInterval(checkInterval);
|
|
174
|
+
socket.close();
|
|
175
|
+
console.log("\n[Claude has finished]");
|
|
176
|
+
process.exit(0);
|
|
177
|
+
}
|
|
178
|
+
}, 500);
|
|
179
|
+
const timeoutMs = timeoutMinutes * 60 * 1e3;
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
clearInterval(checkInterval);
|
|
182
|
+
socket.close();
|
|
183
|
+
console.error(`
|
|
184
|
+
Timeout waiting for Claude to complete (${timeoutMinutes} minutes).`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}, timeoutMs);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (axios.isAxiosError(error)) {
|
|
189
|
+
if (error.response?.status === 401) {
|
|
190
|
+
console.error("Authentication failed. Please run `happy auth` to authenticate.");
|
|
191
|
+
} else {
|
|
192
|
+
console.error(`Failed to send prompt: ${error.response?.data?.message || error.message}`);
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
console.error(`Failed to send prompt: ${error}`);
|
|
196
|
+
}
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export { promptSession };
|