happy-imou-cloud 2.0.6 → 2.0.8
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/bin/happy-cloud.mjs +1 -1
- package/dist/{api-DccDghmF.cjs → api-CN-WqYd_.cjs} +3 -4
- package/dist/{api-Emo3rSZH.mjs → api-D-uiH_TF.mjs} +3 -4
- package/dist/{command-D8Zz6B4t.mjs → command-DGFsZx58.mjs} +3 -3
- package/dist/{command-C2v0VkPq.cjs → command-DjIfRZQS.cjs} +3 -3
- package/dist/{index-Buq7nurH.cjs → index-DM6z3aeG.cjs} +189 -27
- package/dist/{index-Dh8UTgm4.mjs → index-DhheEtRl.mjs} +187 -25
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{BaseReasoningProcessor-1EzrE03x.mjs → names-BjEof0E2.mjs} +98 -3
- package/dist/{BaseReasoningProcessor-BMyfwx3p.cjs → names-BnV67N_O.cjs} +100 -2
- package/dist/{persistence-BrTyBuT7.cjs → persistence-DBGkO8gB.cjs} +110 -1
- package/dist/{persistence-Blm1hTQA.mjs → persistence-DiNg1DPF.mjs} +100 -4
- package/dist/{registerKillSessionHandler-EFAsOnR_.cjs → registerKillSessionHandler-CYc0SIjF.cjs} +26 -2
- package/dist/{registerKillSessionHandler-Bm7E-03E.mjs → registerKillSessionHandler-Cu9rHGsI.mjs} +26 -2
- package/dist/{runClaude-COy1pLhn.cjs → runClaude-CPhWaFrX.cjs} +5 -5
- package/dist/{runClaude-CwA5UCO-.mjs → runClaude-DAR_hw3C.mjs} +4 -4
- package/dist/{runCodex-BRMOT2dJ.cjs → runCodex--QLrOs8X.cjs} +53 -82
- package/dist/{runCodex-DTPmqCyS.mjs → runCodex-B4QAb-Go.mjs} +35 -63
- package/dist/{runGemini-BVPmTGxQ.cjs → runGemini-CDyhCucw.cjs} +20 -21
- package/dist/{runGemini-DDSR8BtO.mjs → runGemini-DqowSR2w.mjs} +5 -6
- package/package.json +3 -4
- package/scripts/build.mjs +66 -0
- package/scripts/release-smoke.mjs +166 -30
|
@@ -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-
|
|
7
|
-
var persistence = require('./persistence-
|
|
8
|
-
var
|
|
9
|
-
var index = require('./index-
|
|
10
|
-
var registerKillSessionHandler = require('./registerKillSessionHandler-
|
|
11
|
-
var happyProtocol = require('happy-protocol');
|
|
6
|
+
var api = require('./api-CN-WqYd_.cjs');
|
|
7
|
+
var persistence = require('./persistence-DBGkO8gB.cjs');
|
|
8
|
+
var names = require('./names-BnV67N_O.cjs');
|
|
9
|
+
var index = require('./index-DM6z3aeG.cjs');
|
|
10
|
+
var registerKillSessionHandler = require('./registerKillSessionHandler-CYc0SIjF.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
|
|
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
|
|
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 } =
|
|
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 } =
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
1111
|
+
canonicalToolName: names.resolveCanonicalToolNameV2("GeminiPatch")
|
|
1113
1112
|
}),
|
|
1114
1113
|
id: node_crypto.randomUUID(),
|
|
1115
1114
|
isError: !success
|
|
@@ -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-
|
|
5
|
-
import { readSettings } from './persistence-
|
|
6
|
-
import { B as BasePermissionHandler, a as BaseReasoningProcessor, c as createSessionMetadata, s as setupOfflineReconnection } from './
|
|
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-
|
|
8
|
-
import { M as MessageQueue2, h as hashObject, a as MessageBuffer, r as registerKillSessionHandler } from './registerKillSessionHandler-
|
|
9
|
-
import { attachToolHappierMetaV2, resolveCanonicalToolNameV2, inferToolResultError } from 'happy-protocol';
|
|
4
|
+
import { l as logger, b as connectionState, A as ApiClient, i as isAuthenticationRequiredError } from './api-D-uiH_TF.mjs';
|
|
5
|
+
import { readSettings } from './persistence-DiNg1DPF.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-BjEof0E2.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-DhheEtRl.mjs';
|
|
8
|
+
import { M as MessageQueue2, h as hashObject, a as MessageBuffer, r as registerKillSessionHandler } from './registerKillSessionHandler-Cu9rHGsI.mjs';
|
|
10
9
|
import 'axios';
|
|
11
10
|
import 'chalk';
|
|
12
11
|
import 'fs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "happy-imou-cloud",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
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": "
|
|
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:
|
|
18
|
-
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
|
|
28
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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', '
|
|
52
|
-
runStep('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', '
|
|
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
|
+
}
|