nterminal 1.2.62 → 1.2.64

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.
Files changed (49) hide show
  1. package/README.md +0 -3
  2. package/bin/nterminal.js +0 -4
  3. package/dist/client/assets/MarkdownPreview-3F25Ss3Q.js +1 -0
  4. package/dist/client/assets/{TranscriptMarkdownBody-B5UquJIY.js → TranscriptMarkdownBody-l6FWnQzY.js} +1 -1
  5. package/dist/client/assets/index-Br8oSqEJ.js +55 -0
  6. package/dist/client/assets/{index-CpqKK80t.css → index-CQdd_vcO.css} +1 -1
  7. package/dist/client/assets/{index-B56tTIJv.js → index-DeuQv7mk.js} +1 -1
  8. package/dist/client/index.html +2 -2
  9. package/dist/server/agent/agentAuth.js +1 -11
  10. package/dist/server/agent/agentAuth.js.map +1 -1
  11. package/dist/server/agent/agentRoutes.js +2 -96
  12. package/dist/server/agent/agentRoutes.js.map +1 -1
  13. package/dist/server/http.d.ts +1 -2
  14. package/dist/server/http.js +0 -2
  15. package/dist/server/http.js.map +1 -1
  16. package/dist/server/routes/agentManagementRoutes.js +2 -14
  17. package/dist/server/routes/agentManagementRoutes.js.map +1 -1
  18. package/dist/server/routes/authRoutes.js +1 -1
  19. package/dist/server/routes/authRoutes.js.map +1 -1
  20. package/dist/server/routes/terminalRoutes.js +1 -1
  21. package/dist/server/routes/terminalRoutes.js.map +1 -1
  22. package/dist/server/routes/updateRoutes.js +2 -2
  23. package/dist/server/routes/updateRoutes.js.map +1 -1
  24. package/dist/server/storage/fileStore.d.ts +1 -8
  25. package/dist/server/storage/fileStore.js +2 -49
  26. package/dist/server/storage/fileStore.js.map +1 -1
  27. package/dist/server/terminal/TerminalManager.d.ts +1 -0
  28. package/dist/server/terminal/TerminalManager.js +9 -0
  29. package/dist/server/terminal/TerminalManager.js.map +1 -1
  30. package/dist/server/update/packageUpdate.d.ts +2 -2
  31. package/dist/server/update/packageUpdate.js +53 -20
  32. package/dist/server/update/packageUpdate.js.map +1 -1
  33. package/dist/shared/protocol.d.ts +0 -71
  34. package/dist/shared/protocol.js.map +1 -1
  35. package/docs/features.md +0 -1
  36. package/package.json +1 -1
  37. package/dist/client/assets/MarkdownPreview-pEj4ukXa.js +0 -1
  38. package/dist/client/assets/index-Kt-1DNuk.js +0 -55
  39. package/dist/scripts/distcall.js +0 -436
  40. package/dist/scripts/distcall.js.map +0 -1
  41. package/dist/server/distcall/envelope.d.ts +0 -6
  42. package/dist/server/distcall/envelope.js +0 -56
  43. package/dist/server/distcall/envelope.js.map +0 -1
  44. package/dist/server/distcall/executor.d.ts +0 -23
  45. package/dist/server/distcall/executor.js +0 -316
  46. package/dist/server/distcall/executor.js.map +0 -1
  47. package/dist/server/routes/distributedCallRoutes.d.ts +0 -9
  48. package/dist/server/routes/distributedCallRoutes.js +0 -305
  49. package/dist/server/routes/distributedCallRoutes.js.map +0 -1
@@ -1,316 +0,0 @@
1
- import dns from 'node:dns/promises';
2
- import net from 'node:net';
3
- export const distCallDefaultTimeoutMs = 120_000;
4
- export const distCallMaxTimeoutMs = 60 * 60_000;
5
- export const distCallDefaultConcurrency = 1;
6
- export const distCallMaxConcurrency = 16;
7
- export const distCallDefaultIntervalMs = 0;
8
- export const distCallMaxIntervalMs = 60_000;
9
- export const distCallDefaultJitterMs = 0;
10
- export const distCallMaxJitterMs = 60_000;
11
- const hopByHopHeaders = new Set([
12
- 'connection',
13
- 'content-length',
14
- 'host',
15
- 'keep-alive',
16
- 'proxy-authenticate',
17
- 'proxy-authorization',
18
- 'te',
19
- 'trailer',
20
- 'transfer-encoding',
21
- 'upgrade'
22
- ]);
23
- export function normalizeDistributedCallOptions(requested, limits) {
24
- return {
25
- maxRequestBytes: clampPositiveInteger(requested?.maxRequestBytes, limits.maxRequestBytes, limits.maxRequestBytes),
26
- maxResponseBytes: clampPositiveInteger(requested?.maxResponseBytes, limits.maxResponseBytes, limits.maxResponseBytes),
27
- allowPrivateTargets: requested?.allowPrivateTargets === true && limits.allowPrivateTargets,
28
- concurrencyPerWorker: clampPositiveInteger(requested?.concurrencyPerWorker, distCallDefaultConcurrency, distCallMaxConcurrency),
29
- intervalMsPerWorker: clampNonNegativeInteger(requested?.intervalMsPerWorker, distCallDefaultIntervalMs, distCallMaxIntervalMs),
30
- jitterMs: clampNonNegativeInteger(requested?.jitterMs, distCallDefaultJitterMs, distCallMaxJitterMs),
31
- timeoutMs: clampPositiveInteger(requested?.timeoutMs, distCallDefaultTimeoutMs, distCallMaxTimeoutMs)
32
- };
33
- }
34
- export async function runDistributedCallTasks(workerId, tasks, options, onResult, abortSignal) {
35
- let nextIndex = 0;
36
- let nextStartAt = Date.now();
37
- const takeTask = () => tasks[nextIndex++];
38
- const waitForLaunchTurn = async () => {
39
- const now = Date.now();
40
- const delayMs = Math.max(0, nextStartAt - now);
41
- const jitter = options.jitterMs > 0 ? Math.floor(Math.random() * (options.jitterMs + 1)) : 0;
42
- nextStartAt = Math.max(nextStartAt, now) + options.intervalMsPerWorker + jitter;
43
- if (delayMs > 0) {
44
- await sleep(delayMs, abortSignal);
45
- }
46
- };
47
- const worker = async () => {
48
- while (!abortSignal?.aborted) {
49
- const task = takeTask();
50
- if (!task) {
51
- return;
52
- }
53
- try {
54
- await waitForLaunchTurn();
55
- }
56
- catch (error) {
57
- if (abortSignal?.aborted) {
58
- return;
59
- }
60
- throw error;
61
- }
62
- if (abortSignal?.aborted) {
63
- return;
64
- }
65
- const result = await executeDistributedCallTask(workerId, task, options, abortSignal);
66
- await onResult(result);
67
- }
68
- };
69
- await Promise.all(Array.from({ length: Math.min(options.concurrencyPerWorker, tasks.length) }, () => worker()));
70
- }
71
- export async function executeDistributedCallTask(workerId, task, options, abortSignal) {
72
- const started = Date.now();
73
- try {
74
- validateTask(task);
75
- await assertTargetAllowed(task.request.url, options.allowPrivateTargets);
76
- const payload = buildTaskPayload(task.request);
77
- if (payload.requestBytes > options.maxRequestBytes) {
78
- throw new Error(`request_too_large:${payload.requestBytes}`);
79
- }
80
- const timeoutSignal = AbortSignal.timeout(options.timeoutMs);
81
- const signal = abortSignal ? AbortSignal.any([abortSignal, timeoutSignal]) : timeoutSignal;
82
- const requestInit = {
83
- method: task.request.method,
84
- headers: payload.headers,
85
- redirect: 'manual',
86
- signal
87
- };
88
- if (payload.body !== undefined) {
89
- requestInit.body = payload.body;
90
- }
91
- const response = await fetch(task.request.url, requestInit);
92
- const body = await readResponseBody(response, options.maxResponseBytes);
93
- return {
94
- taskId: task.taskId,
95
- workerId,
96
- ok: true,
97
- status: response.status,
98
- headers: responseHeaders(response),
99
- bodyBase64: body.toString('base64'),
100
- durationMs: Date.now() - started
101
- };
102
- }
103
- catch (error) {
104
- return {
105
- taskId: typeof task.taskId === 'string' ? task.taskId : 'unknown',
106
- workerId,
107
- ok: false,
108
- error: distributedCallErrorMessage(error),
109
- durationMs: Date.now() - started
110
- };
111
- }
112
- }
113
- function validateTask(task) {
114
- if (!task || typeof task !== 'object') {
115
- throw new Error('invalid_task');
116
- }
117
- if (typeof task.taskId !== 'string' || task.taskId.length === 0 || task.taskId.length > 200) {
118
- throw new Error('invalid_task_id');
119
- }
120
- const request = task.request;
121
- if (!request || typeof request !== 'object') {
122
- throw new Error('invalid_request');
123
- }
124
- if (!['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(request.method)) {
125
- throw new Error('invalid_method');
126
- }
127
- const parsed = new URL(request.url);
128
- if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
129
- throw new Error('invalid_protocol');
130
- }
131
- }
132
- function buildTaskPayload(request) {
133
- const headers = {};
134
- for (const [key, value] of Object.entries(request.headers ?? {})) {
135
- const normalizedKey = key.toLowerCase();
136
- if (hopByHopHeaders.has(normalizedKey) || normalizedKey.startsWith('proxy-')) {
137
- continue;
138
- }
139
- if (typeof value === 'string') {
140
- headers[key] = value;
141
- }
142
- }
143
- if (request.bodyBase64 !== undefined) {
144
- const body = Buffer.from(request.bodyBase64, 'base64');
145
- return { headers, body: copyArrayBuffer(body), requestBytes: body.length };
146
- }
147
- if (request.body === undefined || request.body === null || request.method === 'GET') {
148
- return { headers, body: undefined, requestBytes: 0 };
149
- }
150
- if (typeof request.body === 'string') {
151
- return { headers, body: request.body, requestBytes: Buffer.byteLength(request.body) };
152
- }
153
- if (!hasHeader(headers, 'content-type')) {
154
- headers['content-type'] = 'application/json';
155
- }
156
- const body = JSON.stringify(request.body);
157
- return { headers, body, requestBytes: Buffer.byteLength(body) };
158
- }
159
- function copyArrayBuffer(value) {
160
- const copy = new Uint8Array(value.byteLength);
161
- copy.set(value);
162
- return copy.buffer;
163
- }
164
- async function readResponseBody(response, maxBytes) {
165
- if (!response.body) {
166
- return Buffer.alloc(0);
167
- }
168
- const reader = response.body.getReader();
169
- const chunks = [];
170
- let total = 0;
171
- while (true) {
172
- const { done, value } = await reader.read();
173
- if (done) {
174
- break;
175
- }
176
- const chunk = Buffer.from(value);
177
- total += chunk.length;
178
- if (total > maxBytes) {
179
- await reader.cancel();
180
- throw new Error(`response_too_large:${total}`);
181
- }
182
- chunks.push(chunk);
183
- }
184
- return Buffer.concat(chunks, total);
185
- }
186
- function responseHeaders(response) {
187
- const headers = {};
188
- response.headers.forEach((value, key) => {
189
- if (!hopByHopHeaders.has(key.toLowerCase())) {
190
- headers[key] = value;
191
- }
192
- });
193
- return headers;
194
- }
195
- async function assertTargetAllowed(rawUrl, allowPrivateTargets) {
196
- const url = new URL(rawUrl);
197
- const hostname = stripBrackets(url.hostname);
198
- if (isBlockedHostname(hostname, allowPrivateTargets)) {
199
- throw new Error('target_blocked');
200
- }
201
- const literal = net.isIP(hostname);
202
- const addresses = literal ? [{ address: hostname }] : await dns.lookup(hostname, { all: true, verbatim: true });
203
- if (addresses.length === 0) {
204
- throw new Error('target_unresolved');
205
- }
206
- for (const entry of addresses) {
207
- if (isBlockedAddress(entry.address, allowPrivateTargets)) {
208
- throw new Error('target_blocked');
209
- }
210
- }
211
- }
212
- function isBlockedHostname(hostname, allowPrivateTargets) {
213
- const lower = hostname.toLowerCase();
214
- if (lower === 'localhost' || lower.endsWith('.localhost')) {
215
- return true;
216
- }
217
- if (lower === 'metadata.google.internal') {
218
- return true;
219
- }
220
- return net.isIP(lower) !== 0 && isBlockedAddress(lower, allowPrivateTargets);
221
- }
222
- function isBlockedAddress(address, allowPrivateTargets) {
223
- const family = net.isIP(address);
224
- if (family === 4) {
225
- return isBlockedIpv4(address, allowPrivateTargets);
226
- }
227
- if (family === 6) {
228
- return isBlockedIpv6(address, allowPrivateTargets);
229
- }
230
- return true;
231
- }
232
- function isBlockedIpv4(address, allowPrivateTargets) {
233
- const parts = address.split('.').map((part) => Number(part));
234
- if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
235
- return true;
236
- }
237
- const [a, b, c, d] = parts;
238
- if (a === 0 ||
239
- a === 127 ||
240
- a >= 224 ||
241
- (a === 169 && b === 254) ||
242
- (a === 192 && b === 0 && (c === 0 || c === 2)) ||
243
- (a === 192 && b === 88 && c === 99) ||
244
- (a === 198 && (b === 18 || b === 19)) ||
245
- (a === 198 && b === 51 && c === 100) ||
246
- (a === 203 && b === 0 && c === 113)) {
247
- return true;
248
- }
249
- if (allowPrivateTargets) {
250
- return false;
251
- }
252
- return a === 10 || (a === 100 && b >= 64 && b <= 127) || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168);
253
- }
254
- function isBlockedIpv6(address, allowPrivateTargets) {
255
- const lower = address.toLowerCase();
256
- if (lower === '::' || lower === '::1' || lower.startsWith('fe80:') || lower.startsWith('ff')) {
257
- return true;
258
- }
259
- if (allowPrivateTargets) {
260
- return false;
261
- }
262
- return lower.startsWith('fc') || lower.startsWith('fd');
263
- }
264
- function hasHeader(headers, target) {
265
- return Object.keys(headers).some((key) => key.toLowerCase() === target);
266
- }
267
- function stripBrackets(value) {
268
- return value.startsWith('[') && value.endsWith(']') ? value.slice(1, -1) : value;
269
- }
270
- function clampPositiveInteger(value, fallback, max) {
271
- if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) {
272
- return fallback;
273
- }
274
- return Math.min(value, max);
275
- }
276
- function clampNonNegativeInteger(value, fallback, max) {
277
- if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
278
- return fallback;
279
- }
280
- return Math.min(value, max);
281
- }
282
- async function sleep(ms, signal) {
283
- if (ms <= 0) {
284
- return;
285
- }
286
- await new Promise((resolve, reject) => {
287
- let timeout;
288
- const cleanup = () => {
289
- clearTimeout(timeout);
290
- signal?.removeEventListener('abort', abort);
291
- };
292
- const abort = () => {
293
- cleanup();
294
- reject(new Error('aborted'));
295
- };
296
- timeout = setTimeout(() => {
297
- cleanup();
298
- resolve();
299
- }, ms);
300
- if (signal?.aborted) {
301
- abort();
302
- return;
303
- }
304
- signal?.addEventListener('abort', abort, { once: true });
305
- });
306
- }
307
- function distributedCallErrorMessage(error) {
308
- if (error instanceof Error) {
309
- if (error.name === 'TimeoutError') {
310
- return 'timeout';
311
- }
312
- return error.message || error.name;
313
- }
314
- return 'request_failed';
315
- }
316
- //# sourceMappingURL=executor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../src/server/distcall/executor.ts"],"names":[],"mappings":"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"}
@@ -1,9 +0,0 @@
1
- import type { FastifyInstance } from 'fastify';
2
- import type { AppConfig } from '../config.js';
3
- import type { AuthService } from '../auth/authService.js';
4
- import { type FileStore } from '../storage/fileStore.js';
5
- export interface DistributedCallRouteServices {
6
- authService: AuthService;
7
- fileStore: FileStore;
8
- }
9
- export declare function registerDistributedCallRoutes(app: FastifyInstance, config: AppConfig, services: DistributedCallRouteServices): Promise<void>;
@@ -1,305 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { once } from 'node:events';
3
- import net from 'node:net';
4
- import { defaultDistributedCallMaxRequestBytes, defaultDistributedCallMaxResponseBytes, hardDistributedCallMaxRequestBytes, hardDistributedCallMaxResponseBytes, normalizeDistributedCallWorkerIds } from '../storage/fileStore.js';
5
- import { requireAllowedOrigin } from './authRoutes.js';
6
- import { authenticateTerminalRequest } from './terminalRoutes.js';
7
- import { decryptDistributedCallPayload, encryptDistributedCallPayload, signDistributedCallRequest } from '../distcall/envelope.js';
8
- import { normalizeDistributedCallOptions, runDistributedCallTasks } from '../distcall/executor.js';
9
- import { MAIN_SERVER_ID } from '../../shared/protocol.js';
10
- const distCallRouteBodyLimitBytes = 192 * 1024 * 1024;
11
- const requesterByRequest = new WeakMap();
12
- export async function registerDistributedCallRoutes(app, config, services) {
13
- app.get('/api/distcalls/settings', async (request, reply) => {
14
- const auth = await authenticateTerminalRequest(services.authService, request, reply);
15
- if (!auth)
16
- return reply;
17
- const state = await services.fileStore.read();
18
- return distributedCallSettingsResponse(state.distributedCalls, state.agents);
19
- });
20
- app.put('/api/distcalls/settings', { preHandler: (request, reply) => requireAllowedOrigin(config, request, reply) }, async (request, reply) => {
21
- const auth = await authenticateTerminalRequest(services.authService, request, reply);
22
- if (!auth)
23
- return reply;
24
- let nextSettings = null;
25
- await services.fileStore.update((state) => {
26
- const workerIds = request.body?.workerIds !== undefined
27
- ? normalizeDistributedCallWorkerIds(request.body.workerIds, state.agents)
28
- : normalizeDistributedCallWorkerIds(state.distributedCalls.workerIds, state.agents);
29
- nextSettings = {
30
- ...state.distributedCalls,
31
- ...(typeof request.body?.enabled === 'boolean' ? { enabled: request.body.enabled } : {}),
32
- workerIds,
33
- ...(typeof request.body?.allowPrivateTargets === 'boolean' ? { allowPrivateTargets: request.body.allowPrivateTargets } : {}),
34
- ...(isPositiveSafeInteger(request.body?.maxRequestBytes)
35
- ? { maxRequestBytes: Math.min(request.body.maxRequestBytes, hardDistributedCallMaxRequestBytes) }
36
- : {}),
37
- ...(isPositiveSafeInteger(request.body?.maxResponseBytes)
38
- ? { maxResponseBytes: Math.min(request.body.maxResponseBytes, hardDistributedCallMaxResponseBytes) }
39
- : {})
40
- };
41
- return { ...state, distributedCalls: nextSettings };
42
- }, { flush: 'immediate' });
43
- const state = await services.fileStore.read();
44
- return distributedCallSettingsResponse(nextSettings ?? state.distributedCalls, state.agents);
45
- });
46
- app.post('/api/distcalls/run', {
47
- bodyLimit: distCallRouteBodyLimitBytes,
48
- onRequest: async (request, reply) => {
49
- requesterByRequest.set(request, await authenticateDistributedCallRequester(config, services, request, reply));
50
- }
51
- }, async (request, reply) => {
52
- const requester = requesterByRequest.get(request) ?? null;
53
- if (!requester)
54
- return reply;
55
- const state = await services.fileStore.read();
56
- if (!state.distributedCalls.enabled) {
57
- return reply.code(403).send({ error: 'distributed_calls_disabled' });
58
- }
59
- const tasks = normalizeRunTasks(request.body?.tasks);
60
- if (tasks.length === 0) {
61
- return reply.code(400).send({ error: 'tasks_required' });
62
- }
63
- const assignments = assignWorkers(tasks, request.body?.options?.workerIds, state.distributedCalls.workerIds, state.agents);
64
- if (assignments.length === 0) {
65
- return reply.code(400).send({ error: 'no_workers_enabled' });
66
- }
67
- const options = normalizeDistributedCallOptions(request.body?.options, {
68
- maxRequestBytes: state.distributedCalls.maxRequestBytes || defaultDistributedCallMaxRequestBytes,
69
- maxResponseBytes: state.distributedCalls.maxResponseBytes || defaultDistributedCallMaxResponseBytes,
70
- allowPrivateTargets: state.distributedCalls.allowPrivateTargets
71
- });
72
- return streamDistributedCallRun(config, requester, assignments, options, request, reply);
73
- });
74
- }
75
- function distributedCallSettingsResponse(settings, agents) {
76
- const enabledIds = new Set(settings.workerIds);
77
- return {
78
- settings,
79
- workers: [
80
- { id: MAIN_SERVER_ID, name: 'Local (main)', enabled: enabledIds.has(MAIN_SERVER_ID) },
81
- ...agents.map((agent) => ({ id: agent.id, name: agent.name, enabled: enabledIds.has(agent.id) }))
82
- ]
83
- };
84
- }
85
- async function authenticateDistributedCallRequester(config, services, request, reply) {
86
- const bearer = extractBearer(request);
87
- if (bearer) {
88
- const state = await services.fileStore.read();
89
- const agent = state.agents.find((candidate) => candidate.token === bearer);
90
- if (agent) {
91
- return { kind: 'agent', agentId: agent.id };
92
- }
93
- }
94
- if (isLoopbackRequest(request) && request.headers['x-nterminal-local-secret'] === config.sessionSecret) {
95
- return { kind: 'local' };
96
- }
97
- await requireAllowedOrigin(config, request, reply);
98
- if (reply.sent) {
99
- return null;
100
- }
101
- const auth = await authenticateTerminalRequest(services.authService, request, reply);
102
- return auth ? { kind: 'browser' } : null;
103
- }
104
- function normalizeRunTasks(value) {
105
- if (!Array.isArray(value)) {
106
- return [];
107
- }
108
- return value.filter((task) => Boolean(task) &&
109
- typeof task === 'object' &&
110
- typeof task.taskId === 'string' &&
111
- Boolean(task.request));
112
- }
113
- function assignWorkers(tasks, requestedWorkerIds, enabledWorkerIds, agents) {
114
- const enabled = new Set(enabledWorkerIds);
115
- const requested = Array.isArray(requestedWorkerIds)
116
- ? requestedWorkerIds.filter((id) => typeof id === 'string')
117
- : enabledWorkerIds;
118
- const selected = requested.filter((id) => enabled.has(id));
119
- const agentById = new Map(agents.map((agent) => [agent.id, agent]));
120
- const assignments = selected
121
- .map((id) => {
122
- if (id === MAIN_SERVER_ID) {
123
- return { id, name: 'Local (main)', agent: null, tasks: [] };
124
- }
125
- const agent = agentById.get(id);
126
- return agent ? { id, name: agent.name, agent, tasks: [] } : null;
127
- })
128
- .filter((assignment) => Boolean(assignment));
129
- if (assignments.length === 0) {
130
- return [];
131
- }
132
- tasks.forEach((task, index) => {
133
- assignments[index % assignments.length].tasks.push(task);
134
- });
135
- return assignments.filter((assignment) => assignment.tasks.length > 0);
136
- }
137
- async function streamDistributedCallRun(config, requester, assignments, options, request, reply) {
138
- const jobId = randomUUID();
139
- const abortController = new AbortController();
140
- reply.raw.on('close', () => abortController.abort());
141
- reply.raw.writeHead(200, {
142
- 'Content-Type': 'application/x-ndjson; charset=utf-8',
143
- 'Cache-Control': 'no-store'
144
- });
145
- request.log.info({
146
- event: 'distributed_call_started',
147
- jobId,
148
- requester: requester.kind,
149
- requesterAgentId: requester.agentId,
150
- workers: assignments.map((assignment) => assignment.id),
151
- taskCount: assignments.reduce((total, assignment) => total + assignment.tasks.length, 0)
152
- }, 'distributed call started');
153
- let succeeded = 0;
154
- let failed = 0;
155
- const writeResult = async (result) => {
156
- if (result.ok) {
157
- succeeded += 1;
158
- }
159
- else {
160
- failed += 1;
161
- }
162
- await writeNdjsonLine(reply.raw, result);
163
- };
164
- await Promise.all(assignments.map((assignment) => runWorkerAssignment(config, jobId, assignment, options, writeResult, abortController.signal)));
165
- request.log.info({ event: 'distributed_call_finished', jobId, succeeded, failed }, 'distributed call finished');
166
- reply.raw.end();
167
- return reply;
168
- }
169
- async function runWorkerAssignment(config, jobId, assignment, options, writeResult, signal) {
170
- if (assignment.agent === null) {
171
- await runDistributedCallTasks(MAIN_SERVER_ID, assignment.tasks, options, writeResult, signal);
172
- return;
173
- }
174
- const pendingTaskIds = new Set(assignment.tasks.map((task) => task.taskId));
175
- const writeAssignmentResult = async (result) => {
176
- if (!pendingTaskIds.delete(result.taskId)) {
177
- return;
178
- }
179
- await writeResult(result);
180
- };
181
- const failPending = async (error) => {
182
- await writeWorkerError(assignment, pendingTaskIds, error, writeResult);
183
- };
184
- const body = {
185
- jobId,
186
- envelope: encryptDistributedCallPayload(assignment.agent.token, jobId, 'tasks', {
187
- workerId: assignment.id,
188
- tasks: assignment.tasks,
189
- options
190
- })
191
- };
192
- let response;
193
- try {
194
- const timestamp = Date.now();
195
- response = await fetch(`${assignment.agent.url}/api/agent/distcalls/run`, {
196
- method: 'POST',
197
- headers: {
198
- 'Content-Type': 'application/json',
199
- 'x-nterminal-distcall-job': jobId,
200
- 'x-nterminal-distcall-ts': String(timestamp),
201
- 'x-nterminal-distcall-mac': signDistributedCallRequest(assignment.agent.token, jobId, timestamp)
202
- },
203
- body: JSON.stringify(body),
204
- signal
205
- });
206
- }
207
- catch (error) {
208
- await failPending(`agent_unreachable:${error instanceof Error ? error.message : 'request_failed'}`);
209
- return;
210
- }
211
- if (!response.ok || !response.body) {
212
- await failPending(`agent_error:${response.status}`);
213
- return;
214
- }
215
- try {
216
- await readNdjsonStream(response.body, async (line) => {
217
- if (!line.trim()) {
218
- return;
219
- }
220
- const parsed = JSON.parse(line);
221
- const result = decryptDistributedCallPayload(assignment.agent.token, jobId, 'result', parsed.envelope);
222
- await writeAssignmentResult(result);
223
- });
224
- }
225
- catch (error) {
226
- await failPending(`agent_invalid_result:${error instanceof Error ? error.message : 'parse_failed'}`);
227
- return;
228
- }
229
- if (pendingTaskIds.size > 0) {
230
- await failPending('agent_incomplete');
231
- }
232
- }
233
- async function writeWorkerError(assignment, pendingTaskIds, error, writeResult) {
234
- for (const task of assignment.tasks) {
235
- if (!pendingTaskIds.delete(task.taskId)) {
236
- continue;
237
- }
238
- await writeResult({
239
- taskId: task.taskId,
240
- workerId: assignment.id,
241
- ok: false,
242
- error,
243
- durationMs: 0
244
- });
245
- }
246
- }
247
- async function readNdjsonStream(body, onLine) {
248
- const decoder = new TextDecoder();
249
- const reader = body.getReader();
250
- let buffered = '';
251
- while (true) {
252
- const { done, value } = await reader.read();
253
- if (done) {
254
- break;
255
- }
256
- buffered += decoder.decode(value, { stream: true });
257
- let newline = buffered.indexOf('\n');
258
- while (newline !== -1) {
259
- await onLine(buffered.slice(0, newline));
260
- buffered = buffered.slice(newline + 1);
261
- newline = buffered.indexOf('\n');
262
- }
263
- }
264
- buffered += decoder.decode();
265
- if (buffered.trim()) {
266
- await onLine(buffered);
267
- }
268
- }
269
- async function writeNdjsonLine(stream, value) {
270
- const writable = stream;
271
- if (writable.destroyed || writable.writableEnded || writable.writableFinished) {
272
- return;
273
- }
274
- if (stream.write(`${JSON.stringify(value)}\n`)) {
275
- return;
276
- }
277
- await Promise.race([
278
- once(stream, 'drain'),
279
- once(stream, 'close'),
280
- once(stream, 'error').then(([error]) => {
281
- throw error instanceof Error ? error : new Error('stream_error');
282
- })
283
- ]);
284
- }
285
- function extractBearer(request) {
286
- const header = request.headers.authorization;
287
- return header?.startsWith('Bearer ') ? header.slice(7) : null;
288
- }
289
- function isLoopbackRequest(request) {
290
- const normalized = normalizeIpForLoopback(request.ip);
291
- return normalized === '127.0.0.1' || normalized === '::1';
292
- }
293
- function normalizeIpForLoopback(value) {
294
- if (!value) {
295
- return '';
296
- }
297
- if (value.startsWith('::ffff:')) {
298
- return value.slice('::ffff:'.length);
299
- }
300
- return net.isIP(value) ? value : '';
301
- }
302
- function isPositiveSafeInteger(value) {
303
- return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
304
- }
305
- //# sourceMappingURL=distributedCallRoutes.js.map