@truefoundry/trueforge-core 0.2.1 → 0.3.0-rc.0
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/dist/core/llm/LLMTypes.js +2 -2
- package/dist/core/llm/LLMTypes.js.map +1 -1
- package/dist/core/llm/LLMTypes.mjs +2 -2
- package/dist/core/llm/LLMTypes.mjs.map +1 -1
- package/dist/core/llm/VercelAILLM.d.ts.map +1 -1
- package/dist/core/llm/VercelAILLM.js +3 -0
- package/dist/core/llm/VercelAILLM.js.map +1 -1
- package/dist/core/llm/VercelAILLM.mjs +3 -0
- package/dist/core/llm/VercelAILLM.mjs.map +1 -1
- package/dist/core/llm/toOpenAIChatMessage.d.ts +4 -3
- package/dist/core/llm/toOpenAIChatMessage.d.ts.map +1 -1
- package/dist/core/llm/toOpenAIChatMessage.js.map +1 -1
- package/dist/core/llm/toOpenAIChatMessage.mjs.map +1 -1
- package/dist/core/runtime/AgentThread.d.ts.map +1 -1
- package/dist/core/runtime/AgentThread.js +7 -1
- package/dist/core/runtime/AgentThread.js.map +1 -1
- package/dist/core/runtime/AgentThread.mjs +7 -1
- package/dist/core/runtime/AgentThread.mjs.map +1 -1
- package/dist/core/sandbox/provider/TFYSandboxProvider.d.ts.map +1 -1
- package/dist/core/sandbox/provider/TFYSandboxProvider.js +43 -14
- package/dist/core/sandbox/provider/TFYSandboxProvider.js.map +1 -1
- package/dist/core/sandbox/provider/TFYSandboxProvider.mjs +44 -15
- package/dist/core/sandbox/provider/TFYSandboxProvider.mjs.map +1 -1
- package/dist/core/util/errorLogFields.d.ts +1 -0
- package/dist/core/util/errorLogFields.d.ts.map +1 -1
- package/dist/core/util/errorLogFields.js +33 -6
- package/dist/core/util/errorLogFields.js.map +1 -1
- package/dist/core/util/errorLogFields.mjs +33 -6
- package/dist/core/util/errorLogFields.mjs.map +1 -1
- package/dist/core/web-search/ParallelWebSearchProvider.d.ts.map +1 -1
- package/dist/core/web-search/ParallelWebSearchProvider.js +5 -1
- package/dist/core/web-search/ParallelWebSearchProvider.js.map +1 -1
- package/dist/core/web-search/ParallelWebSearchProvider.mjs +5 -1
- package/dist/core/web-search/ParallelWebSearchProvider.mjs.map +1 -1
- package/dist/request-reply/client.d.ts +2 -2
- package/dist/request-reply/client.d.ts.map +1 -1
- package/dist/request-reply/client.js.map +1 -1
- package/dist/request-reply/client.mjs.map +1 -1
- package/dist/request-reply/executor.d.ts +3 -3
- package/dist/request-reply/executor.d.ts.map +1 -1
- package/dist/request-reply/executor.js +0 -1
- package/dist/request-reply/executor.js.map +1 -1
- package/dist/request-reply/executor.mjs +0 -1
- package/dist/request-reply/executor.mjs.map +1 -1
- package/dist/request-reply/index.d.ts +1 -0
- package/dist/request-reply/index.d.ts.map +1 -1
- package/dist/request-reply/index.js.map +1 -1
- package/dist/request-reply/index.mjs.map +1 -1
- package/dist/request-reply/redisClient.d.ts +4 -0
- package/dist/request-reply/redisClient.d.ts.map +1 -0
- package/dist/request-reply/redisClient.js +19 -0
- package/dist/request-reply/redisClient.js.map +1 -0
- package/dist/request-reply/redisClient.mjs +1 -0
- package/dist/request-reply/redisClient.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { suppressTracing } from "@opentelemetry/core";
|
|
|
4
4
|
import dedent from "dedent";
|
|
5
5
|
import { randomUUID } from "crypto";
|
|
6
6
|
import { join } from "path/posix";
|
|
7
|
-
import { extractErrorLogFields } from "../../util/errorLogFields.mjs";
|
|
7
|
+
import { describeUnknownError, extractErrorLogFields } from "../../util/errorLogFields.mjs";
|
|
8
8
|
import { CodeModeNatsTransport } from "../codeMode/nats/CodeModeNatsTransport.mjs";
|
|
9
9
|
import { DEFAULT_SANDBOX_NATS_WS_PORT } from "../constants.mjs";
|
|
10
10
|
import {
|
|
@@ -112,13 +112,14 @@ var TFYSandboxProvider = class _TFYSandboxProvider {
|
|
|
112
112
|
env: params.env,
|
|
113
113
|
timeout: timeoutSeconds
|
|
114
114
|
};
|
|
115
|
+
const execUrl = `${this.serverUrl}/exec`;
|
|
115
116
|
const controller = new AbortController();
|
|
116
117
|
const clientTimeoutMs = (timeoutSeconds + CLIENT_TIMEOUT_BUFFER_SECONDS) * 1e3;
|
|
117
118
|
const timer = setTimeout(() => {
|
|
118
119
|
controller.abort();
|
|
119
120
|
}, clientTimeoutMs);
|
|
120
121
|
try {
|
|
121
|
-
const response = await fetch(
|
|
122
|
+
const response = await fetch(execUrl, {
|
|
122
123
|
method: "POST",
|
|
123
124
|
headers: { "Content-Type": "application/json" },
|
|
124
125
|
body: JSON.stringify(body),
|
|
@@ -126,19 +127,30 @@ var TFYSandboxProvider = class _TFYSandboxProvider {
|
|
|
126
127
|
});
|
|
127
128
|
if (!response.ok) {
|
|
128
129
|
const text = await response.text();
|
|
129
|
-
this.logger.error(`Sandbox server returned ${String(response.status)}: ${text}
|
|
130
|
-
return {
|
|
130
|
+
this.logger.error(`Sandbox server returned ${String(response.status)}: ${text}`, { url: execUrl });
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
error: `Sandbox server returned ${String(response.status)} from ${execUrl}: ${text}`
|
|
134
|
+
};
|
|
131
135
|
}
|
|
132
136
|
const result = await response.json();
|
|
133
137
|
return result;
|
|
134
138
|
} catch (e) {
|
|
135
139
|
if (e instanceof Error && e.name === "AbortError") {
|
|
136
|
-
this.logger.error(`Sandbox exec timed out after ${String(timeoutSeconds)}s`,
|
|
137
|
-
|
|
140
|
+
this.logger.error(`Sandbox exec timed out after ${String(timeoutSeconds)}s`, {
|
|
141
|
+
url: execUrl,
|
|
142
|
+
...extractErrorLogFields(e)
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
error: `Sandbox exec to ${execUrl} timed out after ${String(timeoutSeconds)}s`
|
|
147
|
+
};
|
|
138
148
|
}
|
|
139
|
-
this.logger.error("Sandbox exec failed", extractErrorLogFields(e));
|
|
140
|
-
|
|
141
|
-
|
|
149
|
+
this.logger.error("Sandbox exec failed", { url: execUrl, ...extractErrorLogFields(e) });
|
|
150
|
+
return {
|
|
151
|
+
success: false,
|
|
152
|
+
error: `Sandbox exec to ${execUrl} failed: ${describeUnknownError(e)}`
|
|
153
|
+
};
|
|
142
154
|
} finally {
|
|
143
155
|
clearTimeout(timer);
|
|
144
156
|
}
|
|
@@ -182,21 +194,38 @@ var TFYSandboxProvider = class _TFYSandboxProvider {
|
|
|
182
194
|
validateSandboxOwnedByTenant({ sandboxId: params.sandboxId, tenantName: this.tenantName });
|
|
183
195
|
return context.with(suppressTracing(context.active()), async () => {
|
|
184
196
|
const query = new URLSearchParams({ sandbox_id: params.sandboxId, path: params.remotePath });
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
197
|
+
const uploadUrl = `${this.serverUrl}/files/upload?${query.toString()}`;
|
|
198
|
+
const bytes = params.content.byteLength;
|
|
199
|
+
this.logger.info("Uploading file to sandbox", {
|
|
200
|
+
sandboxId: params.sandboxId,
|
|
201
|
+
remotePath: params.remotePath,
|
|
202
|
+
bytes
|
|
190
203
|
});
|
|
204
|
+
let response;
|
|
205
|
+
try {
|
|
206
|
+
response = await fetch(uploadUrl, {
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
209
|
+
body: params.content,
|
|
210
|
+
signal: AbortSignal.timeout(FILE_UPLOAD_TIMEOUT_MS)
|
|
211
|
+
});
|
|
212
|
+
} catch (e) {
|
|
213
|
+
throw new Error(`File upload to ${uploadUrl} failed: ${describeUnknownError(e)}`, { cause: e });
|
|
214
|
+
}
|
|
191
215
|
if (!response.ok) {
|
|
192
216
|
throw new Error(
|
|
193
|
-
`File upload to sandbox failed: Sandbox server returned ${String(response.status)}: ${await response.text()}`
|
|
217
|
+
`File upload to sandbox failed: Sandbox server returned ${String(response.status)} from ${uploadUrl}: ${await response.text()}`
|
|
194
218
|
);
|
|
195
219
|
}
|
|
196
220
|
const result = await response.json();
|
|
197
221
|
if (!result.success) {
|
|
198
222
|
throw new Error(`File upload to sandbox failed: ${result.error}`);
|
|
199
223
|
}
|
|
224
|
+
this.logger.info("Uploaded file to sandbox", {
|
|
225
|
+
sandboxId: params.sandboxId,
|
|
226
|
+
remotePath: params.remotePath,
|
|
227
|
+
bytes
|
|
228
|
+
});
|
|
200
229
|
});
|
|
201
230
|
}
|
|
202
231
|
// The TFY sandbox exposes a static, cluster-internal NATS WebSocket URL (no signed URLs).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/sandbox/provider/TFYSandboxProvider.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { suppressTracing } from '@opentelemetry/core';\nimport dedent from 'dedent';\nimport { randomUUID } from 'node:crypto';\nimport { join } from 'node:path/posix';\nimport type { Logger } from 'winston';\nimport { extractErrorLogFields } from '../../util/errorLogFields';\nimport type { CodeModeTransport } from '../codeMode/CodeModeTransport';\nimport { CodeModeNatsTransport } from '../codeMode/nats/CodeModeNatsTransport';\nimport { DEFAULT_SANDBOX_NATS_WS_PORT } from '../constants';\nimport {\n SandboxFileNotFoundError,\n SandboxFileTooLargeError,\n SandboxPathIsDirectoryError,\n validateSandboxOwnedByTenant,\n} from '../SandboxErrors';\nimport { absolutizeRelativeExecEnv } from './execEnv';\nimport {\n shellEscape,\n type ExecResult,\n type SandboxBuild,\n type SandboxExecParams,\n type SandboxFileInfo,\n type SandboxProvider,\n} from './Provider';\n\nconst DEFAULT_TIMEOUT_SECONDS = 60;\n// Buffer for network latency + response processing on top of the server-side timeout.\nconst CLIENT_TIMEOUT_BUFFER_SECONDS = 5;\n\n// TFY sandbox file upload timeout (same as Daytona SDK uploadFile default timeout).\nconst FILE_UPLOAD_TIMEOUT_MS = 30 * 60 * 1000;\n\nconst TFY_MCP_CLIENT_BIN = 'mcp-client/bin';\n\n/** Put the layout CLI first on PATH (cwd-relative; {@link absolutizeRelativeExecEnv} makes it absolute). */\nexport function withMcpClientOnPath(path: string): string {\n const rest = path.split(':').filter(part => part.length > 0 && part !== TFY_MCP_CLIENT_BIN);\n return rest.length === 0 ? TFY_MCP_CLIENT_BIN : `${TFY_MCP_CLIENT_BIN}:${rest.join(':')}`;\n}\n\nfunction parsePwdAndPath(text: string): { root: string; inheritedPath: string } | undefined {\n const lines = text.replace(/\\r\\n/g, '\\n').split('\\n');\n const root = lines[0];\n if (!root?.startsWith('/')) {\n return undefined;\n }\n return { root, inheritedPath: lines[1] ?? '' };\n}\n\nexport interface TFYSandboxProviderOptions {\n serverUrl: string;\n natsBridgeUrl: string;\n tenantName: string;\n fileMaxBytesForDownload: number;\n defaultExecTimeoutMs?: number | undefined;\n logger: Logger;\n}\n\ninterface StatResult {\n size: number;\n type: string;\n}\n\nexport class TFYSandboxProvider implements SandboxProvider {\n readonly type = 'truefoundry';\n private readonly serverUrl: string;\n private readonly natsBridgeUrl: string;\n private readonly tenantName: string;\n private readonly fileMaxBytesForDownload: number;\n private readonly defaultExecTimeoutSeconds: number;\n private readonly logger: Logger;\n /** Discovered via `pwd` + `$PATH` — never a hardcoded host layout. */\n private readonly sandboxLayouts = new Map<string, { root: string; inheritedPath: string }>();\n\n constructor(options: TFYSandboxProviderOptions) {\n this.serverUrl = options.serverUrl.replace(/\\/+$/, '');\n this.natsBridgeUrl = options.natsBridgeUrl;\n this.tenantName = options.tenantName;\n this.fileMaxBytesForDownload = options.fileMaxBytesForDownload;\n this.defaultExecTimeoutSeconds = Math.ceil((options.defaultExecTimeoutMs ?? DEFAULT_TIMEOUT_SECONDS * 1000) / 1000);\n this.logger = options.logger.child({ module: 'TFYSandboxProvider' });\n }\n\n // TFY sandboxes run a prebuilt server image with no per-image build step, so the image is always ready.\n private static readonly readyBuild: SandboxBuild = {\n status: 'ready',\n reason: null,\n metadata: null,\n };\n\n buildImage(): Promise<SandboxBuild> {\n return Promise.resolve(TFYSandboxProvider.readyBuild);\n }\n\n getImageBuildStatus(): Promise<SandboxBuild> {\n return Promise.resolve(TFYSandboxProvider.readyBuild);\n }\n\n createSandbox(): Promise<{ sandboxId: string }> {\n const sandboxId = `${this.tenantName}.${randomUUID()}`;\n this.logger.debug(`Sandbox created: id=${sandboxId}`);\n return Promise.resolve({ sandboxId });\n }\n\n /**\n * No extra FS jail: exec cwd is the logical sandbox root. Layout getters are cwd-relative\n * so writes cannot escape it. `GIT_CONFIG` / PATH / PYTHONPATH must still be absolute so\n * git and `mcp-client` work after `cd` — resolved from `pwd` / `$PATH`, not a hardcoded prefix.\n */\n async exec(params: SandboxExecParams): Promise<ExecResult> {\n validateSandboxOwnedByTenant({ sandboxId: params.sandboxId, tenantName: this.tenantName });\n const { root, inheritedPath } = await this.sandboxLayout(params.sandboxId);\n const incoming = params.env ?? {};\n const env = absolutizeRelativeExecEnv({\n root,\n env: { ...incoming, PATH: withMcpClientOnPath(incoming['PATH'] ?? inheritedPath) },\n });\n return this.postExec({ ...params, env });\n }\n\n private async sandboxLayout(sandboxId: string): Promise<{ root: string; inheritedPath: string }> {\n const cached = this.sandboxLayouts.get(sandboxId);\n if (cached !== undefined) {\n return cached;\n }\n const result = await this.postExec({\n sandboxId,\n command: 'printf \\'%s\\\\n%s\\\\n\\' \"$(pwd)\" \"$PATH\"',\n });\n if (!result.success) {\n throw new Error(`Failed to resolve sandbox working directory: ${result.error}`);\n }\n const parsed = parsePwdAndPath(result.response.result);\n if (parsed === undefined) {\n throw new Error(`sandbox pwd did not return an absolute path: ${result.response.result}`);\n }\n this.sandboxLayouts.set(sandboxId, parsed);\n return parsed;\n }\n\n private async postExec(params: SandboxExecParams): Promise<ExecResult> {\n return context.with(suppressTracing(context.active()), async (): Promise<ExecResult> => {\n // Honor the per-call timeout (e.g. the longer skill-download timeout) for both the server-side\n // request timeout and the client abort timer; fall back to the provider default otherwise.\n const timeoutSeconds = params.timeoutSeconds ?? this.defaultExecTimeoutSeconds;\n const body = {\n sandbox_id: params.sandboxId,\n command: params.command,\n cwd: params.cwd,\n env: params.env,\n timeout: timeoutSeconds,\n };\n\n const controller = new AbortController();\n const clientTimeoutMs = (timeoutSeconds + CLIENT_TIMEOUT_BUFFER_SECONDS) * 1000;\n const timer = setTimeout(() => {\n controller.abort();\n }, clientTimeoutMs);\n\n try {\n const response = await fetch(`${this.serverUrl}/exec`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const text = await response.text();\n this.logger.error(`Sandbox server returned ${String(response.status)}: ${text}`);\n return { success: false, error: `Sandbox server returned ${String(response.status)}: ${text}` };\n }\n\n const result = (await response.json()) as ExecResult;\n return result;\n } catch (e: unknown) {\n if (e instanceof Error && e.name === 'AbortError') {\n this.logger.error(`Sandbox exec timed out after ${String(timeoutSeconds)}s`, extractErrorLogFields(e));\n return { success: false, error: `Sandbox exec timed out after ${String(timeoutSeconds)}s` };\n }\n this.logger.error('Sandbox exec failed', extractErrorLogFields(e));\n const message = e instanceof Error ? e.message : 'Unknown error';\n return { success: false, error: message };\n } finally {\n clearTimeout(timer);\n }\n });\n }\n\n private async getFileInfo(params: { sandboxId: string; path: string }): Promise<SandboxFileInfo> {\n const result = await this.exec({\n sandboxId: params.sandboxId,\n command: `stat -L --printf='{\"size\":%s,\"type\":\"%F\"}' ${shellEscape(params.path)}`,\n });\n\n if (!result.success) {\n throw new Error(`Failed to stat file: ${result.error}`);\n }\n if (result.response.exitCode !== 0) {\n throw new SandboxFileNotFoundError(params.path);\n }\n\n const parsed = JSON.parse(result.response.result) as StatResult;\n return { size: parsed.size, isDir: parsed.type === 'directory' };\n }\n\n async downloadFile(params: { sandboxId: string; path: string }): Promise<Buffer> {\n const info = await this.getFileInfo(params);\n if (info.isDir) {\n throw new SandboxPathIsDirectoryError(params.path);\n }\n if (info.size > this.fileMaxBytesForDownload) {\n throw new SandboxFileTooLargeError(params.path, info.size, this.fileMaxBytesForDownload);\n }\n\n const result = await this.exec({\n sandboxId: params.sandboxId,\n command: `base64 -w0 ${shellEscape(params.path)}`,\n });\n\n if (!result.success) {\n throw new Error(`Failed to download file: ${result.error}`);\n }\n if (result.response.exitCode !== 0) {\n throw new SandboxFileNotFoundError(params.path);\n }\n\n return Buffer.from(result.response.result.trim(), 'base64');\n }\n\n async uploadFile(params: { sandboxId: string; remotePath: string; content: Buffer }): Promise<void> {\n validateSandboxOwnedByTenant({ sandboxId: params.sandboxId, tenantName: this.tenantName });\n\n return context.with(suppressTracing(context.active()), async () => {\n const query = new URLSearchParams({ sandbox_id: params.sandboxId, path: params.remotePath });\n const response = await fetch(`${this.serverUrl}/files/upload?${query.toString()}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/octet-stream' },\n body: params.content,\n signal: AbortSignal.timeout(FILE_UPLOAD_TIMEOUT_MS),\n });\n if (!response.ok) {\n throw new Error(\n `File upload to sandbox failed: Sandbox server returned ${String(response.status)}: ${await response.text()}`,\n );\n }\n\n const result = (await response.json()) as { success: true } | { success: false; error: string };\n if (!result.success) {\n throw new Error(`File upload to sandbox failed: ${result.error}`);\n }\n });\n }\n\n // The TFY sandbox exposes a static, cluster-internal NATS WebSocket URL (no signed URLs).\n createCodeModeTransport(): CodeModeTransport {\n return new CodeModeNatsTransport({\n resolveHostUrl: () => Promise.resolve(this.natsBridgeUrl),\n sandboxClientNatsUrl: `ws://localhost:${String(DEFAULT_SANDBOX_NATS_WS_PORT)}`,\n logger: this.logger,\n mcpClientInstall: { remotePath: join('mcp-client', 'mcp_client.py') },\n });\n }\n\n getAdditionalInstructions(): string {\n return dedent`\n SANDBOX RULES:\n - The Agent's first sandbox command should be \\`pwd\\` to discover the working directory.\n - uploads, skills, and tool-results live in that working directory (not /tmp or /opt).\n - ALL file creation and writes MUST stay within that working directory.\n - The Agent must NOT write to /tmp/, ~/, or any absolute path outside the working directory.\n `;\n }\n\n // Cwd-relative (no FS jail). exec() pwd-joins GIT_CONFIG / PATH / PYTHONPATH.\n // uploads, skills, tool-results, skill_downloader.py, .git-credentials\n // mcp-client/mcp_client.py (no /usr/local/bin symlink)\n getToolResultDumpDir(): string {\n return 'tool-results';\n }\n\n getGitCredentialsPath(): string {\n return '.git-credentials';\n }\n\n getFileUploadsDir(): string {\n return 'uploads';\n }\n\n getSkillsDir(): string {\n return 'skills';\n }\n\n getSkillDownloaderPath(): string {\n return 'skill_downloader.py';\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/sandbox/provider/TFYSandboxProvider.ts"],"sourcesContent":["import { context } from '@opentelemetry/api';\nimport { suppressTracing } from '@opentelemetry/core';\nimport dedent from 'dedent';\nimport { randomUUID } from 'node:crypto';\nimport { join } from 'node:path/posix';\nimport type { Logger } from 'winston';\nimport { describeUnknownError, extractErrorLogFields } from '../../util/errorLogFields';\nimport type { CodeModeTransport } from '../codeMode/CodeModeTransport';\nimport { CodeModeNatsTransport } from '../codeMode/nats/CodeModeNatsTransport';\nimport { DEFAULT_SANDBOX_NATS_WS_PORT } from '../constants';\nimport {\n SandboxFileNotFoundError,\n SandboxFileTooLargeError,\n SandboxPathIsDirectoryError,\n validateSandboxOwnedByTenant,\n} from '../SandboxErrors';\nimport { absolutizeRelativeExecEnv } from './execEnv';\nimport {\n shellEscape,\n type ExecResult,\n type SandboxBuild,\n type SandboxExecParams,\n type SandboxFileInfo,\n type SandboxProvider,\n} from './Provider';\n\nconst DEFAULT_TIMEOUT_SECONDS = 60;\n// Buffer for network latency + response processing on top of the server-side timeout.\nconst CLIENT_TIMEOUT_BUFFER_SECONDS = 5;\n\n// TFY sandbox file upload timeout (same as Daytona SDK uploadFile default timeout).\nconst FILE_UPLOAD_TIMEOUT_MS = 30 * 60 * 1000;\n\nconst TFY_MCP_CLIENT_BIN = 'mcp-client/bin';\n\n/** Put the layout CLI first on PATH (cwd-relative; {@link absolutizeRelativeExecEnv} makes it absolute). */\nexport function withMcpClientOnPath(path: string): string {\n const rest = path.split(':').filter(part => part.length > 0 && part !== TFY_MCP_CLIENT_BIN);\n return rest.length === 0 ? TFY_MCP_CLIENT_BIN : `${TFY_MCP_CLIENT_BIN}:${rest.join(':')}`;\n}\n\nfunction parsePwdAndPath(text: string): { root: string; inheritedPath: string } | undefined {\n const lines = text.replace(/\\r\\n/g, '\\n').split('\\n');\n const root = lines[0];\n if (!root?.startsWith('/')) {\n return undefined;\n }\n return { root, inheritedPath: lines[1] ?? '' };\n}\n\nexport interface TFYSandboxProviderOptions {\n serverUrl: string;\n natsBridgeUrl: string;\n tenantName: string;\n fileMaxBytesForDownload: number;\n defaultExecTimeoutMs?: number | undefined;\n logger: Logger;\n}\n\ninterface StatResult {\n size: number;\n type: string;\n}\n\nexport class TFYSandboxProvider implements SandboxProvider {\n readonly type = 'truefoundry';\n private readonly serverUrl: string;\n private readonly natsBridgeUrl: string;\n private readonly tenantName: string;\n private readonly fileMaxBytesForDownload: number;\n private readonly defaultExecTimeoutSeconds: number;\n private readonly logger: Logger;\n /** Discovered via `pwd` + `$PATH` — never a hardcoded host layout. */\n private readonly sandboxLayouts = new Map<string, { root: string; inheritedPath: string }>();\n\n constructor(options: TFYSandboxProviderOptions) {\n this.serverUrl = options.serverUrl.replace(/\\/+$/, '');\n this.natsBridgeUrl = options.natsBridgeUrl;\n this.tenantName = options.tenantName;\n this.fileMaxBytesForDownload = options.fileMaxBytesForDownload;\n this.defaultExecTimeoutSeconds = Math.ceil((options.defaultExecTimeoutMs ?? DEFAULT_TIMEOUT_SECONDS * 1000) / 1000);\n this.logger = options.logger.child({ module: 'TFYSandboxProvider' });\n }\n\n // TFY sandboxes run a prebuilt server image with no per-image build step, so the image is always ready.\n private static readonly readyBuild: SandboxBuild = {\n status: 'ready',\n reason: null,\n metadata: null,\n };\n\n buildImage(): Promise<SandboxBuild> {\n return Promise.resolve(TFYSandboxProvider.readyBuild);\n }\n\n getImageBuildStatus(): Promise<SandboxBuild> {\n return Promise.resolve(TFYSandboxProvider.readyBuild);\n }\n\n createSandbox(): Promise<{ sandboxId: string }> {\n const sandboxId = `${this.tenantName}.${randomUUID()}`;\n this.logger.debug(`Sandbox created: id=${sandboxId}`);\n return Promise.resolve({ sandboxId });\n }\n\n /**\n * No extra FS jail: exec cwd is the logical sandbox root. Layout getters are cwd-relative\n * so writes cannot escape it. `GIT_CONFIG` / PATH / PYTHONPATH must still be absolute so\n * git and `mcp-client` work after `cd` — resolved from `pwd` / `$PATH`, not a hardcoded prefix.\n */\n async exec(params: SandboxExecParams): Promise<ExecResult> {\n validateSandboxOwnedByTenant({ sandboxId: params.sandboxId, tenantName: this.tenantName });\n const { root, inheritedPath } = await this.sandboxLayout(params.sandboxId);\n const incoming = params.env ?? {};\n const env = absolutizeRelativeExecEnv({\n root,\n env: { ...incoming, PATH: withMcpClientOnPath(incoming['PATH'] ?? inheritedPath) },\n });\n return this.postExec({ ...params, env });\n }\n\n private async sandboxLayout(sandboxId: string): Promise<{ root: string; inheritedPath: string }> {\n const cached = this.sandboxLayouts.get(sandboxId);\n if (cached !== undefined) {\n return cached;\n }\n const result = await this.postExec({\n sandboxId,\n command: 'printf \\'%s\\\\n%s\\\\n\\' \"$(pwd)\" \"$PATH\"',\n });\n if (!result.success) {\n throw new Error(`Failed to resolve sandbox working directory: ${result.error}`);\n }\n const parsed = parsePwdAndPath(result.response.result);\n if (parsed === undefined) {\n throw new Error(`sandbox pwd did not return an absolute path: ${result.response.result}`);\n }\n this.sandboxLayouts.set(sandboxId, parsed);\n return parsed;\n }\n\n private async postExec(params: SandboxExecParams): Promise<ExecResult> {\n return context.with(suppressTracing(context.active()), async (): Promise<ExecResult> => {\n // Honor the per-call timeout (e.g. the longer skill-download timeout) for both the server-side\n // request timeout and the client abort timer; fall back to the provider default otherwise.\n const timeoutSeconds = params.timeoutSeconds ?? this.defaultExecTimeoutSeconds;\n const body = {\n sandbox_id: params.sandboxId,\n command: params.command,\n cwd: params.cwd,\n env: params.env,\n timeout: timeoutSeconds,\n };\n\n const execUrl = `${this.serverUrl}/exec`;\n const controller = new AbortController();\n const clientTimeoutMs = (timeoutSeconds + CLIENT_TIMEOUT_BUFFER_SECONDS) * 1000;\n const timer = setTimeout(() => {\n controller.abort();\n }, clientTimeoutMs);\n\n try {\n const response = await fetch(execUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const text = await response.text();\n this.logger.error(`Sandbox server returned ${String(response.status)}: ${text}`, { url: execUrl });\n return {\n success: false,\n error: `Sandbox server returned ${String(response.status)} from ${execUrl}: ${text}`,\n };\n }\n\n const result = (await response.json()) as ExecResult;\n return result;\n } catch (e: unknown) {\n if (e instanceof Error && e.name === 'AbortError') {\n this.logger.error(`Sandbox exec timed out after ${String(timeoutSeconds)}s`, {\n url: execUrl,\n ...extractErrorLogFields(e),\n });\n return {\n success: false,\n error: `Sandbox exec to ${execUrl} timed out after ${String(timeoutSeconds)}s`,\n };\n }\n this.logger.error('Sandbox exec failed', { url: execUrl, ...extractErrorLogFields(e) });\n return {\n success: false,\n error: `Sandbox exec to ${execUrl} failed: ${describeUnknownError(e)}`,\n };\n } finally {\n clearTimeout(timer);\n }\n });\n }\n\n private async getFileInfo(params: { sandboxId: string; path: string }): Promise<SandboxFileInfo> {\n const result = await this.exec({\n sandboxId: params.sandboxId,\n command: `stat -L --printf='{\"size\":%s,\"type\":\"%F\"}' ${shellEscape(params.path)}`,\n });\n\n if (!result.success) {\n throw new Error(`Failed to stat file: ${result.error}`);\n }\n if (result.response.exitCode !== 0) {\n throw new SandboxFileNotFoundError(params.path);\n }\n\n const parsed = JSON.parse(result.response.result) as StatResult;\n return { size: parsed.size, isDir: parsed.type === 'directory' };\n }\n\n async downloadFile(params: { sandboxId: string; path: string }): Promise<Buffer> {\n const info = await this.getFileInfo(params);\n if (info.isDir) {\n throw new SandboxPathIsDirectoryError(params.path);\n }\n if (info.size > this.fileMaxBytesForDownload) {\n throw new SandboxFileTooLargeError(params.path, info.size, this.fileMaxBytesForDownload);\n }\n\n const result = await this.exec({\n sandboxId: params.sandboxId,\n command: `base64 -w0 ${shellEscape(params.path)}`,\n });\n\n if (!result.success) {\n throw new Error(`Failed to download file: ${result.error}`);\n }\n if (result.response.exitCode !== 0) {\n throw new SandboxFileNotFoundError(params.path);\n }\n\n return Buffer.from(result.response.result.trim(), 'base64');\n }\n\n async uploadFile(params: { sandboxId: string; remotePath: string; content: Buffer }): Promise<void> {\n validateSandboxOwnedByTenant({ sandboxId: params.sandboxId, tenantName: this.tenantName });\n\n return context.with(suppressTracing(context.active()), async () => {\n const query = new URLSearchParams({ sandbox_id: params.sandboxId, path: params.remotePath });\n const uploadUrl = `${this.serverUrl}/files/upload?${query.toString()}`;\n const bytes = params.content.byteLength;\n this.logger.info('Uploading file to sandbox', {\n sandboxId: params.sandboxId,\n remotePath: params.remotePath,\n bytes,\n });\n let response: Response;\n try {\n response = await fetch(uploadUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/octet-stream' },\n body: params.content,\n signal: AbortSignal.timeout(FILE_UPLOAD_TIMEOUT_MS),\n });\n } catch (e: unknown) {\n throw new Error(`File upload to ${uploadUrl} failed: ${describeUnknownError(e)}`, { cause: e });\n }\n if (!response.ok) {\n throw new Error(\n `File upload to sandbox failed: Sandbox server returned ${String(response.status)} from ${uploadUrl}: ${await response.text()}`,\n );\n }\n\n const result = (await response.json()) as { success: true } | { success: false; error: string };\n if (!result.success) {\n throw new Error(`File upload to sandbox failed: ${result.error}`);\n }\n this.logger.info('Uploaded file to sandbox', {\n sandboxId: params.sandboxId,\n remotePath: params.remotePath,\n bytes,\n });\n });\n }\n\n // The TFY sandbox exposes a static, cluster-internal NATS WebSocket URL (no signed URLs).\n createCodeModeTransport(): CodeModeTransport {\n return new CodeModeNatsTransport({\n resolveHostUrl: () => Promise.resolve(this.natsBridgeUrl),\n sandboxClientNatsUrl: `ws://localhost:${String(DEFAULT_SANDBOX_NATS_WS_PORT)}`,\n logger: this.logger,\n mcpClientInstall: { remotePath: join('mcp-client', 'mcp_client.py') },\n });\n }\n\n getAdditionalInstructions(): string {\n return dedent`\n SANDBOX RULES:\n - The Agent's first sandbox command should be \\`pwd\\` to discover the working directory.\n - uploads, skills, and tool-results live in that working directory (not /tmp or /opt).\n - ALL file creation and writes MUST stay within that working directory.\n - The Agent must NOT write to /tmp/, ~/, or any absolute path outside the working directory.\n `;\n }\n\n // Cwd-relative (no FS jail). exec() pwd-joins GIT_CONFIG / PATH / PYTHONPATH.\n // uploads, skills, tool-results, skill_downloader.py, .git-credentials\n // mcp-client/mcp_client.py (no /usr/local/bin symlink)\n getToolResultDumpDir(): string {\n return 'tool-results';\n }\n\n getGitCredentialsPath(): string {\n return '.git-credentials';\n }\n\n getFileUploadsDir(): string {\n return 'uploads';\n }\n\n getSkillsDir(): string {\n return 'skills';\n }\n\n getSkillDownloaderPath(): string {\n return 'skill_downloader.py';\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -5,6 +5,7 @@ export interface ErrorLogFields {
|
|
|
5
5
|
/**
|
|
6
6
|
* User/turn-facing message for any thrown value. Prefer Error.message / `.message`;
|
|
7
7
|
* never surface developer-only strings (e.g. unserialisable dumps) in Agent Steps.
|
|
8
|
+
* Includes nested `cause` messages when present (e.g. undici "fetch failed").
|
|
8
9
|
*/
|
|
9
10
|
export declare function describeUnknownError(error: unknown): string;
|
|
10
11
|
export declare function extractErrorLogFields(error: unknown): ErrorLogFields;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorLogFields.d.ts","sourceRoot":"","sources":["../../../src/core/util/errorLogFields.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;
|
|
1
|
+
{"version":3,"file":"errorLogFields.d.ts","sourceRoot":"","sources":["../../../src/core/util/errorLogFields.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAoDD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAS3D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAgBpE"}
|
|
@@ -31,9 +31,9 @@ function formatObjectErrorForLog(error) {
|
|
|
31
31
|
return `unserialisable error (${Object.keys(error).join(", ")})`;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
function
|
|
34
|
+
function messageOfUnknown(error) {
|
|
35
35
|
if (error instanceof Error) {
|
|
36
|
-
return error.message
|
|
36
|
+
return error.message;
|
|
37
37
|
}
|
|
38
38
|
if (typeof error !== "object" || error === null) {
|
|
39
39
|
return String(error);
|
|
@@ -45,22 +45,49 @@ function describeUnknownError(error) {
|
|
|
45
45
|
try {
|
|
46
46
|
return JSON.stringify(error);
|
|
47
47
|
} catch {
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function describeErrorChain(error, seen) {
|
|
52
|
+
if (error === void 0 || error === null || seen.has(error)) {
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
seen.add(error);
|
|
56
|
+
const head = messageOfUnknown(error).trim();
|
|
57
|
+
const nestedCause = typeof error === "object" && "cause" in error ? Reflect.get(error, "cause") : void 0;
|
|
58
|
+
const tail = describeErrorChain(nestedCause, seen).trim();
|
|
59
|
+
if (head.length === 0) {
|
|
60
|
+
return tail;
|
|
61
|
+
}
|
|
62
|
+
if (tail.length === 0 || head.includes(tail)) {
|
|
63
|
+
return head;
|
|
64
|
+
}
|
|
65
|
+
return `${head}: ${tail}`;
|
|
66
|
+
}
|
|
67
|
+
function describeUnknownError(error) {
|
|
68
|
+
const chain = describeErrorChain(error, /* @__PURE__ */ new Set());
|
|
69
|
+
if (chain.length > 0) {
|
|
70
|
+
return chain;
|
|
71
|
+
}
|
|
72
|
+
if (typeof error === "object" && error !== null) {
|
|
48
73
|
return "An unexpected error occurred";
|
|
49
74
|
}
|
|
75
|
+
return String(error);
|
|
50
76
|
}
|
|
51
77
|
function extractErrorLogFields(error) {
|
|
52
78
|
if (error instanceof Error) {
|
|
79
|
+
const chain2 = describeErrorChain(error, /* @__PURE__ */ new Set());
|
|
53
80
|
return {
|
|
54
|
-
error:
|
|
81
|
+
error: chain2.length > 0 ? chain2 : formatObjectErrorForLog(error),
|
|
55
82
|
stack: error.stack
|
|
56
83
|
};
|
|
57
84
|
}
|
|
58
85
|
if (typeof error !== "object" || error === null) {
|
|
59
86
|
return { error: String(error) };
|
|
60
87
|
}
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
return { error:
|
|
88
|
+
const chain = describeErrorChain(error, /* @__PURE__ */ new Set());
|
|
89
|
+
if (chain.length > 0) {
|
|
90
|
+
return { error: chain };
|
|
64
91
|
}
|
|
65
92
|
return { error: formatObjectErrorForLog(error) };
|
|
66
93
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/util/errorLogFields.ts"],"sourcesContent":["export interface ErrorLogFields {\n error: string;\n stack?: string | undefined;\n}\n\nfunction formatObjectErrorForLog(error: object): string {\n try {\n return JSON.stringify(error);\n } catch {\n return `unserialisable error (${Object.keys(error).join(', ')})`;\n }\n}\n\n
|
|
1
|
+
{"version":3,"sources":["../../../src/core/util/errorLogFields.ts"],"sourcesContent":["export interface ErrorLogFields {\n error: string;\n stack?: string | undefined;\n}\n\nfunction formatObjectErrorForLog(error: object): string {\n try {\n return JSON.stringify(error);\n } catch {\n return `unserialisable error (${Object.keys(error).join(', ')})`;\n }\n}\n\n/** Single-hop message for any thrown value (no cause walk). */\nfunction messageOfUnknown(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (typeof error !== 'object' || error === null) {\n return String(error);\n }\n const message: unknown = Reflect.get(error, 'message');\n if (typeof message === 'string' && message.length > 0) {\n return message;\n }\n try {\n return JSON.stringify(error);\n } catch {\n return '';\n }\n}\n\n/**\n * Walk `Error.cause` (and plain `{ cause }` objects) so undici-style\n * `TypeError: fetch failed` surfaces the nested `ECONNREFUSED` / cert reason.\n */\nfunction describeErrorChain(error: unknown, seen: Set<unknown>): string {\n if (error === undefined || error === null || seen.has(error)) {\n return '';\n }\n seen.add(error);\n\n const head = messageOfUnknown(error).trim();\n const nestedCause = typeof error === 'object' && 'cause' in error ? Reflect.get(error, 'cause') : undefined;\n const tail = describeErrorChain(nestedCause, seen).trim();\n\n if (head.length === 0) {\n return tail;\n }\n if (tail.length === 0 || head.includes(tail)) {\n return head;\n }\n return `${head}: ${tail}`;\n}\n\n/**\n * User/turn-facing message for any thrown value. Prefer Error.message / `.message`;\n * never surface developer-only strings (e.g. unserialisable dumps) in Agent Steps.\n * Includes nested `cause` messages when present (e.g. undici \"fetch failed\").\n */\nexport function describeUnknownError(error: unknown): string {\n const chain = describeErrorChain(error, new Set());\n if (chain.length > 0) {\n return chain;\n }\n if (typeof error === 'object' && error !== null) {\n return 'An unexpected error occurred';\n }\n return String(error);\n}\n\nexport function extractErrorLogFields(error: unknown): ErrorLogFields {\n if (error instanceof Error) {\n const chain = describeErrorChain(error, new Set());\n return {\n error: chain.length > 0 ? chain : formatObjectErrorForLog(error),\n stack: error.stack,\n };\n }\n if (typeof error !== 'object' || error === null) {\n return { error: String(error) };\n }\n const chain = describeErrorChain(error, new Set());\n if (chain.length > 0) {\n return { error: chain };\n }\n return { error: formatObjectErrorForLog(error) };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,SAAS,wBAAwB,OAAuB;AACtD,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,yBAAyB,OAAO,KAAK,KAAK,EAAE,KAAK,IAAI,CAAC;AAAA,EAC/D;AACF;AAGA,SAAS,iBAAiB,OAAwB;AAChD,MAAI,iBAAiB,OAAO;AAC1B,WAAO,MAAM;AAAA,EACf;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,QAAM,UAAmB,QAAQ,IAAI,OAAO,SAAS;AACrD,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,GAAG;AACrD,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,SAAS,mBAAmB,OAAgB,MAA4B;AACtE,MAAI,UAAU,UAAa,UAAU,QAAQ,KAAK,IAAI,KAAK,GAAG;AAC5D,WAAO;AAAA,EACT;AACA,OAAK,IAAI,KAAK;AAEd,QAAM,OAAO,iBAAiB,KAAK,EAAE,KAAK;AAC1C,QAAM,cAAc,OAAO,UAAU,YAAY,WAAW,QAAQ,QAAQ,IAAI,OAAO,OAAO,IAAI;AAClG,QAAM,OAAO,mBAAmB,aAAa,IAAI,EAAE,KAAK;AAExD,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO;AAAA,EACT;AACA,MAAI,KAAK,WAAW,KAAK,KAAK,SAAS,IAAI,GAAG;AAC5C,WAAO;AAAA,EACT;AACA,SAAO,GAAG,IAAI,KAAK,IAAI;AACzB;AAOO,SAAS,qBAAqB,OAAwB;AAC3D,QAAM,QAAQ,mBAAmB,OAAO,oBAAI,IAAI,CAAC;AACjD,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AACA,SAAO,OAAO,KAAK;AACrB;AAEO,SAAS,sBAAsB,OAAgC;AACpE,MAAI,iBAAiB,OAAO;AAC1B,UAAMA,SAAQ,mBAAmB,OAAO,oBAAI,IAAI,CAAC;AACjD,WAAO;AAAA,MACL,OAAOA,OAAM,SAAS,IAAIA,SAAQ,wBAAwB,KAAK;AAAA,MAC/D,OAAO,MAAM;AAAA,IACf;AAAA,EACF;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO,EAAE,OAAO,OAAO,KAAK,EAAE;AAAA,EAChC;AACA,QAAM,QAAQ,mBAAmB,OAAO,oBAAI,IAAI,CAAC;AACjD,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB;AACA,SAAO,EAAE,OAAO,wBAAwB,KAAK,EAAE;AACjD;","names":["chain"]}
|
|
@@ -6,9 +6,9 @@ function formatObjectErrorForLog(error) {
|
|
|
6
6
|
return `unserialisable error (${Object.keys(error).join(", ")})`;
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
function
|
|
9
|
+
function messageOfUnknown(error) {
|
|
10
10
|
if (error instanceof Error) {
|
|
11
|
-
return error.message
|
|
11
|
+
return error.message;
|
|
12
12
|
}
|
|
13
13
|
if (typeof error !== "object" || error === null) {
|
|
14
14
|
return String(error);
|
|
@@ -20,22 +20,49 @@ function describeUnknownError(error) {
|
|
|
20
20
|
try {
|
|
21
21
|
return JSON.stringify(error);
|
|
22
22
|
} catch {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function describeErrorChain(error, seen) {
|
|
27
|
+
if (error === void 0 || error === null || seen.has(error)) {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
seen.add(error);
|
|
31
|
+
const head = messageOfUnknown(error).trim();
|
|
32
|
+
const nestedCause = typeof error === "object" && "cause" in error ? Reflect.get(error, "cause") : void 0;
|
|
33
|
+
const tail = describeErrorChain(nestedCause, seen).trim();
|
|
34
|
+
if (head.length === 0) {
|
|
35
|
+
return tail;
|
|
36
|
+
}
|
|
37
|
+
if (tail.length === 0 || head.includes(tail)) {
|
|
38
|
+
return head;
|
|
39
|
+
}
|
|
40
|
+
return `${head}: ${tail}`;
|
|
41
|
+
}
|
|
42
|
+
function describeUnknownError(error) {
|
|
43
|
+
const chain = describeErrorChain(error, /* @__PURE__ */ new Set());
|
|
44
|
+
if (chain.length > 0) {
|
|
45
|
+
return chain;
|
|
46
|
+
}
|
|
47
|
+
if (typeof error === "object" && error !== null) {
|
|
23
48
|
return "An unexpected error occurred";
|
|
24
49
|
}
|
|
50
|
+
return String(error);
|
|
25
51
|
}
|
|
26
52
|
function extractErrorLogFields(error) {
|
|
27
53
|
if (error instanceof Error) {
|
|
54
|
+
const chain2 = describeErrorChain(error, /* @__PURE__ */ new Set());
|
|
28
55
|
return {
|
|
29
|
-
error:
|
|
56
|
+
error: chain2.length > 0 ? chain2 : formatObjectErrorForLog(error),
|
|
30
57
|
stack: error.stack
|
|
31
58
|
};
|
|
32
59
|
}
|
|
33
60
|
if (typeof error !== "object" || error === null) {
|
|
34
61
|
return { error: String(error) };
|
|
35
62
|
}
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
return { error:
|
|
63
|
+
const chain = describeErrorChain(error, /* @__PURE__ */ new Set());
|
|
64
|
+
if (chain.length > 0) {
|
|
65
|
+
return { error: chain };
|
|
39
66
|
}
|
|
40
67
|
return { error: formatObjectErrorForLog(error) };
|
|
41
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/util/errorLogFields.ts"],"sourcesContent":["export interface ErrorLogFields {\n error: string;\n stack?: string | undefined;\n}\n\nfunction formatObjectErrorForLog(error: object): string {\n try {\n return JSON.stringify(error);\n } catch {\n return `unserialisable error (${Object.keys(error).join(', ')})`;\n }\n}\n\n
|
|
1
|
+
{"version":3,"sources":["../../../src/core/util/errorLogFields.ts"],"sourcesContent":["export interface ErrorLogFields {\n error: string;\n stack?: string | undefined;\n}\n\nfunction formatObjectErrorForLog(error: object): string {\n try {\n return JSON.stringify(error);\n } catch {\n return `unserialisable error (${Object.keys(error).join(', ')})`;\n }\n}\n\n/** Single-hop message for any thrown value (no cause walk). */\nfunction messageOfUnknown(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (typeof error !== 'object' || error === null) {\n return String(error);\n }\n const message: unknown = Reflect.get(error, 'message');\n if (typeof message === 'string' && message.length > 0) {\n return message;\n }\n try {\n return JSON.stringify(error);\n } catch {\n return '';\n }\n}\n\n/**\n * Walk `Error.cause` (and plain `{ cause }` objects) so undici-style\n * `TypeError: fetch failed` surfaces the nested `ECONNREFUSED` / cert reason.\n */\nfunction describeErrorChain(error: unknown, seen: Set<unknown>): string {\n if (error === undefined || error === null || seen.has(error)) {\n return '';\n }\n seen.add(error);\n\n const head = messageOfUnknown(error).trim();\n const nestedCause = typeof error === 'object' && 'cause' in error ? Reflect.get(error, 'cause') : undefined;\n const tail = describeErrorChain(nestedCause, seen).trim();\n\n if (head.length === 0) {\n return tail;\n }\n if (tail.length === 0 || head.includes(tail)) {\n return head;\n }\n return `${head}: ${tail}`;\n}\n\n/**\n * User/turn-facing message for any thrown value. Prefer Error.message / `.message`;\n * never surface developer-only strings (e.g. unserialisable dumps) in Agent Steps.\n * Includes nested `cause` messages when present (e.g. undici \"fetch failed\").\n */\nexport function describeUnknownError(error: unknown): string {\n const chain = describeErrorChain(error, new Set());\n if (chain.length > 0) {\n return chain;\n }\n if (typeof error === 'object' && error !== null) {\n return 'An unexpected error occurred';\n }\n return String(error);\n}\n\nexport function extractErrorLogFields(error: unknown): ErrorLogFields {\n if (error instanceof Error) {\n const chain = describeErrorChain(error, new Set());\n return {\n error: chain.length > 0 ? chain : formatObjectErrorForLog(error),\n stack: error.stack,\n };\n }\n if (typeof error !== 'object' || error === null) {\n return { error: String(error) };\n }\n const chain = describeErrorChain(error, new Set());\n if (chain.length > 0) {\n return { error: chain };\n }\n return { error: formatObjectErrorForLog(error) };\n}\n"],"mappings":";AAKA,SAAS,wBAAwB,OAAuB;AACtD,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,yBAAyB,OAAO,KAAK,KAAK,EAAE,KAAK,IAAI,CAAC;AAAA,EAC/D;AACF;AAGA,SAAS,iBAAiB,OAAwB;AAChD,MAAI,iBAAiB,OAAO;AAC1B,WAAO,MAAM;AAAA,EACf;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,QAAM,UAAmB,QAAQ,IAAI,OAAO,SAAS;AACrD,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,GAAG;AACrD,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,SAAS,mBAAmB,OAAgB,MAA4B;AACtE,MAAI,UAAU,UAAa,UAAU,QAAQ,KAAK,IAAI,KAAK,GAAG;AAC5D,WAAO;AAAA,EACT;AACA,OAAK,IAAI,KAAK;AAEd,QAAM,OAAO,iBAAiB,KAAK,EAAE,KAAK;AAC1C,QAAM,cAAc,OAAO,UAAU,YAAY,WAAW,QAAQ,QAAQ,IAAI,OAAO,OAAO,IAAI;AAClG,QAAM,OAAO,mBAAmB,aAAa,IAAI,EAAE,KAAK;AAExD,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO;AAAA,EACT;AACA,MAAI,KAAK,WAAW,KAAK,KAAK,SAAS,IAAI,GAAG;AAC5C,WAAO;AAAA,EACT;AACA,SAAO,GAAG,IAAI,KAAK,IAAI;AACzB;AAOO,SAAS,qBAAqB,OAAwB;AAC3D,QAAM,QAAQ,mBAAmB,OAAO,oBAAI,IAAI,CAAC;AACjD,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AACA,SAAO,OAAO,KAAK;AACrB;AAEO,SAAS,sBAAsB,OAAgC;AACpE,MAAI,iBAAiB,OAAO;AAC1B,UAAMA,SAAQ,mBAAmB,OAAO,oBAAI,IAAI,CAAC;AACjD,WAAO;AAAA,MACL,OAAOA,OAAM,SAAS,IAAIA,SAAQ,wBAAwB,KAAK;AAAA,MAC/D,OAAO,MAAM;AAAA,IACf;AAAA,EACF;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO,EAAE,OAAO,OAAO,KAAK,EAAE;AAAA,EAChC;AACA,QAAM,QAAQ,mBAAmB,OAAO,oBAAI,IAAI,CAAC;AACjD,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB;AACA,SAAO,EAAE,OAAO,wBAAwB,KAAK,EAAE;AACjD;","names":["chain"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ParallelWebSearchProvider.d.ts","sourceRoot":"","sources":["../../../src/core/web-search/ParallelWebSearchProvider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ParallelWebSearchProvider.d.ts","sourceRoot":"","sources":["../../../src/core/web-search/ParallelWebSearchProvider.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAOzE,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,IAAI,EAAE,kBAAkB,CAAC;CAC1B;AAED;;GAEG;AACH,qBAAa,yBAA0B,YAAW,kBAAkB;;IAClE,QAAQ,CAAC,EAAE,+BAA+B;IAI1C,YAAY,OAAO,EAAE,gCAAgC,EAMpD;IAEK,MAAM,CAAC,KAAK,EAAE;QAAE,cAAc,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAmBvG;IAEK,KAAK,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAoB5F;CACF"}
|
|
@@ -34,6 +34,7 @@ __export(ParallelWebSearchProvider_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(ParallelWebSearchProvider_exports);
|
|
36
36
|
var import_parallel_web = __toESM(require("parallel-web"), 1);
|
|
37
|
+
var import_ssrfGuard = require("../util/ssrfGuard.js");
|
|
37
38
|
var import_WebSearchProvider = require("./WebSearchProvider.js");
|
|
38
39
|
var PARALLEL_SEARCH_MAX_RESULTS = 5;
|
|
39
40
|
var PARALLEL_SEARCH_MAX_CHARS_PER_RESULT = 500;
|
|
@@ -43,7 +44,10 @@ var ParallelWebSearchProvider = class {
|
|
|
43
44
|
#client;
|
|
44
45
|
#mode;
|
|
45
46
|
constructor(options) {
|
|
46
|
-
this.#client = new import_parallel_web.default({
|
|
47
|
+
this.#client = new import_parallel_web.default({
|
|
48
|
+
apiKey: options.apiKey,
|
|
49
|
+
fetch: import_ssrfGuard.ssrfFetch
|
|
50
|
+
});
|
|
47
51
|
this.#mode = options.mode;
|
|
48
52
|
}
|
|
49
53
|
async search(input) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/web-search/ParallelWebSearchProvider.ts"],"sourcesContent":["import Parallel from 'parallel-web';\nimport {\n WebSearchProviders,\n type IWebSearchProvider,\n type WebFetchPages,\n type WebSearchHits,\n} from './WebSearchProvider';\n\nexport type ParallelSearchMode = 'turbo' | 'fast' | 'basic' | 'advanced';\n\n/** Keep search payloads under the large-tool-response individual threshold (~6k tokens). */\nconst PARALLEL_SEARCH_MAX_RESULTS = 5;\nconst PARALLEL_SEARCH_MAX_CHARS_PER_RESULT = 500;\nconst PARALLEL_SEARCH_MAX_CHARS_TOTAL = 4000;\n\nexport interface ParallelWebSearchProviderOptions {\n apiKey: string;\n /** Parallel Search mode preset. Caller supplies the default (e.g. `turbo`). */\n mode: ParallelSearchMode;\n}\n\n/**\n * Parallel Search + Extract via the official `parallel-web` SDK.\n */\nexport class ParallelWebSearchProvider implements IWebSearchProvider {\n readonly id = WebSearchProviders.Parallel;\n readonly #client: Parallel;\n readonly #mode: ParallelSearchMode;\n\n constructor(options: ParallelWebSearchProviderOptions) {\n this.#client = new Parallel({
|
|
1
|
+
{"version":3,"sources":["../../../src/core/web-search/ParallelWebSearchProvider.ts"],"sourcesContent":["import Parallel from 'parallel-web';\nimport { ssrfFetch } from '../util/ssrfGuard';\nimport {\n WebSearchProviders,\n type IWebSearchProvider,\n type WebFetchPages,\n type WebSearchHits,\n} from './WebSearchProvider';\n\nexport type ParallelSearchMode = 'turbo' | 'fast' | 'basic' | 'advanced';\n\n/** Keep search payloads under the large-tool-response individual threshold (~6k tokens). */\nconst PARALLEL_SEARCH_MAX_RESULTS = 5;\nconst PARALLEL_SEARCH_MAX_CHARS_PER_RESULT = 500;\nconst PARALLEL_SEARCH_MAX_CHARS_TOTAL = 4000;\n\nexport interface ParallelWebSearchProviderOptions {\n apiKey: string;\n /** Parallel Search mode preset. Caller supplies the default (e.g. `turbo`). */\n mode: ParallelSearchMode;\n}\n\n/**\n * Parallel Search + Extract via the official `parallel-web` SDK.\n */\nexport class ParallelWebSearchProvider implements IWebSearchProvider {\n readonly id = WebSearchProviders.Parallel;\n readonly #client: Parallel;\n readonly #mode: ParallelSearchMode;\n\n constructor(options: ParallelWebSearchProviderOptions) {\n this.#client = new Parallel({\n apiKey: options.apiKey,\n fetch: ssrfFetch,\n });\n this.#mode = options.mode;\n }\n\n async search(input: { search_queries: string[]; objective: string | undefined }): Promise<WebSearchHits> {\n const response = await this.#client.search({\n search_queries: input.search_queries,\n ...(input.objective ? { objective: input.objective } : {}),\n mode: this.#mode,\n max_chars_total: PARALLEL_SEARCH_MAX_CHARS_TOTAL,\n advanced_settings: {\n max_results: PARALLEL_SEARCH_MAX_RESULTS,\n excerpt_settings: { max_chars_per_result: PARALLEL_SEARCH_MAX_CHARS_PER_RESULT },\n },\n });\n return {\n hits: response.results.map(result => ({\n title: result.title ?? '',\n url: result.url,\n snippet: result.excerpts.join('\\n\\n'),\n published_at: result.publish_date ?? null,\n })),\n };\n }\n\n async fetch(input: { urls: string[]; objective: string | undefined }): Promise<WebFetchPages> {\n const response = await this.#client.extract({\n urls: input.urls,\n ...(input.objective ? { objective: input.objective } : {}),\n });\n const pages = [\n ...response.results.map(result => ({\n url: result.url,\n title: result.title ?? null,\n content: result.full_content ?? result.excerpts.join('\\n\\n'),\n error: null,\n })),\n ...response.errors.map(err => ({\n url: err.url,\n title: null,\n content: '',\n error: err.content ?? err.error_type,\n })),\n ];\n return { pages };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAqB;AACrB,uBAA0B;AAC1B,+BAKO;AAKP,IAAM,8BAA8B;AACpC,IAAM,uCAAuC;AAC7C,IAAM,kCAAkC;AAWjC,IAAM,4BAAN,MAA8D;AAAA,EAC1D,KAAK,4CAAmB;AAAA,EACxB;AAAA,EACA;AAAA,EAET,YAAY,SAA2C;AACrD,SAAK,UAAU,IAAI,oBAAAA,QAAS;AAAA,MAC1B,QAAQ,QAAQ;AAAA,MAChB,OAAO;AAAA,IACT,CAAC;AACD,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,MAAM,OAAO,OAA4F;AACvG,UAAM,WAAW,MAAM,KAAK,QAAQ,OAAO;AAAA,MACzC,gBAAgB,MAAM;AAAA,MACtB,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,MACxD,MAAM,KAAK;AAAA,MACX,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,QACjB,aAAa;AAAA,QACb,kBAAkB,EAAE,sBAAsB,qCAAqC;AAAA,MACjF;AAAA,IACF,CAAC;AACD,WAAO;AAAA,MACL,MAAM,SAAS,QAAQ,IAAI,aAAW;AAAA,QACpC,OAAO,OAAO,SAAS;AAAA,QACvB,KAAK,OAAO;AAAA,QACZ,SAAS,OAAO,SAAS,KAAK,MAAM;AAAA,QACpC,cAAc,OAAO,gBAAgB;AAAA,MACvC,EAAE;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,OAAkF;AAC5F,UAAM,WAAW,MAAM,KAAK,QAAQ,QAAQ;AAAA,MAC1C,MAAM,MAAM;AAAA,MACZ,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,IAC1D,CAAC;AACD,UAAM,QAAQ;AAAA,MACZ,GAAG,SAAS,QAAQ,IAAI,aAAW;AAAA,QACjC,KAAK,OAAO;AAAA,QACZ,OAAO,OAAO,SAAS;AAAA,QACvB,SAAS,OAAO,gBAAgB,OAAO,SAAS,KAAK,MAAM;AAAA,QAC3D,OAAO;AAAA,MACT,EAAE;AAAA,MACF,GAAG,SAAS,OAAO,IAAI,UAAQ;AAAA,QAC7B,KAAK,IAAI;AAAA,QACT,OAAO;AAAA,QACP,SAAS;AAAA,QACT,OAAO,IAAI,WAAW,IAAI;AAAA,MAC5B,EAAE;AAAA,IACJ;AACA,WAAO,EAAE,MAAM;AAAA,EACjB;AACF;","names":["Parallel"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/core/web-search/ParallelWebSearchProvider.ts
|
|
2
2
|
import Parallel from "parallel-web";
|
|
3
|
+
import { ssrfFetch } from "../util/ssrfGuard.mjs";
|
|
3
4
|
import {
|
|
4
5
|
WebSearchProviders
|
|
5
6
|
} from "./WebSearchProvider.mjs";
|
|
@@ -11,7 +12,10 @@ var ParallelWebSearchProvider = class {
|
|
|
11
12
|
#client;
|
|
12
13
|
#mode;
|
|
13
14
|
constructor(options) {
|
|
14
|
-
this.#client = new Parallel({
|
|
15
|
+
this.#client = new Parallel({
|
|
16
|
+
apiKey: options.apiKey,
|
|
17
|
+
fetch: ssrfFetch
|
|
18
|
+
});
|
|
15
19
|
this.#mode = options.mode;
|
|
16
20
|
}
|
|
17
21
|
async search(input) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/web-search/ParallelWebSearchProvider.ts"],"sourcesContent":["import Parallel from 'parallel-web';\nimport {\n WebSearchProviders,\n type IWebSearchProvider,\n type WebFetchPages,\n type WebSearchHits,\n} from './WebSearchProvider';\n\nexport type ParallelSearchMode = 'turbo' | 'fast' | 'basic' | 'advanced';\n\n/** Keep search payloads under the large-tool-response individual threshold (~6k tokens). */\nconst PARALLEL_SEARCH_MAX_RESULTS = 5;\nconst PARALLEL_SEARCH_MAX_CHARS_PER_RESULT = 500;\nconst PARALLEL_SEARCH_MAX_CHARS_TOTAL = 4000;\n\nexport interface ParallelWebSearchProviderOptions {\n apiKey: string;\n /** Parallel Search mode preset. Caller supplies the default (e.g. `turbo`). */\n mode: ParallelSearchMode;\n}\n\n/**\n * Parallel Search + Extract via the official `parallel-web` SDK.\n */\nexport class ParallelWebSearchProvider implements IWebSearchProvider {\n readonly id = WebSearchProviders.Parallel;\n readonly #client: Parallel;\n readonly #mode: ParallelSearchMode;\n\n constructor(options: ParallelWebSearchProviderOptions) {\n this.#client = new Parallel({
|
|
1
|
+
{"version":3,"sources":["../../../src/core/web-search/ParallelWebSearchProvider.ts"],"sourcesContent":["import Parallel from 'parallel-web';\nimport { ssrfFetch } from '../util/ssrfGuard';\nimport {\n WebSearchProviders,\n type IWebSearchProvider,\n type WebFetchPages,\n type WebSearchHits,\n} from './WebSearchProvider';\n\nexport type ParallelSearchMode = 'turbo' | 'fast' | 'basic' | 'advanced';\n\n/** Keep search payloads under the large-tool-response individual threshold (~6k tokens). */\nconst PARALLEL_SEARCH_MAX_RESULTS = 5;\nconst PARALLEL_SEARCH_MAX_CHARS_PER_RESULT = 500;\nconst PARALLEL_SEARCH_MAX_CHARS_TOTAL = 4000;\n\nexport interface ParallelWebSearchProviderOptions {\n apiKey: string;\n /** Parallel Search mode preset. Caller supplies the default (e.g. `turbo`). */\n mode: ParallelSearchMode;\n}\n\n/**\n * Parallel Search + Extract via the official `parallel-web` SDK.\n */\nexport class ParallelWebSearchProvider implements IWebSearchProvider {\n readonly id = WebSearchProviders.Parallel;\n readonly #client: Parallel;\n readonly #mode: ParallelSearchMode;\n\n constructor(options: ParallelWebSearchProviderOptions) {\n this.#client = new Parallel({\n apiKey: options.apiKey,\n fetch: ssrfFetch,\n });\n this.#mode = options.mode;\n }\n\n async search(input: { search_queries: string[]; objective: string | undefined }): Promise<WebSearchHits> {\n const response = await this.#client.search({\n search_queries: input.search_queries,\n ...(input.objective ? { objective: input.objective } : {}),\n mode: this.#mode,\n max_chars_total: PARALLEL_SEARCH_MAX_CHARS_TOTAL,\n advanced_settings: {\n max_results: PARALLEL_SEARCH_MAX_RESULTS,\n excerpt_settings: { max_chars_per_result: PARALLEL_SEARCH_MAX_CHARS_PER_RESULT },\n },\n });\n return {\n hits: response.results.map(result => ({\n title: result.title ?? '',\n url: result.url,\n snippet: result.excerpts.join('\\n\\n'),\n published_at: result.publish_date ?? null,\n })),\n };\n }\n\n async fetch(input: { urls: string[]; objective: string | undefined }): Promise<WebFetchPages> {\n const response = await this.#client.extract({\n urls: input.urls,\n ...(input.objective ? { objective: input.objective } : {}),\n });\n const pages = [\n ...response.results.map(result => ({\n url: result.url,\n title: result.title ?? null,\n content: result.full_content ?? result.excerpts.join('\\n\\n'),\n error: null,\n })),\n ...response.errors.map(err => ({\n url: err.url,\n title: null,\n content: '',\n error: err.content ?? err.error_type,\n })),\n ];\n return { pages };\n }\n}\n"],"mappings":";AAAA,OAAO,cAAc;AACrB,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,OAIK;AAKP,IAAM,8BAA8B;AACpC,IAAM,uCAAuC;AAC7C,IAAM,kCAAkC;AAWjC,IAAM,4BAAN,MAA8D;AAAA,EAC1D,KAAK,mBAAmB;AAAA,EACxB;AAAA,EACA;AAAA,EAET,YAAY,SAA2C;AACrD,SAAK,UAAU,IAAI,SAAS;AAAA,MAC1B,QAAQ,QAAQ;AAAA,MAChB,OAAO;AAAA,IACT,CAAC;AACD,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,MAAM,OAAO,OAA4F;AACvG,UAAM,WAAW,MAAM,KAAK,QAAQ,OAAO;AAAA,MACzC,gBAAgB,MAAM;AAAA,MACtB,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,MACxD,MAAM,KAAK;AAAA,MACX,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,QACjB,aAAa;AAAA,QACb,kBAAkB,EAAE,sBAAsB,qCAAqC;AAAA,MACjF;AAAA,IACF,CAAC;AACD,WAAO;AAAA,MACL,MAAM,SAAS,QAAQ,IAAI,aAAW;AAAA,QACpC,OAAO,OAAO,SAAS;AAAA,QACvB,KAAK,OAAO;AAAA,QACZ,SAAS,OAAO,SAAS,KAAK,MAAM;AAAA,QACpC,cAAc,OAAO,gBAAgB;AAAA,MACvC,EAAE;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,OAAkF;AAC5F,UAAM,WAAW,MAAM,KAAK,QAAQ,QAAQ;AAAA,MAC1C,MAAM,MAAM;AAAA,MACZ,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,IAC1D,CAAC;AACD,UAAM,QAAQ;AAAA,MACZ,GAAG,SAAS,QAAQ,IAAI,aAAW;AAAA,QACjC,KAAK,OAAO;AAAA,QACZ,OAAO,OAAO,SAAS;AAAA,QACvB,SAAS,OAAO,gBAAgB,OAAO,SAAS,KAAK,MAAM;AAAA,QAC3D,OAAO;AAAA,MACT,EAAE;AAAA,MACF,GAAG,SAAS,OAAO,IAAI,UAAQ;AAAA,QAC7B,KAAK,IAAI;AAAA,QACT,OAAO;AAAA,QACP,SAAS;AAAA,QACT,OAAO,IAAI,WAAW,IAAI;AAAA,MAC5B,EAAE;AAAA,IACJ;AACA,WAAO,EAAE,MAAM;AAAA,EACjB;AACF;","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RedisClient } from './redisClient';
|
|
2
2
|
import type { JSONReply, JSONValue, RequestEnvelope } from './types';
|
|
3
3
|
export interface SendRequestOptions {
|
|
4
4
|
/** Max time to wait for a reply after the request is published (ms). */
|
|
@@ -12,7 +12,7 @@ export interface SendRequestOptions {
|
|
|
12
12
|
* heartbeat expires, or the wait budget is exceeded.
|
|
13
13
|
*/
|
|
14
14
|
export declare function redisRequest<T extends JSONValue>({ redis: redisClient, executorId, path, request, options, }: {
|
|
15
|
-
redis:
|
|
15
|
+
redis: RedisClient;
|
|
16
16
|
executorId: string;
|
|
17
17
|
path: string;
|
|
18
18
|
request: RequestEnvelope<T>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/request-reply/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/request-reply/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAoB,eAAe,EAAE,MAAM,SAAS,CAAC;AAIvF,MAAM,WAAW,kBAAkB;IACjC,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,cAAc,EAAE,MAAM,CAAC;CACxB;AAyBD;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,EACtD,KAAK,EAAE,WAAW,EAClB,UAAU,EACV,IAAI,EACJ,OAAO,EACP,OAAO,GACR,EAAE;IACD,KAAK,EAAE,WAAW,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC;CACnD,GAAG,OAAO,CAAC,SAAS,CAAC,CAuCrB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/request-reply/client.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { performance } from 'node:perf_hooks';\nimport
|
|
1
|
+
{"version":3,"sources":["../../src/request-reply/client.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { performance } from 'node:perf_hooks';\nimport { NoResponderError, RequestTimeoutError } from './errors';\nimport type { RedisClient } from './redisClient';\nimport type { JSONReply, JSONValue, PublishedRequest, RequestEnvelope } from './types';\nimport { jsonReplySchema } from './types';\nimport { heartbeatKey, replyKey, requestChannel, sleep } from './utils';\n\nexport interface SendRequestOptions {\n /** Max time to wait for a reply after the request is published (ms). */\n replyTimeoutMs: number;\n /** Sleep between poll attempts when the reply is not yet present (ms). */\n pollIntervalMs: number;\n}\n\nconst DEFAULT_REPLY_TIMEOUT_MS = 60_000;\nconst DEFAULT_POLL_INTERVAL_MS = 500;\n\nfunction resolveSendRequestOptions(options: Partial<SendRequestOptions> | undefined): SendRequestOptions {\n return {\n replyTimeoutMs: options?.replyTimeoutMs ?? DEFAULT_REPLY_TIMEOUT_MS,\n pollIntervalMs: options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,\n };\n}\n\nfunction parseReplyPayload(raw: string) {\n const parsed: unknown = JSON.parse(raw);\n return jsonReplySchema.parse(parsed);\n}\n\nasync function getDelReply(redisClient: RedisClient, rKey: string): Promise<JSONReply | null> {\n const raw = await redisClient.getDel(rKey);\n if (raw === null) {\n return null;\n }\n return parseReplyPayload(raw);\n}\n\n/**\n * Publishes the request (JSON) on the worker channel, then polls the reply key with GETDEL\n * (see https://redis.io/docs/latest/commands/getdel/ ) until a result arrives, the executor\n * heartbeat expires, or the wait budget is exceeded.\n */\nexport async function redisRequest<T extends JSONValue>({\n redis: redisClient,\n executorId,\n path,\n request,\n options,\n}: {\n redis: RedisClient;\n executorId: string;\n path: string;\n request: RequestEnvelope<T>;\n options?: Partial<SendRequestOptions> | undefined;\n}): Promise<JSONReply> {\n const { replyTimeoutMs, pollIntervalMs } = resolveSendRequestOptions(options);\n const aliveK = heartbeatKey(executorId);\n if ((await redisClient.exists(aliveK)) === 0) {\n throw new NoResponderError(executorId);\n }\n\n const requestId = randomUUID();\n const rKey = replyKey(requestId);\n const payload: PublishedRequest = {\n replyKey: rKey,\n path,\n body: request.body,\n headers: request.headers,\n };\n const serializedPayload = JSON.stringify(payload);\n const subscriberCount = await redisClient.publish(requestChannel(executorId), serializedPayload);\n if (subscriberCount === 0) {\n throw new NoResponderError(executorId, 'No executor received the published request');\n }\n\n const replyDeadline = performance.now() + replyTimeoutMs;\n // Give the executor a moment to process before the first poll.\n await sleep(Math.min(200, replyTimeoutMs));\n for (;;) {\n const replyPayload = await getDelReply(redisClient, rKey);\n if (replyPayload) {\n return replyPayload;\n }\n // We already checked that the executor was alive before sending, but it may have\n // died while we wait. Recheck here so we fail soon instead of waiting until the timeout.\n if ((await redisClient.exists(aliveK)) === 0) {\n throw new NoResponderError(executorId);\n }\n if (performance.now() >= replyDeadline) {\n throw new RequestTimeoutError(replyTimeoutMs);\n }\n await sleep(pollIntervalMs);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAA2B;AAC3B,6BAA4B;AAC5B,oBAAsD;AAGtD,mBAAgC;AAChC,mBAA8D;AAS9D,IAAM,2BAA2B;AACjC,IAAM,2BAA2B;AAEjC,SAAS,0BAA0B,SAAsE;AACvG,SAAO;AAAA,IACL,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,gBAAgB,SAAS,kBAAkB;AAAA,EAC7C;AACF;AAEA,SAAS,kBAAkB,KAAa;AACtC,QAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,SAAO,6BAAgB,MAAM,MAAM;AACrC;AAEA,eAAe,YAAY,aAA0B,MAAyC;AAC5F,QAAM,MAAM,MAAM,YAAY,OAAO,IAAI;AACzC,MAAI,QAAQ,MAAM;AAChB,WAAO;AAAA,EACT;AACA,SAAO,kBAAkB,GAAG;AAC9B;AAOA,eAAsB,aAAkC;AAAA,EACtD,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMuB;AACrB,QAAM,EAAE,gBAAgB,eAAe,IAAI,0BAA0B,OAAO;AAC5E,QAAM,aAAS,2BAAa,UAAU;AACtC,MAAK,MAAM,YAAY,OAAO,MAAM,MAAO,GAAG;AAC5C,UAAM,IAAI,+BAAiB,UAAU;AAAA,EACvC;AAEA,QAAM,gBAAY,+BAAW;AAC7B,QAAM,WAAO,uBAAS,SAAS;AAC/B,QAAM,UAA4B;AAAA,IAChC,UAAU;AAAA,IACV;AAAA,IACA,MAAM,QAAQ;AAAA,IACd,SAAS,QAAQ;AAAA,EACnB;AACA,QAAM,oBAAoB,KAAK,UAAU,OAAO;AAChD,QAAM,kBAAkB,MAAM,YAAY,YAAQ,6BAAe,UAAU,GAAG,iBAAiB;AAC/F,MAAI,oBAAoB,GAAG;AACzB,UAAM,IAAI,+BAAiB,YAAY,4CAA4C;AAAA,EACrF;AAEA,QAAM,gBAAgB,mCAAY,IAAI,IAAI;AAE1C,YAAM,oBAAM,KAAK,IAAI,KAAK,cAAc,CAAC;AACzC,aAAS;AACP,UAAM,eAAe,MAAM,YAAY,aAAa,IAAI;AACxD,QAAI,cAAc;AAChB,aAAO;AAAA,IACT;AAGA,QAAK,MAAM,YAAY,OAAO,MAAM,MAAO,GAAG;AAC5C,YAAM,IAAI,+BAAiB,UAAU;AAAA,IACvC;AACA,QAAI,mCAAY,IAAI,KAAK,eAAe;AACtC,YAAM,IAAI,kCAAoB,cAAc;AAAA,IAC9C;AACA,cAAM,oBAAM,cAAc;AAAA,EAC5B;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/request-reply/client.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { performance } from 'node:perf_hooks';\nimport
|
|
1
|
+
{"version":3,"sources":["../../src/request-reply/client.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { performance } from 'node:perf_hooks';\nimport { NoResponderError, RequestTimeoutError } from './errors';\nimport type { RedisClient } from './redisClient';\nimport type { JSONReply, JSONValue, PublishedRequest, RequestEnvelope } from './types';\nimport { jsonReplySchema } from './types';\nimport { heartbeatKey, replyKey, requestChannel, sleep } from './utils';\n\nexport interface SendRequestOptions {\n /** Max time to wait for a reply after the request is published (ms). */\n replyTimeoutMs: number;\n /** Sleep between poll attempts when the reply is not yet present (ms). */\n pollIntervalMs: number;\n}\n\nconst DEFAULT_REPLY_TIMEOUT_MS = 60_000;\nconst DEFAULT_POLL_INTERVAL_MS = 500;\n\nfunction resolveSendRequestOptions(options: Partial<SendRequestOptions> | undefined): SendRequestOptions {\n return {\n replyTimeoutMs: options?.replyTimeoutMs ?? DEFAULT_REPLY_TIMEOUT_MS,\n pollIntervalMs: options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,\n };\n}\n\nfunction parseReplyPayload(raw: string) {\n const parsed: unknown = JSON.parse(raw);\n return jsonReplySchema.parse(parsed);\n}\n\nasync function getDelReply(redisClient: RedisClient, rKey: string): Promise<JSONReply | null> {\n const raw = await redisClient.getDel(rKey);\n if (raw === null) {\n return null;\n }\n return parseReplyPayload(raw);\n}\n\n/**\n * Publishes the request (JSON) on the worker channel, then polls the reply key with GETDEL\n * (see https://redis.io/docs/latest/commands/getdel/ ) until a result arrives, the executor\n * heartbeat expires, or the wait budget is exceeded.\n */\nexport async function redisRequest<T extends JSONValue>({\n redis: redisClient,\n executorId,\n path,\n request,\n options,\n}: {\n redis: RedisClient;\n executorId: string;\n path: string;\n request: RequestEnvelope<T>;\n options?: Partial<SendRequestOptions> | undefined;\n}): Promise<JSONReply> {\n const { replyTimeoutMs, pollIntervalMs } = resolveSendRequestOptions(options);\n const aliveK = heartbeatKey(executorId);\n if ((await redisClient.exists(aliveK)) === 0) {\n throw new NoResponderError(executorId);\n }\n\n const requestId = randomUUID();\n const rKey = replyKey(requestId);\n const payload: PublishedRequest = {\n replyKey: rKey,\n path,\n body: request.body,\n headers: request.headers,\n };\n const serializedPayload = JSON.stringify(payload);\n const subscriberCount = await redisClient.publish(requestChannel(executorId), serializedPayload);\n if (subscriberCount === 0) {\n throw new NoResponderError(executorId, 'No executor received the published request');\n }\n\n const replyDeadline = performance.now() + replyTimeoutMs;\n // Give the executor a moment to process before the first poll.\n await sleep(Math.min(200, replyTimeoutMs));\n for (;;) {\n const replyPayload = await getDelReply(redisClient, rKey);\n if (replyPayload) {\n return replyPayload;\n }\n // We already checked that the executor was alive before sending, but it may have\n // died while we wait. Recheck here so we fail soon instead of waiting until the timeout.\n if ((await redisClient.exists(aliveK)) === 0) {\n throw new NoResponderError(executorId);\n }\n if (performance.now() >= replyDeadline) {\n throw new RequestTimeoutError(replyTimeoutMs);\n }\n await sleep(pollIntervalMs);\n }\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB,2BAA2B;AAGtD,SAAS,uBAAuB;AAChC,SAAS,cAAc,UAAU,gBAAgB,aAAa;AAS9D,IAAM,2BAA2B;AACjC,IAAM,2BAA2B;AAEjC,SAAS,0BAA0B,SAAsE;AACvG,SAAO;AAAA,IACL,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,gBAAgB,SAAS,kBAAkB;AAAA,EAC7C;AACF;AAEA,SAAS,kBAAkB,KAAa;AACtC,QAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,SAAO,gBAAgB,MAAM,MAAM;AACrC;AAEA,eAAe,YAAY,aAA0B,MAAyC;AAC5F,QAAM,MAAM,MAAM,YAAY,OAAO,IAAI;AACzC,MAAI,QAAQ,MAAM;AAChB,WAAO;AAAA,EACT;AACA,SAAO,kBAAkB,GAAG;AAC9B;AAOA,eAAsB,aAAkC;AAAA,EACtD,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMuB;AACrB,QAAM,EAAE,gBAAgB,eAAe,IAAI,0BAA0B,OAAO;AAC5E,QAAM,SAAS,aAAa,UAAU;AACtC,MAAK,MAAM,YAAY,OAAO,MAAM,MAAO,GAAG;AAC5C,UAAM,IAAI,iBAAiB,UAAU;AAAA,EACvC;AAEA,QAAM,YAAY,WAAW;AAC7B,QAAM,OAAO,SAAS,SAAS;AAC/B,QAAM,UAA4B;AAAA,IAChC,UAAU;AAAA,IACV;AAAA,IACA,MAAM,QAAQ;AAAA,IACd,SAAS,QAAQ;AAAA,EACnB;AACA,QAAM,oBAAoB,KAAK,UAAU,OAAO;AAChD,QAAM,kBAAkB,MAAM,YAAY,QAAQ,eAAe,UAAU,GAAG,iBAAiB;AAC/F,MAAI,oBAAoB,GAAG;AACzB,UAAM,IAAI,iBAAiB,YAAY,4CAA4C;AAAA,EACrF;AAEA,QAAM,gBAAgB,YAAY,IAAI,IAAI;AAE1C,QAAM,MAAM,KAAK,IAAI,KAAK,cAAc,CAAC;AACzC,aAAS;AACP,UAAM,eAAe,MAAM,YAAY,aAAa,IAAI;AACxD,QAAI,cAAc;AAChB,aAAO;AAAA,IACT;AAGA,QAAK,MAAM,YAAY,OAAO,MAAM,MAAO,GAAG;AAC5C,YAAM,IAAI,iBAAiB,UAAU;AAAA,IACvC;AACA,QAAI,YAAY,IAAI,KAAK,eAAe;AACtC,YAAM,IAAI,oBAAoB,cAAc;AAAA,IAC9C;AACA,UAAM,MAAM,cAAc;AAAA,EAC5B;AACF;","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { RedisClientType } from 'redis';
|
|
2
1
|
import type { Logger } from 'winston';
|
|
2
|
+
import type { RedisClient } from './redisClient';
|
|
3
3
|
import type { RequestHandler } from './types';
|
|
4
4
|
export interface RunExecutorOptions {
|
|
5
5
|
/** How often to refresh the heartbeat key (ms). */
|
|
@@ -46,9 +46,9 @@ export declare class RequestReplyExecutor {
|
|
|
46
46
|
constructor({ executorId, redis, subscriberClient, requestHandler, onError, logger, options, }: {
|
|
47
47
|
executorId: string;
|
|
48
48
|
/** Connected command client, used only for SET (reply + heartbeat). Caller owns its lifecycle. */
|
|
49
|
-
redis:
|
|
49
|
+
redis: RedisClient;
|
|
50
50
|
/** Connected client to SUBSCRIBE on (duplicate or Sentinel). Caller owns its lifecycle. */
|
|
51
|
-
subscriberClient:
|
|
51
|
+
subscriberClient: RedisClient;
|
|
52
52
|
requestHandler: RequestHandler;
|
|
53
53
|
onError?: RequestReplyErrorHandler | undefined;
|
|
54
54
|
logger: Logger;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/request-reply/executor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/request-reply/executor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAItC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAa,cAAc,EAAE,MAAM,SAAS,CAAC;AAIzD,MAAM,WAAW,kBAAkB;IACjC,mDAAmD;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;CACpB;AAYD,2FAA2F;AAC3F,MAAM,MAAM,wBAAwB,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,CAAC;AAE9G;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAoB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAEhC;IAEF,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAoC;IAC3D,OAAO,CAAC,cAAc,CAA+C;IAErE,YAAY,EACV,UAAU,EACV,KAAK,EACL,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,MAAM,EACN,OAAO,GACR,EAAE;QACD,UAAU,EAAE,MAAM,CAAC;QACnB,kGAAkG;QAClG,KAAK,EAAE,WAAW,CAAC;QACnB,2FAA2F;QAC3F,gBAAgB,EAAE,WAAW,CAAC;QAC9B,cAAc,EAAE,cAAc,CAAC;QAC/B,OAAO,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAC;QAC/C,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC;KACnD,EAaA;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA6B1B;YAEa,eAAe;YAoBf,IAAI;IAYlB,wEAAwE;IACxE,OAAO,CAAC,cAAc;IAYtB,yEAAyE;IACzE,OAAO,CAAC,aAAa;YAQP,yBAAyB;IA8DvC,6CAA6C;IAC7C,OAAO,CAAC,SAAS;IAsBX,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAY3B;IAIK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAG3B;CACF"}
|
|
@@ -101,7 +101,6 @@ var RequestReplyExecutor = class {
|
|
|
101
101
|
...(0, import_errorLogFields.extractErrorLogFields)(err)
|
|
102
102
|
});
|
|
103
103
|
this.onError?.(err, { executorId: this.executorId, channel: this.channel });
|
|
104
|
-
this.stopHeartbeat();
|
|
105
104
|
});
|
|
106
105
|
this.subscriberClient.on("end", () => {
|
|
107
106
|
this.logger.warn("[RequestReplyExecutor] Subscriber connection ended", { executorId: this.executorId });
|