happy-imou-cloud 2.0.7 → 2.0.9

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 (26) hide show
  1. package/bin/happy-cloud.mjs +1 -1
  2. package/dist/{api-Dwkm7s_E.cjs → api-CUTdFiFP.cjs} +3 -4
  3. package/dist/{api-dwwHBzLc.mjs → api-CnvyGas2.mjs} +3 -4
  4. package/dist/{command-Cfq3Uc0S.mjs → command-BGA3qCKR.mjs} +3 -3
  5. package/dist/{command-DiAVIsxX.cjs → command-DLAJZsKX.cjs} +3 -3
  6. package/dist/{index-HyqLXzw-.mjs → index-BpZL4RcT.mjs} +208 -32
  7. package/dist/{index-CfqxEoyl.cjs → index-D4OdFq68.cjs} +210 -34
  8. package/dist/index.cjs +3 -3
  9. package/dist/index.mjs +3 -3
  10. package/dist/lib.cjs +1 -1
  11. package/dist/lib.mjs +1 -1
  12. package/dist/{BaseReasoningProcessor-ClrT-x-H.mjs → names-C9iJODqA.mjs} +98 -3
  13. package/dist/{BaseReasoningProcessor-DphULXS-.cjs → names-YEhZwVT0.cjs} +100 -2
  14. package/dist/{persistence-Dg-rxY2a.mjs → persistence-BPV3AmJL.mjs} +100 -4
  15. package/dist/{persistence-hbhwAYIV.cjs → persistence-CxvL0cwp.cjs} +110 -1
  16. package/dist/{registerKillSessionHandler-BAvk4GYO.mjs → registerKillSessionHandler-C2O8b5wH.mjs} +2 -2
  17. package/dist/{registerKillSessionHandler-D1ouN10n.cjs → registerKillSessionHandler-rqd7duc9.cjs} +2 -2
  18. package/dist/{runClaude-OxYbt3ZQ.mjs → runClaude-8inO7C5p.mjs} +4 -4
  19. package/dist/{runClaude-CZmJ7qEP.cjs → runClaude-KwIVwFp1.cjs} +5 -5
  20. package/dist/{runCodex-ByVTEbSY.mjs → runCodex-BQ-fN5E6.mjs} +5 -6
  21. package/dist/{runCodex-CtncAgso.cjs → runCodex-Ba8COxZe.cjs} +24 -25
  22. package/dist/{runGemini-BRO6A2jm.mjs → runGemini-BE0FizuV.mjs} +5 -6
  23. package/dist/{runGemini-ChwjLmhI.cjs → runGemini-DtdLLX9o.cjs} +20 -21
  24. package/package.json +3 -4
  25. package/scripts/build.mjs +66 -0
  26. package/scripts/release-smoke.mjs +166 -30
@@ -1,14 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  var node_crypto = require('node:crypto');
4
- var api = require('./api-Dwkm7s_E.cjs');
5
- var persistence = require('./persistence-hbhwAYIV.cjs');
6
- var index = require('./index-CfqxEoyl.cjs');
7
- var BaseReasoningProcessor = require('./BaseReasoningProcessor-DphULXS-.cjs');
8
- var registerKillSessionHandler = require('./registerKillSessionHandler-D1ouN10n.cjs');
4
+ var api = require('./api-CUTdFiFP.cjs');
5
+ var persistence = require('./persistence-CxvL0cwp.cjs');
6
+ var index = require('./index-D4OdFq68.cjs');
7
+ var names = require('./names-YEhZwVT0.cjs');
8
+ var registerKillSessionHandler = require('./registerKillSessionHandler-rqd7duc9.cjs');
9
9
  var React = require('react');
10
10
  var ink = require('ink');
11
- var happyProtocol = require('happy-protocol');
12
11
  require('axios');
13
12
  require('chalk');
14
13
  require('fs');
@@ -313,7 +312,7 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit, title }) => {
313
312
  ));
314
313
  };
315
314
 
316
- class CodexPermissionHandler extends BaseReasoningProcessor.BasePermissionHandler {
315
+ class CodexPermissionHandler extends names.BasePermissionHandler {
317
316
  constructor(session) {
318
317
  super(session);
319
318
  }
@@ -352,7 +351,7 @@ class CodexSelectionHandler {
352
351
  };
353
352
  pending.timeoutHandle = setTimeout(() => {
354
353
  this.handleSelectionTimeout(request.id, pending);
355
- }, BaseReasoningProcessor.getPendingInteractionTimeoutMs());
354
+ }, names.getPendingInteractionTimeoutMs());
356
355
  this.pendingRequests.set(request.id, pending);
357
356
  this.session.updateAgentState((currentState) => ({
358
357
  ...currentState,
@@ -388,7 +387,7 @@ class CodexSelectionHandler {
388
387
  hasPendingRequests() {
389
388
  return this.pendingRequests.size > 0;
390
389
  }
391
- supersedePendingRequests(reason = BaseReasoningProcessor.INTERACTION_SUPERSEDED_ERROR) {
390
+ supersedePendingRequests(reason = names.INTERACTION_SUPERSEDED_ERROR) {
392
391
  const pendingSnapshot = Array.from(this.pendingRequests.entries());
393
392
  if (pendingSnapshot.length === 0) {
394
393
  return 0;
@@ -500,7 +499,7 @@ class CodexSelectionHandler {
500
499
  }
501
500
  this.pendingRequests.delete(requestId);
502
501
  this.clearPendingRequestTimeout(active);
503
- active.reject(new Error(BaseReasoningProcessor.INTERACTION_TIMED_OUT_ERROR));
502
+ active.reject(new Error(names.INTERACTION_TIMED_OUT_ERROR));
504
503
  this.session.updateAgentState((currentState) => {
505
504
  const request = currentState.requests?.[requestId] || {
506
505
  tool: "AskUserQuestion",
@@ -523,7 +522,7 @@ class CodexSelectionHandler {
523
522
  ...request,
524
523
  completedAt: Date.now(),
525
524
  status: "canceled",
526
- reason: BaseReasoningProcessor.INTERACTION_TIMED_OUT_ERROR,
525
+ reason: names.INTERACTION_TIMED_OUT_ERROR,
527
526
  requestKind: "selection"
528
527
  }
529
528
  }
@@ -537,7 +536,7 @@ class CodexSelectionHandler {
537
536
  }
538
537
  }
539
538
 
540
- class ReasoningProcessor extends BaseReasoningProcessor.BaseReasoningProcessor {
539
+ class ReasoningProcessor extends names.BaseReasoningProcessor {
541
540
  getToolName() {
542
541
  return "CodexReasoning";
543
542
  }
@@ -943,7 +942,7 @@ async function codexRemoteLauncher(session) {
943
942
  }
944
943
  case "tool-call": {
945
944
  const toolArgs = msg.args ? index.truncateDisplayMessage(msg.args, 100) : "";
946
- const canonicalToolName = happyProtocol.resolveCanonicalToolNameV2(msg.toolName);
945
+ const canonicalToolName = names.resolveCanonicalToolNameV2(msg.toolName);
947
946
  messageBuffer.addMessage(
948
947
  `Executing: ${msg.toolName}${toolArgs ? ` ${toolArgs}` : ""}`,
949
948
  "tool"
@@ -952,7 +951,7 @@ async function codexRemoteLauncher(session) {
952
951
  type: "tool-call",
953
952
  name: canonicalToolName,
954
953
  callId: msg.callId,
955
- input: happyProtocol.attachToolHappierMetaV2(msg.args, {
954
+ input: names.attachToolHappierMetaV2(msg.args, {
956
955
  v: 2,
957
956
  protocol: "acp",
958
957
  provider: "codex",
@@ -964,7 +963,7 @@ async function codexRemoteLauncher(session) {
964
963
  return;
965
964
  }
966
965
  case "tool-result": {
967
- const isError = happyProtocol.inferToolResultError(msg.result);
966
+ const isError = names.inferToolResultError(msg.result);
968
967
  const resultText = index.truncateDisplayMessage(msg.result, 200) || (isError ? "Unknown error" : "");
969
968
  messageBuffer.addMessage(
970
969
  `${isError ? "Error:" : "Result:"} ${resultText}`.trim(),
@@ -973,12 +972,12 @@ async function codexRemoteLauncher(session) {
973
972
  session.runtimeSession.sendCodexMessage({
974
973
  type: "tool-call-result",
975
974
  callId: msg.callId,
976
- output: happyProtocol.attachToolHappierMetaV2(msg.result, {
975
+ output: names.attachToolHappierMetaV2(msg.result, {
977
976
  v: 2,
978
977
  protocol: "acp",
979
978
  provider: "codex",
980
979
  rawToolName: msg.toolName,
981
- canonicalToolName: happyProtocol.resolveCanonicalToolNameV2(msg.toolName)
980
+ canonicalToolName: names.resolveCanonicalToolNameV2(msg.toolName)
982
981
  }),
983
982
  id: node_crypto.randomUUID(),
984
983
  isError
@@ -1021,12 +1020,12 @@ async function codexRemoteLauncher(session) {
1021
1020
  const { call_id, type, ...inputs } = msg;
1022
1021
  messageBuffer.addMessage(`Exec approval requested: ${call_id}`, "tool");
1023
1022
  const rawToolName = "CodexBash";
1024
- const canonicalToolName = happyProtocol.resolveCanonicalToolNameV2(rawToolName);
1023
+ const canonicalToolName = names.resolveCanonicalToolNameV2(rawToolName);
1025
1024
  session.runtimeSession.sendCodexMessage({
1026
1025
  type: "tool-call",
1027
1026
  name: canonicalToolName,
1028
1027
  callId: call_id,
1029
- input: happyProtocol.attachToolHappierMetaV2(inputs, {
1028
+ input: names.attachToolHappierMetaV2(inputs, {
1030
1029
  v: 2,
1031
1030
  protocol: "acp",
1032
1031
  provider: "codex",
@@ -1042,12 +1041,12 @@ async function codexRemoteLauncher(session) {
1042
1041
  const filesMsg = changeCount === 1 ? "1 file" : `${changeCount} files`;
1043
1042
  messageBuffer.addMessage(`Modifying ${filesMsg}...`, "tool");
1044
1043
  const rawToolName = "CodexPatch";
1045
- const canonicalToolName = happyProtocol.resolveCanonicalToolNameV2(rawToolName);
1044
+ const canonicalToolName = names.resolveCanonicalToolNameV2(rawToolName);
1046
1045
  session.runtimeSession.sendCodexMessage({
1047
1046
  type: "tool-call",
1048
1047
  name: canonicalToolName,
1049
1048
  callId: msg.call_id,
1050
- input: happyProtocol.attachToolHappierMetaV2({
1049
+ input: names.attachToolHappierMetaV2({
1051
1050
  auto_approved: msg.auto_approved,
1052
1051
  changes: msg.changes
1053
1052
  }, {
@@ -1070,7 +1069,7 @@ async function codexRemoteLauncher(session) {
1070
1069
  session.runtimeSession.sendCodexMessage({
1071
1070
  type: "tool-call-result",
1072
1071
  callId: msg.call_id,
1073
- output: happyProtocol.attachToolHappierMetaV2({
1072
+ output: names.attachToolHappierMetaV2({
1074
1073
  stdout: msg.stdout,
1075
1074
  stderr: msg.stderr,
1076
1075
  success: msg.success
@@ -1079,7 +1078,7 @@ async function codexRemoteLauncher(session) {
1079
1078
  protocol: "acp",
1080
1079
  provider: "codex",
1081
1080
  rawToolName: "CodexPatch",
1082
- canonicalToolName: happyProtocol.resolveCanonicalToolNameV2("CodexPatch")
1081
+ canonicalToolName: names.resolveCanonicalToolNameV2("CodexPatch")
1083
1082
  }),
1084
1083
  id: node_crypto.randomUUID(),
1085
1084
  isError: !msg.success
@@ -1442,7 +1441,7 @@ async function runCodex(opts) {
1442
1441
  machineId,
1443
1442
  metadata: index.initialMachineMetadata
1444
1443
  });
1445
- const { state, metadata } = BaseReasoningProcessor.createSessionMetadata({
1444
+ const { state, metadata } = names.createSessionMetadata({
1446
1445
  flavor: "codex",
1447
1446
  machineId,
1448
1447
  startedBy: opts.startedBy
@@ -1458,7 +1457,7 @@ async function runCodex(opts) {
1458
1457
  }
1459
1458
  let sessionClient;
1460
1459
  let codexSession = null;
1461
- const { session: initialSession, reconnectionHandle } = BaseReasoningProcessor.setupOfflineReconnection({
1460
+ const { session: initialSession, reconnectionHandle } = names.setupOfflineReconnection({
1462
1461
  api: api$1,
1463
1462
  sessionTag,
1464
1463
  metadata,
@@ -1,12 +1,11 @@
1
1
  import { useStdout, useInput, Box, Text, render } from 'ink';
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { l as logger, b as connectionState, A as ApiClient, i as isAuthenticationRequiredError } from './api-dwwHBzLc.mjs';
5
- import { readSettings } from './persistence-Dg-rxY2a.mjs';
6
- import { B as BasePermissionHandler, a as BaseReasoningProcessor, c as createSessionMetadata, s as setupOfflineReconnection } from './BaseReasoningProcessor-ClrT-x-H.mjs';
7
- import { i as initialMachineMetadata, n as notifyDaemonSessionStarted, g as getInitialGeminiModel, r as readGeminiLocalConfig, G as GEMINI_MODEL_ENV, s as saveGeminiModelToConfig, a as createGeminiBackend, b as stopCaffeinate } from './index-HyqLXzw-.mjs';
8
- import { M as MessageQueue2, h as hashObject, a as MessageBuffer, r as registerKillSessionHandler } from './registerKillSessionHandler-BAvk4GYO.mjs';
9
- import { attachToolHappierMetaV2, resolveCanonicalToolNameV2, inferToolResultError } from 'happy-protocol';
4
+ import { l as logger, b as connectionState, A as ApiClient, i as isAuthenticationRequiredError } from './api-CnvyGas2.mjs';
5
+ import { readSettings } from './persistence-BPV3AmJL.mjs';
6
+ import { B as BasePermissionHandler, a as BaseReasoningProcessor, c as createSessionMetadata, s as setupOfflineReconnection, b as attachToolHappierMetaV2, r as resolveCanonicalToolNameV2, i as inferToolResultError } from './names-C9iJODqA.mjs';
7
+ import { i as initialMachineMetadata, n as notifyDaemonSessionStarted, g as getInitialGeminiModel, r as readGeminiLocalConfig, G as GEMINI_MODEL_ENV, s as saveGeminiModelToConfig, a as createGeminiBackend, b as stopCaffeinate } from './index-BpZL4RcT.mjs';
8
+ import { M as MessageQueue2, h as hashObject, a as MessageBuffer, r as registerKillSessionHandler } from './registerKillSessionHandler-C2O8b5wH.mjs';
10
9
  import 'axios';
11
10
  import 'chalk';
12
11
  import 'fs';
@@ -3,12 +3,11 @@
3
3
  var ink = require('ink');
4
4
  var React = require('react');
5
5
  var node_crypto = require('node:crypto');
6
- var api = require('./api-Dwkm7s_E.cjs');
7
- var persistence = require('./persistence-hbhwAYIV.cjs');
8
- var BaseReasoningProcessor = require('./BaseReasoningProcessor-DphULXS-.cjs');
9
- var index = require('./index-CfqxEoyl.cjs');
10
- var registerKillSessionHandler = require('./registerKillSessionHandler-D1ouN10n.cjs');
11
- var happyProtocol = require('happy-protocol');
6
+ var api = require('./api-CUTdFiFP.cjs');
7
+ var persistence = require('./persistence-CxvL0cwp.cjs');
8
+ var names = require('./names-YEhZwVT0.cjs');
9
+ var index = require('./index-D4OdFq68.cjs');
10
+ var registerKillSessionHandler = require('./registerKillSessionHandler-rqd7duc9.cjs');
12
11
  require('axios');
13
12
  require('chalk');
14
13
  require('fs');
@@ -185,7 +184,7 @@ const GeminiDisplay = ({ messageBuffer, logPath, currentModel, onExit }) => {
185
184
  ));
186
185
  };
187
186
 
188
- class GeminiPermissionHandler extends BaseReasoningProcessor.BasePermissionHandler {
187
+ class GeminiPermissionHandler extends names.BasePermissionHandler {
189
188
  currentPermissionMode = "default";
190
189
  constructor(session) {
191
190
  super(session);
@@ -270,7 +269,7 @@ class GeminiPermissionHandler extends BaseReasoningProcessor.BasePermissionHandl
270
269
  }
271
270
  }
272
271
 
273
- class GeminiReasoningProcessor extends BaseReasoningProcessor.BaseReasoningProcessor {
272
+ class GeminiReasoningProcessor extends names.BaseReasoningProcessor {
274
273
  getToolName() {
275
274
  return "GeminiReasoning";
276
275
  }
@@ -601,7 +600,7 @@ async function runGemini(opts) {
601
600
  } catch (error) {
602
601
  api.logger.debug("[Gemini] Failed to fetch cloud token:", error);
603
602
  }
604
- const { state, metadata } = BaseReasoningProcessor.createSessionMetadata({
603
+ const { state, metadata } = names.createSessionMetadata({
605
604
  flavor: "gemini",
606
605
  machineId,
607
606
  startedBy: opts.startedBy
@@ -629,7 +628,7 @@ async function runGemini(opts) {
629
628
  pendingSessionSwap = null;
630
629
  }
631
630
  };
632
- const { session: initialSession, reconnectionHandle } = BaseReasoningProcessor.setupOfflineReconnection({
631
+ const { session: initialSession, reconnectionHandle } = names.setupOfflineReconnection({
633
632
  api: api$1,
634
633
  sessionTag,
635
634
  metadata,
@@ -951,13 +950,13 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
951
950
  if (isInvestigationTool && msg.args && typeof msg.args === "object" && "objective" in msg.args) {
952
951
  api.logger.debug(`[gemini] \u{1F50D} Investigation objective: ${String(msg.args.objective).substring(0, 150)}...`);
953
952
  }
954
- const canonicalToolName = happyProtocol.resolveCanonicalToolNameV2(msg.toolName);
953
+ const canonicalToolName = names.resolveCanonicalToolNameV2(msg.toolName);
955
954
  messageBuffer.addMessage(`Executing: ${msg.toolName}${toolArgs ? ` ${toolArgs}${toolArgs.length >= 100 ? "..." : ""}` : ""}`, "tool");
956
955
  session.sendAgentMessage("gemini", {
957
956
  type: "tool-call",
958
957
  name: canonicalToolName,
959
958
  callId: msg.callId,
960
- input: happyProtocol.attachToolHappierMetaV2(msg.args, {
959
+ input: names.attachToolHappierMetaV2(msg.args, {
961
960
  v: 2,
962
961
  protocol: "acp",
963
962
  provider: "gemini",
@@ -968,7 +967,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
968
967
  });
969
968
  break;
970
969
  case "tool-result":
971
- const isError = happyProtocol.inferToolResultError(msg.result);
970
+ const isError = names.inferToolResultError(msg.result);
972
971
  const resultText = typeof msg.result === "string" ? msg.result.substring(0, 200) : JSON.stringify(msg.result).substring(0, 200);
973
972
  const truncatedResult = resultText + (typeof msg.result === "string" && msg.result.length > 200 ? "..." : "");
974
973
  const resultSize = typeof msg.result === "string" ? msg.result.length : JSON.stringify(msg.result).length;
@@ -989,12 +988,12 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
989
988
  session.sendAgentMessage("gemini", {
990
989
  type: "tool-result",
991
990
  callId: msg.callId,
992
- output: happyProtocol.attachToolHappierMetaV2(msg.result, {
991
+ output: names.attachToolHappierMetaV2(msg.result, {
993
992
  v: 2,
994
993
  protocol: "acp",
995
994
  provider: "gemini",
996
995
  rawToolName: msg.toolName,
997
- canonicalToolName: happyProtocol.resolveCanonicalToolNameV2(msg.toolName)
996
+ canonicalToolName: names.resolveCanonicalToolNameV2(msg.toolName)
998
997
  }),
999
998
  id: node_crypto.randomUUID(),
1000
999
  isError
@@ -1044,12 +1043,12 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1044
1043
  const { call_id, type, ...inputs } = execApprovalMsg;
1045
1044
  api.logger.debug(`[gemini] Exec approval request received: ${callId}`);
1046
1045
  messageBuffer.addMessage(`Exec approval requested: ${callId}`, "tool");
1047
- const execCanonicalToolName = happyProtocol.resolveCanonicalToolNameV2("GeminiBash");
1046
+ const execCanonicalToolName = names.resolveCanonicalToolNameV2("GeminiBash");
1048
1047
  session.sendAgentMessage("gemini", {
1049
1048
  type: "tool-call",
1050
1049
  name: execCanonicalToolName,
1051
1050
  callId,
1052
- input: happyProtocol.attachToolHappierMetaV2(inputs, {
1051
+ input: names.attachToolHappierMetaV2(inputs, {
1053
1052
  v: 2,
1054
1053
  protocol: "acp",
1055
1054
  provider: "gemini",
@@ -1067,12 +1066,12 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1067
1066
  const filesMsg = changeCount === 1 ? "1 file" : `${changeCount} files`;
1068
1067
  messageBuffer.addMessage(`Modifying ${filesMsg}...`, "tool");
1069
1068
  api.logger.debug(`[gemini] Patch apply begin: ${patchCallId}, files: ${changeCount}`);
1070
- const patchCanonicalToolName = happyProtocol.resolveCanonicalToolNameV2("GeminiPatch");
1069
+ const patchCanonicalToolName = names.resolveCanonicalToolNameV2("GeminiPatch");
1071
1070
  session.sendAgentMessage("gemini", {
1072
1071
  type: "tool-call",
1073
1072
  name: patchCanonicalToolName,
1074
1073
  callId: patchCallId,
1075
- input: happyProtocol.attachToolHappierMetaV2({
1074
+ input: names.attachToolHappierMetaV2({
1076
1075
  auto_approved,
1077
1076
  changes
1078
1077
  }, {
@@ -1100,7 +1099,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1100
1099
  session.sendAgentMessage("gemini", {
1101
1100
  type: "tool-result",
1102
1101
  callId: patchEndCallId,
1103
- output: happyProtocol.attachToolHappierMetaV2({
1102
+ output: names.attachToolHappierMetaV2({
1104
1103
  stdout,
1105
1104
  stderr,
1106
1105
  success
@@ -1109,7 +1108,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1109
1108
  protocol: "acp",
1110
1109
  provider: "gemini",
1111
1110
  rawToolName: "GeminiPatch",
1112
- canonicalToolName: happyProtocol.resolveCanonicalToolNameV2("GeminiPatch")
1111
+ canonicalToolName: names.resolveCanonicalToolNameV2("GeminiPatch")
1113
1112
  }),
1114
1113
  id: node_crypto.randomUUID(),
1115
1114
  isError: !success
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-imou-cloud",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI",
5
5
  "author": "long.zhu",
6
6
  "license": "MIT",
@@ -43,7 +43,7 @@
43
43
  "// ==== TypeScript & Build ====": "",
44
44
  "why do we need to build before running tests / dev?": "We need the binary to be built so we run daemon commands which directly run the binary - we don't want them to go out of sync or have custom spawn logic depending how we started happy",
45
45
  "typecheck": "tsc --noEmit",
46
- "build": "shx rm -rf dist && npx tsc --noEmit && pkgroll",
46
+ "build": "node ./scripts/build.mjs",
47
47
  "// ==== Testing ====": "",
48
48
  "test": "yarn build && vitest run",
49
49
  "test:integration": "yarn build && vitest run --config vitest.integration.config.ts",
@@ -59,7 +59,7 @@
59
59
  "dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
60
60
  "dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
61
61
  "// ==== Release ====": "",
62
- "prepublishOnly": "yarn release:smoke",
62
+ "prepublishOnly": "yarn release:smoke",
63
63
  "release": "yarn install && release-it",
64
64
  "release:smoke": "node ./scripts/release-smoke.mjs",
65
65
  "// ==== Dev/Stable Variant Management ====": "",
@@ -99,7 +99,6 @@
99
99
  "expo-server-sdk": "^3.15.0",
100
100
  "fastify": "^5.6.2",
101
101
  "fastify-type-provider-zod": "4.0.2",
102
- "happy-protocol": "0.1.0",
103
102
  "http-proxy": "^1.18.1",
104
103
  "http-proxy-middleware": "^3.0.5",
105
104
  "ink": "^6.5.1",
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'node:child_process';
4
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
5
+ import { dirname, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const here = dirname(fileURLToPath(import.meta.url));
9
+ const packageRoot = resolve(here, '..');
10
+ const workspaceRoot = resolve(packageRoot, '..', '..');
11
+ const packageJson = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8'));
12
+
13
+ function resolveTool(binName, packageSpecs) {
14
+ const suffix = process.platform === 'win32' ? '.cmd' : '';
15
+ const packageLocalBin = resolve(packageRoot, 'node_modules', '.bin', `${binName}${suffix}`);
16
+ const workspaceLocalBin = resolve(workspaceRoot, 'node_modules', '.bin', `${binName}${suffix}`);
17
+
18
+ if (existsSync(packageLocalBin)) {
19
+ return {
20
+ command: packageLocalBin,
21
+ prefixArgs: [],
22
+ };
23
+ }
24
+
25
+ if (existsSync(workspaceLocalBin)) {
26
+ return {
27
+ command: workspaceLocalBin,
28
+ prefixArgs: [],
29
+ };
30
+ }
31
+
32
+ return {
33
+ command: 'npx',
34
+ prefixArgs: ['-y', ...packageSpecs.flatMap((packageSpec) => ['-p', packageSpec]), binName],
35
+ };
36
+ }
37
+
38
+ function runStep(name, command, args) {
39
+ console.log(`\n[build] ${name}`);
40
+ console.log(`> ${command} ${args.join(' ')}`);
41
+
42
+ const result = spawnSync(command, args, {
43
+ cwd: packageRoot,
44
+ stdio: 'inherit',
45
+ shell: process.platform === 'win32',
46
+ env: process.env,
47
+ });
48
+
49
+ if (result.status !== 0) {
50
+ throw new Error(`[build] Step failed: ${name}`);
51
+ }
52
+ }
53
+
54
+ function runTool(name, tool, args) {
55
+ runStep(name, tool.command, [...tool.prefixArgs, ...args]);
56
+ }
57
+
58
+ const tsc = resolveTool('tsc', [`typescript@${packageJson.devDependencies.typescript}`]);
59
+ const pkgroll = resolveTool('pkgroll', [
60
+ `pkgroll@${packageJson.devDependencies.pkgroll}`,
61
+ `typescript@${packageJson.devDependencies.typescript}`,
62
+ ]);
63
+
64
+ rmSync(resolve(packageRoot, 'dist'), { recursive: true, force: true });
65
+ runTool('typecheck', tsc, ['--noEmit']);
66
+ runTool('bundle', pkgroll, []);
@@ -1,66 +1,202 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { spawnSync } from 'node:child_process';
4
+ import { existsSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
4
6
  import { fileURLToPath } from 'node:url';
5
- import { dirname, resolve } from 'node:path';
7
+ import { dirname, join, resolve } from 'node:path';
6
8
 
7
9
  const here = dirname(fileURLToPath(import.meta.url));
8
10
  const packageRoot = resolve(here, '..');
11
+ const workspaceRoot = resolve(packageRoot, '..', '..');
12
+ const packageJson = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8'));
13
+
14
+ function resolveTool(binName, packageName) {
15
+ const suffix = process.platform === 'win32' ? '.cmd' : '';
16
+ const packageLocalBin = resolve(packageRoot, 'node_modules', '.bin', `${binName}${suffix}`);
17
+ const workspaceLocalBin = resolve(workspaceRoot, 'node_modules', '.bin', `${binName}${suffix}`);
18
+
19
+ if (existsSync(packageLocalBin)) {
20
+ return {
21
+ command: packageLocalBin,
22
+ prefixArgs: [],
23
+ };
24
+ }
25
+
26
+ if (existsSync(workspaceLocalBin)) {
27
+ return {
28
+ command: workspaceLocalBin,
29
+ prefixArgs: [],
30
+ };
31
+ }
32
+
33
+ const version =
34
+ packageJson.devDependencies?.[packageName]
35
+ ?? packageJson.dependencies?.[packageName];
36
+
37
+ return {
38
+ command: 'npx',
39
+ prefixArgs: ['-y', '-p', `${packageName}@${version}`, binName],
40
+ };
41
+ }
42
+
43
+ function shouldUseShell(command) {
44
+ if (process.platform !== 'win32') {
45
+ return false;
46
+ }
47
+
48
+ return !/\.exe$/i.test(command);
49
+ }
9
50
 
10
51
  function runStep(name, command, args, options = {}) {
11
52
  console.log(`\n[release-smoke] ${name}`);
12
53
  console.log(`> ${command} ${args.join(' ')}`);
13
-
54
+
14
55
  const result = spawnSync(command, args, {
15
56
  cwd: packageRoot,
16
57
  stdio: 'inherit',
17
- shell: process.platform === 'win32',
18
- env: process.env,
58
+ shell: shouldUseShell(command),
59
+ env: {
60
+ ...process.env,
61
+ NODE_ENV: 'development',
62
+ YARN_PRODUCTION: 'false',
63
+ npm_config_production: 'false',
64
+ },
65
+ ...options,
66
+ });
67
+
68
+ if (result.status !== 0) {
69
+ throw new Error(`[release-smoke] Step failed: ${name}`);
70
+ }
71
+ }
72
+
73
+ function runStepCapture(name, command, args, options = {}) {
74
+ console.log(`\n[release-smoke] ${name}`);
75
+ console.log(`> ${command} ${args.join(' ')}`);
76
+
77
+ const result = spawnSync(command, args, {
78
+ cwd: packageRoot,
79
+ stdio: 'pipe',
80
+ encoding: 'utf8',
81
+ shell: shouldUseShell(command),
82
+ env: {
83
+ ...process.env,
84
+ NODE_ENV: 'development',
85
+ YARN_PRODUCTION: 'false',
86
+ npm_config_production: 'false',
87
+ },
19
88
  ...options,
20
89
  });
21
90
 
22
91
  if (result.status !== 0) {
92
+ if (result.stdout) {
93
+ process.stdout.write(result.stdout);
94
+ }
95
+ if (result.stderr) {
96
+ process.stderr.write(result.stderr);
97
+ }
23
98
  throw new Error(`[release-smoke] Step failed: ${name}`);
24
99
  }
100
+
101
+ return result.stdout;
25
102
  }
26
103
 
27
- function shouldRunProviderSmoke(provider) {
28
- return process.env[`HAPPY_SMOKE_${provider.toUpperCase()}`] === '1';
104
+ function runToolStep(name, tool, args, options = {}) {
105
+ runStep(name, tool.command, [...tool.prefixArgs, ...args], options);
29
106
  }
30
107
 
108
+ function verifyPackedInstall() {
109
+ const tempRoot = mkdtempSync(join(tmpdir(), 'happy-cli-release-smoke-'));
110
+ let tarballPath = null;
111
+
112
+ try {
113
+ const packOutput = runStepCapture('pack tarball', 'npm', ['pack', '--json', '--ignore-scripts']);
114
+ const packResult = JSON.parse(packOutput);
115
+ const tarballFile = Array.isArray(packResult) ? packResult[0]?.filename : null;
116
+
117
+ if (typeof tarballFile !== 'string' || tarballFile.length === 0) {
118
+ throw new Error('[release-smoke] npm pack did not return a tarball filename');
119
+ }
120
+
121
+ tarballPath = resolve(packageRoot, tarballFile);
122
+
123
+ writeFileSync(
124
+ join(tempRoot, 'package.json'),
125
+ JSON.stringify({ private: true, name: 'happy-cli-release-smoke' }, null, 2)
126
+ );
127
+
128
+ runStep('install packed tarball', 'npm', [
129
+ 'install',
130
+ '--ignore-scripts',
131
+ '--no-audit',
132
+ '--fund=false',
133
+ tarballPath,
134
+ ], {
135
+ cwd: tempRoot,
136
+ });
137
+
138
+ runStep('run packed cli help', 'node', [
139
+ join(tempRoot, 'node_modules', 'happy-imou-cloud', 'bin', 'happy-cloud.mjs'),
140
+ '--help',
141
+ ], {
142
+ cwd: tempRoot,
143
+ });
144
+ } finally {
145
+ if (tarballPath) {
146
+ try {
147
+ unlinkSync(tarballPath);
148
+ } catch {}
149
+ }
150
+
151
+ try {
152
+ rmSync(tempRoot, { recursive: true, force: true });
153
+ } catch {}
154
+ }
155
+ }
156
+
157
+ function shouldRunProviderSmoke(provider) {
158
+ return process.env[`HAPPY_SMOKE_${provider.toUpperCase()}`] === '1';
159
+ }
160
+
31
161
  function main() {
32
- runStep('typecheck', 'yarn', ['tsc', '--noEmit']);
33
- runStep('unit runtime suite', 'yarn', [
34
- 'vitest',
162
+ const tsc = resolveTool('tsc', 'typescript');
163
+ const vitest = resolveTool('vitest', 'vitest');
164
+
165
+ runToolStep('typecheck', tsc, ['--noEmit']);
166
+ runToolStep('unit runtime suite', vitest, [
35
167
  'run',
168
+ 'src/agent/acp/AcpBackend.test.ts',
36
169
  'src/agent/acp/AcpBackend.startup.test.ts',
170
+ 'src/agent/acp/AcpBackend.waitForResponseComplete.test.ts',
171
+ 'src/agent/acp/AcpBackend.initDelay.test.ts',
37
172
  'src/agent/acp/acpSpawn.test.ts',
38
173
  'src/agent/acp/createAcpFilteredStdoutReadable.multiline.test.ts',
39
174
  'src/agent/acp/killProcessTree.test.ts',
40
- 'src/runtime/executeProvider.test.ts',
41
- 'src/runtime/command.test.ts',
42
- 'src/runtime/launch.test.ts',
43
- 'src/runtime/RuntimeShell.test.ts',
44
- 'src/runtime/createDefaultRuntimeShell.test.ts',
45
- 'src/security/signedTransport.test.ts',
46
- 'src/agent/initializeAgents.test.ts',
47
- 'src/agent/factories/factories.test.ts',
48
- 'src/packageContract.test.ts',
175
+ 'src/runtime/executeProvider.test.ts',
176
+ 'src/runtime/command.test.ts',
177
+ 'src/runtime/launch.test.ts',
178
+ 'src/runtime/RuntimeShell.test.ts',
179
+ 'src/runtime/createDefaultRuntimeShell.test.ts',
180
+ 'src/security/signedTransport.test.ts',
181
+ 'src/agent/initializeAgents.test.ts',
182
+ 'src/agent/factories/factories.test.ts',
183
+ 'src/packageContract.test.ts',
49
184
  ]);
50
185
  runStep('build', 'yarn', ['build']);
51
- runStep('runtime providers', 'yarn', ['tsx', 'src/index.ts', 'runtime', 'providers']);
52
- runStep('help', 'yarn', ['tsx', 'src/index.ts', '--help']);
186
+ runStep('runtime providers', 'node', ['./bin/happy-cloud.mjs', 'runtime', 'providers']);
187
+ runStep('help', 'node', ['./bin/happy-cloud.mjs', '--help']);
188
+ verifyPackedInstall();
53
189
 
54
190
  if (shouldRunProviderSmoke('cursor')) {
55
- runStep('cursor smoke', 'yarn', ['tsx', 'src/index.ts', 'cursor', 'release smoke']);
191
+ runStep('cursor smoke', 'node', ['./bin/happy-cloud.mjs', 'cursor', 'release smoke']);
56
192
  }
57
193
  }
58
-
59
- try {
60
- main();
61
- console.log('\n[release-smoke] All required steps passed');
62
- } catch (error) {
63
- const message = error instanceof Error ? error.message : String(error);
64
- console.error(`\n${message}`);
65
- process.exit(1);
66
- }
194
+
195
+ try {
196
+ main();
197
+ console.log('\n[release-smoke] All required steps passed');
198
+ } catch (error) {
199
+ const message = error instanceof Error ? error.message : String(error);
200
+ console.error(`\n${message}`);
201
+ process.exit(1);
202
+ }