happy-imou-cloud 2.0.11 → 2.0.13
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/ConversationHistory-V3VLmjJf.cjs +868 -0
- package/dist/ConversationHistory-_ciJNIgH.mjs +856 -0
- package/dist/{api-BcWf5v4i.mjs → api-D1meoL-9.mjs} +2 -2
- package/dist/{api-Ye-rPX6s.cjs → api-DH5-IqeM.cjs} +2 -2
- package/dist/{command-nOI80Mnm.mjs → command-CMvWClny.mjs} +3 -3
- package/dist/{command-BK93nizl.cjs → command-Ch8Dgidj.cjs} +3 -3
- package/dist/createKeepAliveController-C5cQlDRr.mjs +51 -0
- package/dist/createKeepAliveController-DO8H6d5E.cjs +54 -0
- package/dist/{index-J7QKJ8lc.cjs → index-CryJfCh5.cjs} +54 -23
- package/dist/{index-DnsqY6I_.mjs → index-Cxrx9m5D.mjs} +53 -21
- 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/{persistence-BMa6cyw9.mjs → persistence-9Iu0wGNM.mjs} +1 -1
- package/dist/{persistence-xK5CKhbn.cjs → persistence-Bl3FYvwd.cjs} +1 -1
- package/dist/{registerKillSessionHandler-Dxwg4L4J.mjs → registerKillSessionHandler-BElGmD1E.mjs} +5 -541
- package/dist/{registerKillSessionHandler-CH6yN0eG.cjs → registerKillSessionHandler-BjkY-oUn.cjs} +4 -549
- package/dist/{runClaude-BMHlBny_.cjs → runClaude-CDZxAF3l.cjs} +129 -630
- package/dist/{runClaude-cQ-UT0Ke.mjs → runClaude-D7dF4RDM.mjs} +126 -627
- package/dist/{runCodex-roOSOWWL.cjs → runCodex-Cik8VzFs.cjs} +224 -17
- package/dist/{runCodex-AnJUPIhX.mjs → runCodex-DnGz1XES.mjs} +213 -6
- package/dist/{runGemini-BGo_0mzA.mjs → runGemini-B8tXMHeL.mjs} +5 -5
- package/dist/{runGemini-Cg6Zbqlz.cjs → runGemini-BM2BQ4I7.cjs} +13 -13
- package/package.json +9 -9
- package/scripts/build.mjs +66 -66
- package/scripts/devtools/README.md +9 -9
- package/scripts/e2e/fake-codex-acp-agent.mjs +139 -139
- package/scripts/e2e/local-server-session-roundtrip.mjs +1063 -1063
- package/scripts/release-smoke.mjs +202 -202
- package/dist/BaseReasoningProcessor-5ACv9gKu.mjs +0 -320
- package/dist/BaseReasoningProcessor-COrRWyn0.cjs +0 -323
- package/dist/ProviderSelectionHandler-BljOLMQn.mjs +0 -261
- package/dist/ProviderSelectionHandler-DBGobhGZ.cjs +0 -265
|
@@ -3,10 +3,10 @@
|
|
|
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 registerKillSessionHandler = require('./registerKillSessionHandler-
|
|
8
|
-
var index = require('./index-
|
|
9
|
-
var
|
|
6
|
+
var api = require('./api-DH5-IqeM.cjs');
|
|
7
|
+
var registerKillSessionHandler = require('./registerKillSessionHandler-BjkY-oUn.cjs');
|
|
8
|
+
var index = require('./index-CryJfCh5.cjs');
|
|
9
|
+
var ConversationHistory$1 = require('./ConversationHistory-V3VLmjJf.cjs');
|
|
10
10
|
require('cross-spawn');
|
|
11
11
|
require('@agentclientprotocol/sdk');
|
|
12
12
|
require('ps-list');
|
|
@@ -15,7 +15,7 @@ require('node:path');
|
|
|
15
15
|
require('node:os');
|
|
16
16
|
require('tweetnacl');
|
|
17
17
|
require('axios');
|
|
18
|
-
require('./persistence-
|
|
18
|
+
require('./persistence-Bl3FYvwd.cjs');
|
|
19
19
|
require('open');
|
|
20
20
|
require('chalk');
|
|
21
21
|
require('fs');
|
|
@@ -184,7 +184,7 @@ const GeminiDisplay = ({ messageBuffer, logPath, currentModel, onExit }) => {
|
|
|
184
184
|
));
|
|
185
185
|
};
|
|
186
186
|
|
|
187
|
-
class GeminiPermissionHandler extends
|
|
187
|
+
class GeminiPermissionHandler extends ConversationHistory$1.BasePermissionHandler {
|
|
188
188
|
currentPermissionMode = "default";
|
|
189
189
|
constructor(session) {
|
|
190
190
|
super(session);
|
|
@@ -269,7 +269,7 @@ class GeminiPermissionHandler extends registerKillSessionHandler.BasePermissionH
|
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
class GeminiReasoningProcessor extends
|
|
272
|
+
class GeminiReasoningProcessor extends ConversationHistory$1.BaseReasoningProcessor {
|
|
273
273
|
getToolName() {
|
|
274
274
|
return "GeminiReasoning";
|
|
275
275
|
}
|
|
@@ -421,7 +421,7 @@ function formatOptionsXml(options) {
|
|
|
421
421
|
return "\n<options>\n" + options.map((opt) => ` <option>${opt}</option>`).join("\n") + "\n</options>";
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
class ConversationHistory extends
|
|
424
|
+
class ConversationHistory extends ConversationHistory$1.ConversationHistory {
|
|
425
425
|
currentModel;
|
|
426
426
|
setCurrentModel(model) {
|
|
427
427
|
this.currentModel = model;
|
|
@@ -488,7 +488,7 @@ async function runGemini(opts) {
|
|
|
488
488
|
pendingSessionSwap = null;
|
|
489
489
|
}
|
|
490
490
|
};
|
|
491
|
-
const { metadata, session: initialSession, reconnectionHandle } = await
|
|
491
|
+
const { metadata, session: initialSession, reconnectionHandle } = await ConversationHistory$1.bootstrapManagedProviderSession({
|
|
492
492
|
api: api$1,
|
|
493
493
|
sessionTag,
|
|
494
494
|
flavor: "gemini",
|
|
@@ -731,7 +731,7 @@ async function runGemini(opts) {
|
|
|
731
731
|
};
|
|
732
732
|
function setupGeminiMessageHandler(activeRuntimeHandle) {
|
|
733
733
|
const forwardAgentMessage = (agentMessage) => {
|
|
734
|
-
|
|
734
|
+
ConversationHistory$1.forwardAgentMessageToProviderSession(agentMessage, {
|
|
735
735
|
provider: "gemini",
|
|
736
736
|
send: (body) => session.sendAgentMessage("gemini", body)
|
|
737
737
|
});
|
|
@@ -815,7 +815,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
815
815
|
forwardAgentMessage(msg);
|
|
816
816
|
break;
|
|
817
817
|
case "tool-result":
|
|
818
|
-
const isError =
|
|
818
|
+
const isError = ConversationHistory$1.inferToolResultError(msg.result);
|
|
819
819
|
const resultText = typeof msg.result === "string" ? msg.result.substring(0, 200) : JSON.stringify(msg.result).substring(0, 200);
|
|
820
820
|
const truncatedResult = resultText + (typeof msg.result === "string" && msg.result.length > 200 ? "..." : "");
|
|
821
821
|
const resultSize = typeof msg.result === "string" ? msg.result.length : JSON.stringify(msg.result).length;
|
|
@@ -906,7 +906,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
906
906
|
const modelToUse = mode.model === void 0 ? void 0 : mode.model || null;
|
|
907
907
|
let backendResult;
|
|
908
908
|
updatePermissionMode(mode.permissionMode);
|
|
909
|
-
const { session: nextRuntimeHandle, factoryResult } = await
|
|
909
|
+
const { session: nextRuntimeHandle, factoryResult } = await ConversationHistory$1.launchRuntimeHandleWithFactoryResult({
|
|
910
910
|
provider: "gemini",
|
|
911
911
|
cwd: process.cwd(),
|
|
912
912
|
createBackendResult: (opts2) => {
|
|
@@ -1021,7 +1021,7 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1021
1021
|
try {
|
|
1022
1022
|
await activeHandle.sendPrompt(promptToSend);
|
|
1023
1023
|
api.logger.debug("[gemini] Prompt sent successfully");
|
|
1024
|
-
await
|
|
1024
|
+
await ConversationHistory$1.waitForResponseCompleteWithAbort(activeHandle.backend, abortController.signal, 12e4);
|
|
1025
1025
|
api.logger.debug("[gemini] Response complete");
|
|
1026
1026
|
break;
|
|
1027
1027
|
} catch (promptError) {
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "happy-imou-cloud",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.13",
|
|
4
4
|
"description": "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI",
|
|
5
5
|
"author": "long.zhu",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"hicloud": "bin/happy-cloud.mjs"
|
|
10
|
-
},
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"hicloud": "bin/happy-cloud.mjs"
|
|
10
|
+
},
|
|
11
11
|
"main": "./dist/index.cjs",
|
|
12
12
|
"module": "./dist/index.mjs",
|
|
13
13
|
"types": "./dist/index.d.cts",
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"// ==== Start & Dev ====": "",
|
|
57
57
|
"start": "yarn build && node ./bin/happy-cloud.mjs",
|
|
58
58
|
"dev": "tsx src/index.ts",
|
|
59
|
-
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
60
|
-
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
61
|
-
"e2e:local-server": "yarn build && node ./scripts/e2e/local-server-session-roundtrip.mjs",
|
|
62
|
-
"// ==== Release ====": "",
|
|
59
|
+
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
60
|
+
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
61
|
+
"e2e:local-server": "yarn build && node ./scripts/e2e/local-server-session-roundtrip.mjs",
|
|
62
|
+
"// ==== Release ====": "",
|
|
63
63
|
"prepublishOnly": "yarn release:smoke",
|
|
64
64
|
"release": "yarn install && release-it",
|
|
65
65
|
"release:smoke": "node ./scripts/release-smoke.mjs",
|
package/scripts/build.mjs
CHANGED
|
@@ -1,66 +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
|
+
#!/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,9 +1,9 @@
|
|
|
1
|
-
# CLI Devtools
|
|
2
|
-
|
|
3
|
-
这组脚本用于 CLI 本地开发辅助。
|
|
4
|
-
|
|
5
|
-
当前包含:
|
|
6
|
-
|
|
7
|
-
- mock credentials 生成器
|
|
8
|
-
|
|
9
|
-
新增 CLI 专属开发脚本应优先放在这里。
|
|
1
|
+
# CLI Devtools
|
|
2
|
+
|
|
3
|
+
这组脚本用于 CLI 本地开发辅助。
|
|
4
|
+
|
|
5
|
+
当前包含:
|
|
6
|
+
|
|
7
|
+
- mock credentials 生成器
|
|
8
|
+
|
|
9
|
+
新增 CLI 专属开发脚本应优先放在这里。
|
|
@@ -1,139 +1,139 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { appendFileSync } from 'node:fs';
|
|
4
|
-
|
|
5
|
-
const decoder = new TextDecoder();
|
|
6
|
-
let buffer = '';
|
|
7
|
-
let sessionId = 'fake-codex-session';
|
|
8
|
-
const logPath = process.env.HAPPY_E2E_FAKE_ACP_LOG || '';
|
|
9
|
-
|
|
10
|
-
function send(payload) {
|
|
11
|
-
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function trace(message) {
|
|
15
|
-
if (!logPath) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
appendFileSync(logPath, `${message}\n`, 'utf8');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function ok(id, result) {
|
|
23
|
-
send({
|
|
24
|
-
jsonrpc: '2.0',
|
|
25
|
-
id,
|
|
26
|
-
result,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function parsePromptText(params) {
|
|
31
|
-
const blocks = Array.isArray(params?.prompt) ? params.prompt : [];
|
|
32
|
-
return blocks
|
|
33
|
-
.map((block) => (block && typeof block === 'object' && typeof block.text === 'string' ? block.text : ''))
|
|
34
|
-
.filter(Boolean)
|
|
35
|
-
.join('\n');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function emitPromptResponse(promptText) {
|
|
39
|
-
const replyPrefix = process.env.HAPPY_E2E_FAKE_ACP_RESPONSE || 'LOCAL_E2E_ACK';
|
|
40
|
-
const replyText = `${replyPrefix}: ${promptText}`.trim();
|
|
41
|
-
trace(`prompt=${JSON.stringify(promptText)}`);
|
|
42
|
-
trace(`reply=${JSON.stringify(replyText)}`);
|
|
43
|
-
|
|
44
|
-
setTimeout(() => {
|
|
45
|
-
send({
|
|
46
|
-
jsonrpc: '2.0',
|
|
47
|
-
method: 'session/update',
|
|
48
|
-
params: {
|
|
49
|
-
sessionId,
|
|
50
|
-
update: {
|
|
51
|
-
sessionUpdate: 'agent_message_chunk',
|
|
52
|
-
content: {
|
|
53
|
-
type: 'text',
|
|
54
|
-
text: replyText,
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
send({
|
|
61
|
-
jsonrpc: '2.0',
|
|
62
|
-
method: 'session/update',
|
|
63
|
-
params: {
|
|
64
|
-
sessionId,
|
|
65
|
-
update: {
|
|
66
|
-
sessionUpdate: 'task_complete',
|
|
67
|
-
usage: {
|
|
68
|
-
input_tokens: Math.max(1, promptText.length),
|
|
69
|
-
output_tokens: Math.max(1, replyText.length),
|
|
70
|
-
total_tokens: Math.max(2, promptText.length + replyText.length),
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
}, 50);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
process.stdin.on('data', (chunk) => {
|
|
79
|
-
buffer += decoder.decode(chunk, { stream: true });
|
|
80
|
-
const lines = buffer.split('\n');
|
|
81
|
-
buffer = lines.pop() || '';
|
|
82
|
-
|
|
83
|
-
for (const line of lines) {
|
|
84
|
-
const trimmed = line.trim();
|
|
85
|
-
if (!trimmed) {
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
let request;
|
|
90
|
-
try {
|
|
91
|
-
request = JSON.parse(trimmed);
|
|
92
|
-
} catch {
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const id = request?.id;
|
|
97
|
-
const method = request?.method;
|
|
98
|
-
|
|
99
|
-
if (typeof method !== 'string' || id === undefined || id === null) {
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
trace(`method=${method}`);
|
|
104
|
-
|
|
105
|
-
if (method === 'initialize') {
|
|
106
|
-
ok(id, {
|
|
107
|
-
protocolVersion: 1,
|
|
108
|
-
authMethods: [],
|
|
109
|
-
});
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (method === 'session/new') {
|
|
114
|
-
sessionId = request?.params?.cwd
|
|
115
|
-
? `fake-codex-session:${request.params.cwd}`
|
|
116
|
-
: 'fake-codex-session';
|
|
117
|
-
ok(id, { sessionId });
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (method === 'session/prompt') {
|
|
122
|
-
ok(id, {});
|
|
123
|
-
emitPromptResponse(parsePromptText(request?.params));
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (method === 'session/cancel') {
|
|
128
|
-
ok(id, {});
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
ok(id, {});
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
process.stdin.on('end', () => {
|
|
137
|
-
trace('stdin=end');
|
|
138
|
-
process.exit(0);
|
|
139
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { appendFileSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
const decoder = new TextDecoder();
|
|
6
|
+
let buffer = '';
|
|
7
|
+
let sessionId = 'fake-codex-session';
|
|
8
|
+
const logPath = process.env.HAPPY_E2E_FAKE_ACP_LOG || '';
|
|
9
|
+
|
|
10
|
+
function send(payload) {
|
|
11
|
+
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function trace(message) {
|
|
15
|
+
if (!logPath) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
appendFileSync(logPath, `${message}\n`, 'utf8');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function ok(id, result) {
|
|
23
|
+
send({
|
|
24
|
+
jsonrpc: '2.0',
|
|
25
|
+
id,
|
|
26
|
+
result,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parsePromptText(params) {
|
|
31
|
+
const blocks = Array.isArray(params?.prompt) ? params.prompt : [];
|
|
32
|
+
return blocks
|
|
33
|
+
.map((block) => (block && typeof block === 'object' && typeof block.text === 'string' ? block.text : ''))
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.join('\n');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function emitPromptResponse(promptText) {
|
|
39
|
+
const replyPrefix = process.env.HAPPY_E2E_FAKE_ACP_RESPONSE || 'LOCAL_E2E_ACK';
|
|
40
|
+
const replyText = `${replyPrefix}: ${promptText}`.trim();
|
|
41
|
+
trace(`prompt=${JSON.stringify(promptText)}`);
|
|
42
|
+
trace(`reply=${JSON.stringify(replyText)}`);
|
|
43
|
+
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
send({
|
|
46
|
+
jsonrpc: '2.0',
|
|
47
|
+
method: 'session/update',
|
|
48
|
+
params: {
|
|
49
|
+
sessionId,
|
|
50
|
+
update: {
|
|
51
|
+
sessionUpdate: 'agent_message_chunk',
|
|
52
|
+
content: {
|
|
53
|
+
type: 'text',
|
|
54
|
+
text: replyText,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
send({
|
|
61
|
+
jsonrpc: '2.0',
|
|
62
|
+
method: 'session/update',
|
|
63
|
+
params: {
|
|
64
|
+
sessionId,
|
|
65
|
+
update: {
|
|
66
|
+
sessionUpdate: 'task_complete',
|
|
67
|
+
usage: {
|
|
68
|
+
input_tokens: Math.max(1, promptText.length),
|
|
69
|
+
output_tokens: Math.max(1, replyText.length),
|
|
70
|
+
total_tokens: Math.max(2, promptText.length + replyText.length),
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}, 50);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
process.stdin.on('data', (chunk) => {
|
|
79
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
80
|
+
const lines = buffer.split('\n');
|
|
81
|
+
buffer = lines.pop() || '';
|
|
82
|
+
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
const trimmed = line.trim();
|
|
85
|
+
if (!trimmed) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let request;
|
|
90
|
+
try {
|
|
91
|
+
request = JSON.parse(trimmed);
|
|
92
|
+
} catch {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const id = request?.id;
|
|
97
|
+
const method = request?.method;
|
|
98
|
+
|
|
99
|
+
if (typeof method !== 'string' || id === undefined || id === null) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
trace(`method=${method}`);
|
|
104
|
+
|
|
105
|
+
if (method === 'initialize') {
|
|
106
|
+
ok(id, {
|
|
107
|
+
protocolVersion: 1,
|
|
108
|
+
authMethods: [],
|
|
109
|
+
});
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (method === 'session/new') {
|
|
114
|
+
sessionId = request?.params?.cwd
|
|
115
|
+
? `fake-codex-session:${request.params.cwd}`
|
|
116
|
+
: 'fake-codex-session';
|
|
117
|
+
ok(id, { sessionId });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (method === 'session/prompt') {
|
|
122
|
+
ok(id, {});
|
|
123
|
+
emitPromptResponse(parsePromptText(request?.params));
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (method === 'session/cancel') {
|
|
128
|
+
ok(id, {});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
ok(id, {});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
process.stdin.on('end', () => {
|
|
137
|
+
trace('stdin=end');
|
|
138
|
+
process.exit(0);
|
|
139
|
+
});
|