@zonease/happy 0.6.38 → 0.6.40
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.mjs +10 -0
- package/dist/{types-DvTsFaqR.cjs → api-C7FyJfZ1.cjs} +400 -1964
- package/dist/{types-BWpRU1VN.mjs → api-D6WL40-U.mjs} +220 -1729
- package/dist/auth-C7ZScWEw.cjs +274 -0
- package/dist/auth-DCsIoZLT.cjs +183 -0
- package/dist/auth-DO9GexAR.mjs +181 -0
- package/dist/auth-DxJ3VvrX.mjs +270 -0
- package/dist/browser-AoVGRX7y.mjs +20 -0
- package/dist/browser-DzgO4oaD.cjs +22 -0
- package/dist/claudeLocal-C6BdzhDx.cjs +352 -0
- package/dist/claudeLocal-CWNGpJ0-.mjs +344 -0
- package/dist/codex/happyMcpStdioBridge.cjs +4 -1
- package/dist/codex/happyMcpStdioBridge.mjs +4 -1
- package/dist/common-BdNgghUJ.mjs +75 -0
- package/dist/common-CZvSRU4p.cjs +83 -0
- package/dist/{config-0Xnc8ARM.cjs → config-CclB3QW9.cjs} +21 -34
- package/dist/{config-_GW4YBCP.mjs → config-DZk-mJ0K.mjs} +7 -20
- package/dist/connect-BXLUKJ5P.mjs +679 -0
- package/dist/connect-k9CqDJ-T.cjs +681 -0
- package/dist/{constants-B4x1K7by.cjs → constants-E_UWYTo3.cjs} +2 -2
- package/dist/{constants-BO9OAFOT.mjs → constants-pmXZGibX.mjs} +1 -1
- package/dist/controlClient-B23M-WPp.cjs +177 -0
- package/dist/controlClient-DB5tDU7V.mjs +167 -0
- package/dist/deviceKeys-BTdo34Uz.mjs +175 -0
- package/dist/deviceKeys-C540DcXl.cjs +182 -0
- package/dist/diagnose-B3D1a_v0.cjs +60 -0
- package/dist/diagnose-CuRKXxt1.mjs +58 -0
- package/dist/doctor-BQGLhrcI.cjs +214 -0
- package/dist/doctor-C08m5Z9f.cjs +78 -0
- package/dist/doctor-CU4JLGQ1.mjs +211 -0
- package/dist/doctor-ON7pN5G5.mjs +74 -0
- package/dist/downloadImage-DZXI3mXL.mjs +887 -0
- package/dist/downloadImage-cQ1Z7hkC.cjs +897 -0
- package/dist/enroll-B55BQR56.mjs +113 -0
- package/dist/enroll-Bo-CNio1.cjs +115 -0
- package/dist/evidence-DQp8QWcv.cjs +129 -0
- package/dist/evidence-DTYRAaOZ.mjs +127 -0
- package/dist/formatImageMessage-CixDYY6G.cjs +46 -0
- package/dist/formatImageMessage-DbKSSjMx.mjs +43 -0
- package/dist/github-DKcyF1zp.mjs +57 -0
- package/dist/github-h9GhJJ_C.cjs +59 -0
- package/dist/index-CDTNgfUf.cjs +578 -0
- package/dist/index-CPKdvi7J.mjs +554 -0
- package/dist/index.cjs +812 -40
- package/dist/index.mjs +813 -40
- package/dist/install-B1e6rdy7.mjs +94 -0
- package/dist/install-CgUPGIoF.mjs +95 -0
- package/dist/install-D7Ir7ciZ.cjs +96 -0
- package/dist/install-DSDufaPd.cjs +98 -0
- package/dist/lib.cjs +18 -12
- package/dist/lib.d.cts +77 -0
- package/dist/lib.d.mts +77 -0
- package/dist/lib.mjs +13 -7
- package/dist/{loadableToolOutput-va3RLplh.mjs → loadableToolOutput-Bfei9wPq.mjs} +4 -2
- package/dist/{loadableToolOutput-5vdQB98M.cjs → loadableToolOutput-CbLGtvST.cjs} +63 -61
- package/dist/mcpToolSchemas-CjeTfrAs.mjs +62 -0
- package/dist/mcpToolSchemas-DK42yoZ_.cjs +64 -0
- package/dist/persistence-CBTtC_dm.cjs +996 -0
- package/dist/persistence-DKfF81pd.mjs +944 -0
- package/dist/projectPath-BTBIMB57.mjs +10 -0
- package/dist/projectPath-ChLhdOyt.cjs +13 -0
- package/dist/prompt-BTWDZZO0.cjs +25 -0
- package/dist/prompt-BsAZsPbo.mjs +22 -0
- package/dist/release-BMEzNONE.mjs +168 -0
- package/dist/release-EhOJeAZe.cjs +171 -0
- package/dist/run-BVZsMNwN.mjs +2164 -0
- package/dist/run-CURx7LhK.cjs +2188 -0
- package/dist/runClaude-6Gi0sYQy.mjs +3763 -0
- package/dist/runClaude-CL3Kt5ex.cjs +3766 -0
- package/dist/{runCodex-DiKzBf9q.mjs → runCodex-ClwEgJ7M.mjs} +36 -20
- package/dist/{runCodex-skq0j05d.cjs → runCodex-YKyVBkJ3.cjs} +169 -153
- package/dist/{runGemini-BeqrVq8R.mjs → runGemini-BPWxVr60.mjs} +34 -17
- package/dist/{runGemini-CVp-CEIh.cjs → runGemini-Xz2W1spn.cjs} +212 -195
- package/dist/{runOneShot-ltCpHbBB.mjs → runOneShot-CdCWiiLr.mjs} +14 -38
- package/dist/{runOneShot-BQNkNW61.cjs → runOneShot-DjZyCvDI.cjs} +31 -55
- package/dist/{serve-BNdoEDVx.mjs → serve-B48JttQ3.mjs} +12 -35
- package/dist/{serve-BECdtivY.cjs → serve-B8vaWQA0.cjs} +35 -58
- package/dist/session-BXboDn2V.mjs +108 -0
- package/dist/session-BZO25zxp.cjs +110 -0
- package/dist/sessionKeyCache-B6cGadYL.mjs +33 -0
- package/dist/sessionKeyCache-CDmexJ9p.cjs +36 -0
- package/dist/sessionList-CgAYZpNj.mjs +51 -0
- package/dist/sessionList-lQ-nqEhc.cjs +54 -0
- package/dist/sessionSummary-CcORiQSq.cjs +393 -0
- package/dist/sessionSummary-DCWQoaDP.mjs +387 -0
- package/dist/spawnHappyCLI-KiazE3kL.cjs +70 -0
- package/dist/spawnHappyCLI-yBIP_Oqi.mjs +66 -0
- package/dist/ssh-q5uZhqBf.mjs +359 -0
- package/dist/ssh-yMllSHu5.cjs +365 -0
- package/dist/systemdScope-BeOtAkhk.mjs +25 -0
- package/dist/systemdScope-DW3-UoKB.cjs +29 -0
- package/dist/task-_nr7J19j.cjs +167 -0
- package/dist/task-wNX1N2i4.mjs +165 -0
- package/dist/time-Bj2HRkKH.cjs +33 -0
- package/dist/time-DgsBLJA_.mjs +29 -0
- package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
- package/dist/trimIdent-BAjmv_TG.mjs +20 -0
- package/dist/types-BoJh4Qfv.mjs +57 -0
- package/dist/types-DWeB7aqh.cjs +59 -0
- package/dist/uninstall-Buaz7d-X.cjs +77 -0
- package/dist/uninstall-Dxf6_4mQ.mjs +75 -0
- package/dist/update-CGlIfa8e.cjs +111 -0
- package/dist/update-CnnoX2cV.mjs +109 -0
- package/dist/workflowCommon-B0d01Y_a.mjs +56 -0
- package/dist/workflowCommon-Bl568-rt.cjs +62 -0
- package/dist/workflowReports-BEWuQesA.cjs +113 -0
- package/dist/workflowReports-Dd8-4-8f.mjs +108 -0
- package/package.json +1 -1
- package/dist/index-BbCDDvoB.cjs +0 -11376
- package/dist/index-vvZXdswy.mjs +0 -11336
- package/dist/mcpToolSchemas-CdPXJQmb.mjs +0 -175
- package/dist/mcpToolSchemas-f-kttjFw.cjs +0 -186
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { resolve, join } from 'node:path';
|
|
3
|
+
import { createInterface } from 'node:readline';
|
|
4
|
+
import { existsSync, readFileSync, readdirSync, statSync, mkdirSync } from 'node:fs';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { l as logger } from './persistence-DKfF81pd.mjs';
|
|
7
|
+
import { homedir } from 'node:os';
|
|
8
|
+
import { p as projectPath } from './projectPath-BTBIMB57.mjs';
|
|
9
|
+
import { g as getBaseSystemPrompt } from './prompt-BsAZsPbo.mjs';
|
|
10
|
+
|
|
11
|
+
function getProjectPath(workingDirectory) {
|
|
12
|
+
const projectId = resolve(workingDirectory).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
13
|
+
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
|
|
14
|
+
return join(claudeConfigDir, "projects", projectId);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function claudeCheckSession(sessionId, path) {
|
|
18
|
+
const projectDir = getProjectPath(path);
|
|
19
|
+
const sessionFile = join(projectDir, `${sessionId}.jsonl`);
|
|
20
|
+
const sessionExists = existsSync(sessionFile);
|
|
21
|
+
if (!sessionExists) {
|
|
22
|
+
logger.debug(`[claudeCheckSession] Path ${sessionFile} does not exist`);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const sessionData = readFileSync(sessionFile, "utf-8").split("\n");
|
|
26
|
+
const hasGoodMessage = !!sessionData.find((v, index) => {
|
|
27
|
+
if (!v.trim()) return false;
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(v);
|
|
30
|
+
return typeof parsed.uuid === "string" && parsed.uuid.length > 0 || // Claude Code 2.1.x
|
|
31
|
+
typeof parsed.messageId === "string" && parsed.messageId.length > 0 || // Older Claude Code
|
|
32
|
+
typeof parsed.leafUuid === "string" && parsed.leafUuid.length > 0;
|
|
33
|
+
} catch (e) {
|
|
34
|
+
logger.debug(`[claudeCheckSession] Malformed JSON at line ${index + 1}:`, e);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
logger.debug(`[claudeCheckSession] Session ${sessionId}: ${hasGoodMessage ? "valid" : "invalid"}`);
|
|
39
|
+
return hasGoodMessage;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function claudeFindLastSession(workingDirectory) {
|
|
43
|
+
try {
|
|
44
|
+
const projectDir = getProjectPath(workingDirectory);
|
|
45
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
46
|
+
const files = readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
47
|
+
const sessionId = f.replace(".jsonl", "");
|
|
48
|
+
if (!uuidPattern.test(sessionId)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (claudeCheckSession(sessionId, workingDirectory)) {
|
|
52
|
+
return {
|
|
53
|
+
name: f,
|
|
54
|
+
sessionId,
|
|
55
|
+
mtime: statSync(join(projectDir, f)).mtime.getTime()
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
|
|
60
|
+
return files.length > 0 ? files[0].sessionId : null;
|
|
61
|
+
} catch (e) {
|
|
62
|
+
logger.debug("[claudeFindLastSession] Error finding sessions:", e);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const DEFAULT_COMMIT_ATTRIBUTION = [
|
|
68
|
+
"Generated with [Claude Code](https://claude.ai/code)",
|
|
69
|
+
"",
|
|
70
|
+
"Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
71
|
+
].join("\n");
|
|
72
|
+
function getClaudeSettingsPath() {
|
|
73
|
+
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
|
|
74
|
+
return join(claudeConfigDir, "settings.json");
|
|
75
|
+
}
|
|
76
|
+
function readClaudeSettings() {
|
|
77
|
+
try {
|
|
78
|
+
const settingsPath = getClaudeSettingsPath();
|
|
79
|
+
if (!existsSync(settingsPath)) {
|
|
80
|
+
logger.debug(`[ClaudeSettings] No Claude settings file found at ${settingsPath}`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const settingsContent = readFileSync(settingsPath, "utf-8");
|
|
84
|
+
const settings = JSON.parse(settingsContent);
|
|
85
|
+
logger.debug(`[ClaudeSettings] Successfully read Claude settings from ${settingsPath}`);
|
|
86
|
+
logger.debug(`[ClaudeSettings] attribution: ${JSON.stringify(settings.attribution)}, includeCoAuthoredBy: ${settings.includeCoAuthoredBy}`);
|
|
87
|
+
return settings;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
logger.debug(`[ClaudeSettings] Error reading Claude settings: ${error}`);
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function getCommitAttribution() {
|
|
94
|
+
const settings = readClaudeSettings();
|
|
95
|
+
if (!settings) return null;
|
|
96
|
+
if (settings.attribution?.commit !== void 0) {
|
|
97
|
+
return settings.attribution.commit || null;
|
|
98
|
+
}
|
|
99
|
+
if (settings.includeCoAuthoredBy === true) {
|
|
100
|
+
return DEFAULT_COMMIT_ATTRIBUTION;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const systemPrompt = (() => {
|
|
106
|
+
const base = getBaseSystemPrompt();
|
|
107
|
+
if (base === null) return "";
|
|
108
|
+
const attribution = getCommitAttribution();
|
|
109
|
+
if (!attribution) return base;
|
|
110
|
+
return base + "\n\n# Commit\n\nWhen making commit messages, add this footer:\n\n" + attribution;
|
|
111
|
+
})();
|
|
112
|
+
|
|
113
|
+
class ExitCodeError extends Error {
|
|
114
|
+
exitCode;
|
|
115
|
+
constructor(exitCode) {
|
|
116
|
+
super(`Process exited with code: ${exitCode}`);
|
|
117
|
+
this.name = "ExitCodeError";
|
|
118
|
+
this.exitCode = exitCode;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const claudeCliPath = resolve(join(projectPath(), "scripts", "claude_local_launcher.cjs"));
|
|
122
|
+
async function claudeLocal(opts) {
|
|
123
|
+
const projectDir = getProjectPath(opts.path);
|
|
124
|
+
mkdirSync(projectDir, { recursive: true });
|
|
125
|
+
const hasContinueFlag = opts.claudeArgs?.includes("--continue");
|
|
126
|
+
const hasResumeFlag = opts.claudeArgs?.includes("--resume");
|
|
127
|
+
const hasUserSessionControl = hasContinueFlag || hasResumeFlag;
|
|
128
|
+
let startFrom = opts.sessionId;
|
|
129
|
+
const extractFlag = (flags, withValue = false) => {
|
|
130
|
+
if (!opts.claudeArgs) return { found: false };
|
|
131
|
+
for (const flag of flags) {
|
|
132
|
+
const index = opts.claudeArgs.indexOf(flag);
|
|
133
|
+
if (index !== -1) {
|
|
134
|
+
if (withValue && index + 1 < opts.claudeArgs.length) {
|
|
135
|
+
const nextArg = opts.claudeArgs[index + 1];
|
|
136
|
+
if (!nextArg.startsWith("-")) {
|
|
137
|
+
const value = nextArg;
|
|
138
|
+
opts.claudeArgs = opts.claudeArgs.filter((_, i) => i !== index && i !== index + 1);
|
|
139
|
+
return { found: true, value };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (!withValue) {
|
|
143
|
+
opts.claudeArgs = opts.claudeArgs.filter((_, i) => i !== index);
|
|
144
|
+
return { found: true };
|
|
145
|
+
}
|
|
146
|
+
return { found: false };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { found: false };
|
|
150
|
+
};
|
|
151
|
+
const sessionIdFlag = extractFlag(["--session-id"], true);
|
|
152
|
+
if (sessionIdFlag.found && sessionIdFlag.value) {
|
|
153
|
+
startFrom = null;
|
|
154
|
+
logger.debug(`[ClaudeLocal] Using explicit --session-id: ${sessionIdFlag.value}`);
|
|
155
|
+
}
|
|
156
|
+
if (!startFrom && !sessionIdFlag.value) {
|
|
157
|
+
const resumeFlag = extractFlag(["--resume", "-r"], true);
|
|
158
|
+
if (resumeFlag.found) {
|
|
159
|
+
if (resumeFlag.value) {
|
|
160
|
+
startFrom = resumeFlag.value;
|
|
161
|
+
logger.debug(`[ClaudeLocal] Using provided session ID from --resume: ${startFrom}`);
|
|
162
|
+
} else {
|
|
163
|
+
const lastSession = claudeFindLastSession(opts.path);
|
|
164
|
+
if (lastSession) {
|
|
165
|
+
startFrom = lastSession;
|
|
166
|
+
logger.debug(`[ClaudeLocal] --resume: Found last session: ${lastSession}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (!startFrom && !sessionIdFlag.value) {
|
|
172
|
+
const continueFlag = extractFlag(["--continue", "-c"], false);
|
|
173
|
+
if (continueFlag.found) {
|
|
174
|
+
const lastSession = claudeFindLastSession(opts.path);
|
|
175
|
+
if (lastSession) {
|
|
176
|
+
startFrom = lastSession;
|
|
177
|
+
logger.debug(`[ClaudeLocal] --continue: Found last session: ${lastSession}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const explicitSessionId = sessionIdFlag.value || null;
|
|
182
|
+
let newSessionId = null;
|
|
183
|
+
let effectiveSessionId = startFrom;
|
|
184
|
+
if (!opts.hookSettingsPath) {
|
|
185
|
+
newSessionId = startFrom ? null : explicitSessionId || randomUUID();
|
|
186
|
+
effectiveSessionId = startFrom || newSessionId;
|
|
187
|
+
if (startFrom) {
|
|
188
|
+
logger.debug(`[ClaudeLocal] Resuming session: ${startFrom}`);
|
|
189
|
+
opts.onSessionFound(startFrom);
|
|
190
|
+
} else if (explicitSessionId) {
|
|
191
|
+
logger.debug(`[ClaudeLocal] Using explicit session ID: ${explicitSessionId}`);
|
|
192
|
+
opts.onSessionFound(explicitSessionId);
|
|
193
|
+
} else {
|
|
194
|
+
logger.debug(`[ClaudeLocal] Generated new session ID: ${newSessionId}`);
|
|
195
|
+
opts.onSessionFound(newSessionId);
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
if (startFrom) {
|
|
199
|
+
logger.debug(`[ClaudeLocal] Will resume existing session: ${startFrom}`);
|
|
200
|
+
} else if (hasUserSessionControl) {
|
|
201
|
+
logger.debug(`[ClaudeLocal] User passed ${hasContinueFlag ? "--continue" : "--resume"} flag, session ID will be determined by hook`);
|
|
202
|
+
} else {
|
|
203
|
+
logger.debug(`[ClaudeLocal] Fresh start, session ID will be provided by hook`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
let thinking = false;
|
|
207
|
+
let stopThinkingTimeout = null;
|
|
208
|
+
const updateThinking = (newThinking) => {
|
|
209
|
+
if (thinking !== newThinking) {
|
|
210
|
+
thinking = newThinking;
|
|
211
|
+
logger.debug(`[ClaudeLocal] Thinking state changed to: ${thinking}`);
|
|
212
|
+
if (opts.onThinkingChange) {
|
|
213
|
+
opts.onThinkingChange(thinking);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
try {
|
|
218
|
+
process.stdin.pause();
|
|
219
|
+
await new Promise((r, reject) => {
|
|
220
|
+
const args = [];
|
|
221
|
+
if (!opts.hookSettingsPath) {
|
|
222
|
+
const hasResumeFlag2 = opts.claudeArgs?.includes("--resume") || opts.claudeArgs?.includes("-r");
|
|
223
|
+
if (startFrom) {
|
|
224
|
+
args.push("--resume", startFrom);
|
|
225
|
+
} else if (!hasResumeFlag2 && newSessionId) {
|
|
226
|
+
args.push("--session-id", newSessionId);
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
if (startFrom) {
|
|
230
|
+
args.push("--resume", startFrom);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
args.push("--append-system-prompt", systemPrompt);
|
|
234
|
+
if (opts.mcpServers && Object.keys(opts.mcpServers).length > 0) {
|
|
235
|
+
args.push("--mcp-config", JSON.stringify({ mcpServers: opts.mcpServers }));
|
|
236
|
+
}
|
|
237
|
+
if (opts.allowedTools && opts.allowedTools.length > 0) {
|
|
238
|
+
args.push("--allowedTools", opts.allowedTools.join(","));
|
|
239
|
+
}
|
|
240
|
+
if (opts.claudeArgs) {
|
|
241
|
+
args.push(...opts.claudeArgs);
|
|
242
|
+
}
|
|
243
|
+
if (opts.hookSettingsPath) {
|
|
244
|
+
args.push("--settings", opts.hookSettingsPath);
|
|
245
|
+
logger.debug(`[ClaudeLocal] Using hook settings: ${opts.hookSettingsPath}`);
|
|
246
|
+
}
|
|
247
|
+
if (!claudeCliPath || !existsSync(claudeCliPath)) {
|
|
248
|
+
throw new Error("Claude local launcher not found. Please ensure HAPPY_PROJECT_ROOT is set correctly for development.");
|
|
249
|
+
}
|
|
250
|
+
const env = {
|
|
251
|
+
...process.env,
|
|
252
|
+
...opts.claudeEnvVars
|
|
253
|
+
};
|
|
254
|
+
logger.debug(`[ClaudeLocal] Spawning launcher: ${claudeCliPath}`);
|
|
255
|
+
logger.debug(`[ClaudeLocal] Args: ${JSON.stringify(args)}`);
|
|
256
|
+
const child = spawn("node", [claudeCliPath, ...args], {
|
|
257
|
+
stdio: ["inherit", "inherit", "inherit", "pipe"],
|
|
258
|
+
signal: opts.abort,
|
|
259
|
+
cwd: opts.path,
|
|
260
|
+
env
|
|
261
|
+
});
|
|
262
|
+
if (child.stdio[3]) {
|
|
263
|
+
const rl = createInterface({
|
|
264
|
+
input: child.stdio[3],
|
|
265
|
+
crlfDelay: Infinity
|
|
266
|
+
});
|
|
267
|
+
const activeFetches = /* @__PURE__ */ new Map();
|
|
268
|
+
rl.on("line", (line) => {
|
|
269
|
+
try {
|
|
270
|
+
const message = JSON.parse(line);
|
|
271
|
+
switch (message.type) {
|
|
272
|
+
case "fetch-start":
|
|
273
|
+
activeFetches.set(message.id, {
|
|
274
|
+
hostname: message.hostname,
|
|
275
|
+
path: message.path,
|
|
276
|
+
startTime: message.timestamp
|
|
277
|
+
});
|
|
278
|
+
if (stopThinkingTimeout) {
|
|
279
|
+
clearTimeout(stopThinkingTimeout);
|
|
280
|
+
stopThinkingTimeout = null;
|
|
281
|
+
}
|
|
282
|
+
updateThinking(true);
|
|
283
|
+
break;
|
|
284
|
+
case "fetch-end":
|
|
285
|
+
activeFetches.delete(message.id);
|
|
286
|
+
if (activeFetches.size === 0 && thinking && !stopThinkingTimeout) {
|
|
287
|
+
stopThinkingTimeout = setTimeout(() => {
|
|
288
|
+
if (activeFetches.size === 0) {
|
|
289
|
+
updateThinking(false);
|
|
290
|
+
}
|
|
291
|
+
stopThinkingTimeout = null;
|
|
292
|
+
}, 500);
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
default:
|
|
296
|
+
logger.debug(`[ClaudeLocal] Unknown message type: ${message.type}`);
|
|
297
|
+
}
|
|
298
|
+
} catch (e) {
|
|
299
|
+
logger.debug(`[ClaudeLocal] Non-JSON line from fd3: ${line}`);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
rl.on("error", (err) => {
|
|
303
|
+
console.error("Error reading from fd 3:", err);
|
|
304
|
+
});
|
|
305
|
+
child.on("exit", () => {
|
|
306
|
+
if (stopThinkingTimeout) {
|
|
307
|
+
clearTimeout(stopThinkingTimeout);
|
|
308
|
+
}
|
|
309
|
+
updateThinking(false);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
child.on("error", (error) => {
|
|
313
|
+
});
|
|
314
|
+
child.on("exit", (code, signal) => {
|
|
315
|
+
if (signal === "SIGTERM" && opts.abort.aborted) {
|
|
316
|
+
r();
|
|
317
|
+
} else if (signal) {
|
|
318
|
+
reject(new Error(`Process terminated with signal: ${signal}`));
|
|
319
|
+
} else if (code !== 0 && code !== null) {
|
|
320
|
+
reject(new ExitCodeError(code));
|
|
321
|
+
} else {
|
|
322
|
+
r();
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
} finally {
|
|
327
|
+
process.stdin.resume();
|
|
328
|
+
if (stopThinkingTimeout) {
|
|
329
|
+
clearTimeout(stopThinkingTimeout);
|
|
330
|
+
stopThinkingTimeout = null;
|
|
331
|
+
}
|
|
332
|
+
updateThinking(false);
|
|
333
|
+
}
|
|
334
|
+
return effectiveSessionId;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
var claudeLocal$1 = /*#__PURE__*/Object.freeze({
|
|
338
|
+
__proto__: null,
|
|
339
|
+
ExitCodeError: ExitCodeError,
|
|
340
|
+
claudeCliPath: claudeCliPath,
|
|
341
|
+
claudeLocal: claudeLocal
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
export { ExitCodeError as E, claudeLocal as a, claudeCheckSession as b, claudeCliPath as c, claudeLocal$1 as d, getProjectPath as g, systemPrompt as s };
|
|
@@ -5,7 +5,10 @@ var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
|
5
5
|
var index_js = require('@modelcontextprotocol/sdk/client/index.js');
|
|
6
6
|
var streamableHttp_js = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
7
7
|
var z = require('zod');
|
|
8
|
-
require('../
|
|
8
|
+
require('../prompt-BTWDZZO0.cjs');
|
|
9
|
+
require('../mcpToolSchemas-DK42yoZ_.cjs');
|
|
10
|
+
require('../trimIdent-4gvk9Wk6.cjs');
|
|
11
|
+
require('../common-CZvSRU4p.cjs');
|
|
9
12
|
|
|
10
13
|
function parseArgs(argv) {
|
|
11
14
|
let url = null;
|
|
@@ -3,7 +3,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
3
3
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
4
4
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import '../
|
|
6
|
+
import '../prompt-BsAZsPbo.mjs';
|
|
7
|
+
import '../mcpToolSchemas-CjeTfrAs.mjs';
|
|
8
|
+
import '../trimIdent-BAjmv_TG.mjs';
|
|
9
|
+
import '../common-BdNgghUJ.mjs';
|
|
7
10
|
|
|
8
11
|
function parseArgs(argv) {
|
|
9
12
|
let url = null;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const ORCHESTRATOR_PROVIDERS = ["claude", "codex", "gemini"];
|
|
2
|
+
const ORCHESTRATOR_ENV_KEYS = {
|
|
3
|
+
oneshot: "HAPPY_ORCH_ONESHOT",
|
|
4
|
+
executionId: "HAPPY_ORCH_EXECUTION_ID",
|
|
5
|
+
runId: "HAPPY_ORCH_RUN_ID",
|
|
6
|
+
taskId: "HAPPY_ORCH_TASK_ID",
|
|
7
|
+
executionType: "HAPPY_ORCH_EXECUTION_TYPE",
|
|
8
|
+
childSessionId: "HAPPY_ORCH_CHILD_SESSION_ID",
|
|
9
|
+
modelMode: "HAPPY_ORCH_MODEL_MODE",
|
|
10
|
+
promptB64: "HAPPY_ORCH_PROMPT_B64",
|
|
11
|
+
timeoutMs: "HAPPY_ORCH_TIMEOUT_MS",
|
|
12
|
+
workingDirectory: "HAPPY_ORCH_WORKING_DIRECTORY"
|
|
13
|
+
};
|
|
14
|
+
function isOrchestratorProvider(value) {
|
|
15
|
+
return typeof value === "string" && ORCHESTRATOR_PROVIDERS.includes(value);
|
|
16
|
+
}
|
|
17
|
+
function mapFinishStatus(reason) {
|
|
18
|
+
if (reason.watchdogTriggered) {
|
|
19
|
+
return "timeout";
|
|
20
|
+
}
|
|
21
|
+
if (reason.cancelRequested) {
|
|
22
|
+
return "cancelled";
|
|
23
|
+
}
|
|
24
|
+
if (reason.exitCode === 0) {
|
|
25
|
+
return "completed";
|
|
26
|
+
}
|
|
27
|
+
return "failed";
|
|
28
|
+
}
|
|
29
|
+
function encodePromptToBase64(prompt) {
|
|
30
|
+
return Buffer.from(prompt, "utf8").toString("base64");
|
|
31
|
+
}
|
|
32
|
+
function decodePromptFromBase64(promptB64) {
|
|
33
|
+
return Buffer.from(promptB64, "base64").toString("utf8");
|
|
34
|
+
}
|
|
35
|
+
function buildOrchestratorEnv(payload) {
|
|
36
|
+
const env = {
|
|
37
|
+
[ORCHESTRATOR_ENV_KEYS.oneshot]: "1",
|
|
38
|
+
[ORCHESTRATOR_ENV_KEYS.executionId]: payload.executionId,
|
|
39
|
+
[ORCHESTRATOR_ENV_KEYS.runId]: payload.runId,
|
|
40
|
+
[ORCHESTRATOR_ENV_KEYS.taskId]: payload.taskId,
|
|
41
|
+
[ORCHESTRATOR_ENV_KEYS.executionType]: payload.executionType,
|
|
42
|
+
[ORCHESTRATOR_ENV_KEYS.promptB64]: encodePromptToBase64(payload.prompt),
|
|
43
|
+
[ORCHESTRATOR_ENV_KEYS.timeoutMs]: String(payload.timeoutMs)
|
|
44
|
+
};
|
|
45
|
+
if (payload.childSessionId) {
|
|
46
|
+
env[ORCHESTRATOR_ENV_KEYS.childSessionId] = payload.childSessionId;
|
|
47
|
+
}
|
|
48
|
+
if (payload.workingDirectory) {
|
|
49
|
+
env[ORCHESTRATOR_ENV_KEYS.workingDirectory] = payload.workingDirectory;
|
|
50
|
+
}
|
|
51
|
+
if (payload.model) {
|
|
52
|
+
env[ORCHESTRATOR_ENV_KEYS.modelMode] = payload.model;
|
|
53
|
+
}
|
|
54
|
+
return env;
|
|
55
|
+
}
|
|
56
|
+
function appendOutputChunk(current, chunk, maxChars) {
|
|
57
|
+
const combined = `${current}${chunk}`;
|
|
58
|
+
if (combined.length <= maxChars) {
|
|
59
|
+
return combined;
|
|
60
|
+
}
|
|
61
|
+
return combined.slice(combined.length - maxChars);
|
|
62
|
+
}
|
|
63
|
+
function buildOutputSummary(stdout, stderr, maxChars = 400) {
|
|
64
|
+
const source = stdout.trim() ? stdout : stderr;
|
|
65
|
+
if (!source.trim()) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const lastLine = source.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).at(-1) ?? source.trim();
|
|
69
|
+
if (lastLine.length <= maxChars) {
|
|
70
|
+
return lastLine;
|
|
71
|
+
}
|
|
72
|
+
return `${lastLine.slice(0, maxChars - 3)}...`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { ORCHESTRATOR_ENV_KEYS as O, buildOrchestratorEnv as a, buildOutputSummary as b, appendOutputChunk as c, decodePromptFromBase64 as d, isOrchestratorProvider as i, mapFinishStatus as m };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ORCHESTRATOR_PROVIDERS = ["claude", "codex", "gemini"];
|
|
4
|
+
const ORCHESTRATOR_ENV_KEYS = {
|
|
5
|
+
oneshot: "HAPPY_ORCH_ONESHOT",
|
|
6
|
+
executionId: "HAPPY_ORCH_EXECUTION_ID",
|
|
7
|
+
runId: "HAPPY_ORCH_RUN_ID",
|
|
8
|
+
taskId: "HAPPY_ORCH_TASK_ID",
|
|
9
|
+
executionType: "HAPPY_ORCH_EXECUTION_TYPE",
|
|
10
|
+
childSessionId: "HAPPY_ORCH_CHILD_SESSION_ID",
|
|
11
|
+
modelMode: "HAPPY_ORCH_MODEL_MODE",
|
|
12
|
+
promptB64: "HAPPY_ORCH_PROMPT_B64",
|
|
13
|
+
timeoutMs: "HAPPY_ORCH_TIMEOUT_MS",
|
|
14
|
+
workingDirectory: "HAPPY_ORCH_WORKING_DIRECTORY"
|
|
15
|
+
};
|
|
16
|
+
function isOrchestratorProvider(value) {
|
|
17
|
+
return typeof value === "string" && ORCHESTRATOR_PROVIDERS.includes(value);
|
|
18
|
+
}
|
|
19
|
+
function mapFinishStatus(reason) {
|
|
20
|
+
if (reason.watchdogTriggered) {
|
|
21
|
+
return "timeout";
|
|
22
|
+
}
|
|
23
|
+
if (reason.cancelRequested) {
|
|
24
|
+
return "cancelled";
|
|
25
|
+
}
|
|
26
|
+
if (reason.exitCode === 0) {
|
|
27
|
+
return "completed";
|
|
28
|
+
}
|
|
29
|
+
return "failed";
|
|
30
|
+
}
|
|
31
|
+
function encodePromptToBase64(prompt) {
|
|
32
|
+
return Buffer.from(prompt, "utf8").toString("base64");
|
|
33
|
+
}
|
|
34
|
+
function decodePromptFromBase64(promptB64) {
|
|
35
|
+
return Buffer.from(promptB64, "base64").toString("utf8");
|
|
36
|
+
}
|
|
37
|
+
function buildOrchestratorEnv(payload) {
|
|
38
|
+
const env = {
|
|
39
|
+
[ORCHESTRATOR_ENV_KEYS.oneshot]: "1",
|
|
40
|
+
[ORCHESTRATOR_ENV_KEYS.executionId]: payload.executionId,
|
|
41
|
+
[ORCHESTRATOR_ENV_KEYS.runId]: payload.runId,
|
|
42
|
+
[ORCHESTRATOR_ENV_KEYS.taskId]: payload.taskId,
|
|
43
|
+
[ORCHESTRATOR_ENV_KEYS.executionType]: payload.executionType,
|
|
44
|
+
[ORCHESTRATOR_ENV_KEYS.promptB64]: encodePromptToBase64(payload.prompt),
|
|
45
|
+
[ORCHESTRATOR_ENV_KEYS.timeoutMs]: String(payload.timeoutMs)
|
|
46
|
+
};
|
|
47
|
+
if (payload.childSessionId) {
|
|
48
|
+
env[ORCHESTRATOR_ENV_KEYS.childSessionId] = payload.childSessionId;
|
|
49
|
+
}
|
|
50
|
+
if (payload.workingDirectory) {
|
|
51
|
+
env[ORCHESTRATOR_ENV_KEYS.workingDirectory] = payload.workingDirectory;
|
|
52
|
+
}
|
|
53
|
+
if (payload.model) {
|
|
54
|
+
env[ORCHESTRATOR_ENV_KEYS.modelMode] = payload.model;
|
|
55
|
+
}
|
|
56
|
+
return env;
|
|
57
|
+
}
|
|
58
|
+
function appendOutputChunk(current, chunk, maxChars) {
|
|
59
|
+
const combined = `${current}${chunk}`;
|
|
60
|
+
if (combined.length <= maxChars) {
|
|
61
|
+
return combined;
|
|
62
|
+
}
|
|
63
|
+
return combined.slice(combined.length - maxChars);
|
|
64
|
+
}
|
|
65
|
+
function buildOutputSummary(stdout, stderr, maxChars = 400) {
|
|
66
|
+
const source = stdout.trim() ? stdout : stderr;
|
|
67
|
+
if (!source.trim()) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const lastLine = source.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).at(-1) ?? source.trim();
|
|
71
|
+
if (lastLine.length <= maxChars) {
|
|
72
|
+
return lastLine;
|
|
73
|
+
}
|
|
74
|
+
return `${lastLine.slice(0, maxChars - 3)}...`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
exports.ORCHESTRATOR_ENV_KEYS = ORCHESTRATOR_ENV_KEYS;
|
|
78
|
+
exports.appendOutputChunk = appendOutputChunk;
|
|
79
|
+
exports.buildOrchestratorEnv = buildOrchestratorEnv;
|
|
80
|
+
exports.buildOutputSummary = buildOutputSummary;
|
|
81
|
+
exports.decodePromptFromBase64 = decodePromptFromBase64;
|
|
82
|
+
exports.isOrchestratorProvider = isOrchestratorProvider;
|
|
83
|
+
exports.mapFinishStatus = mapFinishStatus;
|
|
@@ -4,32 +4,19 @@ var fs = require('fs');
|
|
|
4
4
|
var path = require('path');
|
|
5
5
|
var os = require('os');
|
|
6
6
|
var child_process = require('child_process');
|
|
7
|
-
var
|
|
8
|
-
var constants = require('./constants-
|
|
9
|
-
require('
|
|
10
|
-
require('chalk');
|
|
7
|
+
var logger = require('./persistence-CBTtC_dm.cjs');
|
|
8
|
+
var constants = require('./constants-E_UWYTo3.cjs');
|
|
9
|
+
require('node:fs/promises');
|
|
11
10
|
require('node:fs');
|
|
12
11
|
require('node:os');
|
|
13
12
|
require('node:path');
|
|
14
|
-
require('node:events');
|
|
15
|
-
require('socket.io-client');
|
|
16
13
|
require('zod');
|
|
17
|
-
require('@paralleldrive/cuid2');
|
|
18
14
|
require('node:crypto');
|
|
19
15
|
require('tweetnacl');
|
|
20
|
-
require('
|
|
21
|
-
require('
|
|
22
|
-
require('
|
|
23
|
-
require('
|
|
24
|
-
require('fs/promises');
|
|
25
|
-
require('url');
|
|
26
|
-
require('node:module');
|
|
27
|
-
require('ws');
|
|
28
|
-
require('crypto');
|
|
29
|
-
require('@stablelib/base64');
|
|
30
|
-
require('node:readline');
|
|
31
|
-
require('expo-server-sdk');
|
|
32
|
-
require('./mcpToolSchemas-f-kttjFw.cjs');
|
|
16
|
+
require('chalk');
|
|
17
|
+
require('./prompt-BTWDZZO0.cjs');
|
|
18
|
+
require('./trimIdent-4gvk9Wk6.cjs');
|
|
19
|
+
require('./common-CZvSRU4p.cjs');
|
|
33
20
|
|
|
34
21
|
function readGeminiLocalConfig() {
|
|
35
22
|
let token = null;
|
|
@@ -52,14 +39,14 @@ function readGeminiLocalConfig() {
|
|
|
52
39
|
const foundToken = config.access_token || config.token || config.apiKey || config.GEMINI_API_KEY;
|
|
53
40
|
if (foundToken && typeof foundToken === "string") {
|
|
54
41
|
token = foundToken;
|
|
55
|
-
|
|
42
|
+
logger.logger.debug(`[Gemini] Found token in ${configPath}`);
|
|
56
43
|
}
|
|
57
44
|
}
|
|
58
45
|
if (!model) {
|
|
59
46
|
const foundModel = config.model || config.GEMINI_MODEL;
|
|
60
47
|
if (foundModel && typeof foundModel === "string") {
|
|
61
48
|
model = foundModel;
|
|
62
|
-
|
|
49
|
+
logger.logger.debug(`[Gemini] Found model in ${configPath}: ${model}`);
|
|
63
50
|
}
|
|
64
51
|
}
|
|
65
52
|
if (!googleCloudProject) {
|
|
@@ -69,11 +56,11 @@ function readGeminiLocalConfig() {
|
|
|
69
56
|
if (config.googleCloudProjectEmail && typeof config.googleCloudProjectEmail === "string") {
|
|
70
57
|
googleCloudProjectEmail = config.googleCloudProjectEmail;
|
|
71
58
|
}
|
|
72
|
-
|
|
59
|
+
logger.logger.debug(`[Gemini] Found Google Cloud Project in ${configPath}: ${googleCloudProject}${googleCloudProjectEmail ? ` (for ${googleCloudProjectEmail})` : ""}`);
|
|
73
60
|
}
|
|
74
61
|
}
|
|
75
62
|
} catch (error) {
|
|
76
|
-
|
|
63
|
+
logger.logger.debug(`[Gemini] Failed to read config from ${configPath}:`, error);
|
|
77
64
|
}
|
|
78
65
|
}
|
|
79
66
|
}
|
|
@@ -86,10 +73,10 @@ function readGeminiLocalConfig() {
|
|
|
86
73
|
}).trim();
|
|
87
74
|
if (gcloudToken && gcloudToken.length > 0) {
|
|
88
75
|
token = gcloudToken;
|
|
89
|
-
|
|
76
|
+
logger.logger.debug("[Gemini] Found token via gcloud Application Default Credentials");
|
|
90
77
|
}
|
|
91
78
|
} catch (error) {
|
|
92
|
-
|
|
79
|
+
logger.logger.debug("[Gemini] gcloud Application Default Credentials not available");
|
|
93
80
|
}
|
|
94
81
|
}
|
|
95
82
|
if (!googleCloudProject) {
|
|
@@ -97,7 +84,7 @@ function readGeminiLocalConfig() {
|
|
|
97
84
|
if (envProject) {
|
|
98
85
|
googleCloudProject = envProject;
|
|
99
86
|
googleCloudProjectEmail = null;
|
|
100
|
-
|
|
87
|
+
logger.logger.debug(`[Gemini] Found Google Cloud Project from env: ${googleCloudProject}`);
|
|
101
88
|
}
|
|
102
89
|
}
|
|
103
90
|
return { token, model, googleCloudProject, googleCloudProjectEmail };
|
|
@@ -111,9 +98,9 @@ function determineGeminiModel(explicitModel, localConfig) {
|
|
|
111
98
|
}
|
|
112
99
|
} else {
|
|
113
100
|
const envModel = process.env[constants.GEMINI_MODEL_ENV];
|
|
114
|
-
|
|
101
|
+
logger.logger.debug(`[Gemini] Model selection: env[GEMINI_MODEL_ENV]=${envModel}, localConfig.model=${localConfig.model}, DEFAULT=${constants.DEFAULT_GEMINI_MODEL}`);
|
|
115
102
|
const model = envModel || localConfig.model || constants.DEFAULT_GEMINI_MODEL;
|
|
116
|
-
|
|
103
|
+
logger.logger.debug(`[Gemini] Selected model: ${model}`);
|
|
117
104
|
return model;
|
|
118
105
|
}
|
|
119
106
|
}
|
|
@@ -129,15 +116,15 @@ function saveGeminiModelToConfig(model) {
|
|
|
129
116
|
try {
|
|
130
117
|
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
131
118
|
} catch (error) {
|
|
132
|
-
|
|
119
|
+
logger.logger.debug(`[Gemini] Failed to read existing config, creating new one`);
|
|
133
120
|
config = {};
|
|
134
121
|
}
|
|
135
122
|
}
|
|
136
123
|
config.model = model;
|
|
137
124
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
138
|
-
|
|
125
|
+
logger.logger.debug(`[Gemini] Saved model "${model}" to ${configPath}`);
|
|
139
126
|
} catch (error) {
|
|
140
|
-
|
|
127
|
+
logger.logger.debug(`[Gemini] Failed to save model to config:`, error);
|
|
141
128
|
}
|
|
142
129
|
}
|
|
143
130
|
function saveGoogleCloudProjectToConfig(projectId, email) {
|
|
@@ -160,9 +147,9 @@ function saveGoogleCloudProjectToConfig(projectId, email) {
|
|
|
160
147
|
config.googleCloudProjectEmail = email;
|
|
161
148
|
}
|
|
162
149
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
163
|
-
|
|
150
|
+
logger.logger.debug(`[Gemini] Saved Google Cloud Project "${projectId}"${email ? ` for ${email}` : ""} to ${configPath}`);
|
|
164
151
|
} catch (error) {
|
|
165
|
-
|
|
152
|
+
logger.logger.debug(`[Gemini] Failed to save Google Cloud Project to config:`, error);
|
|
166
153
|
throw error;
|
|
167
154
|
}
|
|
168
155
|
}
|