claude-code-infinite 1.0.35 → 1.0.37

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 (117) hide show
  1. package/README.md +114 -10
  2. package/dist/body-shape.d.ts +49 -0
  3. package/dist/body-shape.d.ts.map +1 -0
  4. package/dist/body-shape.js +232 -0
  5. package/dist/body-shape.js.map +1 -0
  6. package/dist/cc-request.d.ts +15 -0
  7. package/dist/cc-request.d.ts.map +1 -1
  8. package/dist/cc-request.js +30 -0
  9. package/dist/cc-request.js.map +1 -1
  10. package/dist/claude-env.d.ts +24 -1
  11. package/dist/claude-env.d.ts.map +1 -1
  12. package/dist/claude-env.js +48 -7
  13. package/dist/claude-env.js.map +1 -1
  14. package/dist/cli-args.d.ts +1 -1
  15. package/dist/cli-args.d.ts.map +1 -1
  16. package/dist/cli-args.js +1 -1
  17. package/dist/cli-args.js.map +1 -1
  18. package/dist/cli-main.js +113 -11
  19. package/dist/cli-main.js.map +1 -1
  20. package/dist/config.d.ts +5 -0
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js.map +1 -1
  23. package/dist/count-tokens.d.ts +15 -2
  24. package/dist/count-tokens.d.ts.map +1 -1
  25. package/dist/count-tokens.js +13 -4
  26. package/dist/count-tokens.js.map +1 -1
  27. package/dist/desktop-command.d.ts +38 -0
  28. package/dist/desktop-command.d.ts.map +1 -0
  29. package/dist/desktop-command.js +324 -0
  30. package/dist/desktop-command.js.map +1 -0
  31. package/dist/desktop-service.d.ts +41 -0
  32. package/dist/desktop-service.d.ts.map +1 -0
  33. package/dist/desktop-service.js +449 -0
  34. package/dist/desktop-service.js.map +1 -0
  35. package/dist/desktop-setup.d.ts +94 -0
  36. package/dist/desktop-setup.d.ts.map +1 -0
  37. package/dist/desktop-setup.js +288 -0
  38. package/dist/desktop-setup.js.map +1 -0
  39. package/dist/financial-redaction.d.ts +12 -0
  40. package/dist/financial-redaction.d.ts.map +1 -0
  41. package/dist/financial-redaction.js +509 -0
  42. package/dist/financial-redaction.js.map +1 -0
  43. package/dist/hooks.d.ts +2 -0
  44. package/dist/hooks.d.ts.map +1 -1
  45. package/dist/hooks.js +4 -0
  46. package/dist/hooks.js.map +1 -1
  47. package/dist/https-proxy-eligibility.d.ts +31 -0
  48. package/dist/https-proxy-eligibility.d.ts.map +1 -0
  49. package/dist/https-proxy-eligibility.js +204 -0
  50. package/dist/https-proxy-eligibility.js.map +1 -0
  51. package/dist/https-proxy-mode.d.ts +59 -0
  52. package/dist/https-proxy-mode.d.ts.map +1 -0
  53. package/dist/https-proxy-mode.js +255 -0
  54. package/dist/https-proxy-mode.js.map +1 -0
  55. package/dist/https-proxy-session.d.ts +64 -0
  56. package/dist/https-proxy-session.d.ts.map +1 -0
  57. package/dist/https-proxy-session.js +165 -0
  58. package/dist/https-proxy-session.js.map +1 -0
  59. package/dist/image-tokens.d.ts +6 -1
  60. package/dist/image-tokens.d.ts.map +1 -1
  61. package/dist/image-tokens.js +75 -14
  62. package/dist/image-tokens.js.map +1 -1
  63. package/dist/lane-activity.d.ts +42 -0
  64. package/dist/lane-activity.d.ts.map +1 -0
  65. package/dist/lane-activity.js +79 -0
  66. package/dist/lane-activity.js.map +1 -0
  67. package/dist/memtree-mcp.d.ts +32 -1
  68. package/dist/memtree-mcp.d.ts.map +1 -1
  69. package/dist/memtree-mcp.js +73 -7
  70. package/dist/memtree-mcp.js.map +1 -1
  71. package/dist/memtree.d.ts +10 -0
  72. package/dist/memtree.d.ts.map +1 -1
  73. package/dist/memtree.js +59 -12
  74. package/dist/memtree.js.map +1 -1
  75. package/dist/message-refs.d.ts +81 -0
  76. package/dist/message-refs.d.ts.map +1 -0
  77. package/dist/message-refs.js +378 -0
  78. package/dist/message-refs.js.map +1 -0
  79. package/dist/mitm-ca.d.ts +31 -0
  80. package/dist/mitm-ca.d.ts.map +1 -0
  81. package/dist/mitm-ca.js +236 -0
  82. package/dist/mitm-ca.js.map +1 -0
  83. package/dist/paused-session.d.ts.map +1 -1
  84. package/dist/paused-session.js +9 -2
  85. package/dist/paused-session.js.map +1 -1
  86. package/dist/proxy.d.ts +25 -0
  87. package/dist/proxy.d.ts.map +1 -1
  88. package/dist/proxy.js +168 -22
  89. package/dist/proxy.js.map +1 -1
  90. package/dist/refusal-reason.d.ts +3 -2
  91. package/dist/refusal-reason.d.ts.map +1 -1
  92. package/dist/refusal-reason.js +4 -3
  93. package/dist/refusal-reason.js.map +1 -1
  94. package/dist/reqlog.d.ts +13 -3
  95. package/dist/reqlog.d.ts.map +1 -1
  96. package/dist/reqlog.js +1 -1
  97. package/dist/reqlog.js.map +1 -1
  98. package/dist/request-sizing.d.ts +38 -2
  99. package/dist/request-sizing.d.ts.map +1 -1
  100. package/dist/request-sizing.js +72 -6
  101. package/dist/request-sizing.js.map +1 -1
  102. package/dist/secret-redaction.d.ts +25 -0
  103. package/dist/secret-redaction.d.ts.map +1 -0
  104. package/dist/secret-redaction.js +492 -0
  105. package/dist/secret-redaction.js.map +1 -0
  106. package/dist/statusline-auto.d.ts +15 -0
  107. package/dist/statusline-auto.d.ts.map +1 -0
  108. package/dist/statusline-auto.js +125 -0
  109. package/dist/statusline-auto.js.map +1 -0
  110. package/dist/statusline.d.ts.map +1 -1
  111. package/dist/statusline.js +3 -2
  112. package/dist/statusline.js.map +1 -1
  113. package/dist/utf8-bytes.d.ts +10 -0
  114. package/dist/utf8-bytes.d.ts.map +1 -0
  115. package/dist/utf8-bytes.js +107 -0
  116. package/dist/utf8-bytes.js.map +1 -0
  117. package/package.json +1 -1
@@ -0,0 +1,255 @@
1
+ /**
2
+ * ccc's loopback server as Claude Code's HTTPS_PROXY (opt-in: CCC_HTTPS_PROXY_MODE=1 or
3
+ * the `httpsProxyMode` config key; https-proxy-session.ts decides and sets it up). Claude
4
+ * Code then keeps api.anthropic.com as its base URL, and with it Remote Control, while its
5
+ * model calls still reach ccc and MemTree.
6
+ *
7
+ * - A CONNECT without this session's proxy token gets 407: other local users and programs
8
+ * can use neither the tunnel nor the interception.
9
+ * - CONNECT api.anthropic.com:443: TLS is terminated with the session CA's leaf
10
+ * (mitm-ca.ts) and the decrypted stream goes to a separate, never-listening http.Server
11
+ * that shares ccc's request handler. proxy.ts routes only POST /v1/messages and
12
+ * /v1/messages/count_tokens from it to MemTree; every other path (Remote Control's
13
+ * bridge, sessions, files, OAuth, telemetry) goes verbatim to api.anthropic.com, and
14
+ * ccc's loopback-only routes (hooks, /memtree/*) are not reachable from it.
15
+ * - Every other CONNECT: a blind TCP tunnel. ccc never decrypts or logs those bytes.
16
+ * - An HTTP upgrade (WebSocket) on an intercepted stream: piped raw to api.anthropic.com
17
+ * over a fresh TLS connection. The loopback server itself gets no upgrade listener, so
18
+ * its requests behave exactly as without proxy mode.
19
+ *
20
+ * The optional trace (CCC_PROXY_TRACE) records CONNECT targets and, for intercepted
21
+ * requests, method, path (no query), status and User-Agent: no other header values, no
22
+ * bodies.
23
+ */
24
+ import http from "node:http";
25
+ import net from "node:net";
26
+ import tls from "node:tls";
27
+ import { appendFileSync } from "node:fs";
28
+ import { timingSafeEqual } from "node:crypto";
29
+ import { INTERCEPT_HOST } from "./mitm-ca.js";
30
+ /** The user name in HTTPS_PROXY; the session token is its password. */
31
+ export const PROXY_USER = "ccc";
32
+ /** The HTTPS_PROXY value Claude Code gets: the loopback port with the token. */
33
+ export function proxyUrlFor(port, proxyToken) {
34
+ return `http://${PROXY_USER}:${proxyToken}@127.0.0.1:${port}`;
35
+ }
36
+ export function attachHttpsProxyMode(server, onInterceptedRequest, upstream, opts) {
37
+ const tunnels = new TunnelSet();
38
+ const trace = tracer(opts.tracePath);
39
+ const secureContext = tls.createSecureContext({
40
+ cert: opts.ca.leafCertPem, key: opts.ca.leafKeyPem,
41
+ });
42
+ const intercept = interceptServer(onInterceptedRequest, upstream, tunnels, trace);
43
+ server.on("connect", (req, socket, head) => {
44
+ onConnect(req, socket, head, { intercept, secureContext, tunnels, trace, opts });
45
+ });
46
+ return {
47
+ // Drain starts: blind tunnels carry no ccc work, and idle decrypted keep-alive
48
+ // connections none either (a never-listening server can't close those itself).
49
+ blind: () => {
50
+ tunnels.destroyBlind();
51
+ tunnels.closeIdleIntercepted();
52
+ },
53
+ all: () => tunnels.destroyAll(),
54
+ };
55
+ }
56
+ function onConnect(req, socket, head, ctx) {
57
+ const target = parseAuthority(req.url ?? "");
58
+ const mitm = target !== null && target.host === INTERCEPT_HOST && target.port === 443;
59
+ ctx.trace({ ev: "connect", target: req.url, ua: userAgent(req), mitm });
60
+ // Before any answer: a reset must not be an unhandled error, and drain must see it.
61
+ socket.on("error", () => { });
62
+ if (!ctx.tunnels.track(socket, mitm ? "intercepted" : "blind"))
63
+ return;
64
+ if (!target)
65
+ return refuse(socket, "HTTP/1.1 400 Bad Request\r\n\r\n");
66
+ if (!authorized(req, ctx.opts.proxyToken)) {
67
+ ctx.trace({ ev: "connect_refused", target: req.url });
68
+ return refuse(socket, "HTTP/1.1 407 Proxy Authentication Required\r\n" +
69
+ `Proxy-Authenticate: Basic realm="${PROXY_USER}"\r\n\r\n`);
70
+ }
71
+ if (mitm) {
72
+ return interceptConnect(ctx.intercept, socket, head, ctx.secureContext, ctx.tunnels, ctx.trace);
73
+ }
74
+ tunnelConnect(socket, head, target.host, target.port, ctx.trace);
75
+ }
76
+ /**
77
+ * The decrypted api.anthropic.com streams' own server: never listening, fed only by
78
+ * interceptConnect, so nothing on the loopback port can reach it.
79
+ */
80
+ function interceptServer(onRequest, upstream, tunnels, trace) {
81
+ const server = http.createServer(onRequest);
82
+ // Same as the loopback server: long SSE responses and long polls must not be cut.
83
+ server.requestTimeout = 0;
84
+ server.headersTimeout = 60_000;
85
+ server.on("upgrade", (req, socket, head) => {
86
+ trace({ ev: "upgrade", path: pathOf(req) });
87
+ socket.on("error", () => { });
88
+ if (!tunnels.track(socket, "intercepted"))
89
+ return;
90
+ tunnels.requestStarted(socket); // a live WebSocket is never idle: only the deadline cuts it
91
+ upgradeToUpstream(req, socket, head, upstream);
92
+ });
93
+ server.on("request", (req, res) => {
94
+ tunnels.requestStarted(req.socket);
95
+ res.once("close", () => tunnels.requestEnded(req.socket));
96
+ const started = Date.now();
97
+ res.on("finish", () => trace({
98
+ ev: "request", method: req.method, path: pathOf(req), status: res.statusCode,
99
+ ms: Date.now() - started, ua: userAgent(req),
100
+ }));
101
+ });
102
+ return server;
103
+ }
104
+ function interceptConnect(intercept, socket, head, secureContext, tunnels, trace) {
105
+ socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
106
+ if (head.length > 0)
107
+ socket.unshift(head);
108
+ const tlsSocket = new tls.TLSSocket(socket, {
109
+ isServer: true, secureContext, ALPNProtocols: ["http/1.1"],
110
+ });
111
+ tlsSocket.on("error", (err) => {
112
+ trace({ ev: "tls_error", message: err.message });
113
+ socket.destroy();
114
+ });
115
+ intercept.emit("connection", tlsSocket);
116
+ return tunnels.trackDecrypted(tlsSocket);
117
+ }
118
+ function tunnelConnect(socket, head, host, port, trace) {
119
+ let established = false;
120
+ const upstream = net.connect(port, host, () => {
121
+ established = true;
122
+ socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
123
+ if (head.length > 0)
124
+ upstream.write(head);
125
+ upstream.pipe(socket);
126
+ socket.pipe(upstream);
127
+ });
128
+ upstream.on("error", (err) => {
129
+ trace({ ev: "tunnel_error", host, port, message: err.message });
130
+ if (socket.destroyed)
131
+ return;
132
+ if (established)
133
+ socket.destroy();
134
+ else
135
+ socket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n");
136
+ });
137
+ socket.on("close", () => upstream.destroy());
138
+ upstream.on("close", () => socket.destroy());
139
+ }
140
+ /** Replays the upgrade request upstream byte for byte (headers as sent), then pipes. */
141
+ function upgradeToUpstream(req, socket, head, upstream) {
142
+ const connected = () => {
143
+ const lines = [`${req.method} ${req.url} HTTP/1.1`];
144
+ for (let i = 0; i < req.rawHeaders.length; i += 2) {
145
+ lines.push(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}`);
146
+ }
147
+ up.write(`${lines.join("\r\n")}\r\n\r\n`);
148
+ if (head.length > 0)
149
+ up.write(head);
150
+ up.pipe(socket);
151
+ socket.pipe(up);
152
+ };
153
+ const up = upstream.secure
154
+ ? tls.connect({ host: upstream.host, port: upstream.port, servername: upstream.host,
155
+ ALPNProtocols: ["http/1.1"] }, connected)
156
+ : net.connect(upstream.port, upstream.host, connected);
157
+ up.on("error", () => socket.destroy());
158
+ socket.on("close", () => up.destroy());
159
+ up.on("close", () => socket.destroy());
160
+ }
161
+ /** Answer, then close: a refused client can't hold the socket open. */
162
+ function refuse(socket, response) {
163
+ socket.end(response);
164
+ setTimeout(() => socket.destroy(), 1_000).unref();
165
+ }
166
+ function authorized(req, token) {
167
+ const credentials = Buffer.from(`${PROXY_USER}:${token}`).toString("base64");
168
+ const expected = Buffer.from(`Basic ${credentials}`);
169
+ const got = Buffer.from(String(req.headers["proxy-authorization"] ?? ""));
170
+ return got.length === expected.length && timingSafeEqual(got, expected);
171
+ }
172
+ /** host:port of a CONNECT (IPv6 in brackets); null when malformed. */
173
+ export function parseAuthority(authority) {
174
+ const m = /^(?:\[([0-9a-fA-F:.]+)\]|([^\s:[\]/@]+)):(\d{1,5})$/.exec(authority);
175
+ if (!m)
176
+ return null;
177
+ const port = Number(m[3]);
178
+ const host = (m[1] ?? m[2]).toLowerCase();
179
+ return port > 0 && port < 65536 ? { host, port } : null;
180
+ }
181
+ function pathOf(req) {
182
+ return (req.url ?? "").split("?")[0];
183
+ }
184
+ function userAgent(req) {
185
+ return String(req.headers["user-agent"] ?? "");
186
+ }
187
+ function tracer(path) {
188
+ if (!path)
189
+ return () => { };
190
+ return (event) => {
191
+ try {
192
+ appendFileSync(path, `${JSON.stringify({ t: Date.now(), ...event })}\n`, { mode: 0o600 });
193
+ }
194
+ catch { /* tracing is best effort */ }
195
+ };
196
+ }
197
+ /**
198
+ * Raw CONNECT and upgrade sockets, by kind, so drain can close them; and each decrypted
199
+ * stream's requests in flight, so drain can close the idle ones at once.
200
+ */
201
+ class TunnelSet {
202
+ sets = { blind: new Set(), intercepted: new Set() };
203
+ inFlight = new Map();
204
+ draining = false;
205
+ /** false (and the socket destroyed) once drain started: no new tunnels or upgrades. */
206
+ track(socket, kind) {
207
+ if (this.draining) {
208
+ socket.destroy();
209
+ return false;
210
+ }
211
+ const set = this.sets[kind];
212
+ set.add(socket);
213
+ socket.once("close", () => set.delete(socket));
214
+ return true;
215
+ }
216
+ trackDecrypted(socket) {
217
+ this.inFlight.set(socket, 0);
218
+ socket.once("close", () => this.inFlight.delete(socket));
219
+ if (this.draining)
220
+ socket.destroy();
221
+ }
222
+ requestStarted(socket) {
223
+ const n = this.inFlight.get(socket);
224
+ if (n !== undefined)
225
+ this.inFlight.set(socket, n + 1);
226
+ }
227
+ requestEnded(socket) {
228
+ const n = this.inFlight.get(socket);
229
+ if (n === undefined)
230
+ return;
231
+ this.inFlight.set(socket, n - 1);
232
+ if (this.draining && n - 1 <= 0)
233
+ socket.end();
234
+ }
235
+ destroyBlind() {
236
+ destroyAll(this.sets.blind);
237
+ }
238
+ /** Drain started: close idle decrypted streams now, busy ones after their request. */
239
+ closeIdleIntercepted() {
240
+ this.draining = true;
241
+ for (const [socket, n] of this.inFlight)
242
+ if (n <= 0)
243
+ socket.end();
244
+ }
245
+ destroyAll() {
246
+ destroyAll(this.sets.blind);
247
+ destroyAll(this.sets.intercepted);
248
+ }
249
+ }
250
+ function destroyAll(set) {
251
+ for (const socket of set)
252
+ socket.destroy();
253
+ set.clear();
254
+ }
255
+ //# sourceMappingURL=https-proxy-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https-proxy-mode.js","sourceRoot":"","sources":["../src/https-proxy-mode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { type HttpsProxyModeOptions } from "./https-proxy-mode.js";
2
+ /** What claudeChildEnv sets for proxy mode in place of the base URL. */
3
+ export interface HttpsProxyChild {
4
+ /** HTTPS_PROXY: the loopback with the per-session token. */
5
+ proxyUrl: string;
6
+ /** NODE_EXTRA_CA_CERTS: the user's extra CAs, then the session CA. */
7
+ caBundlePath: string;
8
+ /** CLAUDE_ENV_FILE: sourced by Claude Code before each Bash command. */
9
+ bashEnvPath: string;
10
+ /** NO_PROXY / no_proxy with api.anthropic.com entries removed (only those set). */
11
+ noProxy: Partial<Record<NoProxyVar, string>>;
12
+ }
13
+ export interface HttpsProxySession {
14
+ /** For startProxy's `httpsProxyMode`. */
15
+ proxyOptions: HttpsProxyModeOptions & {
16
+ onModelCall: () => void;
17
+ };
18
+ /** Writes the Bash env file for the proxy's port; what Claude Code's env needs. */
19
+ childFor(port: number): HttpsProxyChild;
20
+ /** A one-time warning when prompts go by with no model call through ccc. */
21
+ promptLine(): string | undefined;
22
+ cleanup(): void;
23
+ }
24
+ declare const NO_PROXY_VARS: readonly ["NO_PROXY", "no_proxy"];
25
+ type NoProxyVar = typeof NO_PROXY_VARS[number];
26
+ export declare function startHttpsProxySession(env: NodeJS.ProcessEnv, tracePath?: string, root?: string): HttpsProxySession;
27
+ /**
28
+ * NO_PROXY / no_proxy for Claude Code: entries that would match api.anthropic.com
29
+ * (`*`, `anthropic.com`, `.anthropic.com`, `*.anthropic.com`, the host itself, with or
30
+ * without a port) are dropped, or Claude Code would bypass ccc. Only changed ones.
31
+ */
32
+ export declare function sanitizedNoProxy(env: NodeJS.ProcessEnv): Partial<Record<NoProxyVar, string>>;
33
+ interface BashEnvInput {
34
+ parentEnv: NodeJS.ProcessEnv;
35
+ port: number;
36
+ bundlePath: string;
37
+ noProxy: Partial<Record<NoProxyVar, string>>;
38
+ }
39
+ /** The POSIX sh script Claude Code sources before each Bash command (see the header). */
40
+ export declare function bashEnvScript(input: BashEnvInput): string;
41
+ /**
42
+ * Bash env lines that put back `env`'s HTTPS_PROXY, https_proxy and NODE_EXTRA_CA_CERTS
43
+ * (or unset them), each only while it still holds ccc's value: the token proxy on `port`
44
+ * (matched by pattern, so the token is never written) and the CA file `caPath`. Values
45
+ * Claude Code's sandbox set before the file is sourced are left alone. Shared with the
46
+ * desktop service's env file (desktop-setup.ts).
47
+ */
48
+ export declare function cccProxyRestoreLines(port: number, caPath: string, env: NodeJS.ProcessEnv): string[];
49
+ export declare function ifStill(name: string, value: string, then: string): string;
50
+ export declare function shellQuote(value: string): string;
51
+ /**
52
+ * Settings that change mid-session (a proxy in a settings `env`, the sandbox turned on)
53
+ * can send Claude Code's model calls past ccc with no error. Two consecutive prompts with
54
+ * no model call through ccc get one warning, at the next prompt.
55
+ */
56
+ export declare class ModelCallWatch {
57
+ private promptsWithoutCall;
58
+ private warned;
59
+ modelCall(): void;
60
+ promptLine(): string | undefined;
61
+ }
62
+ export declare const MODEL_CALLS_MISSING: string;
63
+ export {};
64
+ //# sourceMappingURL=https-proxy-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https-proxy-session.d.ts","sourceRoot":"","sources":["../src/https-proxy-session.ts"],"names":[],"mappings":"AAyBA,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAI5F,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,iBAAiB;IAChC,yCAAyC;IACzC,YAAY,EAAE,qBAAqB,GAAG;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IAClE,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAAC;IACxC,4EAA4E;IAC5E,UAAU,IAAI,MAAM,GAAG,SAAS,CAAC;IACjC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,QAAA,MAAM,aAAa,mCAAoC,CAAC;AACxD,KAAK,UAAU,GAAG,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;AAE/C,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,SAA2C,GAC1F,iBAAiB,CAmBnB;AA4BD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAU5F;AAQD,UAAU,YAAY;IACpB,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;CAC9C;AAED,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAgBzD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GACnD,MAAM,EAAE,CAQV;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzE;AAOD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,MAAM,CAAS;IAEvB,SAAS,IAAI,IAAI;IAIjB,UAAU,IAAI,MAAM,GAAG,SAAS;CAQjC;AAED,eAAO,MAAM,mBAAmB,QAG0C,CAAC"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * One ccc session in HTTPS-proxy mode (https-proxy-mode.ts): its CA, proxy token and
3
+ * temp files, the environment its Claude Code gets, and the Bash env file that gives
4
+ * programs run from Claude Code's Bash tool what they see today under ccc.
5
+ *
6
+ * - The CA (mitm-ca.ts) is made fresh, in memory, at every start; only its public
7
+ * certificate goes to disk, in a 0700 dir of its own under ~/.claude-code-infinite/
8
+ * https-proxy/, removed when ccc exits (a startup sweep removes ones a killed ccc
9
+ * left, once their owner is gone). Not the system temp dir: it can hold hundreds of
10
+ * thousands of entries (a 200 ms sweep), and on Linux it is shared with other users.
11
+ * - Claude Code gets `HTTPS_PROXY=http://ccc:<token>@127.0.0.1:<port>`, the bundle as
12
+ * NODE_EXTRA_CA_CERTS (the user's own extra CAs first), no ANTHROPIC_BASE_URL, and
13
+ * NO_PROXY without entries that would send api.anthropic.com past ccc.
14
+ * - Bash commands source CLAUDE_ENV_FILE (bash-env.sh) first: it puts back the user's
15
+ * own proxy, CA and NO_PROXY values, each only while it still holds ccc's (Claude
16
+ * Code's sandbox sets its own before the file is sourced, and those must survive),
17
+ * and exports today's ANTHROPIC_BASE_URL and first-party marker, so scripts, SDK apps,
18
+ * `$ANTHROPIC_BASE_URL/memtree/...` and a nested ccc behave as in base-URL mode. The
19
+ * proxy token is never written to it: ccc's value is matched by pattern.
20
+ */
21
+ import { randomBytes } from "node:crypto";
22
+ import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
23
+ import path from "node:path";
24
+ import { INTERCEPT_HOST, createSessionCa, sweepStaleSessionDirs, writeCaBundle } from "./mitm-ca.js";
25
+ import { PROXY_USER, proxyUrlFor } from "./https-proxy-mode.js";
26
+ import { HTTPS_PROXY_MODE_ENV } from "./https-proxy-eligibility.js";
27
+ import { getConfigDir } from "./config.js";
28
+ const NO_PROXY_VARS = ["NO_PROXY", "no_proxy"];
29
+ export function startHttpsProxySession(env, tracePath, root = path.join(getConfigDir(), "https-proxy")) {
30
+ mkdirSync(root, { recursive: true, mode: 0o700 });
31
+ chmodSync(root, 0o700);
32
+ sweepStaleSessionDirs(root);
33
+ const ca = createSessionCa();
34
+ const files = writeCaBundle(ca, userExtraCaPem(env), root);
35
+ const proxyToken = randomBytes(24).toString("hex");
36
+ const watch = new ModelCallWatch();
37
+ return {
38
+ proxyOptions: {
39
+ ca: { leafCertPem: ca.leafCertPem, leafKeyPem: ca.leafKeyPem },
40
+ proxyToken,
41
+ ...(tracePath ? { tracePath } : {}),
42
+ onModelCall: () => watch.modelCall(),
43
+ },
44
+ childFor: (port) => childFor(env, files, port, proxyToken),
45
+ promptLine: () => watch.promptLine(),
46
+ cleanup: files.cleanup,
47
+ };
48
+ }
49
+ function childFor(env, files, port, proxyToken) {
50
+ const noProxy = sanitizedNoProxy(env);
51
+ const bashEnvPath = path.join(files.dir, "bash-env.sh");
52
+ writeFileSync(bashEnvPath, bashEnvScript({
53
+ parentEnv: env, port, bundlePath: files.bundlePath, noProxy,
54
+ }), { mode: 0o600 });
55
+ return {
56
+ proxyUrl: proxyUrlFor(port, proxyToken),
57
+ caBundlePath: files.bundlePath,
58
+ bashEnvPath,
59
+ noProxy,
60
+ };
61
+ }
62
+ /** The user's NODE_EXTRA_CA_CERTS file, kept first in the bundle; "" when unreadable. */
63
+ function userExtraCaPem(env) {
64
+ if (!env.NODE_EXTRA_CA_CERTS)
65
+ return "";
66
+ try {
67
+ return readFileSync(env.NODE_EXTRA_CA_CERTS, "utf8");
68
+ }
69
+ catch {
70
+ return ""; // Claude Code would have warned and ignored it too.
71
+ }
72
+ }
73
+ /**
74
+ * NO_PROXY / no_proxy for Claude Code: entries that would match api.anthropic.com
75
+ * (`*`, `anthropic.com`, `.anthropic.com`, `*.anthropic.com`, the host itself, with or
76
+ * without a port) are dropped, or Claude Code would bypass ccc. Only changed ones.
77
+ */
78
+ export function sanitizedNoProxy(env) {
79
+ const out = {};
80
+ for (const name of NO_PROXY_VARS) {
81
+ const value = env[name];
82
+ if (value === undefined)
83
+ continue;
84
+ const entries = value.split(/[\s,]+/).filter(Boolean);
85
+ const kept = entries.filter((entry) => !noProxyCoversIntercept(entry));
86
+ if (kept.length !== entries.length)
87
+ out[name] = kept.join(",");
88
+ }
89
+ return out;
90
+ }
91
+ function noProxyCoversIntercept(entry) {
92
+ const host = entry.trim().toLowerCase()
93
+ .replace(/:\d+$/, "").replace(/^\*/, "").replace(/^\./, "").replace(/\.$/, "");
94
+ return host === "" || host === INTERCEPT_HOST || INTERCEPT_HOST.endsWith(`.${host}`);
95
+ }
96
+ /** The POSIX sh script Claude Code sources before each Bash command (see the header). */
97
+ export function bashEnvScript(input) {
98
+ const { parentEnv, port, bundlePath, noProxy } = input;
99
+ const restore = (name) => restoreLine(parentEnv, name);
100
+ const lines = [
101
+ ...cccProxyRestoreLines(port, bundlePath, parentEnv),
102
+ ...NO_PROXY_VARS.filter((name) => noProxy[name] !== undefined)
103
+ .map((name) => ifStill(name, noProxy[name], restore(name))),
104
+ restore("CLAUDE_ENV_FILE"), restore("CCC_MEMTREE_PROXY"), restore(HTTPS_PROXY_MODE_ENV),
105
+ `export ANTHROPIC_BASE_URL=${shellQuote(`http://127.0.0.1:${port}`)}`,
106
+ "export _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL=1",
107
+ ];
108
+ const userFile = parentEnv.CLAUDE_ENV_FILE;
109
+ if (userFile) {
110
+ lines.push(`if [ -r ${shellQuote(userFile)} ]; then . ${shellQuote(userFile)}; fi`);
111
+ }
112
+ return `${lines.join("\n")}\n`;
113
+ }
114
+ /**
115
+ * Bash env lines that put back `env`'s HTTPS_PROXY, https_proxy and NODE_EXTRA_CA_CERTS
116
+ * (or unset them), each only while it still holds ccc's value: the token proxy on `port`
117
+ * (matched by pattern, so the token is never written) and the CA file `caPath`. Values
118
+ * Claude Code's sandbox set before the file is sourced are left alone. Shared with the
119
+ * desktop service's env file (desktop-setup.ts).
120
+ */
121
+ export function cccProxyRestoreLines(port, caPath, env) {
122
+ const restore = (name) => restoreLine(env, name);
123
+ const ours = `http://${PROXY_USER}:*@127.0.0.1:${port}`;
124
+ return [
125
+ // ccc removed https_proxy from Claude Code's env exactly when it set HTTPS_PROXY.
126
+ `case "\${HTTPS_PROXY-}" in ${ours}) ${restore("HTTPS_PROXY")}; ${restore("https_proxy")};; esac`,
127
+ ifStill("NODE_EXTRA_CA_CERTS", caPath, restore("NODE_EXTRA_CA_CERTS")),
128
+ ];
129
+ }
130
+ export function ifStill(name, value, then) {
131
+ return `if [ "\${${name}-}" = ${shellQuote(value)} ]; then ${then}; fi`;
132
+ }
133
+ function restoreLine(env, name) {
134
+ const value = env[name];
135
+ return value !== undefined ? `export ${name}=${shellQuote(value)}` : `unset ${name}`;
136
+ }
137
+ export function shellQuote(value) {
138
+ return `'${value.replace(/'/g, "'\\''")}'`;
139
+ }
140
+ /**
141
+ * Settings that change mid-session (a proxy in a settings `env`, the sandbox turned on)
142
+ * can send Claude Code's model calls past ccc with no error. Two consecutive prompts with
143
+ * no model call through ccc get one warning, at the next prompt.
144
+ */
145
+ export class ModelCallWatch {
146
+ promptsWithoutCall = 0;
147
+ warned = false;
148
+ modelCall() {
149
+ this.promptsWithoutCall = 0;
150
+ }
151
+ promptLine() {
152
+ if (this.warned)
153
+ return undefined;
154
+ this.promptsWithoutCall++;
155
+ // Two whole prompts with no call (a prompt a user's hook blocks makes none): at the third.
156
+ if (this.promptsWithoutCall < 3)
157
+ return undefined;
158
+ this.warned = true;
159
+ return MODEL_CALLS_MISSING;
160
+ }
161
+ }
162
+ export const MODEL_CALLS_MISSING = "ccc: your last prompt's model calls didn't reach ccc, so MemTree isn't compressing this " +
163
+ "session. A proxy or the Bash sandbox enabled in Claude Code's settings may be routing " +
164
+ `them elsewhere: restart ccc, or run it with ${HTTPS_PROXY_MODE_ENV}=0.`;
165
+ //# sourceMappingURL=https-proxy-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https-proxy-session.js","sourceRoot":"","sources":["../src/https-proxy-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,qBAAqB,EAAE,aAAa,EACvD,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,WAAW,EAA8B,MAAM,uBAAuB,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAwB3C,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE,UAAU,CAAU,CAAC;AAGxD,MAAM,UAAU,sBAAsB,CACpC,GAAsB,EAAE,SAAkB,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,aAAa,CAAC;IAE3F,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvB,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,EAAE,GAAG,eAAe,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,cAAc,EAAE,CAAC;IACnC,OAAO;QACL,YAAY,EAAE;YACZ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,EAAE;YAC9D,UAAU;YACV,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE;SACrC;QACD,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,CAAC;QAC1D,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE;QACpC,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CACf,GAAsB,EAAE,KAAqB,EAAE,IAAY,EAAE,UAAkB;IAE/E,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IACxD,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC;QACvC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO;KAC5D,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrB,OAAO;QACL,QAAQ,EAAE,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC;QACvC,YAAY,EAAE,KAAK,CAAC,UAAU;QAC9B,WAAW;QACX,OAAO;KACR,CAAC;AACJ,CAAC;AAED,yFAAyF;AACzF,SAAS,cAAc,CAAC,GAAsB;IAC5C,IAAI,CAAC,GAAG,CAAC,mBAAmB;QAAE,OAAO,EAAE,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,oDAAoD;IACjE,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAsB;IACrD,MAAM,GAAG,GAAwC,EAAE,CAAC;IACpD,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC;QACvE,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjF,OAAO,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,cAAc,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;AACvF,CAAC;AASD,yFAAyF;AACzF,MAAM,UAAU,aAAa,CAAC,KAAmB;IAC/C,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACvD,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG;QACZ,GAAG,oBAAoB,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC;QACpD,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;aAC3D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9D,OAAO,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC;QACvF,6BAA6B,UAAU,CAAC,oBAAoB,IAAI,EAAE,CAAC,EAAE;QACrE,mDAAmD;KACpD,CAAC;IACF,MAAM,QAAQ,GAAG,SAAS,CAAC,eAAe,CAAC;IAC3C,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,WAAW,UAAU,CAAC,QAAQ,CAAC,cAAc,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAY,EAAE,MAAc,EAAE,GAAsB;IAEpD,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjE,MAAM,IAAI,GAAG,UAAU,UAAU,gBAAgB,IAAI,EAAE,CAAC;IACxD,OAAO;QACL,kFAAkF;QAClF,8BAA8B,IAAI,KAAK,OAAO,CAAC,aAAa,CAAC,KAAK,OAAO,CAAC,aAAa,CAAC,SAAS;QACjG,OAAO,CAAC,qBAAqB,EAAE,MAAM,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;KACvE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,KAAa,EAAE,IAAY;IAC/D,OAAO,YAAY,IAAI,SAAS,UAAU,CAAC,KAAK,CAAC,YAAY,IAAI,MAAM,CAAC;AAC1E,CAAC;AAED,SAAS,WAAW,CAAC,GAAsB,EAAE,IAAY;IACvD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,cAAc;IACjB,kBAAkB,GAAG,CAAC,CAAC;IACvB,MAAM,GAAG,KAAK,CAAC;IAEvB,SAAS;QACP,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,UAAU;QACR,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAClC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,2FAA2F;QAC3F,IAAI,IAAI,CAAC,kBAAkB,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QAClD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,OAAO,mBAAmB,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,mBAAmB,GAC9B,0FAA0F;IAC1F,wFAAwF;IACxF,+CAA+C,oBAAoB,KAAK,CAAC"}
@@ -31,7 +31,12 @@ export declare function visualTokens(width: number, height: number, tier?: {
31
31
  }): number;
32
32
  /** Claude before 4.7 is standard; 4.7+, other and unknown models are high-res. */
33
33
  export declare function imageTier(model: string | undefined): typeof HIGH_RES;
34
- /** Width and height from a PNG, GIF, WebP or JPEG header; undefined when unreadable. */
34
+ /**
35
+ * Width and height from a PNG, GIF, WebP or JPEG header; undefined when unreadable.
36
+ * Only the header is decoded (the first HEADER_BASE64_CHARS characters); a JPEG whose
37
+ * frame header sits past that (large EXIF or ICC segments) is walked segment by
38
+ * segment, decoding a few bytes at each marker (jpegSizeAt), never the whole image.
39
+ */
35
40
  export declare function imageDimensions(base64: string): [number, number] | undefined;
36
41
  export {};
37
42
  //# sourceMappingURL=image-tokens.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-tokens.d.ts","sourceRoot":"","sources":["../src/image-tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,QAAA,MAAM,QAAQ;;;CAAsC,CAAC;AAOrD,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CAM/E;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAEjG;AAsBD,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI;;;CAAW,GAAG,MAAM,CAYnF;AAED,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,QAAQ,CAMpE;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAQ5E"}
1
+ {"version":3,"file":"image-tokens.d.ts","sourceRoot":"","sources":["../src/image-tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,QAAA,MAAM,QAAQ;;;CAAsC,CAAC;AAOrD,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CAM/E;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAEjG;AAsBD,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI;;;CAAW,GAAG,MAAM,CAYnF;AAED,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,QAAQ,CAMpE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAS5E"}
@@ -81,7 +81,12 @@ export function imageTier(model) {
81
81
  const minor = match[2] ? Number(match[2]) : 0;
82
82
  return major > 4 || (major === 4 && minor >= 7) ? HIGH_RES : STANDARD;
83
83
  }
84
- /** Width and height from a PNG, GIF, WebP or JPEG header; undefined when unreadable. */
84
+ /**
85
+ * Width and height from a PNG, GIF, WebP or JPEG header; undefined when unreadable.
86
+ * Only the header is decoded (the first HEADER_BASE64_CHARS characters); a JPEG whose
87
+ * frame header sits past that (large EXIF or ICC segments) is walked segment by
88
+ * segment, decoding a few bytes at each marker (jpegSizeAt), never the whole image.
89
+ */
85
90
  export function imageDimensions(base64) {
86
91
  let bytes;
87
92
  try {
@@ -90,7 +95,8 @@ export function imageDimensions(base64) {
90
95
  catch {
91
96
  return undefined;
92
97
  }
93
- return pngSize(bytes) ?? gifSize(bytes) ?? webpSize(bytes) ?? jpegSize(bytes);
98
+ return pngSize(bytes) ?? gifSize(bytes) ?? webpSize(bytes) ?? jpegSize(bytes) ??
99
+ (isJpeg(bytes) ? jpegSizeAt(base64) : undefined);
94
100
  }
95
101
  function addImages(blocks, tier, totals) {
96
102
  for (const item of blocks) {
@@ -137,22 +143,77 @@ function webpSize(b) {
137
143
  }
138
144
  const JPEG_FRAME_MARKERS = new Set([0xc0, 0xc1, 0xc2, 0xc3, 0xc5, 0xc6, 0xc7, 0xc9, 0xca, 0xcb,
139
145
  0xcd, 0xce, 0xcf]);
146
+ function isJpeg(b) {
147
+ return b.length >= 4 && b[0] === 0xff && b[1] === 0xd8;
148
+ }
140
149
  function jpegSize(b) {
141
- if (b.length < 4 || b[0] !== 0xff || b[1] !== 0xd8)
150
+ if (!isJpeg(b))
142
151
  return undefined;
143
152
  let i = 2;
144
- for (let steps = 0; steps < 2_048 && i + 9 < b.length; steps++) {
145
- if (b[i] !== 0xff)
146
- return undefined;
147
- const marker = b[i + 1];
148
- if (marker === 0xff) {
149
- i++;
150
- continue;
151
- }
152
- if (JPEG_FRAME_MARKERS.has(marker))
153
- return [b.readUInt16BE(i + 7), b.readUInt16BE(i + 5)];
154
- i += 2 + b.readUInt16BE(i + 2);
153
+ for (let steps = 0; steps < 2_048 && i + 9 <= b.length; steps++) {
154
+ const step = jpegStep(b.subarray(i, i + 9));
155
+ if (step === undefined || Array.isArray(step))
156
+ return step;
157
+ i += step;
158
+ }
159
+ return undefined;
160
+ }
161
+ /**
162
+ * The same walk as jpegSize over the whole base64 image, decoding 9 bytes per segment.
163
+ * Only for plain base64 throughout: one whitespace character inside a skipped segment
164
+ * would shift every later offset and read wrong bytes as a frame header (Codex r1 P2).
165
+ */
166
+ function jpegSizeAt(base64) {
167
+ if (!BASE64_RE.test(base64))
168
+ return undefined;
169
+ const total = Math.floor(base64.length / 4) * 3;
170
+ let i = 2;
171
+ for (let steps = 0; steps < 2_048 && i + 9 <= total; steps++) {
172
+ const b = base64Slice(base64, i, 9);
173
+ const step = b && jpegStep(b);
174
+ if (step === undefined || Array.isArray(step))
175
+ return step;
176
+ i += step;
155
177
  }
156
178
  return undefined;
157
179
  }
180
+ /**
181
+ * One marker of a JPEG walk, from the 9 bytes at it: the frame's [width, height], the
182
+ * bytes to the next marker, or undefined when the walk must stop. Standalone markers
183
+ * (TEM, RST0-7) carry no length (Codex r4 P2); SOI, EOI and SOS before any frame header,
184
+ * a length under 2, or a zero dimension end the walk unreadable (the tier cap applies).
185
+ */
186
+ function jpegStep(b) {
187
+ if (b.length < 9 || b[0] !== 0xff)
188
+ return undefined;
189
+ const marker = b[1];
190
+ if (marker === 0xff)
191
+ return 1;
192
+ if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7))
193
+ return 2;
194
+ if (marker === 0x00 || marker === 0xd8 || marker === 0xd9 || marker === 0xda)
195
+ return undefined;
196
+ if (JPEG_FRAME_MARKERS.has(marker)) {
197
+ const width = b.readUInt16BE(7);
198
+ const height = b.readUInt16BE(5);
199
+ return width > 0 && height > 0 ? [width, height] : undefined;
200
+ }
201
+ const length = b.readUInt16BE(2);
202
+ return length >= 2 ? 2 + length : undefined;
203
+ }
204
+ const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
205
+ /**
206
+ * `length` bytes of the image from byte `offset`, decoded from only the base64 quads that
207
+ * hold them; undefined when those characters are not plain base64 (whitespace would shift
208
+ * every later offset, so the walk stops rather than read the wrong bytes).
209
+ */
210
+ function base64Slice(base64, offset, length) {
211
+ const start = Math.floor(offset / 3) * 4;
212
+ const chars = base64.slice(start, Math.ceil((offset + length) / 3) * 4);
213
+ if (!BASE64_RE.test(chars))
214
+ return undefined;
215
+ const skip = offset - (start / 4) * 3;
216
+ const bytes = Buffer.from(chars, "base64").subarray(skip, skip + length);
217
+ return bytes.length === length ? bytes : undefined;
218
+ }
158
219
  //# sourceMappingURL=image-tokens.js.map