@uvl000/claude-trace 3.0.13-workflowexp.1
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/README.md +477 -0
- package/README.zh-CN.md +477 -0
- package/dist/adapt/api-format.d.ts +41 -0
- package/dist/adapt/api-format.d.ts.map +1 -0
- package/dist/adapt/api-format.js +132 -0
- package/dist/adapt/api-format.js.map +1 -0
- package/dist/adapt/openai-adapter.d.ts +123 -0
- package/dist/adapt/openai-adapter.d.ts.map +1 -0
- package/dist/adapt/openai-adapter.js +628 -0
- package/dist/adapt/openai-adapter.js.map +1 -0
- package/dist/cli/cli-common.d.ts +73 -0
- package/dist/cli/cli-common.d.ts.map +1 -0
- package/dist/cli/cli-common.js +189 -0
- package/dist/cli/cli-common.js.map +1 -0
- package/dist/cli/cli.d.ts +23 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +175 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/codex-cli.d.ts +24 -0
- package/dist/cli/codex-cli.d.ts.map +1 -0
- package/dist/cli/codex-cli.js +146 -0
- package/dist/cli/codex-cli.js.map +1 -0
- package/dist/cli/opencode-cli.d.ts +23 -0
- package/dist/cli/opencode-cli.d.ts.map +1 -0
- package/dist/cli/opencode-cli.js +156 -0
- package/dist/cli/opencode-cli.js.map +1 -0
- package/dist/cli/trace-runner.d.ts +21 -0
- package/dist/cli/trace-runner.d.ts.map +1 -0
- package/dist/cli/trace-runner.js +282 -0
- package/dist/cli/trace-runner.js.map +1 -0
- package/dist/cli/validate-trace.d.ts +30 -0
- package/dist/cli/validate-trace.d.ts.map +1 -0
- package/dist/cli/validate-trace.js +230 -0
- package/dist/cli/validate-trace.js.map +1 -0
- package/dist/cli/verify-offline-bundle.d.ts +13 -0
- package/dist/cli/verify-offline-bundle.d.ts.map +1 -0
- package/dist/cli/verify-offline-bundle.js +89 -0
- package/dist/cli/verify-offline-bundle.js.map +1 -0
- package/dist/cli/vibe-coding-proxy-cli.d.ts +12 -0
- package/dist/cli/vibe-coding-proxy-cli.d.ts.map +1 -0
- package/dist/cli/vibe-coding-proxy-cli.js +218 -0
- package/dist/cli/vibe-coding-proxy-cli.js.map +1 -0
- package/dist/config/claude-config-overlay.d.ts +69 -0
- package/dist/config/claude-config-overlay.d.ts.map +1 -0
- package/dist/config/claude-config-overlay.js +229 -0
- package/dist/config/claude-config-overlay.js.map +1 -0
- package/dist/config/codex-config-overlay.d.ts +122 -0
- package/dist/config/codex-config-overlay.d.ts.map +1 -0
- package/dist/config/codex-config-overlay.js +352 -0
- package/dist/config/codex-config-overlay.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/intercept/forward-proxy.d.ts +77 -0
- package/dist/intercept/forward-proxy.d.ts.map +1 -0
- package/dist/intercept/forward-proxy.js +368 -0
- package/dist/intercept/forward-proxy.js.map +1 -0
- package/dist/intercept/header-redaction.d.ts +7 -0
- package/dist/intercept/header-redaction.d.ts.map +1 -0
- package/dist/intercept/header-redaction.js +28 -0
- package/dist/intercept/header-redaction.js.map +1 -0
- package/dist/intercept/interceptor-loader.js +26 -0
- package/dist/intercept/interceptor.d.ts +164 -0
- package/dist/intercept/interceptor.d.ts.map +1 -0
- package/dist/intercept/interceptor.js +496 -0
- package/dist/intercept/interceptor.js.map +1 -0
- package/dist/intercept/mitm-cert.d.ts +53 -0
- package/dist/intercept/mitm-cert.d.ts.map +1 -0
- package/dist/intercept/mitm-cert.js +259 -0
- package/dist/intercept/mitm-cert.js.map +1 -0
- package/dist/intercept/proxy-log-writer.d.ts +77 -0
- package/dist/intercept/proxy-log-writer.d.ts.map +1 -0
- package/dist/intercept/proxy-log-writer.js +323 -0
- package/dist/intercept/proxy-log-writer.js.map +1 -0
- package/dist/intercept/proxy-targets.d.ts +69 -0
- package/dist/intercept/proxy-targets.d.ts.map +1 -0
- package/dist/intercept/proxy-targets.js +129 -0
- package/dist/intercept/proxy-targets.js.map +1 -0
- package/dist/intercept/reverse-proxy.d.ts +191 -0
- package/dist/intercept/reverse-proxy.d.ts.map +1 -0
- package/dist/intercept/reverse-proxy.js +807 -0
- package/dist/intercept/reverse-proxy.js.map +1 -0
- package/dist/intercept/token-extractor.js +28 -0
- package/dist/report/html-generator.d.ts +96 -0
- package/dist/report/html-generator.d.ts.map +1 -0
- package/dist/report/html-generator.js +225 -0
- package/dist/report/html-generator.js.map +1 -0
- package/dist/report/index-generator.d.ts +89 -0
- package/dist/report/index-generator.d.ts.map +1 -0
- package/dist/report/index-generator.js +319 -0
- package/dist/report/index-generator.js.map +1 -0
- package/dist/report/shared-conversation-processor.d.ts +200 -0
- package/dist/report/shared-conversation-processor.d.ts.map +1 -0
- package/dist/report/shared-conversation-processor.js +720 -0
- package/dist/report/shared-conversation-processor.js.map +1 -0
- package/dist/routing/codex-routing.d.ts +40 -0
- package/dist/routing/codex-routing.d.ts.map +1 -0
- package/dist/routing/codex-routing.js +96 -0
- package/dist/routing/codex-routing.js.map +1 -0
- package/dist/routing/proxy-routing.d.ts +48 -0
- package/dist/routing/proxy-routing.d.ts.map +1 -0
- package/dist/routing/proxy-routing.js +113 -0
- package/dist/routing/proxy-routing.js.map +1 -0
- package/dist/tools/binary-utils.d.ts +29 -0
- package/dist/tools/binary-utils.d.ts.map +1 -0
- package/dist/tools/binary-utils.js +134 -0
- package/dist/tools/binary-utils.js.map +1 -0
- package/dist/tools/claude.d.ts +61 -0
- package/dist/tools/claude.d.ts.map +1 -0
- package/dist/tools/claude.js +449 -0
- package/dist/tools/claude.js.map +1 -0
- package/dist/tools/codex.d.ts +70 -0
- package/dist/tools/codex.d.ts.map +1 -0
- package/dist/tools/codex.js +492 -0
- package/dist/tools/codex.js.map +1 -0
- package/dist/tools/opencode.d.ts +85 -0
- package/dist/tools/opencode.d.ts.map +1 -0
- package/dist/tools/opencode.js +401 -0
- package/dist/tools/opencode.js.map +1 -0
- package/dist/tools/types.d.ts +91 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +11 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types.d.ts +158 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/frontend/dist/index.global.js +1551 -0
- package/frontend/dist/styles.css +1027 -0
- package/frontend/template.html +19 -0
- package/node_modules/agent-base/LICENSE +22 -0
- package/node_modules/agent-base/README.md +69 -0
- package/node_modules/agent-base/dist/helpers.d.ts +15 -0
- package/node_modules/agent-base/dist/helpers.d.ts.map +1 -0
- package/node_modules/agent-base/dist/helpers.js +66 -0
- package/node_modules/agent-base/dist/helpers.js.map +1 -0
- package/node_modules/agent-base/dist/index.d.ts +41 -0
- package/node_modules/agent-base/dist/index.d.ts.map +1 -0
- package/node_modules/agent-base/dist/index.js +178 -0
- package/node_modules/agent-base/dist/index.js.map +1 -0
- package/node_modules/agent-base/package.json +46 -0
- package/node_modules/debug/LICENSE +20 -0
- package/node_modules/debug/README.md +481 -0
- package/node_modules/debug/package.json +64 -0
- package/node_modules/debug/src/browser.js +272 -0
- package/node_modules/debug/src/common.js +292 -0
- package/node_modules/debug/src/index.js +10 -0
- package/node_modules/debug/src/node.js +263 -0
- package/node_modules/http-proxy-agent/LICENSE +22 -0
- package/node_modules/http-proxy-agent/README.md +44 -0
- package/node_modules/http-proxy-agent/dist/index.d.ts +44 -0
- package/node_modules/http-proxy-agent/dist/index.d.ts.map +1 -0
- package/node_modules/http-proxy-agent/dist/index.js +148 -0
- package/node_modules/http-proxy-agent/dist/index.js.map +1 -0
- package/node_modules/http-proxy-agent/package.json +47 -0
- package/node_modules/https-proxy-agent/LICENSE +22 -0
- package/node_modules/https-proxy-agent/README.md +70 -0
- package/node_modules/https-proxy-agent/dist/index.d.ts +47 -0
- package/node_modules/https-proxy-agent/dist/index.d.ts.map +1 -0
- package/node_modules/https-proxy-agent/dist/index.js +180 -0
- package/node_modules/https-proxy-agent/dist/index.js.map +1 -0
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.d.ts +15 -0
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.d.ts.map +1 -0
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.js +101 -0
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.js.map +1 -0
- package/node_modules/https-proxy-agent/package.json +50 -0
- package/node_modules/ms/index.js +162 -0
- package/node_modules/ms/license.md +21 -0
- package/node_modules/ms/package.json +38 -0
- package/node_modules/ms/readme.md +59 -0
- package/node_modules/node-forge/LICENSE +331 -0
- package/node_modules/node-forge/README.md +2071 -0
- package/node_modules/node-forge/dist/forge.all.min.js +2 -0
- package/node_modules/node-forge/dist/forge.all.min.js.map +1 -0
- package/node_modules/node-forge/dist/forge.min.js +2 -0
- package/node_modules/node-forge/dist/forge.min.js.map +1 -0
- package/node_modules/node-forge/dist/prime.worker.min.js +2 -0
- package/node_modules/node-forge/dist/prime.worker.min.js.map +1 -0
- package/node_modules/node-forge/flash/swf/SocketPool.swf +0 -0
- package/node_modules/node-forge/lib/aes.js +1091 -0
- package/node_modules/node-forge/lib/aesCipherSuites.js +282 -0
- package/node_modules/node-forge/lib/asn1-validator.js +91 -0
- package/node_modules/node-forge/lib/asn1.js +1503 -0
- package/node_modules/node-forge/lib/baseN.js +186 -0
- package/node_modules/node-forge/lib/cipher.js +230 -0
- package/node_modules/node-forge/lib/cipherModes.js +999 -0
- package/node_modules/node-forge/lib/des.js +496 -0
- package/node_modules/node-forge/lib/ed25519.js +1091 -0
- package/node_modules/node-forge/lib/forge.js +13 -0
- package/node_modules/node-forge/lib/form.js +149 -0
- package/node_modules/node-forge/lib/hmac.js +146 -0
- package/node_modules/node-forge/lib/http.js +1346 -0
- package/node_modules/node-forge/lib/index.all.js +16 -0
- package/node_modules/node-forge/lib/index.js +33 -0
- package/node_modules/node-forge/lib/jsbn.js +1294 -0
- package/node_modules/node-forge/lib/kem.js +168 -0
- package/node_modules/node-forge/lib/log.js +319 -0
- package/node_modules/node-forge/lib/md.all.js +13 -0
- package/node_modules/node-forge/lib/md.js +11 -0
- package/node_modules/node-forge/lib/md5.js +289 -0
- package/node_modules/node-forge/lib/mgf.js +12 -0
- package/node_modules/node-forge/lib/mgf1.js +57 -0
- package/node_modules/node-forge/lib/oids.js +180 -0
- package/node_modules/node-forge/lib/pbe.js +1023 -0
- package/node_modules/node-forge/lib/pbkdf2.js +211 -0
- package/node_modules/node-forge/lib/pem.js +237 -0
- package/node_modules/node-forge/lib/pkcs1.js +276 -0
- package/node_modules/node-forge/lib/pkcs12.js +1078 -0
- package/node_modules/node-forge/lib/pkcs7.js +1260 -0
- package/node_modules/node-forge/lib/pkcs7asn1.js +410 -0
- package/node_modules/node-forge/lib/pki.js +102 -0
- package/node_modules/node-forge/lib/prime.js +297 -0
- package/node_modules/node-forge/lib/prime.worker.js +168 -0
- package/node_modules/node-forge/lib/prng.js +419 -0
- package/node_modules/node-forge/lib/pss.js +241 -0
- package/node_modules/node-forge/lib/random.js +191 -0
- package/node_modules/node-forge/lib/rc2.js +410 -0
- package/node_modules/node-forge/lib/rsa.js +1968 -0
- package/node_modules/node-forge/lib/sha1.js +319 -0
- package/node_modules/node-forge/lib/sha256.js +327 -0
- package/node_modules/node-forge/lib/sha512.js +561 -0
- package/node_modules/node-forge/lib/socket.js +287 -0
- package/node_modules/node-forge/lib/ssh.js +236 -0
- package/node_modules/node-forge/lib/tls.js +4282 -0
- package/node_modules/node-forge/lib/tlssocket.js +249 -0
- package/node_modules/node-forge/lib/util.js +2652 -0
- package/node_modules/node-forge/lib/x509.js +3251 -0
- package/node_modules/node-forge/lib/xhr.js +738 -0
- package/node_modules/node-forge/package.json +123 -0
- package/node_modules/smol-toml/LICENSE +24 -0
- package/node_modules/smol-toml/README.md +239 -0
- package/node_modules/smol-toml/dist/date.d.ts +41 -0
- package/node_modules/smol-toml/dist/date.js +127 -0
- package/node_modules/smol-toml/dist/error.d.ts +38 -0
- package/node_modules/smol-toml/dist/error.js +63 -0
- package/node_modules/smol-toml/dist/extract.d.ts +30 -0
- package/node_modules/smol-toml/dist/extract.js +100 -0
- package/node_modules/smol-toml/dist/index.cjs +897 -0
- package/node_modules/smol-toml/dist/index.d.ts +43 -0
- package/node_modules/smol-toml/dist/index.js +33 -0
- package/node_modules/smol-toml/dist/parse.d.ts +37 -0
- package/node_modules/smol-toml/dist/parse.js +148 -0
- package/node_modules/smol-toml/dist/primitive.d.ts +31 -0
- package/node_modules/smol-toml/dist/primitive.js +179 -0
- package/node_modules/smol-toml/dist/stringify.d.ts +31 -0
- package/node_modules/smol-toml/dist/stringify.js +167 -0
- package/node_modules/smol-toml/dist/struct.d.ts +32 -0
- package/node_modules/smol-toml/dist/struct.js +184 -0
- package/node_modules/smol-toml/dist/util.d.ts +42 -0
- package/node_modules/smol-toml/dist/util.js +111 -0
- package/node_modules/smol-toml/package.json +54 -0
- package/offline/bin/claude-trace +6 -0
- package/offline/bin/claude-wrapper +13 -0
- package/offline/bin/install-agent +14 -0
- package/offline/bin/validate-trace +6 -0
- package/offline/bin/verify-bundle +7 -0
- package/package.json +103 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file mitm-cert.ts
|
|
4
|
+
* @description Local CA and leaf certificate management for HTTPS MITM proxying.
|
|
5
|
+
*
|
|
6
|
+
* vibe-coding-proxy only decrypts HTTPS traffic for explicitly allowlisted model
|
|
7
|
+
* API hosts. This module creates a reusable local certificate authority and
|
|
8
|
+
* caches per-host leaf certificates signed by that CA. It never installs the CA
|
|
9
|
+
* into the system trust store; users must opt in by trusting the printed CA path.
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.MitmCertificateAuthority = void 0;
|
|
49
|
+
exports.defaultCaDir = defaultCaDir;
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const os = __importStar(require("os"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const crypto = __importStar(require("crypto"));
|
|
54
|
+
const node_forge_1 = __importDefault(require("node-forge"));
|
|
55
|
+
/**
|
|
56
|
+
* Creates, loads, and reuses local MITM certificates for allowlisted hosts.
|
|
57
|
+
*/
|
|
58
|
+
class MitmCertificateAuthority {
|
|
59
|
+
/**
|
|
60
|
+
* @param caDir - Directory used to persist CA and cached leaf certificates.
|
|
61
|
+
*/
|
|
62
|
+
constructor(caDir = defaultCaDir()) {
|
|
63
|
+
this.authority = null;
|
|
64
|
+
this.caDir = caDir;
|
|
65
|
+
this.caKeyPath = path.join(caDir, "ca.key.pem");
|
|
66
|
+
this.caCertPath = path.join(caDir, "ca.crt");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Ensure the local CA exists and return its user-facing paths.
|
|
70
|
+
* @returns CA certificate path and PEM content.
|
|
71
|
+
*/
|
|
72
|
+
ensureAuthority() {
|
|
73
|
+
const authority = this.loadOrCreateAuthority();
|
|
74
|
+
return {
|
|
75
|
+
certPath: this.caCertPath,
|
|
76
|
+
keyPath: this.caKeyPath,
|
|
77
|
+
certPem: authority.certPem,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Return a cached or newly generated leaf certificate for `hostname`.
|
|
82
|
+
* @param hostname - DNS name from the CONNECT target.
|
|
83
|
+
*/
|
|
84
|
+
getCertificateForHost(hostname) {
|
|
85
|
+
const normalizedHost = normalizeCertHost(hostname);
|
|
86
|
+
const certPath = path.join(this.caDir, `${safeFileName(normalizedHost)}.crt.pem`);
|
|
87
|
+
const keyPath = path.join(this.caDir, `${safeFileName(normalizedHost)}.key.pem`);
|
|
88
|
+
if (fs.existsSync(certPath) && fs.existsSync(keyPath)) {
|
|
89
|
+
const cachedCertPem = fs.readFileSync(certPath, "utf-8");
|
|
90
|
+
if (isLeafCertificateUsable(cachedCertPem)) {
|
|
91
|
+
return {
|
|
92
|
+
cert: cachedCertPem,
|
|
93
|
+
key: fs.readFileSync(keyPath, "utf-8"),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
fs.rmSync(certPath, { force: true });
|
|
97
|
+
fs.rmSync(keyPath, { force: true });
|
|
98
|
+
}
|
|
99
|
+
const authority = this.loadOrCreateAuthority();
|
|
100
|
+
const keys = node_forge_1.default.pki.rsa.generateKeyPair(2048);
|
|
101
|
+
const cert = node_forge_1.default.pki.createCertificate();
|
|
102
|
+
cert.publicKey = keys.publicKey;
|
|
103
|
+
cert.serialNumber = crypto.randomBytes(16).toString("hex");
|
|
104
|
+
cert.validity.notBefore = new Date(Date.now() - 60000);
|
|
105
|
+
cert.validity.notAfter = new Date();
|
|
106
|
+
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
|
|
107
|
+
const attrs = [
|
|
108
|
+
{ name: "commonName", value: normalizedHost },
|
|
109
|
+
{ name: "organizationName", value: "vibe-coding-proxy" },
|
|
110
|
+
];
|
|
111
|
+
cert.setSubject(attrs);
|
|
112
|
+
cert.setIssuer(authority.cert.subject.attributes);
|
|
113
|
+
cert.setExtensions([
|
|
114
|
+
{ name: "basicConstraints", cA: false, critical: true },
|
|
115
|
+
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true, critical: true },
|
|
116
|
+
{ name: "extKeyUsage", serverAuth: true },
|
|
117
|
+
{
|
|
118
|
+
name: "subjectAltName",
|
|
119
|
+
altNames: buildSubjectAltNames(normalizedHost),
|
|
120
|
+
},
|
|
121
|
+
{ name: "subjectKeyIdentifier" },
|
|
122
|
+
{
|
|
123
|
+
name: "authorityKeyIdentifier",
|
|
124
|
+
keyIdentifier: authority.cert.generateSubjectKeyIdentifier().getBytes(),
|
|
125
|
+
},
|
|
126
|
+
]);
|
|
127
|
+
cert.sign(authority.key, node_forge_1.default.md.sha256.create());
|
|
128
|
+
const pair = {
|
|
129
|
+
key: node_forge_1.default.pki.privateKeyToPem(keys.privateKey),
|
|
130
|
+
cert: node_forge_1.default.pki.certificateToPem(cert),
|
|
131
|
+
};
|
|
132
|
+
fs.writeFileSync(keyPath, pair.key, { mode: 0o600 });
|
|
133
|
+
fs.writeFileSync(certPath, pair.cert);
|
|
134
|
+
return pair;
|
|
135
|
+
}
|
|
136
|
+
/** Load persisted CA material or create it on first use. */
|
|
137
|
+
loadOrCreateAuthority() {
|
|
138
|
+
if (this.authority) {
|
|
139
|
+
return this.authority;
|
|
140
|
+
}
|
|
141
|
+
fs.mkdirSync(this.caDir, { recursive: true });
|
|
142
|
+
if (fs.existsSync(this.caKeyPath) && fs.existsSync(this.caCertPath)) {
|
|
143
|
+
const keyPem = fs.readFileSync(this.caKeyPath, "utf-8");
|
|
144
|
+
const certPem = fs.readFileSync(this.caCertPath, "utf-8");
|
|
145
|
+
if (isAuthorityCertificateUsable(certPem)) {
|
|
146
|
+
this.authority = {
|
|
147
|
+
key: node_forge_1.default.pki.privateKeyFromPem(keyPem),
|
|
148
|
+
cert: node_forge_1.default.pki.certificateFromPem(certPem),
|
|
149
|
+
certPem,
|
|
150
|
+
};
|
|
151
|
+
return this.authority;
|
|
152
|
+
}
|
|
153
|
+
fs.rmSync(this.caKeyPath, { force: true });
|
|
154
|
+
fs.rmSync(this.caCertPath, { force: true });
|
|
155
|
+
removeCachedLeafCertificates(this.caDir);
|
|
156
|
+
}
|
|
157
|
+
const keys = node_forge_1.default.pki.rsa.generateKeyPair(2048);
|
|
158
|
+
const cert = node_forge_1.default.pki.createCertificate();
|
|
159
|
+
cert.publicKey = keys.publicKey;
|
|
160
|
+
cert.serialNumber = crypto.randomBytes(16).toString("hex");
|
|
161
|
+
cert.validity.notBefore = new Date(Date.now() - 60000);
|
|
162
|
+
cert.validity.notAfter = new Date();
|
|
163
|
+
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10);
|
|
164
|
+
const attrs = [
|
|
165
|
+
{ name: "commonName", value: "vibe-coding-proxy Local CA" },
|
|
166
|
+
{ name: "organizationName", value: "vibe-coding-proxy" },
|
|
167
|
+
];
|
|
168
|
+
cert.setSubject(attrs);
|
|
169
|
+
cert.setIssuer(attrs);
|
|
170
|
+
cert.setExtensions([
|
|
171
|
+
{ name: "basicConstraints", cA: true, critical: true, pathLenConstraint: 1 },
|
|
172
|
+
{ name: "keyUsage", keyCertSign: true, digitalSignature: true, cRLSign: true, critical: true },
|
|
173
|
+
{ name: "subjectKeyIdentifier" },
|
|
174
|
+
{ name: "authorityKeyIdentifier", keyIdentifier: true },
|
|
175
|
+
]);
|
|
176
|
+
cert.sign(keys.privateKey, node_forge_1.default.md.sha256.create());
|
|
177
|
+
const keyPem = node_forge_1.default.pki.privateKeyToPem(keys.privateKey);
|
|
178
|
+
const certPem = node_forge_1.default.pki.certificateToPem(cert);
|
|
179
|
+
fs.writeFileSync(this.caKeyPath, keyPem, { mode: 0o600 });
|
|
180
|
+
fs.writeFileSync(this.caCertPath, certPem);
|
|
181
|
+
this.authority = { key: keys.privateKey, cert, certPem };
|
|
182
|
+
return this.authority;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.MitmCertificateAuthority = MitmCertificateAuthority;
|
|
186
|
+
/** @returns Default persistent CA directory under the user's home directory. */
|
|
187
|
+
function defaultCaDir() {
|
|
188
|
+
return path.join(os.homedir(), ".claude-trace", "vibe-coding-proxy-ca");
|
|
189
|
+
}
|
|
190
|
+
/** Normalize hostnames before certificate generation and cache lookup. */
|
|
191
|
+
function normalizeCertHost(hostname) {
|
|
192
|
+
return hostname.trim().replace(/^\[|\]$/g, "").toLowerCase();
|
|
193
|
+
}
|
|
194
|
+
/** Convert hostnames into filesystem-safe cache file stems. */
|
|
195
|
+
function safeFileName(hostname) {
|
|
196
|
+
return hostname.replace(/[^a-zA-Z0-9.-]/g, "_");
|
|
197
|
+
}
|
|
198
|
+
/** Return true when a PEM certificate is already expired. */
|
|
199
|
+
function isCertificateExpired(certPem, now = new Date()) {
|
|
200
|
+
const cert = node_forge_1.default.pki.certificateFromPem(certPem);
|
|
201
|
+
return cert.validity.notAfter.getTime() <= now.getTime();
|
|
202
|
+
}
|
|
203
|
+
/** Return true when a cached CA has the stricter MITM root extensions we expect. */
|
|
204
|
+
function isAuthorityCertificateUsable(certPem) {
|
|
205
|
+
try {
|
|
206
|
+
const cert = node_forge_1.default.pki.certificateFromPem(certPem);
|
|
207
|
+
return (!isCertificateExpired(certPem) &&
|
|
208
|
+
hasExtension(cert, "basicConstraints") &&
|
|
209
|
+
hasExtension(cert, "keyUsage") &&
|
|
210
|
+
hasExtension(cert, "subjectKeyIdentifier") &&
|
|
211
|
+
hasExtension(cert, "authorityKeyIdentifier"));
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/** Return true when a cached leaf certificate has SAN and chain-linking extensions. */
|
|
218
|
+
function isLeafCertificateUsable(certPem) {
|
|
219
|
+
try {
|
|
220
|
+
const cert = node_forge_1.default.pki.certificateFromPem(certPem);
|
|
221
|
+
return (!isCertificateExpired(certPem) &&
|
|
222
|
+
hasExtension(cert, "basicConstraints") &&
|
|
223
|
+
hasExtension(cert, "keyUsage") &&
|
|
224
|
+
hasExtension(cert, "extKeyUsage") &&
|
|
225
|
+
hasExtension(cert, "subjectAltName") &&
|
|
226
|
+
hasExtension(cert, "subjectKeyIdentifier") &&
|
|
227
|
+
hasExtension(cert, "authorityKeyIdentifier"));
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/** Check whether a parsed certificate contains an extension by name. */
|
|
234
|
+
function hasExtension(cert, name) {
|
|
235
|
+
return Boolean(cert.getExtension({ name }));
|
|
236
|
+
}
|
|
237
|
+
/** Remove cached leaf certificates after the signing CA has been rotated. */
|
|
238
|
+
function removeCachedLeafCertificates(caDir) {
|
|
239
|
+
for (const entry of fs.readdirSync(caDir)) {
|
|
240
|
+
if (entry === "ca.crt" || entry === "ca.key.pem") {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (entry.endsWith(".crt.pem") || entry.endsWith(".key.pem")) {
|
|
244
|
+
fs.rmSync(path.join(caDir, entry), { force: true });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/** Build subjectAltName entries for DNS names and IP literals. */
|
|
249
|
+
function buildSubjectAltNames(hostname) {
|
|
250
|
+
if (isIpAddress(hostname)) {
|
|
251
|
+
return [{ type: 7, ip: hostname }];
|
|
252
|
+
}
|
|
253
|
+
return [{ type: 2, value: hostname }];
|
|
254
|
+
}
|
|
255
|
+
/** @returns true when `hostname` is an IPv4 or IPv6 literal. */
|
|
256
|
+
function isIpAddress(hostname) {
|
|
257
|
+
return /^[0-9.]+$/.test(hostname) || hostname.includes(":");
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=mitm-cert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mitm-cert.js","sourceRoot":"","sources":["../../src/intercept/mitm-cert.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file proxy-log-writer.ts
|
|
3
|
+
* @description Shared JSONL/JSON/HTML logging for vibe-coding-proxy traffic.
|
|
4
|
+
*
|
|
5
|
+
* The forward proxy captures requests through a different protocol surface than
|
|
6
|
+
* the existing reverse proxy, but the viewer expects the same RawPair shape.
|
|
7
|
+
* This writer centralizes body decoding, sensitive header redaction, SSE parsing,
|
|
8
|
+
* and real-time HTML regeneration for the independent proxy service.
|
|
9
|
+
*/
|
|
10
|
+
import type { IncomingHttpHeaders } from "http";
|
|
11
|
+
/** Runtime options for {@link ProxyLogWriter}. */
|
|
12
|
+
export interface ProxyLogWriterConfig {
|
|
13
|
+
/** Directory for JSONL, JSON, and HTML log files. */
|
|
14
|
+
logDirectory?: string;
|
|
15
|
+
/** Base name for log files; defaults to a timestamped `log-...`. */
|
|
16
|
+
logBaseName?: string;
|
|
17
|
+
/** Include non-target traffic when the proxy asks to log metadata. */
|
|
18
|
+
includeAllRequests?: boolean;
|
|
19
|
+
/** Open the HTML report when the proxy stops. */
|
|
20
|
+
openBrowser?: boolean;
|
|
21
|
+
/** Log auth headers verbatim instead of redacting them. */
|
|
22
|
+
logSensitiveHeaders?: boolean;
|
|
23
|
+
/** Tool identifier embedded in report metadata. */
|
|
24
|
+
tool?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Files maintained by {@link ProxyLogWriter}. */
|
|
27
|
+
export interface ProxyLogPaths {
|
|
28
|
+
jsonl: string;
|
|
29
|
+
json: string;
|
|
30
|
+
html: string;
|
|
31
|
+
}
|
|
32
|
+
/** Captured HTTP exchange passed from the proxy core into the log writer. */
|
|
33
|
+
export interface ProxyExchange {
|
|
34
|
+
method: string;
|
|
35
|
+
url: string;
|
|
36
|
+
requestHeaders: IncomingHttpHeaders;
|
|
37
|
+
requestBody: Buffer;
|
|
38
|
+
statusCode: number;
|
|
39
|
+
responseHeaders: IncomingHttpHeaders;
|
|
40
|
+
responseChunks: Buffer[];
|
|
41
|
+
requestTimestamp: number;
|
|
42
|
+
responseTimestamp: number;
|
|
43
|
+
forceLog?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Persists forward-proxy traffic using the same artifact set as existing CLIs.
|
|
47
|
+
*/
|
|
48
|
+
export declare class ProxyLogWriter {
|
|
49
|
+
private readonly config;
|
|
50
|
+
private readonly paths;
|
|
51
|
+
private readonly htmlGenerator;
|
|
52
|
+
private readonly pairs;
|
|
53
|
+
/** @param config - Log location, redaction, and report options. */
|
|
54
|
+
constructor(config?: ProxyLogWriterConfig);
|
|
55
|
+
/** @returns Absolute paths for JSONL, JSON, and HTML artifacts. */
|
|
56
|
+
getPaths(): ProxyLogPaths;
|
|
57
|
+
/**
|
|
58
|
+
* Write a completed request/response exchange when it matches logging policy.
|
|
59
|
+
* @param exchange - Buffered HTTP request and response data.
|
|
60
|
+
*/
|
|
61
|
+
logExchange(exchange: ProxyExchange): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Write CONNECT tunnel metadata when body logging is unavailable.
|
|
64
|
+
* @param authority - CONNECT target such as `api.example.com:443`.
|
|
65
|
+
* @param note - Reason this is metadata-only.
|
|
66
|
+
*/
|
|
67
|
+
logConnectMetadata(authority: string, note: string): Promise<void>;
|
|
68
|
+
/** Print summary and optionally open the generated HTML report. */
|
|
69
|
+
stop(): void;
|
|
70
|
+
/** Redact sensitive headers unless explicitly disabled. */
|
|
71
|
+
private processHeaders;
|
|
72
|
+
/** Append one pair to JSONL, rewrite JSON, and regenerate HTML. */
|
|
73
|
+
private writePair;
|
|
74
|
+
/** Open an HTML file in the platform default browser. */
|
|
75
|
+
private openHtmlInBrowser;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=proxy-log-writer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-log-writer.d.ts","sourceRoot":"","sources":["../../src/intercept/proxy-log-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAgBhD,kDAAkD;AAClD,MAAM,WAAW,oBAAoB;IACpC,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2DAA2D;IAC3D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,mBAAmB,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,mBAAmB,CAAC;IACrC,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAKD;;GAEG;AACH,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmE;IAC1F,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAuB;IACrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IAEvC,mEAAmE;gBACvD,MAAM,GAAE,oBAAyB;IA4B7C,mEAAmE;IACnE,QAAQ,IAAI,aAAa;IAQzB;;;OAGG;IACG,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAkCzD;;;;OAIG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBxE,mEAAmE;IACnE,IAAI,IAAI,IAAI;IAOZ,2DAA2D;IAC3D,OAAO,CAAC,cAAc;IAItB,mEAAmE;YACrD,SAAS;IAWvB,yDAAyD;IACzD,OAAO,CAAC,iBAAiB;CAazB"}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file proxy-log-writer.ts
|
|
4
|
+
* @description Shared JSONL/JSON/HTML logging for vibe-coding-proxy traffic.
|
|
5
|
+
*
|
|
6
|
+
* The forward proxy captures requests through a different protocol surface than
|
|
7
|
+
* the existing reverse proxy, but the viewer expects the same RawPair shape.
|
|
8
|
+
* This writer centralizes body decoding, sensitive header redaction, SSE parsing,
|
|
9
|
+
* and real-time HTML regeneration for the independent proxy service.
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.ProxyLogWriter = void 0;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const zlib = __importStar(require("zlib"));
|
|
49
|
+
const child_process_1 = require("child_process");
|
|
50
|
+
const openai_adapter_1 = require("../adapt/openai-adapter");
|
|
51
|
+
const api_format_1 = require("../adapt/api-format");
|
|
52
|
+
const html_generator_1 = require("../report/html-generator");
|
|
53
|
+
const header_redaction_1 = require("./header-redaction");
|
|
54
|
+
const shared_conversation_processor_1 = require("../report/shared-conversation-processor");
|
|
55
|
+
const cli_common_1 = require("../cli/cli-common");
|
|
56
|
+
/**
|
|
57
|
+
* Persists forward-proxy traffic using the same artifact set as existing CLIs.
|
|
58
|
+
*/
|
|
59
|
+
class ProxyLogWriter {
|
|
60
|
+
/** @param config - Log location, redaction, and report options. */
|
|
61
|
+
constructor(config = {}) {
|
|
62
|
+
this.htmlGenerator = new html_generator_1.HTMLGenerator();
|
|
63
|
+
this.pairs = [];
|
|
64
|
+
this.config = {
|
|
65
|
+
logDirectory: config.logDirectory || ".vibe-coding-proxy",
|
|
66
|
+
logBaseName: config.logBaseName || "",
|
|
67
|
+
includeAllRequests: config.includeAllRequests || false,
|
|
68
|
+
openBrowser: config.openBrowser || false,
|
|
69
|
+
logSensitiveHeaders: config.logSensitiveHeaders || false,
|
|
70
|
+
tool: config.tool,
|
|
71
|
+
};
|
|
72
|
+
if (!fs.existsSync(this.config.logDirectory)) {
|
|
73
|
+
fs.mkdirSync(this.config.logDirectory, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
const fileBaseName = this.config.logBaseName ||
|
|
76
|
+
`log-${new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, -5)}`;
|
|
77
|
+
this.paths = {
|
|
78
|
+
jsonl: path.join(this.config.logDirectory, `${fileBaseName}.jsonl`),
|
|
79
|
+
json: path.join(this.config.logDirectory, `${fileBaseName}.json`),
|
|
80
|
+
html: path.join(this.config.logDirectory, `${fileBaseName}.html`),
|
|
81
|
+
};
|
|
82
|
+
fs.writeFileSync(this.paths.jsonl, "");
|
|
83
|
+
fs.writeFileSync(this.paths.json, "[]");
|
|
84
|
+
}
|
|
85
|
+
/** @returns Absolute paths for JSONL, JSON, and HTML artifacts. */
|
|
86
|
+
getPaths() {
|
|
87
|
+
return {
|
|
88
|
+
jsonl: path.resolve(this.paths.jsonl),
|
|
89
|
+
json: path.resolve(this.paths.json),
|
|
90
|
+
html: path.resolve(this.paths.html),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Write a completed request/response exchange when it matches logging policy.
|
|
95
|
+
* @param exchange - Buffered HTTP request and response data.
|
|
96
|
+
*/
|
|
97
|
+
async logExchange(exchange) {
|
|
98
|
+
if (!exchange.forceLog && !this.config.includeAllRequests) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const requestBody = parseRequestBodyForLog(exchange.requestHeaders, exchange.requestBody);
|
|
103
|
+
const responseBodyText = decodeBodyText(exchange.responseHeaders, Buffer.concat(exchange.responseChunks));
|
|
104
|
+
const apiFormat = resolveApiFormat(exchange.url, requestBody);
|
|
105
|
+
const parsedResponse = parseResponseBody(exchange.responseHeaders, responseBodyText, apiFormat, requestBody);
|
|
106
|
+
const pair = {
|
|
107
|
+
request: {
|
|
108
|
+
timestamp: exchange.requestTimestamp / 1000,
|
|
109
|
+
method: exchange.method,
|
|
110
|
+
url: exchange.url,
|
|
111
|
+
headers: this.processHeaders(exchange.requestHeaders),
|
|
112
|
+
body: requestBody,
|
|
113
|
+
},
|
|
114
|
+
response: {
|
|
115
|
+
timestamp: exchange.responseTimestamp / 1000,
|
|
116
|
+
status_code: exchange.statusCode,
|
|
117
|
+
headers: this.processHeaders(exchange.responseHeaders),
|
|
118
|
+
api_format: apiFormat !== "unknown" ? apiFormat : undefined,
|
|
119
|
+
...parsedResponse,
|
|
120
|
+
},
|
|
121
|
+
logged_at: new Date().toISOString(),
|
|
122
|
+
};
|
|
123
|
+
await this.writePair(pair);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
(0, cli_common_1.traceRuntimeError)(`vibe-coding-proxy: failed to log exchange: ${error}`, this.config.logDirectory);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Write CONNECT tunnel metadata when body logging is unavailable.
|
|
131
|
+
* @param authority - CONNECT target such as `api.example.com:443`.
|
|
132
|
+
* @param note - Reason this is metadata-only.
|
|
133
|
+
*/
|
|
134
|
+
async logConnectMetadata(authority, note) {
|
|
135
|
+
if (!this.config.includeAllRequests) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const now = Date.now() / 1000;
|
|
139
|
+
await this.writePair({
|
|
140
|
+
request: {
|
|
141
|
+
timestamp: now,
|
|
142
|
+
method: "CONNECT",
|
|
143
|
+
url: authority,
|
|
144
|
+
headers: {},
|
|
145
|
+
body: null,
|
|
146
|
+
},
|
|
147
|
+
response: {
|
|
148
|
+
timestamp: now,
|
|
149
|
+
status_code: 200,
|
|
150
|
+
headers: {},
|
|
151
|
+
body: { tunnel: true },
|
|
152
|
+
},
|
|
153
|
+
logged_at: new Date().toISOString(),
|
|
154
|
+
note,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/** Print summary and optionally open the generated HTML report. */
|
|
158
|
+
stop() {
|
|
159
|
+
console.error(`Logged ${this.pairs.length} request/response pairs`);
|
|
160
|
+
if (this.config.openBrowser && fs.existsSync(this.paths.html)) {
|
|
161
|
+
this.openHtmlInBrowser(this.paths.html);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Redact sensitive headers unless explicitly disabled. */
|
|
165
|
+
processHeaders(headers) {
|
|
166
|
+
return (0, header_redaction_1.processHeaders)(headers, this.config.logSensitiveHeaders);
|
|
167
|
+
}
|
|
168
|
+
/** Append one pair to JSONL, rewrite JSON, and regenerate HTML. */
|
|
169
|
+
async writePair(pair) {
|
|
170
|
+
this.pairs.push(pair);
|
|
171
|
+
fs.appendFileSync(this.paths.jsonl, JSON.stringify(pair) + "\n");
|
|
172
|
+
fs.writeFileSync(this.paths.json, JSON.stringify(this.pairs, null, 2));
|
|
173
|
+
await this.htmlGenerator.generateHTML(this.pairs, this.paths.html, {
|
|
174
|
+
timestamp: new Date().toISOString().replace("T", " ").slice(0, -5),
|
|
175
|
+
includeAllRequests: this.config.includeAllRequests,
|
|
176
|
+
tool: this.config.tool,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/** Open an HTML file in the platform default browser. */
|
|
180
|
+
openHtmlInBrowser(htmlFile) {
|
|
181
|
+
try {
|
|
182
|
+
if (process.platform === "win32") {
|
|
183
|
+
(0, child_process_1.spawn)("cmd", ["/c", "start", "", htmlFile], { detached: true, stdio: "ignore" }).unref();
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const cmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
187
|
+
(0, child_process_1.spawn)(cmd, [htmlFile], { detached: true, stdio: "ignore" }).unref();
|
|
188
|
+
}
|
|
189
|
+
console.error(`Opening ${htmlFile} in browser`);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
(0, cli_common_1.traceRuntimeError)(`vibe-coding-proxy: failed to open HTML report: ${error}`, this.config.logDirectory);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
exports.ProxyLogWriter = ProxyLogWriter;
|
|
197
|
+
/** Decode and decompress a body buffer using the corresponding headers. */
|
|
198
|
+
function decodeBodyText(headers, buffer) {
|
|
199
|
+
if (!buffer.length) {
|
|
200
|
+
return "";
|
|
201
|
+
}
|
|
202
|
+
const encoding = String(headers["content-encoding"] || "").toLowerCase();
|
|
203
|
+
try {
|
|
204
|
+
if (encoding === "gzip")
|
|
205
|
+
return zlib.gunzipSync(buffer).toString("utf-8");
|
|
206
|
+
if (encoding === "br")
|
|
207
|
+
return zlib.brotliDecompressSync(buffer).toString("utf-8");
|
|
208
|
+
if (encoding === "deflate")
|
|
209
|
+
return zlib.inflateSync(buffer).toString("utf-8");
|
|
210
|
+
if (encoding === "zstd") {
|
|
211
|
+
const zstdDecompressSync = zlib.zstdDecompressSync;
|
|
212
|
+
if (zstdDecompressSync)
|
|
213
|
+
return zstdDecompressSync(buffer).toString("utf-8");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return buffer.toString("utf-8");
|
|
218
|
+
}
|
|
219
|
+
return buffer.toString("utf-8");
|
|
220
|
+
}
|
|
221
|
+
/** Parse a request body as JSON when possible, otherwise return raw text. */
|
|
222
|
+
function parseRequestBodyForLog(headers, buffer) {
|
|
223
|
+
if (!buffer.length) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const text = decodeBodyText(headers, buffer);
|
|
227
|
+
try {
|
|
228
|
+
return JSON.parse(text);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return text;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** Infer API format from URL and body shape. */
|
|
235
|
+
function resolveApiFormat(url, requestBody) {
|
|
236
|
+
const fromUrl = (0, api_format_1.inferApiFormatFromUrl)(url);
|
|
237
|
+
if (fromUrl !== "unknown") {
|
|
238
|
+
return fromUrl;
|
|
239
|
+
}
|
|
240
|
+
return (0, api_format_1.inferApiFormatFromBody)(requestBody);
|
|
241
|
+
}
|
|
242
|
+
/** Parse response body into viewer-friendly fields. */
|
|
243
|
+
function parseResponseBody(headers, responseBody, apiFormat, requestBody) {
|
|
244
|
+
const contentType = String(headers["content-type"] || "");
|
|
245
|
+
const model = requestBody && typeof requestBody === "object" && "model" in requestBody
|
|
246
|
+
? String(requestBody.model)
|
|
247
|
+
: "unknown";
|
|
248
|
+
try {
|
|
249
|
+
if (contentType.includes("application/json")) {
|
|
250
|
+
const parsed = JSON.parse(responseBody);
|
|
251
|
+
if (parsed && typeof parsed === "object" && "error" in parsed) {
|
|
252
|
+
return { body: parsed };
|
|
253
|
+
}
|
|
254
|
+
if (apiFormat === "openai" || (parsed && typeof parsed === "object" && "choices" in parsed)) {
|
|
255
|
+
return { body: (0, openai_adapter_1.parseOpenAIChatCompletionBody)(parsed, model) };
|
|
256
|
+
}
|
|
257
|
+
if (apiFormat === "openai-responses" || (parsed && typeof parsed === "object" && "output" in parsed)) {
|
|
258
|
+
return { body: (0, openai_adapter_1.parseOpenAIResponsesBody)(parsed, model) };
|
|
259
|
+
}
|
|
260
|
+
return { body: parsed };
|
|
261
|
+
}
|
|
262
|
+
if (contentType.includes("text/event-stream")) {
|
|
263
|
+
const events = parseSSEEvents(responseBody);
|
|
264
|
+
if (apiFormat === "openai") {
|
|
265
|
+
try {
|
|
266
|
+
const completion = (0, openai_adapter_1.buildOpenAIChatCompletionFromSSE)(responseBody, model);
|
|
267
|
+
return { body: (0, openai_adapter_1.parseOpenAIChatCompletionBody)(completion, model), events };
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
return { body_raw: responseBody, events };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (apiFormat === "openai-responses") {
|
|
274
|
+
try {
|
|
275
|
+
const responsesBody = (0, openai_adapter_1.buildOpenAIResponsesFromSSE)(responseBody, model);
|
|
276
|
+
return { body: (0, openai_adapter_1.parseOpenAIResponsesBody)(responsesBody, model), events };
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return { body_raw: responseBody, events };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const message = new shared_conversation_processor_1.SharedConversationProcessor().parseStreamingResponse(responseBody);
|
|
284
|
+
return { body: message, events };
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return { body_raw: responseBody, events };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { body_raw: responseBody };
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return { body_raw: responseBody };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/** Parse Server-Sent Events lines into structured event records. */
|
|
297
|
+
function parseSSEEvents(body) {
|
|
298
|
+
const events = [];
|
|
299
|
+
let currentEvent = "";
|
|
300
|
+
for (const line of body.split("\n")) {
|
|
301
|
+
if (line.startsWith("event: ")) {
|
|
302
|
+
currentEvent = line.substring(7).trim();
|
|
303
|
+
}
|
|
304
|
+
else if (line.startsWith("data: ")) {
|
|
305
|
+
const data = line.substring(6).trim();
|
|
306
|
+
if (data === "[DONE]")
|
|
307
|
+
break;
|
|
308
|
+
try {
|
|
309
|
+
const parsed = JSON.parse(data);
|
|
310
|
+
const eventType = currentEvent ||
|
|
311
|
+
(parsed && typeof parsed === "object" && "type" in parsed
|
|
312
|
+
? String(parsed.type)
|
|
313
|
+
: "unknown");
|
|
314
|
+
events.push({ event: eventType, data: parsed, timestamp: new Date().toISOString() });
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
// Keep logging robust when one SSE line is malformed.
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return events;
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=proxy-log-writer.js.map
|