@juspay/neurolink 10.0.1 → 10.1.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/CHANGELOG.md +6 -0
- package/dist/browser/neurolink.min.js +305 -305
- package/dist/cli/commands/proxyReplay.d.ts +3 -0
- package/dist/cli/commands/proxyReplay.js +175 -0
- package/dist/cli/parser.js +3 -1
- package/dist/lib/proxy/proxyReplay.d.ts +10 -0
- package/dist/lib/proxy/proxyReplay.js +765 -0
- package/dist/lib/proxy/requestLogger.d.ts +13 -0
- package/dist/lib/proxy/requestLogger.js +13 -0
- package/dist/lib/server/routes/claudeProxyRoutes.d.ts +1 -0
- package/dist/lib/server/routes/claudeProxyRoutes.js +9 -2
- package/dist/lib/types/cli.d.ts +16 -0
- package/dist/lib/types/proxy.d.ts +121 -0
- package/dist/proxy/proxyReplay.d.ts +10 -0
- package/dist/proxy/proxyReplay.js +764 -0
- package/dist/proxy/requestLogger.d.ts +13 -0
- package/dist/proxy/requestLogger.js +13 -0
- package/dist/server/routes/claudeProxyRoutes.d.ts +1 -0
- package/dist/server/routes/claudeProxyRoutes.js +9 -2
- package/dist/types/cli.d.ts +16 -0
- package/dist/types/proxy.d.ts +121 -0
- package/package.json +1 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { compareProxyReplayBundle, exportProxyReplayBundle, isValidProxyReplayHeaderName, readProxyReplayBundle, writeProxyReplayDocument, } from "../../lib/proxy/proxyReplay.js";
|
|
5
|
+
import { logger } from "../../lib/utils/logger.js";
|
|
6
|
+
const ACTIONS = ["export", "compare"];
|
|
7
|
+
const MAX_BODY_OVERRIDE_BYTES = 16 * 1024 * 1024;
|
|
8
|
+
function requiredValue(value, option) {
|
|
9
|
+
if (!value) {
|
|
10
|
+
throw new Error(`${option} is required for this action`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function resolveHeaderValues(mappings) {
|
|
15
|
+
const values = {};
|
|
16
|
+
for (const mapping of mappings ?? []) {
|
|
17
|
+
const separator = mapping.indexOf("=");
|
|
18
|
+
if (separator <= 0 || separator === mapping.length - 1) {
|
|
19
|
+
throw new Error(`Invalid --header-env value "${mapping}". Use header-name=ENV_VAR.`);
|
|
20
|
+
}
|
|
21
|
+
const header = mapping.slice(0, separator).trim().toLowerCase();
|
|
22
|
+
if (!isValidProxyReplayHeaderName(header)) {
|
|
23
|
+
throw new Error(`Invalid header name in --header-env: ${header}`);
|
|
24
|
+
}
|
|
25
|
+
const environmentName = mapping.slice(separator + 1).trim();
|
|
26
|
+
const value = process.env[environmentName];
|
|
27
|
+
if (!value) {
|
|
28
|
+
throw new Error(`Environment variable ${environmentName} is empty or unset for header ${header}`);
|
|
29
|
+
}
|
|
30
|
+
values[header] = value;
|
|
31
|
+
}
|
|
32
|
+
return values;
|
|
33
|
+
}
|
|
34
|
+
async function readBodyOverride(filePath) {
|
|
35
|
+
if (!filePath) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const resolvedPath = resolve(filePath);
|
|
39
|
+
const info = await stat(resolvedPath);
|
|
40
|
+
if (!info.isFile() || info.size > MAX_BODY_OVERRIDE_BYTES) {
|
|
41
|
+
throw new Error(`Body override must be a regular file no larger than ${MAX_BODY_OVERRIDE_BYTES} bytes`);
|
|
42
|
+
}
|
|
43
|
+
return readFile(resolvedPath, "utf8");
|
|
44
|
+
}
|
|
45
|
+
function printResult(format, value) {
|
|
46
|
+
logger.always(format === "json"
|
|
47
|
+
? JSON.stringify(value)
|
|
48
|
+
: Object.entries(value)
|
|
49
|
+
.map(([key, child]) => `${key}: ${String(child)}`)
|
|
50
|
+
.join("\n"));
|
|
51
|
+
}
|
|
52
|
+
export const proxyReplayCommand = {
|
|
53
|
+
command: "replay <action>",
|
|
54
|
+
describe: "Export captured proxy requests or compare them directly upstream",
|
|
55
|
+
builder: (yargs) => yargs
|
|
56
|
+
.positional("action", {
|
|
57
|
+
type: "string",
|
|
58
|
+
choices: [...ACTIONS],
|
|
59
|
+
describe: "Replay action",
|
|
60
|
+
})
|
|
61
|
+
.option("request-id", {
|
|
62
|
+
type: "string",
|
|
63
|
+
alias: "requestId",
|
|
64
|
+
description: "Captured proxy request ID to export",
|
|
65
|
+
})
|
|
66
|
+
.option("attempt", {
|
|
67
|
+
type: "number",
|
|
68
|
+
description: "Specific upstream attempt to reconstruct",
|
|
69
|
+
})
|
|
70
|
+
.option("logs-dir", {
|
|
71
|
+
type: "string",
|
|
72
|
+
alias: "logsDir",
|
|
73
|
+
description: "Proxy log directory",
|
|
74
|
+
})
|
|
75
|
+
.option("bundle", {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "Replay bundle to compare directly upstream",
|
|
78
|
+
})
|
|
79
|
+
.option("output", {
|
|
80
|
+
type: "string",
|
|
81
|
+
alias: "o",
|
|
82
|
+
description: "Destination JSON file",
|
|
83
|
+
})
|
|
84
|
+
.option("execute", {
|
|
85
|
+
type: "boolean",
|
|
86
|
+
default: false,
|
|
87
|
+
description: "Explicitly permit the direct upstream request",
|
|
88
|
+
})
|
|
89
|
+
.option("header-env", {
|
|
90
|
+
type: "string",
|
|
91
|
+
array: true,
|
|
92
|
+
alias: "headerEnv",
|
|
93
|
+
description: "Inject a redacted header from an environment variable (header=ENV_VAR)",
|
|
94
|
+
})
|
|
95
|
+
.option("body-file", {
|
|
96
|
+
type: "string",
|
|
97
|
+
alias: "bodyFile",
|
|
98
|
+
description: "Complete request body override for redacted/truncated captures",
|
|
99
|
+
})
|
|
100
|
+
.option("url", {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "Explicit direct endpoint override",
|
|
103
|
+
})
|
|
104
|
+
.option("timeout-ms", {
|
|
105
|
+
type: "number",
|
|
106
|
+
alias: "timeoutMs",
|
|
107
|
+
default: 120_000,
|
|
108
|
+
description: "Direct request timeout",
|
|
109
|
+
})
|
|
110
|
+
.option("format", {
|
|
111
|
+
type: "string",
|
|
112
|
+
choices: ["text", "json"],
|
|
113
|
+
default: "text",
|
|
114
|
+
description: "Command summary format",
|
|
115
|
+
})
|
|
116
|
+
.option("quiet", {
|
|
117
|
+
type: "boolean",
|
|
118
|
+
alias: "q",
|
|
119
|
+
default: false,
|
|
120
|
+
description: "Suppress non-essential output",
|
|
121
|
+
})
|
|
122
|
+
.example("neurolink proxy replay export --request-id req-123 --output replay.json", "Create a deterministic offline replay bundle")
|
|
123
|
+
.example("neurolink proxy replay compare --bundle replay.json --output comparison.json --execute --header-env authorization=ANTHROPIC_AUTHORIZATION", "Execute the captured upstream request with an environment-backed credential"),
|
|
124
|
+
handler: async (argv) => {
|
|
125
|
+
try {
|
|
126
|
+
const output = requiredValue(argv.output, "--output");
|
|
127
|
+
if (argv.action === "export") {
|
|
128
|
+
const requestId = requiredValue(argv.requestId, "--request-id");
|
|
129
|
+
const bundle = await exportProxyReplayBundle({
|
|
130
|
+
requestId,
|
|
131
|
+
logsDir: argv.logsDir,
|
|
132
|
+
attempt: argv.attempt,
|
|
133
|
+
});
|
|
134
|
+
const written = await writeProxyReplayDocument(output, bundle);
|
|
135
|
+
if (!argv.quiet) {
|
|
136
|
+
printResult(argv.format ?? "text", {
|
|
137
|
+
action: "export",
|
|
138
|
+
output: written.path,
|
|
139
|
+
sha256: written.sha256,
|
|
140
|
+
captures: bundle.completeness.captures,
|
|
141
|
+
replayable: bundle.completeness.replayable,
|
|
142
|
+
blockers: bundle.completeness.blockers.join(",") || "none",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const bundlePath = requiredValue(argv.bundle, "--bundle");
|
|
148
|
+
const bundle = await readProxyReplayBundle(bundlePath);
|
|
149
|
+
const comparison = await compareProxyReplayBundle(bundle, {
|
|
150
|
+
execute: argv.execute === true,
|
|
151
|
+
headerValues: resolveHeaderValues(argv.headerEnv),
|
|
152
|
+
bodyOverride: await readBodyOverride(argv.bodyFile),
|
|
153
|
+
urlOverride: argv.url,
|
|
154
|
+
timeoutMs: argv.timeoutMs,
|
|
155
|
+
});
|
|
156
|
+
const written = await writeProxyReplayDocument(output, comparison);
|
|
157
|
+
if (!argv.quiet) {
|
|
158
|
+
printResult(argv.format ?? "text", {
|
|
159
|
+
action: "compare",
|
|
160
|
+
output: written.path,
|
|
161
|
+
sha256: written.sha256,
|
|
162
|
+
status: comparison.direct.status,
|
|
163
|
+
statusMatches: comparison.comparison.statusMatches,
|
|
164
|
+
bodyHashMatches: comparison.comparison.bodyHashMatches,
|
|
165
|
+
jsonShapeMatches: comparison.comparison.jsonShapeMatches,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
171
|
+
process.exitCode = 1;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
//# sourceMappingURL=proxyReplay.js.map
|
package/dist/cli/parser.js
CHANGED
|
@@ -15,6 +15,7 @@ import { ObservabilityCommandFactory } from "./commands/observability.js";
|
|
|
15
15
|
import { TelemetryCommandFactory } from "./commands/telemetry.js";
|
|
16
16
|
import { proxyStartCommand, proxyStatusCommand, proxyTelemetryCommand, proxySetupCommand, proxyGuardCommand, proxyInstallCommand, proxyUninstallCommand, } from "./commands/proxy.js";
|
|
17
17
|
import { proxyAnalyzeCommand } from "./commands/proxyAnalyze.js";
|
|
18
|
+
import { proxyReplayCommand } from "./commands/proxyReplay.js";
|
|
18
19
|
import { EvaluateCommandFactory } from "./commands/evaluate.js";
|
|
19
20
|
import { TaskCommandFactory } from "./commands/task.js";
|
|
20
21
|
import { AutoresearchCommandFactory } from "./commands/autoresearch.js";
|
|
@@ -202,12 +203,13 @@ export function initializeCliParser() {
|
|
|
202
203
|
.command(proxyStartCommand)
|
|
203
204
|
.command(proxyStatusCommand)
|
|
204
205
|
.command(proxyAnalyzeCommand)
|
|
206
|
+
.command(proxyReplayCommand)
|
|
205
207
|
.command(proxyTelemetryCommand)
|
|
206
208
|
.command(proxySetupCommand)
|
|
207
209
|
.command(proxyGuardCommand)
|
|
208
210
|
.command(proxyInstallCommand)
|
|
209
211
|
.command(proxyUninstallCommand)
|
|
210
|
-
.demandCommand(1, "Please specify a proxy subcommand: start, status, analyze, telemetry <setup|start|stop|status|logs|import-dashboard>, setup, guard, install, or uninstall"),
|
|
212
|
+
.demandCommand(1, "Please specify a proxy subcommand: start, status, analyze, replay <export|compare>, telemetry <setup|start|stop|status|logs|import-dashboard>, setup, guard, install, or uninstall"),
|
|
211
213
|
handler: () => { },
|
|
212
214
|
})
|
|
213
215
|
// Evaluate Command Group - Using EvaluateCommandFactory
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CompareProxyReplayOptions, ExportProxyReplayOptions, ProxyReplayBundle, ProxyReplayComparison } from "../types/index.js";
|
|
2
|
+
export declare function isValidProxyReplayHeaderName(name: string): boolean;
|
|
3
|
+
export declare function serializeProxyReplayDocument(value: unknown): string;
|
|
4
|
+
export declare function exportProxyReplayBundle(options: ExportProxyReplayOptions): Promise<ProxyReplayBundle>;
|
|
5
|
+
export declare function writeProxyReplayDocument(outputPath: string, document: unknown): Promise<{
|
|
6
|
+
path: string;
|
|
7
|
+
sha256: string;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function readProxyReplayBundle(bundlePath: string): Promise<ProxyReplayBundle>;
|
|
10
|
+
export declare function compareProxyReplayBundle(bundle: ProxyReplayBundle, options: CompareProxyReplayOptions): Promise<ProxyReplayComparison>;
|