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.
Files changed (27) hide show
  1. package/dist/{BaseReasoningProcessor-BRCQXCZY.cjs → BaseReasoningProcessor-B6tJ_eL5.cjs} +96 -9
  2. package/dist/{BaseReasoningProcessor-BKLRCKTU.mjs → BaseReasoningProcessor-D8VhEbs2.mjs} +95 -10
  3. package/dist/{api-D7OK-mML.cjs → api-D2Njw9Im.cjs} +124 -6
  4. package/dist/{api-BGXYX0yH.mjs → api-MYhAGPLn.mjs} +122 -7
  5. package/dist/{command-G85giEAF.cjs → command-CVldr51S.cjs} +3 -3
  6. package/dist/{command-CnLtKtP-.mjs → command-nmK6O-ab.mjs} +3 -3
  7. package/dist/{index-C7Y0R-MI.mjs → index-B97L7qLD.mjs} +689 -229
  8. package/dist/{index-B_wlQBy2.cjs → index-Bg-YziG2.cjs} +691 -229
  9. package/dist/index.cjs +4 -4
  10. package/dist/index.mjs +4 -4
  11. package/dist/lib.cjs +1 -1
  12. package/dist/lib.d.cts +7 -0
  13. package/dist/lib.d.mts +7 -0
  14. package/dist/lib.mjs +1 -1
  15. package/dist/{persistence-DHgf1CTG.cjs → persistence-D_2GkJAO.cjs} +28 -6
  16. package/dist/{persistence-BA_unuca.mjs → persistence-Dkm7rm8k.mjs} +29 -7
  17. package/dist/{registerKillSessionHandler-C2-yHm1V.mjs → registerKillSessionHandler-5GbrO0FM.mjs} +6 -4
  18. package/dist/{registerKillSessionHandler-CLREXN11.cjs → registerKillSessionHandler-BAXmJQRt.cjs} +6 -4
  19. package/dist/{runClaude-CwAitpX-.cjs → runClaude-B-GNEkKg.cjs} +237 -45
  20. package/dist/{runClaude-uNC5Eym4.mjs → runClaude-Cii3R2Fv.mjs} +238 -46
  21. package/dist/{runCodex-B-05E-YZ.mjs → runCodex-C--ZwAhl.mjs} +636 -819
  22. package/dist/{runCodex-Cm0VTqw_.cjs → runCodex-CPHyGwj9.cjs} +639 -819
  23. package/dist/{runGemini-_biXvQAH.mjs → runGemini-CQp7Nuzn.mjs} +20 -16
  24. package/dist/{runGemini-CLWjwDYS.cjs → runGemini-DaDz1bzQ.cjs} +20 -16
  25. package/package.json +14 -15
  26. package/scripts/env-wrapper.cjs +11 -11
  27. 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-D7OK-mML.cjs');
6
- var index = require('./index-B_wlQBy2.cjs');
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
- return {
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: () => null,
61
- getAgentStateSnapshot: () => null,
62
- waitForMetadataUpdate: async () => null,
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-BGXYX0yH.mjs';
4
- import { p as projectPath } from './index-C7Y0R-MI.mjs';
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
- return {
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: () => null,
59
- getAgentStateSnapshot: () => null,
60
- waitForMetadataUpdate: async () => null,
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.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.8.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-DHgf1CTG.cjs'); });
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 `happy-cloud auth`.");
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 `happy-cloud auth`",
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;