@juspay/neurolink 12.14.7 → 12.14.9
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/CHANGELOG.md +2 -2
- package/dist/adapters/providerImageAdapter.js +1 -0
- package/dist/browser/neurolink.min.js +379 -379
- package/dist/cli/commands/proxy.js +7 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +11 -0
- package/dist/mcp/servers/aiProviders/aiAnalysisTools.js +43 -30
- package/dist/neurolink.d.ts +4 -1
- package/dist/neurolink.js +41 -6
- package/dist/providers/azureOpenai.js +1 -1
- package/dist/providers/catalog/loader.d.ts +1 -1
- package/dist/providers/catalog/loader.js +30 -1
- package/dist/providers/cohere.js +1 -1
- package/dist/providers/deepseek.js +1 -1
- package/dist/providers/googleAiStudio/client.js +6 -5
- package/dist/providers/googleVertex/client.d.ts +11 -30
- package/dist/providers/googleVertex/client.js +49 -131
- package/dist/providers/huggingFace/client.js +1 -1
- package/dist/providers/llamaCpp.js +1 -1
- package/dist/providers/lmStudio.js +1 -1
- package/dist/providers/nvidiaNim/client.js +1 -1
- package/dist/providers/ollama/client.js +1 -1
- package/dist/providers/openRouter/client.js +1 -1
- package/dist/providers/openaiCompatible/client.js +1 -1
- package/dist/proxy/bodyCaptureProcessing.js +47 -3
- package/dist/proxy/bodyCaptureWorker.js +14 -10
- package/dist/proxy/claudeOutputObservation.d.ts +9 -0
- package/dist/proxy/claudeOutputObservation.js +85 -0
- package/dist/proxy/codexUsage.js +12 -10
- package/dist/proxy/otelLogSink.js +1 -1
- package/dist/proxy/processTelemetry.d.ts +3 -0
- package/dist/proxy/processTelemetry.js +25 -0
- package/dist/proxy/rawStreamCapture.js +17 -26
- package/dist/proxy/requestLogger.d.ts +2 -0
- package/dist/proxy/requestLogger.js +5 -1
- package/dist/proxy/restartControl.d.ts +3 -1
- package/dist/proxy/restartControl.js +118 -1
- package/dist/proxy/sseInterceptor.js +71 -3
- package/dist/server/routes/claudeProxyRoutes.d.ts +0 -11
- package/dist/server/routes/claudeProxyRoutes.js +27 -12
- package/dist/server/routes/codexProxyRoutes.js +83 -2
- package/dist/types/proxy.d.ts +14 -0
- package/dist/types/proxyRestart.d.ts +25 -0
- package/dist/utils/logSanitize.js +1 -1
- package/dist/utils/timeout.d.ts +13 -2
- package/dist/utils/timeout.js +14 -3
- package/docs-site/static/search-index.json +1 -1
- package/package.json +1 -1
- package/scripts/observability/proxy-telemetry-check.mjs +39 -5
|
@@ -25,7 +25,10 @@ import { createCodexUsageTap } from "../../proxy/codexUsage.js";
|
|
|
25
25
|
import { CODEX_ACCOUNT_PREFIX, parseCodexRateLimitHeaders, } from "../../proxy/codexAccountUsage.js";
|
|
26
26
|
import { buildClientAttribution } from "../../proxy/clientAttribution.js";
|
|
27
27
|
import { registerProxyResponseObserver } from "../../proxy/proxyActivity.js";
|
|
28
|
-
import { logRequest, logRequestAttempt } from "../../proxy/requestLogger.js";
|
|
28
|
+
import { logRequest, logRequestAttempt, logBodyCapture, isProxyBodyCaptureEnabled, } from "../../proxy/requestLogger.js";
|
|
29
|
+
import { createRawStreamCapture } from "../../proxy/rawStreamCapture.js";
|
|
30
|
+
import { resolveProxyLogTraceContext } from "../../proxy/proxyTraceContext.js";
|
|
31
|
+
import { isBorrowedRequest } from "../../proxy/shareContext.js";
|
|
29
32
|
import { ProxyTracer } from "../../proxy/proxyTracer.js";
|
|
30
33
|
import { parseRetryAfterMs } from "../../proxy/routingPolicy.js";
|
|
31
34
|
import { recordAttempt, recordAttemptError, recordFinalError, recordFinalSuccess, } from "../../proxy/usageStats.js";
|
|
@@ -272,6 +275,68 @@ function publishCodexHeaders(ctx, account, attempt, quota) {
|
|
|
272
275
|
}
|
|
273
276
|
/** Core pooled handler for POST /backend-api/codex/responses. */
|
|
274
277
|
export async function handleCodexResponsesRequest(ctx) {
|
|
278
|
+
if (ctx.metadata?.[CODEX_FALLBACK_METADATA_KEY] !== true) {
|
|
279
|
+
void logBodyCapture({
|
|
280
|
+
timestamp: new Date().toISOString(),
|
|
281
|
+
requestId: ctx.requestId,
|
|
282
|
+
phase: "client_request",
|
|
283
|
+
model: codexCaptureModel(ctx),
|
|
284
|
+
stream: true,
|
|
285
|
+
headers: ctx.headers,
|
|
286
|
+
body: ctx.body,
|
|
287
|
+
contentType: "application/json",
|
|
288
|
+
...resolveProxyLogTraceContext({ requestId: ctx.requestId }),
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
const response = await executeCodexResponsesRequest(ctx);
|
|
292
|
+
return ctx.metadata?.[CODEX_FALLBACK_METADATA_KEY] === true
|
|
293
|
+
? response
|
|
294
|
+
: captureCodexResponse(ctx, response, "client_response");
|
|
295
|
+
}
|
|
296
|
+
function codexCaptureModel(ctx) {
|
|
297
|
+
const body = ctx.body;
|
|
298
|
+
return typeof body?.model === "string" ? body.model : "-";
|
|
299
|
+
}
|
|
300
|
+
/** Observe bytes through the existing bounded pass-through; cancellation still reaches upstream. */
|
|
301
|
+
function captureCodexResponse(ctx, response, phase, account, attempt) {
|
|
302
|
+
if (!isProxyBodyCaptureEnabled()) {
|
|
303
|
+
return response;
|
|
304
|
+
}
|
|
305
|
+
const metadata = {
|
|
306
|
+
timestamp: new Date().toISOString(),
|
|
307
|
+
requestId: ctx.requestId,
|
|
308
|
+
phase,
|
|
309
|
+
model: codexCaptureModel(ctx),
|
|
310
|
+
stream: true,
|
|
311
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
312
|
+
contentType: response.headers.get("content-type") ?? undefined,
|
|
313
|
+
responseStatus: response.status,
|
|
314
|
+
account: account?.label,
|
|
315
|
+
accountType: account ? CODEX_ACCOUNT_TYPE : undefined,
|
|
316
|
+
attempt,
|
|
317
|
+
...resolveProxyLogTraceContext({ requestId: ctx.requestId }),
|
|
318
|
+
};
|
|
319
|
+
if (!response.body || isBorrowedRequest()) {
|
|
320
|
+
void logBodyCapture(metadata);
|
|
321
|
+
return response;
|
|
322
|
+
}
|
|
323
|
+
const observed = createRawStreamCapture();
|
|
324
|
+
void observed.capture
|
|
325
|
+
.then((capture) => logBodyCapture({
|
|
326
|
+
...metadata,
|
|
327
|
+
timestamp: new Date().toISOString(),
|
|
328
|
+
body: capture.text,
|
|
329
|
+
bodySize: capture.totalBytes,
|
|
330
|
+
sourceTruncated: capture.truncated,
|
|
331
|
+
}))
|
|
332
|
+
.catch(() => undefined);
|
|
333
|
+
return new Response(response.body.pipeThrough(observed.stream), {
|
|
334
|
+
status: response.status,
|
|
335
|
+
statusText: response.statusText,
|
|
336
|
+
headers: response.headers,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
async function executeCodexResponsesRequest(ctx) {
|
|
275
340
|
const requestStartTime = Date.now();
|
|
276
341
|
const body = ctx.body ?? {};
|
|
277
342
|
const bodyStr = JSON.stringify(body);
|
|
@@ -475,9 +540,24 @@ export async function handleCodexResponsesRequest(ctx) {
|
|
|
475
540
|
recordAttempt(account.label, CODEX_ACCOUNT_TYPE);
|
|
476
541
|
let upstream;
|
|
477
542
|
try {
|
|
543
|
+
const upstreamHeaders = buildCodexUpstreamHeaders(ctx.headers, account);
|
|
544
|
+
void logBodyCapture({
|
|
545
|
+
timestamp: new Date().toISOString(),
|
|
546
|
+
requestId: ctx.requestId,
|
|
547
|
+
phase: "upstream_request",
|
|
548
|
+
model,
|
|
549
|
+
stream: true,
|
|
550
|
+
body: bodyStr,
|
|
551
|
+
headers: upstreamHeaders,
|
|
552
|
+
contentType: "application/json",
|
|
553
|
+
account: account.label,
|
|
554
|
+
accountType: CODEX_ACCOUNT_TYPE,
|
|
555
|
+
attempt,
|
|
556
|
+
...tracer?.getTraceContext(),
|
|
557
|
+
});
|
|
478
558
|
upstream = await fetch(CODEX_RESPONSES_URL, {
|
|
479
559
|
method: "POST",
|
|
480
|
-
headers:
|
|
560
|
+
headers: upstreamHeaders,
|
|
481
561
|
body: bodyStr,
|
|
482
562
|
signal: ctx.abortSignal
|
|
483
563
|
? AbortSignal.any([
|
|
@@ -541,6 +621,7 @@ export async function handleCodexResponsesRequest(ctx) {
|
|
|
541
621
|
lastErrorStatus = 502;
|
|
542
622
|
break; // rotate to next account
|
|
543
623
|
}
|
|
624
|
+
upstream = captureCodexResponse(ctx, upstream, "upstream_response", account, attempt);
|
|
544
625
|
if (upstream.ok) {
|
|
545
626
|
const quota = parseCodexRateLimitHeaders(upstream.headers);
|
|
546
627
|
if (quota) {
|
package/dist/types/proxy.d.ts
CHANGED
|
@@ -561,6 +561,8 @@ export type RequestLogEntry = {
|
|
|
561
561
|
firstUsefulOutputStatus?: "observed" | "no_useful_output" | "not_observed";
|
|
562
562
|
/** Native protocol event that established the first useful output. */
|
|
563
563
|
firstUsefulOutputEvent?: string;
|
|
564
|
+
/** Why first-output timing could not be established for this response. */
|
|
565
|
+
firstUsefulOutputUnavailableReason?: string;
|
|
564
566
|
/** Requested reasoning effort retained independently of body capture. */
|
|
565
567
|
reasoningEffort?: string;
|
|
566
568
|
/** Small routing evidence retained even when response bodies are pruned. */
|
|
@@ -645,6 +647,8 @@ export type ProcessedProxyBodyCapture = {
|
|
|
645
647
|
export type ProxyBodyCaptureAdmission = {
|
|
646
648
|
limitingResource: "entry" | "captures" | "bytes" | "worker";
|
|
647
649
|
estimatedBytes?: number;
|
|
650
|
+
/** Per-entry clone ceiling, independent of the aggregate queue ceiling. */
|
|
651
|
+
maxEntryBytes?: number;
|
|
648
652
|
pending: number;
|
|
649
653
|
pendingBytes: number;
|
|
650
654
|
maxPending: number;
|
|
@@ -739,6 +743,7 @@ export type ProxyTelemetryCheck = {
|
|
|
739
743
|
};
|
|
740
744
|
/** Small stored metadata used by the doctor; bodies are queried separately. */
|
|
741
745
|
export type ProxyTelemetryStoredRecord = Partial<RequestLogEntry> & {
|
|
746
|
+
recordedAtMicroseconds?: number;
|
|
742
747
|
captureId?: string;
|
|
743
748
|
bodySha256?: string;
|
|
744
749
|
redactedBodyBytes?: number;
|
|
@@ -837,6 +842,8 @@ export type CodexTokenRefresher = (refreshToken: string) => Promise<{
|
|
|
837
842
|
}>;
|
|
838
843
|
export type ProxyBodyCaptureInput = {
|
|
839
844
|
phase: string;
|
|
845
|
+
/** The stream observer retained only a bounded prefix. */
|
|
846
|
+
sourceTruncated?: boolean;
|
|
840
847
|
headers?: Record<string, string>;
|
|
841
848
|
body?: unknown;
|
|
842
849
|
bodySize?: number;
|
|
@@ -2165,6 +2172,8 @@ export type ProxyBodyCaptureEntry = {
|
|
|
2165
2172
|
headers?: Record<string, string>;
|
|
2166
2173
|
body?: unknown;
|
|
2167
2174
|
bodySize?: number;
|
|
2175
|
+
/** The bounded stream observer omitted a suffix before redaction/processing. */
|
|
2176
|
+
sourceTruncated?: boolean;
|
|
2168
2177
|
contentType?: string;
|
|
2169
2178
|
responseStatus?: number;
|
|
2170
2179
|
durationMs?: number;
|
|
@@ -2335,6 +2344,8 @@ export type SSEContentBlock = {
|
|
|
2335
2344
|
export type SSETelemetry = {
|
|
2336
2345
|
messageStopReceived: boolean;
|
|
2337
2346
|
firstUsefulOutputAt?: number;
|
|
2347
|
+
firstUsefulOutputEvent?: string;
|
|
2348
|
+
observationIncomplete?: boolean;
|
|
2338
2349
|
messageId: string;
|
|
2339
2350
|
model: string;
|
|
2340
2351
|
usage: {
|
|
@@ -2358,6 +2369,7 @@ export type SSETelemetry = {
|
|
|
2358
2369
|
/** Error carried as a terminal SSE `event: error`, if one was observed. */
|
|
2359
2370
|
streamErrorMessage?: string;
|
|
2360
2371
|
rawText?: string;
|
|
2372
|
+
rawTextTruncated?: boolean;
|
|
2361
2373
|
};
|
|
2362
2374
|
/** Terminal outcome of a response body after the HTTP headers were sent. */
|
|
2363
2375
|
export type StreamTerminalOutcome = {
|
|
@@ -2379,6 +2391,8 @@ export type StreamTerminalOutcomeTracker = {
|
|
|
2379
2391
|
export type TelemetryAccumulator = {
|
|
2380
2392
|
messageStopReceived: boolean;
|
|
2381
2393
|
firstUsefulOutputAt?: number;
|
|
2394
|
+
firstUsefulOutputEvent?: string;
|
|
2395
|
+
observationIncomplete?: boolean;
|
|
2382
2396
|
messageId: string;
|
|
2383
2397
|
model: string;
|
|
2384
2398
|
inputTokens: number;
|
|
@@ -44,6 +44,31 @@ export type ProxyRestartControlOptions = {
|
|
|
44
44
|
getInstalledVersion: () => Promise<string | undefined>;
|
|
45
45
|
isUpdatePending: () => boolean;
|
|
46
46
|
getStatus: () => Promise<unknown>;
|
|
47
|
+
/** Current supervisor process evidence; never infer it from a worker/plist. */
|
|
48
|
+
getTelemetry?: () => ProxyProcessTelemetrySnapshot;
|
|
47
49
|
/** Report control-server errors without stopping the serving listener. */
|
|
48
50
|
log?: (message: string) => void;
|
|
49
51
|
};
|
|
52
|
+
/** Small read-only process evidence, excluding environment variables and log content. */
|
|
53
|
+
export type ProxyProcessTelemetrySnapshot = {
|
|
54
|
+
pid: number;
|
|
55
|
+
checkedAt: string;
|
|
56
|
+
configuredSink: "otel" | "file";
|
|
57
|
+
lifecycleSink: string;
|
|
58
|
+
otelInitialized: boolean;
|
|
59
|
+
stdio: {
|
|
60
|
+
stdout: "file" | "non_file" | "unavailable";
|
|
61
|
+
stderr: "file" | "non_file" | "unavailable";
|
|
62
|
+
};
|
|
63
|
+
exportDropped: number;
|
|
64
|
+
exportUnconfirmed: number;
|
|
65
|
+
};
|
|
66
|
+
export type ProxySupervisorTelemetry = {
|
|
67
|
+
status: "available";
|
|
68
|
+
process: ProxyProcessTelemetrySnapshot;
|
|
69
|
+
} | {
|
|
70
|
+
status: "unavailable";
|
|
71
|
+
reason: string;
|
|
72
|
+
} | {
|
|
73
|
+
status: "not_applicable";
|
|
74
|
+
};
|
|
@@ -47,7 +47,7 @@ const SECRET_PATTERN = new RegExp(
|
|
|
47
47
|
// Bare tokens by known prefix (e.g. `sk-abc…`, `r8_xyz…`)
|
|
48
48
|
`|\\b(?:${PREFIX_PATTERN})[_\\-][A-Za-z0-9_\\-\\.]{8,}\\b` +
|
|
49
49
|
// Generic key=value pairs (URL params, JSON bodies, header dumps)
|
|
50
|
-
"|\\b(?:api[_-]?key|access[_-]?token|secret[_-]?key|refresh[_-]?token)
|
|
50
|
+
"|\\b(?:api[_-]?key|access[_-]?token|secret[_-]?key|refresh[_-]?token)['\"]?\\s*[:=]\\s*['\"]?[^\\s,;'\"&]+", "gi");
|
|
51
51
|
/** Header names that should always be redacted regardless of value shape. */
|
|
52
52
|
const SENSITIVE_HEADER_NAMES = [
|
|
53
53
|
"authorization",
|
package/dist/utils/timeout.d.ts
CHANGED
|
@@ -5,10 +5,21 @@
|
|
|
5
5
|
* Supports multiple time formats: milliseconds, seconds, minutes, hours.
|
|
6
6
|
*/
|
|
7
7
|
import type { TimeoutConfig, TimeoutResult } from "../types/index.js";
|
|
8
|
+
import { TimeoutError as BaseTimeoutError } from "./async/withTimeout.js";
|
|
8
9
|
/**
|
|
9
|
-
* Custom error class for timeout operations
|
|
10
|
+
* Custom error class for timeout operations.
|
|
11
|
+
*
|
|
12
|
+
* Extends the base {@link BaseTimeoutError} (from `./async/withTimeout.js`)
|
|
13
|
+
* rather than `Error` directly, merging what were two independent internal
|
|
14
|
+
* `TimeoutError` classes onto one prototype chain — an `instanceof` check
|
|
15
|
+
* against either module's `TimeoutError` now recognizes errors thrown by
|
|
16
|
+
* this class. Constructor signature, `.name`, and the extra
|
|
17
|
+
* `timeout`/`provider`/`operation` fields are unchanged so all 15 existing
|
|
18
|
+
* `instanceof TimeoutError` call sites keep working exactly as before;
|
|
19
|
+
* `super(message, timeout)` additionally populates the base class's
|
|
20
|
+
* `timeoutMs` field (equal to `timeout`) as a byproduct.
|
|
10
21
|
*/
|
|
11
|
-
export declare class TimeoutError extends
|
|
22
|
+
export declare class TimeoutError extends BaseTimeoutError {
|
|
12
23
|
readonly timeout: number;
|
|
13
24
|
readonly provider?: string | undefined;
|
|
14
25
|
readonly operation?: "generate" | "stream" | undefined;
|
package/dist/utils/timeout.js
CHANGED
|
@@ -4,15 +4,26 @@
|
|
|
4
4
|
* Provides flexible timeout parsing and error handling for AI operations.
|
|
5
5
|
* Supports multiple time formats: milliseconds, seconds, minutes, hours.
|
|
6
6
|
*/
|
|
7
|
+
import { TimeoutError as BaseTimeoutError } from "./async/withTimeout.js";
|
|
7
8
|
/**
|
|
8
|
-
* Custom error class for timeout operations
|
|
9
|
+
* Custom error class for timeout operations.
|
|
10
|
+
*
|
|
11
|
+
* Extends the base {@link BaseTimeoutError} (from `./async/withTimeout.js`)
|
|
12
|
+
* rather than `Error` directly, merging what were two independent internal
|
|
13
|
+
* `TimeoutError` classes onto one prototype chain — an `instanceof` check
|
|
14
|
+
* against either module's `TimeoutError` now recognizes errors thrown by
|
|
15
|
+
* this class. Constructor signature, `.name`, and the extra
|
|
16
|
+
* `timeout`/`provider`/`operation` fields are unchanged so all 15 existing
|
|
17
|
+
* `instanceof TimeoutError` call sites keep working exactly as before;
|
|
18
|
+
* `super(message, timeout)` additionally populates the base class's
|
|
19
|
+
* `timeoutMs` field (equal to `timeout`) as a byproduct.
|
|
9
20
|
*/
|
|
10
|
-
export class TimeoutError extends
|
|
21
|
+
export class TimeoutError extends BaseTimeoutError {
|
|
11
22
|
timeout;
|
|
12
23
|
provider;
|
|
13
24
|
operation;
|
|
14
25
|
constructor(message, timeout, provider, operation) {
|
|
15
|
-
super(message);
|
|
26
|
+
super(message, timeout);
|
|
16
27
|
this.timeout = timeout;
|
|
17
28
|
this.provider = provider;
|
|
18
29
|
this.operation = operation;
|