@xcanwin/manyoyo 5.8.9 → 5.8.11

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.
@@ -1,153 +0,0 @@
1
- 'use strict';
2
-
3
- const {
4
- resolveAgentProgram,
5
- buildAgentResumeCommand
6
- } = require('../agent-resume');
7
-
8
- function normalizeAgentPromptCommandTemplate(value, sourceLabel = 'agentPromptCommand') {
9
- if (value === undefined || value === null) {
10
- return '';
11
- }
12
- if (typeof value !== 'string') {
13
- throw new Error(`${sourceLabel} 必须是字符串`);
14
- }
15
- const text = value.trim();
16
- if (!text) {
17
- return '';
18
- }
19
- if (!text.includes('{prompt}')) {
20
- throw new Error(`${sourceLabel} 必须包含 {prompt} 占位符`);
21
- }
22
- if (/^codex\s+exec(?:\s|$)/.test(text) && !text.includes('--skip-git-repo-check')) {
23
- return text.replace(/^codex\s+exec\b/, 'codex exec --skip-git-repo-check');
24
- }
25
- return text;
26
- }
27
-
28
- function isAgentPromptCommandEnabled(value) {
29
- return typeof value === 'string' && value.includes('{prompt}') && Boolean(value.trim());
30
- }
31
-
32
- function quoteBashSingleValue(value) {
33
- const text = String(value || '');
34
- return `'${text.replace(/'/g, `'\"'\"'`)}'`;
35
- }
36
-
37
- function renderAgentPromptCommand(template, prompt) {
38
- const templateText = normalizeAgentPromptCommandTemplate(template, 'agentPromptCommand');
39
- const safePrompt = quoteBashSingleValue(prompt);
40
- return templateText.replace(/\{prompt\}/g, safePrompt);
41
- }
42
-
43
- function prependAgentFlags(commandText, matchPattern, flagSpecs) {
44
- const matched = String(commandText || '').match(matchPattern);
45
- if (!matched) {
46
- return String(commandText || '');
47
- }
48
- const prefix = matched[1] || '';
49
- let suffix = matched[matched.length - 1] || '';
50
- for (let i = flagSpecs.length - 1; i >= 0; i -= 1) {
51
- const spec = flagSpecs[i];
52
- if (!spec || !spec.flag || !(spec.pattern instanceof RegExp) || spec.pattern.test(suffix)) {
53
- continue;
54
- }
55
- suffix = ` ${spec.flag}${suffix}`;
56
- }
57
- return `${prefix}${suffix}`;
58
- }
59
-
60
- function buildCodexAgentExecCommand(template, prompt) {
61
- const templateText = normalizeAgentPromptCommandTemplate(template, 'agentPromptCommand');
62
- const execMatch = templateText.match(
63
- /^((?:(?:[A-Za-z_][A-Za-z0-9_]*=)(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s]+)\s+)*)codex\s+exec\b/
64
- );
65
- let codexTemplate = templateText;
66
- if (execMatch) {
67
- const prefix = execMatch[1] || '';
68
- const suffix = templateText.slice(execMatch[0].length);
69
- const hasJson = /(?:^|\s)--json(?:\s|$)/.test(suffix);
70
- const injectedFlags = hasJson ? '' : ' --json';
71
- codexTemplate = `${prefix}codex exec${injectedFlags}${suffix}`;
72
- }
73
- return codexTemplate === templateText
74
- ? renderAgentPromptCommand(templateText, prompt)
75
- : renderAgentPromptCommand(codexTemplate, prompt);
76
- }
77
-
78
- function buildClaudeAgentExecCommand(template, prompt) {
79
- const templateText = normalizeAgentPromptCommandTemplate(template, 'agentPromptCommand');
80
- const claudeTemplate = prependAgentFlags(
81
- templateText,
82
- /^(((?:(?:[A-Za-z_][A-Za-z0-9_]*=)(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s]+)\s+)*)claude\b)(.*)$/,
83
- [
84
- { flag: '--verbose', pattern: /(?:^|\s)--verbose(?:\s|$)/ },
85
- { flag: '--output-format stream-json', pattern: /(?:^|\s)--output-format(?:\s|$)/ }
86
- ]
87
- );
88
- return claudeTemplate === templateText
89
- ? renderAgentPromptCommand(templateText, prompt)
90
- : renderAgentPromptCommand(claudeTemplate, prompt);
91
- }
92
-
93
- function buildGeminiAgentExecCommand(template, prompt) {
94
- const templateText = normalizeAgentPromptCommandTemplate(template, 'agentPromptCommand');
95
- const geminiTemplate = prependAgentFlags(
96
- templateText,
97
- /^(((?:(?:[A-Za-z_][A-Za-z0-9_]*=)(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s]+)\s+)*)gemini\b)(.*)$/,
98
- [
99
- { flag: '--output-format stream-json', pattern: /(?:^|\s)--output-format(?:\s|$)/ }
100
- ]
101
- );
102
- return geminiTemplate === templateText
103
- ? renderAgentPromptCommand(templateText, prompt)
104
- : renderAgentPromptCommand(geminiTemplate, prompt);
105
- }
106
-
107
- function buildOpenCodeAgentExecCommand(template, prompt) {
108
- const templateText = normalizeAgentPromptCommandTemplate(template, 'agentPromptCommand');
109
- const opencodeTemplate = prependAgentFlags(
110
- templateText,
111
- /^(((?:(?:[A-Za-z_][A-Za-z0-9_]*=)(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s]+)\s+)*)opencode\s+run\b)(.*)$/,
112
- [
113
- { flag: '--format json', pattern: /(?:^|\s)--format(?:\s|$)/ }
114
- ]
115
- );
116
- return opencodeTemplate === templateText
117
- ? renderAgentPromptCommand(templateText, prompt)
118
- : renderAgentPromptCommand(opencodeTemplate, prompt);
119
- }
120
-
121
- function buildWebAgentExecCommand(template, prompt, agentProgram) {
122
- switch (agentProgram) {
123
- case 'claude':
124
- return buildClaudeAgentExecCommand(template, prompt);
125
- case 'gemini':
126
- return buildGeminiAgentExecCommand(template, prompt);
127
- case 'codex':
128
- return buildCodexAgentExecCommand(template, prompt);
129
- case 'opencode':
130
- return buildOpenCodeAgentExecCommand(template, prompt);
131
- default:
132
- break;
133
- }
134
- return renderAgentPromptCommand(template, prompt);
135
- }
136
-
137
- function getAgentRuntimeMeta(template) {
138
- const normalizedTemplate = normalizeAgentPromptCommandTemplate(template, 'agentPromptCommand');
139
- const agentProgram = resolveAgentProgram(normalizedTemplate);
140
- const resumeCommand = buildAgentResumeCommand(agentProgram);
141
- return {
142
- agentProgram: agentProgram || '',
143
- resumeCommand: resumeCommand || '',
144
- resumeSupported: Boolean(resumeCommand)
145
- };
146
- }
147
-
148
- module.exports = {
149
- normalizeAgentPromptCommandTemplate,
150
- isAgentPromptCommandEnabled,
151
- buildWebAgentExecCommand,
152
- getAgentRuntimeMeta
153
- };
@@ -1,88 +0,0 @@
1
- 'use strict';
2
-
3
- function createApiRouteHelpers(deps) {
4
- const {
5
- req,
6
- res,
7
- ctx,
8
- state,
9
- sendJson,
10
- readJsonBody,
11
- getValidSessionRef,
12
- prepareWebAgentExecution
13
- } = deps;
14
-
15
- const withSessionRef = handler => async match => {
16
- const sessionRef = getValidSessionRef(ctx, res, match[1]);
17
- if (!sessionRef) {
18
- return;
19
- }
20
- await handler(sessionRef, match);
21
- };
22
-
23
- const withJsonBody = handler => async (...args) => {
24
- const payload = await readJsonBody(req);
25
- await handler(payload, ...args);
26
- };
27
-
28
- const withSessionJsonBody = (handler, fallbackError = '') => withSessionRef(async (sessionRef, match) => {
29
- let payload = null;
30
- if (fallbackError) {
31
- try {
32
- payload = await readJsonBody(req);
33
- } catch (e) {
34
- sendJson(res, 400, { error: e.message || fallbackError });
35
- return;
36
- }
37
- } else {
38
- payload = await readJsonBody(req);
39
- }
40
- await handler(sessionRef, payload, match);
41
- });
42
-
43
- const getRequiredBodyText = (payload, key, emptyMessage) => {
44
- const value = String(payload && payload[key] || '').trim();
45
- if (!value) {
46
- sendJson(res, 400, { error: emptyMessage });
47
- return null;
48
- }
49
- return value;
50
- };
51
-
52
- const prepareAgentRequest = async (sessionRef, prompt) => {
53
- try {
54
- return await prepareWebAgentExecution(ctx, state, sessionRef, prompt);
55
- } catch (e) {
56
- sendJson(res, 400, { error: e && e.message ? e.message : 'Agent 执行准备失败' });
57
- return null;
58
- }
59
- };
60
-
61
- return {
62
- withSessionRef,
63
- withJsonBody,
64
- withSessionJsonBody,
65
- getRequiredBodyText,
66
- prepareAgentRequest
67
- };
68
- }
69
-
70
- async function runMatchedRoute(routes, method, pathname) {
71
- for (const route of routes) {
72
- if (route.method !== method) {
73
- continue;
74
- }
75
- const matched = route.match(pathname);
76
- if (!matched) {
77
- continue;
78
- }
79
- await route.handler(matched);
80
- return true;
81
- }
82
- return false;
83
- }
84
-
85
- module.exports = {
86
- createApiRouteHelpers,
87
- runMatchedRoute
88
- };
@@ -1,215 +0,0 @@
1
- 'use strict';
2
-
3
- const { spawn } = require('child_process');
4
-
5
- function createTextBuffer(maxChars) {
6
- let value = '';
7
- let truncated = false;
8
- return {
9
- append(chunk) {
10
- if (!chunk) {
11
- return;
12
- }
13
- const text = chunk.toString('utf-8');
14
- if (!text) {
15
- return;
16
- }
17
- if (value.length >= maxChars) {
18
- truncated = true;
19
- return;
20
- }
21
- const remain = maxChars - value.length;
22
- if (text.length > remain) {
23
- value += text.slice(0, remain);
24
- truncated = true;
25
- return;
26
- }
27
- value += text;
28
- },
29
- buildOutput(suffix) {
30
- return truncated ? `${value}\n...${suffix}` : value;
31
- }
32
- };
33
- }
34
-
35
- function drainLines(text, carry, handleLine) {
36
- let pending = carry + String(text || '');
37
- let newlineIndex = pending.indexOf('\n');
38
- while (newlineIndex !== -1) {
39
- const line = pending.slice(0, newlineIndex).replace(/\r$/, '');
40
- handleLine(line);
41
- pending = pending.slice(newlineIndex + 1);
42
- newlineIndex = pending.indexOf('\n');
43
- }
44
- return pending;
45
- }
46
-
47
- function createWebContainerExecHelpers(options = {}) {
48
- const buildWebSessionKey = options.buildWebSessionKey || (() => '');
49
- const defaultAgentId = options.defaultAgentId || 'default';
50
- const extractAgentMessageFromStructuredOutput = options.extractAgentMessageFromStructuredOutput || (() => '');
51
- const parseJsonObjectLine = options.parseJsonObjectLine || (() => null);
52
- const prepareStructuredTraceEvents = options.prepareStructuredTraceEvents || (() => []);
53
- const extractContentDeltaFromPayload = options.extractContentDeltaFromPayload || (() => null);
54
- const structuredTraceDeps = options.structuredTraceDeps || {};
55
- const clipText = options.clipText || (text => String(text || ''));
56
- const stripAnsi = options.stripAnsi || (text => String(text || ''));
57
- const maxRawOutputChars = Number.isInteger(options.maxRawOutputChars) ? options.maxRawOutputChars : 32 * 1024 * 1024;
58
-
59
- function buildFinalOutput(agentProgram, stdoutBuffer, stderrBuffer) {
60
- const clippedStdout = stdoutBuffer.buildOutput('[stdout-truncated]');
61
- const clippedStderr = stderrBuffer.buildOutput('[stderr-truncated]');
62
- const clippedRaw = `${clippedStdout}${clippedStdout && clippedStderr ? '\n' : ''}${clippedStderr}`;
63
- const extractedAgentMessage = extractAgentMessageFromStructuredOutput(agentProgram, clippedStdout);
64
- const cleanOutputSource = extractedAgentMessage || clippedRaw;
65
- return clipText(stripAnsi(cleanOutputSource).trim() || '(无输出)');
66
- }
67
-
68
- return {
69
- async execCommandInWebContainer(ctx, containerName, command, options = {}) {
70
- const opts = options && typeof options === 'object' ? options : {};
71
- const agentProgram = typeof opts.agentProgram === 'string' ? opts.agentProgram : '';
72
- return await new Promise((resolve, reject) => {
73
- const process = spawn(
74
- ctx.dockerCmd,
75
- ['exec', containerName, '/bin/bash', '-lc', command],
76
- { stdio: ['ignore', 'pipe', 'pipe'] }
77
- );
78
-
79
- const stdoutBuffer = createTextBuffer(maxRawOutputChars);
80
- const stderrBuffer = createTextBuffer(maxRawOutputChars);
81
-
82
- process.stdout.on('data', chunk => stdoutBuffer.append(chunk));
83
- process.stderr.on('data', chunk => stderrBuffer.append(chunk));
84
-
85
- process.on('error', reject);
86
- process.on('close', code => {
87
- const exitCode = typeof code === 'number' ? code : 1;
88
- resolve({
89
- exitCode,
90
- output: buildFinalOutput(agentProgram, stdoutBuffer, stderrBuffer)
91
- });
92
- });
93
- });
94
- },
95
- async execAgentInWebContainerStream(ctx, state, sessionRefOrContainerName, command, options = {}) {
96
- const opts = options && typeof options === 'object' ? options : {};
97
- const sessionRef = typeof sessionRefOrContainerName === 'string'
98
- ? { containerName: sessionRefOrContainerName, agentId: defaultAgentId }
99
- : sessionRefOrContainerName;
100
- const sessionKey = buildWebSessionKey(sessionRef.containerName, sessionRef.agentId);
101
- const agentProgram = typeof opts.agentProgram === 'string' ? opts.agentProgram : '';
102
- const onEvent = typeof opts.onEvent === 'function' ? opts.onEvent : () => {};
103
- const process = spawn(
104
- ctx.dockerCmd,
105
- ['exec', sessionRef.containerName, '/bin/bash', '-lc', command],
106
- { stdio: ['ignore', 'pipe', 'pipe'] }
107
- );
108
-
109
- const runState = {
110
- containerName: sessionRef.containerName,
111
- sessionKey,
112
- process,
113
- command,
114
- startedAt: new Date().toISOString(),
115
- stopping: false
116
- };
117
- state.agentRuns.set(sessionRef.containerName, runState);
118
-
119
- return await new Promise((resolve, reject) => {
120
- const stdoutBuffer = createTextBuffer(maxRawOutputChars);
121
- const stderrBuffer = createTextBuffer(maxRawOutputChars);
122
- let stdoutPending = '';
123
- let stderrPending = '';
124
- const structuredTraceState = {
125
- toolNamesById: new Map()
126
- };
127
- let contentDeltaAccumulator = '';
128
-
129
- function emitStdoutTraceLine(line) {
130
- const rawLine = String(line || '').trim();
131
- if (!rawLine) {
132
- return;
133
- }
134
- if (agentProgram === 'claude' || agentProgram === 'gemini' || agentProgram === 'codex' || agentProgram === 'opencode') {
135
- const payload = parseJsonObjectLine(rawLine);
136
- if (payload) {
137
- const traceEvents = prepareStructuredTraceEvents(agentProgram, payload, structuredTraceState, structuredTraceDeps);
138
- traceEvents.forEach(traceEvent => {
139
- if (!traceEvent || !traceEvent.text) {
140
- return;
141
- }
142
- onEvent({
143
- type: 'trace',
144
- stream: 'stdout',
145
- text: traceEvent.text,
146
- traceEvent
147
- });
148
- });
149
- const deltaContent = extractContentDeltaFromPayload(agentProgram, payload, structuredTraceDeps);
150
- if (deltaContent !== null) {
151
- if (deltaContent.reset) {
152
- contentDeltaAccumulator = deltaContent.text;
153
- } else {
154
- contentDeltaAccumulator += deltaContent.text;
155
- }
156
- onEvent({
157
- type: 'content_delta',
158
- content: contentDeltaAccumulator
159
- });
160
- }
161
- return;
162
- }
163
- if (agentProgram === 'codex' && (/^OpenAI Codex\b/.test(rawLine) || /^tokens used\b/i.test(rawLine))) {
164
- return;
165
- }
166
- }
167
- onEvent({ type: 'trace', stream: 'stdout', text: rawLine });
168
- }
169
-
170
- function emitStderrTraceLine(line) {
171
- const rawLine = String(line || '').trim();
172
- if (!rawLine) {
173
- return;
174
- }
175
- onEvent({ type: 'trace', stream: 'stderr', text: `[stderr] ${rawLine}` });
176
- }
177
-
178
- process.stdout.on('data', chunk => {
179
- stdoutBuffer.append(chunk);
180
- stdoutPending = drainLines(chunk.toString('utf-8'), stdoutPending, emitStdoutTraceLine);
181
- });
182
- process.stderr.on('data', chunk => {
183
- stderrBuffer.append(chunk);
184
- stderrPending = drainLines(chunk.toString('utf-8'), stderrPending, emitStderrTraceLine);
185
- });
186
-
187
- process.on('error', error => {
188
- state.agentRuns.delete(sessionRef.containerName);
189
- reject(error);
190
- });
191
- process.on('close', code => {
192
- state.agentRuns.delete(sessionRef.containerName);
193
- if (stdoutPending) {
194
- emitStdoutTraceLine(stdoutPending);
195
- stdoutPending = '';
196
- }
197
- if (stderrPending) {
198
- emitStderrTraceLine(stderrPending);
199
- stderrPending = '';
200
- }
201
- const exitCode = typeof code === 'number' ? code : 1;
202
- resolve({
203
- exitCode,
204
- output: buildFinalOutput(agentProgram, stdoutBuffer, stderrBuffer),
205
- interrupted: exitCode !== 0 && runState.stopping === true
206
- });
207
- });
208
- });
209
- }
210
- };
211
- }
212
-
213
- module.exports = {
214
- createWebContainerExecHelpers
215
- };
@@ -1,163 +0,0 @@
1
- 'use strict';
2
-
3
- function createWebHttpHandlers(deps) {
4
- const {
5
- loadTemplate,
6
- sendHtml,
7
- sendJson,
8
- sendRedirect,
9
- sendStaticAsset,
10
- sendVendorAsset,
11
- readJsonBody,
12
- secureStringEqual,
13
- createWebAuthSession,
14
- clearWebAuthSession,
15
- getWebAuthCookie,
16
- getWebAuthClearCookie,
17
- getWebAuthSession,
18
- AUTH_FRONTEND_ASSETS,
19
- APP_FRONTEND_ASSETS,
20
- APP_VENDOR_ASSETS,
21
- handleWebApi
22
- } = deps;
23
-
24
- function serveAllowedStaticAsset(req, res, pathname, pattern, allowedAssets, sendAsset) {
25
- const matched = req.method === 'GET' ? pathname.match(pattern) : null;
26
- if (!matched) {
27
- return false;
28
- }
29
- const assetName = matched[1];
30
- if (!allowedAssets.has(assetName)) {
31
- sendHtml(res, 404, '<h1>404 Not Found</h1>');
32
- return true;
33
- }
34
- sendAsset(res, assetName);
35
- return true;
36
- }
37
-
38
- async function handleWebAuthRoutes(req, res, pathname, ctx, state) {
39
- if (req.method === 'GET' && pathname === '/favicon.ico') {
40
- res.writeHead(204, { 'Cache-Control': 'no-store' });
41
- res.end();
42
- return true;
43
- }
44
-
45
- if (req.method === 'GET' && pathname === '/auth/login') {
46
- sendHtml(res, 200, loadTemplate('login.html'));
47
- return true;
48
- }
49
-
50
- if (serveAllowedStaticAsset(req, res, pathname, /^\/auth\/frontend\/([A-Za-z0-9._-]+)$/, AUTH_FRONTEND_ASSETS, sendStaticAsset)) {
51
- return true;
52
- }
53
-
54
- if (req.method === 'POST' && pathname === '/auth/login') {
55
- const payload = await readJsonBody(req);
56
- const username = String(payload.username || '').trim();
57
- const password = String(payload.password || '');
58
-
59
- if (!username || !password) {
60
- sendJson(res, 400, { error: '用户名和密码不能为空' });
61
- return true;
62
- }
63
-
64
- const userOk = secureStringEqual(username, ctx.authUser);
65
- const passOk = secureStringEqual(password, ctx.authPass);
66
- if (!(userOk && passOk)) {
67
- sendJson(res, 401, { error: '用户名或密码错误' });
68
- return true;
69
- }
70
-
71
- const sessionId = createWebAuthSession(state, username);
72
- sendJson(
73
- res,
74
- 200,
75
- { ok: true, username },
76
- { 'Set-Cookie': getWebAuthCookie(sessionId) }
77
- );
78
- return true;
79
- }
80
-
81
- if (req.method === 'POST' && pathname === '/auth/logout') {
82
- clearWebAuthSession(state, req);
83
- sendJson(
84
- res,
85
- 200,
86
- { ok: true },
87
- { 'Set-Cookie': getWebAuthClearCookie() }
88
- );
89
- return true;
90
- }
91
-
92
- return false;
93
- }
94
-
95
- function sendWebUnauthorized(res, pathname) {
96
- if (pathname.startsWith('/api/') || pathname.startsWith('/auth/')) {
97
- sendJson(res, 401, { error: 'UNAUTHORIZED' });
98
- return;
99
- }
100
- if (pathname === '/' || pathname === '') {
101
- sendRedirect(res, 302, '/auth/login', { 'Set-Cookie': getWebAuthClearCookie() });
102
- return;
103
- }
104
- sendHtml(
105
- res,
106
- 401,
107
- loadTemplate('login.html'),
108
- { 'Set-Cookie': getWebAuthClearCookie() }
109
- );
110
- }
111
-
112
- async function handleWebHttpRequest(req, res, pathname, ctx, state) {
113
- if (await handleWebAuthRoutes(req, res, pathname, ctx, state)) {
114
- return true;
115
- }
116
-
117
- const authSession = getWebAuthSession(state, req);
118
- if (!authSession) {
119
- sendWebUnauthorized(res, pathname);
120
- return true;
121
- }
122
-
123
- if (req.method === 'GET' && pathname === '/') {
124
- sendHtml(res, 200, loadTemplate('app.html'));
125
- return true;
126
- }
127
-
128
- if (serveAllowedStaticAsset(req, res, pathname, /^\/app\/frontend\/([A-Za-z0-9._-]+)$/, APP_FRONTEND_ASSETS, sendStaticAsset)) {
129
- return true;
130
- }
131
-
132
- if (serveAllowedStaticAsset(req, res, pathname, /^\/app\/vendor\/([A-Za-z0-9._-]+)$/, APP_VENDOR_ASSETS, sendVendorAsset)) {
133
- return true;
134
- }
135
-
136
- if (pathname === '/healthz') {
137
- sendJson(res, 200, { ok: true });
138
- return true;
139
- }
140
-
141
- if (pathname.startsWith('/api/')) {
142
- const handled = await handleWebApi(req, res, pathname, ctx, state);
143
- if (!handled) {
144
- sendJson(res, 404, { error: 'Not Found' });
145
- }
146
- return true;
147
- }
148
-
149
- sendHtml(res, 404, '<h1>404 Not Found</h1>');
150
- return true;
151
- }
152
-
153
- return {
154
- serveAllowedStaticAsset,
155
- handleWebAuthRoutes,
156
- sendWebUnauthorized,
157
- handleWebHttpRequest
158
- };
159
- }
160
-
161
- module.exports = {
162
- createWebHttpHandlers
163
- };
@@ -1,50 +0,0 @@
1
- 'use strict';
2
-
3
- function createWebRuntimeStateHelpers(options = {}) {
4
- const createMap = () => new Map();
5
-
6
- return {
7
- createInitialWebRuntimeState(baseState = {}) {
8
- return {
9
- ...baseState,
10
- authSessions: createMap(),
11
- terminalSessions: createMap(),
12
- agentRuns: createMap()
13
- };
14
- },
15
- stopWebAgentRun(state, containerName) {
16
- const runState = state.agentRuns.get(containerName);
17
- if (!runState || !runState.process || runState.process.killed) {
18
- return false;
19
- }
20
- runState.stopping = true;
21
- try {
22
- runState.process.kill('SIGTERM');
23
- } catch {
24
- return false;
25
- }
26
- return true;
27
- },
28
- cleanupWebRuntimeState(state) {
29
- for (const session of state.terminalSessions.values()) {
30
- const ptyProcess = session && session.ptyProcess;
31
- if (ptyProcess && !ptyProcess.killed) {
32
- try { ptyProcess.kill('SIGTERM'); } catch {}
33
- }
34
- }
35
- state.terminalSessions.clear();
36
-
37
- for (const runState of state.agentRuns.values()) {
38
- const child = runState && runState.process;
39
- if (child && !child.killed) {
40
- try { child.kill('SIGTERM'); } catch {}
41
- }
42
- }
43
- state.agentRuns.clear();
44
- }
45
- };
46
- }
47
-
48
- module.exports = {
49
- createWebRuntimeStateHelpers
50
- };