happy-imou-cloud 2.0.0 → 2.0.2
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/{BaseReasoningProcessor-BRCQXCZY.cjs → BaseReasoningProcessor-B6tJ_eL5.cjs} +96 -9
- package/dist/{BaseReasoningProcessor-BKLRCKTU.mjs → BaseReasoningProcessor-D8VhEbs2.mjs} +95 -10
- package/dist/{api-D7OK-mML.cjs → api-D2Njw9Im.cjs} +124 -6
- package/dist/{api-BGXYX0yH.mjs → api-MYhAGPLn.mjs} +122 -7
- package/dist/{command-G85giEAF.cjs → command-CVldr51S.cjs} +3 -3
- package/dist/{command-CnLtKtP-.mjs → command-nmK6O-ab.mjs} +3 -3
- package/dist/{index-C7Y0R-MI.mjs → index-B97L7qLD.mjs} +689 -229
- package/dist/{index-B_wlQBy2.cjs → index-Bg-YziG2.cjs} +691 -229
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +7 -0
- package/dist/lib.d.mts +7 -0
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-DHgf1CTG.cjs → persistence-D_2GkJAO.cjs} +28 -6
- package/dist/{persistence-BA_unuca.mjs → persistence-Dkm7rm8k.mjs} +29 -7
- package/dist/{registerKillSessionHandler-C2-yHm1V.mjs → registerKillSessionHandler-5GbrO0FM.mjs} +6 -4
- package/dist/{registerKillSessionHandler-CLREXN11.cjs → registerKillSessionHandler-BAXmJQRt.cjs} +6 -4
- package/dist/{runClaude-CwAitpX-.cjs → runClaude-B-GNEkKg.cjs} +237 -45
- package/dist/{runClaude-uNC5Eym4.mjs → runClaude-Cii3R2Fv.mjs} +238 -46
- package/dist/{runCodex-B-05E-YZ.mjs → runCodex-C--ZwAhl.mjs} +636 -819
- package/dist/{runCodex-Cm0VTqw_.cjs → runCodex-CPHyGwj9.cjs} +639 -819
- package/dist/{runGemini-_biXvQAH.mjs → runGemini-CQp7Nuzn.mjs} +20 -16
- package/dist/{runGemini-CLWjwDYS.cjs → runGemini-DaDz1bzQ.cjs} +20 -16
- package/package.json +14 -15
- package/scripts/env-wrapper.cjs +11 -11
- package/scripts/setup-dev.cjs +4 -4
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var os = require('node:os');
|
|
4
4
|
var path = require('node:path');
|
|
5
|
-
var api = require('./api-
|
|
6
|
-
var index = require('./index-
|
|
5
|
+
var api = require('./api-D2Njw9Im.cjs');
|
|
6
|
+
var index = require('./index-Bg-YziG2.cjs');
|
|
7
|
+
var node_events = require('node:events');
|
|
7
8
|
var node_crypto = require('node:crypto');
|
|
8
9
|
|
|
9
10
|
function createSessionMetadata(opts) {
|
|
@@ -31,7 +32,10 @@ function createSessionMetadata(opts) {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
function createOfflineSessionStub(sessionTag) {
|
|
34
|
-
|
|
35
|
+
const emitter = new node_events.EventEmitter();
|
|
36
|
+
let metadata = null;
|
|
37
|
+
let agentState = null;
|
|
38
|
+
const stub = {
|
|
35
39
|
sessionId: `offline-${sessionTag}`,
|
|
36
40
|
sendCodexMessage: () => {
|
|
37
41
|
},
|
|
@@ -53,20 +57,29 @@ function createOfflineSessionStub(sessionTag) {
|
|
|
53
57
|
},
|
|
54
58
|
close: async () => {
|
|
55
59
|
},
|
|
56
|
-
updateMetadata: () => {
|
|
60
|
+
updateMetadata: (handler) => {
|
|
61
|
+
metadata = handler(metadata || {});
|
|
62
|
+
emitter.emit("metadata-updated", metadata);
|
|
57
63
|
},
|
|
58
|
-
updateAgentState: () => {
|
|
64
|
+
updateAgentState: (handler) => {
|
|
65
|
+
agentState = handler(agentState || {});
|
|
66
|
+
emitter.emit("agent-state-updated", agentState);
|
|
59
67
|
},
|
|
60
|
-
getMetadataSnapshot: () =>
|
|
61
|
-
getAgentStateSnapshot: () =>
|
|
62
|
-
waitForMetadataUpdate: async () =>
|
|
68
|
+
getMetadataSnapshot: () => metadata,
|
|
69
|
+
getAgentStateSnapshot: () => agentState,
|
|
70
|
+
waitForMetadataUpdate: async () => metadata,
|
|
63
71
|
onUserMessage: () => {
|
|
64
72
|
},
|
|
65
73
|
rpcHandlerManager: {
|
|
66
74
|
registerHandler: () => {
|
|
67
75
|
}
|
|
68
|
-
}
|
|
76
|
+
},
|
|
77
|
+
on: emitter.on.bind(emitter),
|
|
78
|
+
once: emitter.once.bind(emitter),
|
|
79
|
+
off: emitter.off.bind(emitter),
|
|
80
|
+
emit: emitter.emit.bind(emitter)
|
|
69
81
|
};
|
|
82
|
+
return stub;
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
function setupOfflineReconnection(opts) {
|
|
@@ -96,6 +109,15 @@ function setupOfflineReconnection(opts) {
|
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
const INTERACTION_SUPERSEDED_ERROR = "Interaction superseded by new user message";
|
|
112
|
+
const INTERACTION_TIMED_OUT_ERROR = "Interaction timed out waiting for user response";
|
|
113
|
+
const DEFAULT_INTERACTION_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
114
|
+
function getPendingInteractionTimeoutMs() {
|
|
115
|
+
const raw = Number(process.env.HAPPY_INTERACTION_TIMEOUT_MS);
|
|
116
|
+
if (Number.isFinite(raw) && raw > 0) {
|
|
117
|
+
return raw;
|
|
118
|
+
}
|
|
119
|
+
return DEFAULT_INTERACTION_TIMEOUT_MS;
|
|
120
|
+
}
|
|
99
121
|
class BasePermissionHandler {
|
|
100
122
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
101
123
|
session;
|
|
@@ -126,6 +148,7 @@ class BasePermissionHandler {
|
|
|
126
148
|
return;
|
|
127
149
|
}
|
|
128
150
|
this.pendingRequests.delete(response.id);
|
|
151
|
+
this.clearPendingRequestTimeout(pending);
|
|
129
152
|
const result = response.approved ? { decision: response.decision === "approved_for_session" ? "approved_for_session" : "approved" } : { decision: response.decision === "denied" ? "denied" : "abort" };
|
|
130
153
|
pending.resolve(result);
|
|
131
154
|
this.session.updateAgentState((currentState) => {
|
|
@@ -167,6 +190,22 @@ class BasePermissionHandler {
|
|
|
167
190
|
}
|
|
168
191
|
}));
|
|
169
192
|
}
|
|
193
|
+
registerPendingRequest(toolCallId, toolName, input, logSuffix = "") {
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const pending = {
|
|
196
|
+
resolve,
|
|
197
|
+
reject,
|
|
198
|
+
toolName,
|
|
199
|
+
input
|
|
200
|
+
};
|
|
201
|
+
pending.timeoutHandle = setTimeout(() => {
|
|
202
|
+
this.handlePendingRequestTimeout(toolCallId, pending);
|
|
203
|
+
}, getPendingInteractionTimeoutMs());
|
|
204
|
+
this.pendingRequests.set(toolCallId, pending);
|
|
205
|
+
this.addPendingRequestToState(toolCallId, toolName, input);
|
|
206
|
+
api.logger.debug(`${this.getLogPrefix()} Permission request sent for tool: ${toolName} (${toolCallId})${logSuffix}`);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
170
209
|
hasPendingRequests() {
|
|
171
210
|
return this.pendingRequests.size > 0;
|
|
172
211
|
}
|
|
@@ -178,6 +217,7 @@ class BasePermissionHandler {
|
|
|
178
217
|
this.pendingRequests.clear();
|
|
179
218
|
const completedAt = Date.now();
|
|
180
219
|
for (const [, pending] of pendingSnapshot) {
|
|
220
|
+
this.clearPendingRequestTimeout(pending);
|
|
181
221
|
pending.resolve({ decision: "abort" });
|
|
182
222
|
}
|
|
183
223
|
this.session.updateAgentState((currentState) => {
|
|
@@ -221,6 +261,7 @@ class BasePermissionHandler {
|
|
|
221
261
|
this.pendingRequests.clear();
|
|
222
262
|
for (const [id, pending] of pendingSnapshot) {
|
|
223
263
|
try {
|
|
264
|
+
this.clearPendingRequestTimeout(pending);
|
|
224
265
|
pending.reject(new Error("Session reset"));
|
|
225
266
|
} catch (err) {
|
|
226
267
|
api.logger.debug(`${this.getLogPrefix()} Error rejecting pending request ${id}:`, err);
|
|
@@ -248,6 +289,50 @@ class BasePermissionHandler {
|
|
|
248
289
|
this.isResetting = false;
|
|
249
290
|
}
|
|
250
291
|
}
|
|
292
|
+
clearPendingRequestTimeout(pending) {
|
|
293
|
+
if (pending?.timeoutHandle) {
|
|
294
|
+
clearTimeout(pending.timeoutHandle);
|
|
295
|
+
pending.timeoutHandle = void 0;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
handlePendingRequestTimeout(toolCallId, pending) {
|
|
299
|
+
const active = this.pendingRequests.get(toolCallId);
|
|
300
|
+
if (!active || active !== pending) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
this.pendingRequests.delete(toolCallId);
|
|
304
|
+
this.clearPendingRequestTimeout(active);
|
|
305
|
+
active.resolve({ decision: "abort" });
|
|
306
|
+
this.session.updateAgentState((currentState) => {
|
|
307
|
+
const request = currentState.requests?.[toolCallId] || {
|
|
308
|
+
tool: active.toolName,
|
|
309
|
+
arguments: active.input,
|
|
310
|
+
createdAt: Date.now(),
|
|
311
|
+
requestKind: "permission"
|
|
312
|
+
};
|
|
313
|
+
const { [toolCallId]: _, ...remainingRequests } = currentState.requests || {};
|
|
314
|
+
return {
|
|
315
|
+
...currentState,
|
|
316
|
+
requests: remainingRequests,
|
|
317
|
+
completedRequests: {
|
|
318
|
+
...currentState.completedRequests,
|
|
319
|
+
[toolCallId]: {
|
|
320
|
+
...request,
|
|
321
|
+
completedAt: Date.now(),
|
|
322
|
+
status: "canceled",
|
|
323
|
+
reason: INTERACTION_TIMED_OUT_ERROR,
|
|
324
|
+
decision: "abort",
|
|
325
|
+
requestKind: request.requestKind || "permission"
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
this.session.sendSessionEvent({
|
|
331
|
+
type: "message",
|
|
332
|
+
message: "Pending interaction timed out waiting for a response. Send a new message to continue."
|
|
333
|
+
});
|
|
334
|
+
api.logger.debug(`${this.getLogPrefix()} Permission request timed out for ${active.toolName} (${toolCallId})`);
|
|
335
|
+
}
|
|
251
336
|
}
|
|
252
337
|
|
|
253
338
|
class BaseReasoningProcessor {
|
|
@@ -447,5 +532,7 @@ class BaseReasoningProcessor {
|
|
|
447
532
|
exports.BasePermissionHandler = BasePermissionHandler;
|
|
448
533
|
exports.BaseReasoningProcessor = BaseReasoningProcessor;
|
|
449
534
|
exports.INTERACTION_SUPERSEDED_ERROR = INTERACTION_SUPERSEDED_ERROR;
|
|
535
|
+
exports.INTERACTION_TIMED_OUT_ERROR = INTERACTION_TIMED_OUT_ERROR;
|
|
450
536
|
exports.createSessionMetadata = createSessionMetadata;
|
|
537
|
+
exports.getPendingInteractionTimeoutMs = getPendingInteractionTimeoutMs;
|
|
451
538
|
exports.setupOfflineReconnection = setupOfflineReconnection;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
-
import { c as configuration, p as packageJson, s as startOfflineReconnection, l as logger } from './api-
|
|
4
|
-
import { p as projectPath } from './index-
|
|
3
|
+
import { c as configuration, p as packageJson, s as startOfflineReconnection, l as logger } from './api-MYhAGPLn.mjs';
|
|
4
|
+
import { p as projectPath } from './index-B97L7qLD.mjs';
|
|
5
|
+
import { EventEmitter } from 'node:events';
|
|
5
6
|
import { randomUUID } from 'node:crypto';
|
|
6
7
|
|
|
7
8
|
function createSessionMetadata(opts) {
|
|
@@ -29,7 +30,10 @@ function createSessionMetadata(opts) {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
function createOfflineSessionStub(sessionTag) {
|
|
32
|
-
|
|
33
|
+
const emitter = new EventEmitter();
|
|
34
|
+
let metadata = null;
|
|
35
|
+
let agentState = null;
|
|
36
|
+
const stub = {
|
|
33
37
|
sessionId: `offline-${sessionTag}`,
|
|
34
38
|
sendCodexMessage: () => {
|
|
35
39
|
},
|
|
@@ -51,20 +55,29 @@ function createOfflineSessionStub(sessionTag) {
|
|
|
51
55
|
},
|
|
52
56
|
close: async () => {
|
|
53
57
|
},
|
|
54
|
-
updateMetadata: () => {
|
|
58
|
+
updateMetadata: (handler) => {
|
|
59
|
+
metadata = handler(metadata || {});
|
|
60
|
+
emitter.emit("metadata-updated", metadata);
|
|
55
61
|
},
|
|
56
|
-
updateAgentState: () => {
|
|
62
|
+
updateAgentState: (handler) => {
|
|
63
|
+
agentState = handler(agentState || {});
|
|
64
|
+
emitter.emit("agent-state-updated", agentState);
|
|
57
65
|
},
|
|
58
|
-
getMetadataSnapshot: () =>
|
|
59
|
-
getAgentStateSnapshot: () =>
|
|
60
|
-
waitForMetadataUpdate: async () =>
|
|
66
|
+
getMetadataSnapshot: () => metadata,
|
|
67
|
+
getAgentStateSnapshot: () => agentState,
|
|
68
|
+
waitForMetadataUpdate: async () => metadata,
|
|
61
69
|
onUserMessage: () => {
|
|
62
70
|
},
|
|
63
71
|
rpcHandlerManager: {
|
|
64
72
|
registerHandler: () => {
|
|
65
73
|
}
|
|
66
|
-
}
|
|
74
|
+
},
|
|
75
|
+
on: emitter.on.bind(emitter),
|
|
76
|
+
once: emitter.once.bind(emitter),
|
|
77
|
+
off: emitter.off.bind(emitter),
|
|
78
|
+
emit: emitter.emit.bind(emitter)
|
|
67
79
|
};
|
|
80
|
+
return stub;
|
|
68
81
|
}
|
|
69
82
|
|
|
70
83
|
function setupOfflineReconnection(opts) {
|
|
@@ -94,6 +107,15 @@ function setupOfflineReconnection(opts) {
|
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
const INTERACTION_SUPERSEDED_ERROR = "Interaction superseded by new user message";
|
|
110
|
+
const INTERACTION_TIMED_OUT_ERROR = "Interaction timed out waiting for user response";
|
|
111
|
+
const DEFAULT_INTERACTION_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
112
|
+
function getPendingInteractionTimeoutMs() {
|
|
113
|
+
const raw = Number(process.env.HAPPY_INTERACTION_TIMEOUT_MS);
|
|
114
|
+
if (Number.isFinite(raw) && raw > 0) {
|
|
115
|
+
return raw;
|
|
116
|
+
}
|
|
117
|
+
return DEFAULT_INTERACTION_TIMEOUT_MS;
|
|
118
|
+
}
|
|
97
119
|
class BasePermissionHandler {
|
|
98
120
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
99
121
|
session;
|
|
@@ -124,6 +146,7 @@ class BasePermissionHandler {
|
|
|
124
146
|
return;
|
|
125
147
|
}
|
|
126
148
|
this.pendingRequests.delete(response.id);
|
|
149
|
+
this.clearPendingRequestTimeout(pending);
|
|
127
150
|
const result = response.approved ? { decision: response.decision === "approved_for_session" ? "approved_for_session" : "approved" } : { decision: response.decision === "denied" ? "denied" : "abort" };
|
|
128
151
|
pending.resolve(result);
|
|
129
152
|
this.session.updateAgentState((currentState) => {
|
|
@@ -165,6 +188,22 @@ class BasePermissionHandler {
|
|
|
165
188
|
}
|
|
166
189
|
}));
|
|
167
190
|
}
|
|
191
|
+
registerPendingRequest(toolCallId, toolName, input, logSuffix = "") {
|
|
192
|
+
return new Promise((resolve, reject) => {
|
|
193
|
+
const pending = {
|
|
194
|
+
resolve,
|
|
195
|
+
reject,
|
|
196
|
+
toolName,
|
|
197
|
+
input
|
|
198
|
+
};
|
|
199
|
+
pending.timeoutHandle = setTimeout(() => {
|
|
200
|
+
this.handlePendingRequestTimeout(toolCallId, pending);
|
|
201
|
+
}, getPendingInteractionTimeoutMs());
|
|
202
|
+
this.pendingRequests.set(toolCallId, pending);
|
|
203
|
+
this.addPendingRequestToState(toolCallId, toolName, input);
|
|
204
|
+
logger.debug(`${this.getLogPrefix()} Permission request sent for tool: ${toolName} (${toolCallId})${logSuffix}`);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
168
207
|
hasPendingRequests() {
|
|
169
208
|
return this.pendingRequests.size > 0;
|
|
170
209
|
}
|
|
@@ -176,6 +215,7 @@ class BasePermissionHandler {
|
|
|
176
215
|
this.pendingRequests.clear();
|
|
177
216
|
const completedAt = Date.now();
|
|
178
217
|
for (const [, pending] of pendingSnapshot) {
|
|
218
|
+
this.clearPendingRequestTimeout(pending);
|
|
179
219
|
pending.resolve({ decision: "abort" });
|
|
180
220
|
}
|
|
181
221
|
this.session.updateAgentState((currentState) => {
|
|
@@ -219,6 +259,7 @@ class BasePermissionHandler {
|
|
|
219
259
|
this.pendingRequests.clear();
|
|
220
260
|
for (const [id, pending] of pendingSnapshot) {
|
|
221
261
|
try {
|
|
262
|
+
this.clearPendingRequestTimeout(pending);
|
|
222
263
|
pending.reject(new Error("Session reset"));
|
|
223
264
|
} catch (err) {
|
|
224
265
|
logger.debug(`${this.getLogPrefix()} Error rejecting pending request ${id}:`, err);
|
|
@@ -246,6 +287,50 @@ class BasePermissionHandler {
|
|
|
246
287
|
this.isResetting = false;
|
|
247
288
|
}
|
|
248
289
|
}
|
|
290
|
+
clearPendingRequestTimeout(pending) {
|
|
291
|
+
if (pending?.timeoutHandle) {
|
|
292
|
+
clearTimeout(pending.timeoutHandle);
|
|
293
|
+
pending.timeoutHandle = void 0;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
handlePendingRequestTimeout(toolCallId, pending) {
|
|
297
|
+
const active = this.pendingRequests.get(toolCallId);
|
|
298
|
+
if (!active || active !== pending) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
this.pendingRequests.delete(toolCallId);
|
|
302
|
+
this.clearPendingRequestTimeout(active);
|
|
303
|
+
active.resolve({ decision: "abort" });
|
|
304
|
+
this.session.updateAgentState((currentState) => {
|
|
305
|
+
const request = currentState.requests?.[toolCallId] || {
|
|
306
|
+
tool: active.toolName,
|
|
307
|
+
arguments: active.input,
|
|
308
|
+
createdAt: Date.now(),
|
|
309
|
+
requestKind: "permission"
|
|
310
|
+
};
|
|
311
|
+
const { [toolCallId]: _, ...remainingRequests } = currentState.requests || {};
|
|
312
|
+
return {
|
|
313
|
+
...currentState,
|
|
314
|
+
requests: remainingRequests,
|
|
315
|
+
completedRequests: {
|
|
316
|
+
...currentState.completedRequests,
|
|
317
|
+
[toolCallId]: {
|
|
318
|
+
...request,
|
|
319
|
+
completedAt: Date.now(),
|
|
320
|
+
status: "canceled",
|
|
321
|
+
reason: INTERACTION_TIMED_OUT_ERROR,
|
|
322
|
+
decision: "abort",
|
|
323
|
+
requestKind: request.requestKind || "permission"
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
});
|
|
328
|
+
this.session.sendSessionEvent({
|
|
329
|
+
type: "message",
|
|
330
|
+
message: "Pending interaction timed out waiting for a response. Send a new message to continue."
|
|
331
|
+
});
|
|
332
|
+
logger.debug(`${this.getLogPrefix()} Permission request timed out for ${active.toolName} (${toolCallId})`);
|
|
333
|
+
}
|
|
249
334
|
}
|
|
250
335
|
|
|
251
336
|
class BaseReasoningProcessor {
|
|
@@ -442,4 +527,4 @@ class BaseReasoningProcessor {
|
|
|
442
527
|
}
|
|
443
528
|
}
|
|
444
529
|
|
|
445
|
-
export { BasePermissionHandler as B, INTERACTION_SUPERSEDED_ERROR as I, BaseReasoningProcessor as a, createSessionMetadata as c, setupOfflineReconnection as s };
|
|
530
|
+
export { BasePermissionHandler as B, INTERACTION_SUPERSEDED_ERROR as I, BaseReasoningProcessor as a, INTERACTION_TIMED_OUT_ERROR as b, createSessionMetadata as c, getPendingInteractionTimeoutMs as g, setupOfflineReconnection as s };
|
|
@@ -19,7 +19,7 @@ var path$1 = require('path');
|
|
|
19
19
|
var expoServerSdk = require('expo-server-sdk');
|
|
20
20
|
|
|
21
21
|
var name = "happy-imou-cloud";
|
|
22
|
-
var version = "2.0.
|
|
22
|
+
var version = "2.0.2";
|
|
23
23
|
var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
|
|
24
24
|
var author = "long.zhu";
|
|
25
25
|
var license = "MIT";
|
|
@@ -102,8 +102,7 @@ var scripts = {
|
|
|
102
102
|
"unlink:dev": "node scripts/link-dev.cjs unlink"
|
|
103
103
|
};
|
|
104
104
|
var dependencies = {
|
|
105
|
-
"@agentclientprotocol/sdk": "^0.
|
|
106
|
-
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
105
|
+
"@agentclientprotocol/sdk": "^0.14.1",
|
|
107
106
|
"@stablelib/base64": "^2.0.1",
|
|
108
107
|
"@stablelib/hex": "^2.0.1",
|
|
109
108
|
"@types/cross-spawn": "^6.0.6",
|
|
@@ -433,7 +432,7 @@ async function listDaemonLogFiles(limit = 50) {
|
|
|
433
432
|
return { file, path: fullPath, modified: stats.mtime };
|
|
434
433
|
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
435
434
|
try {
|
|
436
|
-
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-
|
|
435
|
+
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-D_2GkJAO.cjs'); });
|
|
437
436
|
const state = await readDaemonState();
|
|
438
437
|
if (!state) {
|
|
439
438
|
return logs;
|
|
@@ -1362,6 +1361,8 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1362
1361
|
encryptionKey;
|
|
1363
1362
|
encryptionVariant;
|
|
1364
1363
|
pendingReliableCodexMessages = [];
|
|
1364
|
+
reconnectAfterServerDisconnectTimer = null;
|
|
1365
|
+
lastSocketServerError = null;
|
|
1365
1366
|
constructor(credentials, session) {
|
|
1366
1367
|
super();
|
|
1367
1368
|
this.credentials = credentials;
|
|
@@ -1396,6 +1397,8 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1396
1397
|
});
|
|
1397
1398
|
this.socket.on("connect", () => {
|
|
1398
1399
|
logger.debug("Socket connected successfully");
|
|
1400
|
+
this.clearReconnectAfterServerDisconnectTimer();
|
|
1401
|
+
this.lastSocketServerError = null;
|
|
1399
1402
|
this.rpcHandlerManager.onSocketConnect(this.socket);
|
|
1400
1403
|
this.flushReliableCodexMessages();
|
|
1401
1404
|
});
|
|
@@ -1405,6 +1408,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1405
1408
|
this.socket.on("disconnect", (reason) => {
|
|
1406
1409
|
logger.debug("[API] Socket disconnected:", reason);
|
|
1407
1410
|
this.rpcHandlerManager.onSocketDisconnect();
|
|
1411
|
+
this.retryAfterServerDisconnect(reason);
|
|
1408
1412
|
});
|
|
1409
1413
|
this.socket.on("connect_error", (error) => {
|
|
1410
1414
|
logger.debug("[API] Socket connection error:", error);
|
|
@@ -1452,6 +1456,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1452
1456
|
});
|
|
1453
1457
|
this.socket.on("error", (error) => {
|
|
1454
1458
|
logger.debug("[API] Socket error:", error);
|
|
1459
|
+
this.lastSocketServerError = this.normalizeSocketError(error);
|
|
1455
1460
|
});
|
|
1456
1461
|
this.socket.connect();
|
|
1457
1462
|
}
|
|
@@ -1723,6 +1728,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1723
1728
|
}
|
|
1724
1729
|
async close() {
|
|
1725
1730
|
logger.debug("[API] socket.close() called");
|
|
1731
|
+
this.clearReconnectAfterServerDisconnectTimer();
|
|
1726
1732
|
this.socket.close();
|
|
1727
1733
|
}
|
|
1728
1734
|
emitEncryptedSessionMessage(encrypted) {
|
|
@@ -1758,6 +1764,53 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1758
1764
|
return false;
|
|
1759
1765
|
}
|
|
1760
1766
|
}
|
|
1767
|
+
retryAfterServerDisconnect(reason) {
|
|
1768
|
+
if (reason !== "io server disconnect") {
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
const errorCode = this.lastSocketServerError?.error;
|
|
1772
|
+
if (errorCode !== "REQUEST_EXPIRED" && errorCode !== "REQUEST_REPLAYED") {
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
if (this.reconnectAfterServerDisconnectTimer || this.socket.connected) {
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
logger.debug("[API] Scheduling manual reconnect after retryable server disconnect", {
|
|
1779
|
+
reason,
|
|
1780
|
+
errorCode,
|
|
1781
|
+
message: this.lastSocketServerError?.message
|
|
1782
|
+
});
|
|
1783
|
+
this.reconnectAfterServerDisconnectTimer = setTimeout(() => {
|
|
1784
|
+
this.reconnectAfterServerDisconnectTimer = null;
|
|
1785
|
+
if (this.socket.connected) {
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
logger.debug("[API] Retrying socket connection after server disconnect", {
|
|
1789
|
+
errorCode,
|
|
1790
|
+
message: this.lastSocketServerError?.message
|
|
1791
|
+
});
|
|
1792
|
+
this.socket.connect();
|
|
1793
|
+
}, 1e3);
|
|
1794
|
+
}
|
|
1795
|
+
clearReconnectAfterServerDisconnectTimer() {
|
|
1796
|
+
if (!this.reconnectAfterServerDisconnectTimer) {
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
clearTimeout(this.reconnectAfterServerDisconnectTimer);
|
|
1800
|
+
this.reconnectAfterServerDisconnectTimer = null;
|
|
1801
|
+
}
|
|
1802
|
+
normalizeSocketError(error) {
|
|
1803
|
+
if (!error || typeof error !== "object") {
|
|
1804
|
+
return null;
|
|
1805
|
+
}
|
|
1806
|
+
const candidate = error;
|
|
1807
|
+
const errorCode = typeof candidate.error === "string" ? candidate.error : void 0;
|
|
1808
|
+
const message = typeof candidate.message === "string" ? candidate.message : void 0;
|
|
1809
|
+
if (!errorCode && !message) {
|
|
1810
|
+
return null;
|
|
1811
|
+
}
|
|
1812
|
+
return { error: errorCode, message };
|
|
1813
|
+
}
|
|
1761
1814
|
}
|
|
1762
1815
|
function createAbortError() {
|
|
1763
1816
|
const error = new Error("The operation was aborted");
|
|
@@ -2099,6 +2152,42 @@ class PushNotificationClient {
|
|
|
2099
2152
|
}
|
|
2100
2153
|
}
|
|
2101
2154
|
|
|
2155
|
+
const AUTHENTICATION_REQUIRED_MESSAGE = 'Happy authentication failed. Run "hicloud auth login --force" to re-authenticate.';
|
|
2156
|
+
const SIGNING_BOOTSTRAP_REQUIRED_MESSAGE = 'Happy request signing initialization failed. Run "hicloud auth login --force" to retry. If it still fails, inspect the server response and local credentials state.';
|
|
2157
|
+
let authenticationWarningShown = false;
|
|
2158
|
+
class AuthenticationRequiredError extends Error {
|
|
2159
|
+
constructor(message = AUTHENTICATION_REQUIRED_MESSAGE) {
|
|
2160
|
+
super(message);
|
|
2161
|
+
this.name = "AuthenticationRequiredError";
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
function isAuthenticationRequiredError(error) {
|
|
2165
|
+
return error instanceof AuthenticationRequiredError;
|
|
2166
|
+
}
|
|
2167
|
+
class SigningBootstrapRequiredError extends AuthenticationRequiredError {
|
|
2168
|
+
constructor(message = SIGNING_BOOTSTRAP_REQUIRED_MESSAGE) {
|
|
2169
|
+
super(message);
|
|
2170
|
+
this.name = "SigningBootstrapRequiredError";
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
function isSigningBootstrapRequiredError(error) {
|
|
2174
|
+
return error instanceof SigningBootstrapRequiredError;
|
|
2175
|
+
}
|
|
2176
|
+
function printAuthenticationRequiredWarning(error) {
|
|
2177
|
+
if (authenticationWarningShown) {
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
authenticationWarningShown = true;
|
|
2181
|
+
if (isSigningBootstrapRequiredError(error)) {
|
|
2182
|
+
console.log(chalk.yellow("\u26A0\uFE0F Happy request signing initialization failed"));
|
|
2183
|
+
console.log(chalk.gray(' Run "hicloud auth login --force" to retry signing initialization.'));
|
|
2184
|
+
console.log(chalk.gray(" If it still fails, inspect the server response and local credentials state."));
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
console.log(chalk.yellow("\u26A0\uFE0F Happy authentication failed"));
|
|
2188
|
+
console.log(chalk.gray(' Run "hicloud auth login --force" to re-authenticate.'));
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2102
2191
|
function startOfflineReconnection(config) {
|
|
2103
2192
|
let reconnected = false;
|
|
2104
2193
|
let session = null;
|
|
@@ -2125,9 +2214,19 @@ function startOfflineReconnection(config) {
|
|
|
2125
2214
|
config.onNotify("\u2705 Reconnected! Session syncing in background.");
|
|
2126
2215
|
logger.debug("[OfflineReconnection] Successfully reconnected");
|
|
2127
2216
|
} catch (e) {
|
|
2217
|
+
if (isSigningBootstrapRequiredError(e)) {
|
|
2218
|
+
logger.debug("[OfflineReconnection] Signing bootstrap incomplete, stopping retries");
|
|
2219
|
+
config.onNotify("\u274C Request signing initialization failed. Re-run `hicloud auth login --force`; if it still fails, inspect the server response and local credentials state.");
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2128
2222
|
if (axios.isAxiosError(e) && e.response?.status === 401) {
|
|
2129
2223
|
logger.debug("[OfflineReconnection] Authentication error, stopping retries");
|
|
2130
|
-
config.onNotify("\u274C Authentication failed. Please re-authenticate with `
|
|
2224
|
+
config.onNotify("\u274C Authentication failed. Please re-authenticate with `hicloud auth`.");
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
2227
|
+
if (isAuthenticationRequiredError(e)) {
|
|
2228
|
+
logger.debug("[OfflineReconnection] Authentication error, stopping retries");
|
|
2229
|
+
config.onNotify("\u274C Authentication failed. Please re-authenticate with `hicloud auth`.");
|
|
2131
2230
|
return;
|
|
2132
2231
|
}
|
|
2133
2232
|
failureCount++;
|
|
@@ -2172,7 +2271,7 @@ const ERROR_DESCRIPTIONS = {
|
|
|
2172
2271
|
EHOSTUNREACH: "server host unreachable",
|
|
2173
2272
|
ENETUNREACH: "network unreachable",
|
|
2174
2273
|
// HTTP errors
|
|
2175
|
-
"401": "authentication failed - run `
|
|
2274
|
+
"401": "authentication failed - run `hicloud auth`",
|
|
2176
2275
|
"403": "access forbidden",
|
|
2177
2276
|
"404": "endpoint not found, check server deployment",
|
|
2178
2277
|
"500": "server internal error",
|
|
@@ -2234,6 +2333,12 @@ class ApiClient {
|
|
|
2234
2333
|
this.credential = credential;
|
|
2235
2334
|
this.pushClient = new PushNotificationClient(credential, configuration.serverUrl);
|
|
2236
2335
|
}
|
|
2336
|
+
createAuthenticationError() {
|
|
2337
|
+
if (!this.credential.signing) {
|
|
2338
|
+
return new SigningBootstrapRequiredError(SIGNING_BOOTSTRAP_REQUIRED_MESSAGE);
|
|
2339
|
+
}
|
|
2340
|
+
return new AuthenticationRequiredError(AUTHENTICATION_REQUIRED_MESSAGE);
|
|
2341
|
+
}
|
|
2237
2342
|
async request(opts) {
|
|
2238
2343
|
return axios.request({
|
|
2239
2344
|
method: opts.method,
|
|
@@ -2318,6 +2423,11 @@ class ApiClient {
|
|
|
2318
2423
|
});
|
|
2319
2424
|
return null;
|
|
2320
2425
|
}
|
|
2426
|
+
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
|
2427
|
+
const authError = this.createAuthenticationError();
|
|
2428
|
+
printAuthenticationRequiredWarning(authError);
|
|
2429
|
+
throw authError;
|
|
2430
|
+
}
|
|
2321
2431
|
if (axios.isAxiosError(error) && error.response?.status) {
|
|
2322
2432
|
const status = error.response.status;
|
|
2323
2433
|
if (status >= 500) {
|
|
@@ -2397,6 +2507,11 @@ class ApiClient {
|
|
|
2397
2507
|
}
|
|
2398
2508
|
if (axios.isAxiosError(error) && error.response?.status) {
|
|
2399
2509
|
const status = error.response.status;
|
|
2510
|
+
if (status === 401) {
|
|
2511
|
+
const authError = this.createAuthenticationError();
|
|
2512
|
+
printAuthenticationRequiredWarning(authError);
|
|
2513
|
+
throw authError;
|
|
2514
|
+
}
|
|
2400
2515
|
if (status === 403 || status === 409) {
|
|
2401
2516
|
console.log(chalk.yellow(
|
|
2402
2517
|
`\u26A0\uFE0F Machine registration rejected by the server with status ${status}`
|
|
@@ -2543,6 +2658,8 @@ exports.ApiClient = ApiClient;
|
|
|
2543
2658
|
exports.ApiSessionClient = ApiSessionClient;
|
|
2544
2659
|
exports.AsyncLock = AsyncLock;
|
|
2545
2660
|
exports.HAPPY_CLOUD_DAEMON_PORT = HAPPY_CLOUD_DAEMON_PORT;
|
|
2661
|
+
exports.SIGNING_BOOTSTRAP_REQUIRED_MESSAGE = SIGNING_BOOTSTRAP_REQUIRED_MESSAGE;
|
|
2662
|
+
exports.SigningBootstrapRequiredError = SigningBootstrapRequiredError;
|
|
2546
2663
|
exports.api = api;
|
|
2547
2664
|
exports.backoff = backoff;
|
|
2548
2665
|
exports.buildAuthenticatedHeaders = buildAuthenticatedHeaders;
|
|
@@ -2554,6 +2671,7 @@ exports.delay = delay;
|
|
|
2554
2671
|
exports.encodeBase64 = encodeBase64;
|
|
2555
2672
|
exports.encodeBase64Url = encodeBase64Url;
|
|
2556
2673
|
exports.getLatestDaemonLog = getLatestDaemonLog;
|
|
2674
|
+
exports.isAuthenticationRequiredError = isAuthenticationRequiredError;
|
|
2557
2675
|
exports.logger = logger;
|
|
2558
2676
|
exports.packageJson = packageJson;
|
|
2559
2677
|
exports.startOfflineReconnection = startOfflineReconnection;
|