claude-code-infinite 1.0.28 → 1.0.30
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 +96 -19
- package/dist/auto-update.d.ts +2 -0
- package/dist/auto-update.d.ts.map +1 -1
- package/dist/auto-update.js +4 -0
- package/dist/auto-update.js.map +1 -1
- package/dist/claude-auth.d.ts +27 -2
- package/dist/claude-auth.d.ts.map +1 -1
- package/dist/claude-auth.js +76 -4
- package/dist/claude-auth.js.map +1 -1
- package/dist/claude-install.d.ts +73 -0
- package/dist/claude-install.d.ts.map +1 -0
- package/dist/claude-install.js +142 -0
- package/dist/claude-install.js.map +1 -0
- package/dist/claude-plan-warning.d.ts +70 -7
- package/dist/claude-plan-warning.d.ts.map +1 -1
- package/dist/claude-plan-warning.js +152 -8
- package/dist/claude-plan-warning.js.map +1 -1
- package/dist/cli-lifecycle.d.ts +45 -1
- package/dist/cli-lifecycle.d.ts.map +1 -1
- package/dist/cli-lifecycle.js +59 -0
- package/dist/cli-lifecycle.js.map +1 -1
- package/dist/cli-main.js +136 -29
- package/dist/cli-main.js.map +1 -1
- package/dist/count-tokens.d.ts +15 -0
- package/dist/count-tokens.d.ts.map +1 -1
- package/dist/count-tokens.js +23 -1
- package/dist/count-tokens.js.map +1 -1
- package/dist/diagnostics-schema.d.ts +4 -0
- package/dist/diagnostics-schema.d.ts.map +1 -1
- package/dist/diagnostics-schema.js +25 -0
- package/dist/diagnostics-schema.js.map +1 -1
- package/dist/diagnostics.d.ts +24 -1
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +86 -5
- package/dist/diagnostics.js.map +1 -1
- package/dist/final-index.d.ts +7 -1
- package/dist/final-index.d.ts.map +1 -1
- package/dist/final-index.js +10 -3
- package/dist/final-index.js.map +1 -1
- package/dist/image-refs.d.ts +51 -0
- package/dist/image-refs.d.ts.map +1 -0
- package/dist/image-refs.js +169 -0
- package/dist/image-refs.js.map +1 -0
- package/dist/image-tokens.d.ts +37 -0
- package/dist/image-tokens.d.ts.map +1 -0
- package/dist/image-tokens.js +158 -0
- package/dist/image-tokens.js.map +1 -0
- package/dist/install-ping.d.ts +54 -0
- package/dist/install-ping.d.ts.map +1 -0
- package/dist/install-ping.js +177 -0
- package/dist/install-ping.js.map +1 -0
- package/dist/memtree-body.d.ts +52 -0
- package/dist/memtree-body.d.ts.map +1 -0
- package/dist/memtree-body.js +99 -0
- package/dist/memtree-body.js.map +1 -0
- package/dist/memtree-finder.d.ts +2 -1
- package/dist/memtree-finder.d.ts.map +1 -1
- package/dist/memtree-finder.js +4 -5
- package/dist/memtree-finder.js.map +1 -1
- package/dist/memtree-mcp.d.ts.map +1 -1
- package/dist/memtree-mcp.js +17 -7
- package/dist/memtree-mcp.js.map +1 -1
- package/dist/memtree-times.d.ts +35 -0
- package/dist/memtree-times.d.ts.map +1 -0
- package/dist/memtree-times.js +166 -0
- package/dist/memtree-times.js.map +1 -0
- package/dist/memtree-tools.d.ts +12 -0
- package/dist/memtree-tools.d.ts.map +1 -1
- package/dist/memtree-tools.js +63 -7
- package/dist/memtree-tools.js.map +1 -1
- package/dist/memtree.d.ts +77 -3
- package/dist/memtree.d.ts.map +1 -1
- package/dist/memtree.js +267 -53
- package/dist/memtree.js.map +1 -1
- package/dist/paused-session.d.ts.map +1 -1
- package/dist/paused-session.js +11 -8
- package/dist/paused-session.js.map +1 -1
- package/dist/postinstall.d.ts +2 -0
- package/dist/postinstall.d.ts.map +1 -0
- package/dist/postinstall.js +4 -0
- package/dist/postinstall.js.map +1 -0
- package/dist/prefix-store.d.ts +2 -0
- package/dist/prefix-store.d.ts.map +1 -1
- package/dist/prefix-store.js +4 -0
- package/dist/prefix-store.js.map +1 -1
- package/dist/prepared-compression.d.ts +78 -0
- package/dist/prepared-compression.d.ts.map +1 -0
- package/dist/prepared-compression.js +127 -0
- package/dist/prepared-compression.js.map +1 -0
- package/dist/proxy.d.ts +370 -13
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +790 -92
- package/dist/proxy.js.map +1 -1
- package/dist/rate-limit.d.ts +64 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +126 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/reqlog.d.ts +66 -2
- package/dist/reqlog.d.ts.map +1 -1
- package/dist/reqlog.js.map +1 -1
- package/dist/request-sizing.d.ts +21 -2
- package/dist/request-sizing.d.ts.map +1 -1
- package/dist/request-sizing.js +57 -11
- package/dist/request-sizing.js.map +1 -1
- package/dist/route-identity.d.ts +27 -0
- package/dist/route-identity.d.ts.map +1 -1
- package/dist/route-identity.js +88 -8
- package/dist/route-identity.js.map +1 -1
- package/dist/token-estimate.d.ts +3 -20
- package/dist/token-estimate.d.ts.map +1 -1
- package/dist/token-estimate.js +9 -50
- package/dist/token-estimate.js.map +1 -1
- package/dist/tool-output-store.d.ts +12 -0
- package/dist/tool-output-store.d.ts.map +1 -0
- package/dist/tool-output-store.js +146 -0
- package/dist/tool-output-store.js.map +1 -0
- package/dist/tool-result-truncation.d.ts +32 -0
- package/dist/tool-result-truncation.d.ts.map +1 -0
- package/dist/tool-result-truncation.js +135 -0
- package/dist/tool-result-truncation.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image references on MemTree calls: images MemTree already stores are not uploaded again.
|
|
3
|
+
*
|
|
4
|
+
* Every index and compress call sends the whole conversation, so a screenshot-heavy
|
|
5
|
+
* session re-sent every image as base64 on every call (2026-10: 8.7, 12.5 and 18.5 MB
|
|
6
|
+
* bodies for 194k-384k tokens of context), heading for the 32 MiB HTTP/1 limit. A
|
|
7
|
+
* server that resolves references lists, on each answer, the request's images it
|
|
8
|
+
* stores (`memtree_image_sha256s`); later calls send those images as their block with
|
|
9
|
+
* `source.data` replaced by IMAGE_REF_PREFIX + the sha256 of the image bytes, and the
|
|
10
|
+
* server puts the bytes back before reading the request, so hashing, billing and what
|
|
11
|
+
* it returns are unchanged. Only the MemTree upload changes: what ccc forwards to
|
|
12
|
+
* Anthropic never carries a reference. Servers that never list stored images (older
|
|
13
|
+
* ones, or with the image store off) only ever get bytes.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
export const IMAGE_REF_PREFIX = "memtree-image:sha256:";
|
|
17
|
+
/** Declared in x-client-features: this client reads STORED_IMAGES_FIELD. */
|
|
18
|
+
export const IMAGE_REFS_FEATURE = "image-refs";
|
|
19
|
+
/** Answer field: shas of the request's images the server stores. */
|
|
20
|
+
export const STORED_IMAGES_FIELD = "memtree_image_sha256s";
|
|
21
|
+
/** 409 detail field: references the server could not serve. */
|
|
22
|
+
export const MISSING_IMAGES_FIELD = "missing_image_sha256s";
|
|
23
|
+
/** The server's answer to references it cannot serve; the call is sent again with bytes. */
|
|
24
|
+
export const MISSING_IMAGES_STATUS = 409;
|
|
25
|
+
/** Stored shas remembered (a 1M-token window holds ~600 images). */
|
|
26
|
+
const KNOWN_MAX = 4096;
|
|
27
|
+
/** Base64 strings whose sha is remembered, by total length (memory bound). */
|
|
28
|
+
const SHA_CACHE_MAX_CHARS = 64 * 1024 * 1024;
|
|
29
|
+
const SHA_HEX_RE = /^[0-9a-f]{64}$/;
|
|
30
|
+
const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
31
|
+
export class ImageRefs {
|
|
32
|
+
known = new Map();
|
|
33
|
+
shaOf = new Map();
|
|
34
|
+
shaChars = 0;
|
|
35
|
+
disabled = false;
|
|
36
|
+
/** Whether calls may carry references at all (the server listed stored images). */
|
|
37
|
+
get active() {
|
|
38
|
+
return !this.disabled && this.known.size > 0;
|
|
39
|
+
}
|
|
40
|
+
/** Learn the stored images an answer lists; a server that lists none changes nothing. */
|
|
41
|
+
noteAnswer(json) {
|
|
42
|
+
const listed = json?.[STORED_IMAGES_FIELD];
|
|
43
|
+
if (!Array.isArray(listed))
|
|
44
|
+
return;
|
|
45
|
+
for (const sha of listed) {
|
|
46
|
+
if (typeof sha !== "string" || !SHA_HEX_RE.test(sha))
|
|
47
|
+
continue;
|
|
48
|
+
this.known.delete(sha);
|
|
49
|
+
this.known.set(sha, true);
|
|
50
|
+
}
|
|
51
|
+
while (this.known.size > KNOWN_MAX) {
|
|
52
|
+
const first = this.known.keys().next().value;
|
|
53
|
+
if (first === undefined)
|
|
54
|
+
break;
|
|
55
|
+
this.known.delete(first);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** The server could not serve these (or, without a list, any): send bytes for them. */
|
|
59
|
+
forget(shas) {
|
|
60
|
+
for (const sha of shas)
|
|
61
|
+
this.known.delete(sha);
|
|
62
|
+
}
|
|
63
|
+
/** The server refused references outright (an older or misconfigured server). */
|
|
64
|
+
disable() {
|
|
65
|
+
this.disabled = true;
|
|
66
|
+
this.known.clear();
|
|
67
|
+
}
|
|
68
|
+
/** `messages` with each stored image as a reference; copy on write. */
|
|
69
|
+
referenceMessages(messages) {
|
|
70
|
+
if (!this.active)
|
|
71
|
+
return { value: messages, refs: [] };
|
|
72
|
+
const refs = [];
|
|
73
|
+
const value = messages.map((message) => {
|
|
74
|
+
const result = this.referenceMessage(message);
|
|
75
|
+
refs.push(...result.refs);
|
|
76
|
+
return result.value;
|
|
77
|
+
});
|
|
78
|
+
return refs.length ? { value, refs } : { value: messages, refs };
|
|
79
|
+
}
|
|
80
|
+
/** One message with each stored image as a reference; the same object if none is. */
|
|
81
|
+
referenceMessage(message) {
|
|
82
|
+
if (!this.active || !Array.isArray(message?.content))
|
|
83
|
+
return { value: message, refs: [] };
|
|
84
|
+
const refs = [];
|
|
85
|
+
const content = this.referenceBlocks(message.content, refs);
|
|
86
|
+
return { value: content === message.content ? message : { ...message, content }, refs };
|
|
87
|
+
}
|
|
88
|
+
referenceBlocks(blocks, refs) {
|
|
89
|
+
let changed = false;
|
|
90
|
+
const out = blocks.map((block) => {
|
|
91
|
+
const next = this.referenceBlock(block, refs);
|
|
92
|
+
if (next !== block)
|
|
93
|
+
changed = true;
|
|
94
|
+
return next;
|
|
95
|
+
});
|
|
96
|
+
return changed ? out : blocks;
|
|
97
|
+
}
|
|
98
|
+
referenceBlock(block, refs) {
|
|
99
|
+
if (block?.type === "tool_result" && Array.isArray(block.content)) {
|
|
100
|
+
const content = this.referenceBlocks(block.content, refs);
|
|
101
|
+
return content === block.content ? block : { ...block, content };
|
|
102
|
+
}
|
|
103
|
+
const source = block?.type === "image" ? block.source : undefined;
|
|
104
|
+
if (source?.type !== "base64" || typeof source.data !== "string")
|
|
105
|
+
return block;
|
|
106
|
+
const sha = this.storedSha(source.data);
|
|
107
|
+
if (sha === undefined)
|
|
108
|
+
return block;
|
|
109
|
+
refs.push(sha);
|
|
110
|
+
return { ...block, source: { ...source, data: IMAGE_REF_PREFIX + sha } };
|
|
111
|
+
}
|
|
112
|
+
/** The image's sha when the server stores it and its base64 re-encodes identically. */
|
|
113
|
+
storedSha(data) {
|
|
114
|
+
let sha = this.shaOf.get(data);
|
|
115
|
+
if (sha === undefined) {
|
|
116
|
+
if (!isCanonicalBase64(data))
|
|
117
|
+
return undefined;
|
|
118
|
+
sha = createHash("sha256").update(Buffer.from(data, "base64")).digest("hex");
|
|
119
|
+
this.rememberSha(data, sha);
|
|
120
|
+
}
|
|
121
|
+
return this.known.has(sha) ? sha : undefined;
|
|
122
|
+
}
|
|
123
|
+
rememberSha(data, sha) {
|
|
124
|
+
if (data.length > SHA_CACHE_MAX_CHARS)
|
|
125
|
+
return;
|
|
126
|
+
this.shaOf.set(data, sha);
|
|
127
|
+
this.shaChars += data.length;
|
|
128
|
+
while (this.shaChars > SHA_CACHE_MAX_CHARS) {
|
|
129
|
+
const first = this.shaOf.keys().next().value;
|
|
130
|
+
if (first === undefined)
|
|
131
|
+
break;
|
|
132
|
+
this.shaOf.delete(first);
|
|
133
|
+
this.shaChars -= first.length;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** The shas a refusal names (409 detail), or undefined when it names none. */
|
|
138
|
+
export function missingImageShas(bodyText) {
|
|
139
|
+
try {
|
|
140
|
+
const listed = JSON.parse(bodyText)?.detail?.[MISSING_IMAGES_FIELD];
|
|
141
|
+
if (!Array.isArray(listed))
|
|
142
|
+
return undefined;
|
|
143
|
+
return listed.filter((sha) => typeof sha === "string");
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* A 400 about references: "never" when this key may not send any (stop for the
|
|
151
|
+
* process), "this" when only this request's were refused (too many: send its bytes).
|
|
152
|
+
*/
|
|
153
|
+
export function refusesImageRefs(status, bodyText) {
|
|
154
|
+
if (status !== 400 || !/image reference/i.test(bodyText))
|
|
155
|
+
return false;
|
|
156
|
+
return /own MemTree key/i.test(bodyText) ? "never" : "this";
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The server re-encodes the bytes it stores, so only base64 that decodes and re-encodes
|
|
160
|
+
* to the same text may be referenced (padding included, no whitespace).
|
|
161
|
+
*/
|
|
162
|
+
export function isCanonicalBase64(data) {
|
|
163
|
+
if (data.length === 0 || data.length % 4 !== 0 || !BASE64_RE.test(data))
|
|
164
|
+
return false;
|
|
165
|
+
// Only the last quantum can hide non-zero padding bits ("QR==" decodes like "QQ==").
|
|
166
|
+
const tail = data.slice(-4);
|
|
167
|
+
return Buffer.from(tail, "base64").toString("base64") === tail;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=image-refs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-refs.js","sourceRoot":"","sources":["../src/image-refs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an inline image costs, from its header alone (polychat
|
|
3
|
+
* image_tokens.py mirrors the same rule): Anthropic counts
|
|
4
|
+
* ceil(w / 28) * ceil(h / 28) visual tokens after scaling the image down,
|
|
5
|
+
* aspect kept, to its model tier's limits (Claude 4.7+ and unknown models:
|
|
6
|
+
* long edge 2576 px and 4,784 tokens; older Claude: 1568 px and 1,568).
|
|
7
|
+
* A 574x551 screenshot is 420 tokens, not the ~74k its base64 would be as text.
|
|
8
|
+
*/
|
|
9
|
+
declare const HIGH_RES: {
|
|
10
|
+
maxEdge: number;
|
|
11
|
+
maxTokens: number;
|
|
12
|
+
};
|
|
13
|
+
export interface ImageTotals {
|
|
14
|
+
/** Base64 characters of the inline images (their JSON bytes). */
|
|
15
|
+
imageBytes: number;
|
|
16
|
+
/** Their visual tokens by the rule above (the tier cap when a header is unreadable). */
|
|
17
|
+
imageTokens: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Inline images among a request body's message (and tool result) content
|
|
21
|
+
* blocks; `model` picks the tier. Image-shaped JSON anywhere else (tool
|
|
22
|
+
* inputs, schemas, system) is ordinary text.
|
|
23
|
+
*/
|
|
24
|
+
export declare function bodyImages(body: Buffer, model: string | undefined): ImageTotals;
|
|
25
|
+
/** Remember `totals` for another copy of the same bytes. */
|
|
26
|
+
export declare function rememberImages(body: Buffer, model: string | undefined, totals: ImageTotals): void;
|
|
27
|
+
/** Visual tokens of a w x h image in `tier` (polychat visual_tokens). */
|
|
28
|
+
export declare function visualTokens(width: number, height: number, tier?: {
|
|
29
|
+
maxEdge: number;
|
|
30
|
+
maxTokens: number;
|
|
31
|
+
}): number;
|
|
32
|
+
/** Claude before 4.7 is standard; 4.7+, other and unknown models are high-res. */
|
|
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. */
|
|
35
|
+
export declare function imageDimensions(base64: string): [number, number] | undefined;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=image-tokens.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an inline image costs, from its header alone (polychat
|
|
3
|
+
* image_tokens.py mirrors the same rule): Anthropic counts
|
|
4
|
+
* ceil(w / 28) * ceil(h / 28) visual tokens after scaling the image down,
|
|
5
|
+
* aspect kept, to its model tier's limits (Claude 4.7+ and unknown models:
|
|
6
|
+
* long edge 2576 px and 4,784 tokens; older Claude: 1568 px and 1,568).
|
|
7
|
+
* A 574x551 screenshot is 420 tokens, not the ~74k its base64 would be as text.
|
|
8
|
+
*/
|
|
9
|
+
const PATCH_PX = 28;
|
|
10
|
+
const HIGH_RES = { maxEdge: 2576, maxTokens: 4_784 };
|
|
11
|
+
const STANDARD = { maxEdge: 1568, maxTokens: 1_568 };
|
|
12
|
+
/** Base64 characters decoded to find a header (48 KB of bytes). */
|
|
13
|
+
const HEADER_BASE64_CHARS = 65_536;
|
|
14
|
+
const CLAUDE_VERSION_RE = /claude-(?:opus|sonnet|haiku|fable|mythos|instant)?-?(\d{1,2})(?:[-.](\d{1,2}))?(?![0-9])/i;
|
|
15
|
+
const NO_IMAGES = Object.freeze({ imageBytes: 0, imageTokens: 0 });
|
|
16
|
+
/** Totals already worked out for a body and model: a body is parsed once,
|
|
17
|
+
* however often it is sized (a weak key: dropped with the body). */
|
|
18
|
+
const totalsByBody = new WeakMap();
|
|
19
|
+
/**
|
|
20
|
+
* Inline images among a request body's message (and tool result) content
|
|
21
|
+
* blocks; `model` picks the tier. Image-shaped JSON anywhere else (tool
|
|
22
|
+
* inputs, schemas, system) is ordinary text.
|
|
23
|
+
*/
|
|
24
|
+
export function bodyImages(body, model) {
|
|
25
|
+
const known = totalsByBody.get(body);
|
|
26
|
+
if (known && known.model === model)
|
|
27
|
+
return known.totals;
|
|
28
|
+
const totals = parseImages(body, model);
|
|
29
|
+
totalsByBody.set(body, { model, totals });
|
|
30
|
+
return totals;
|
|
31
|
+
}
|
|
32
|
+
/** Remember `totals` for another copy of the same bytes. */
|
|
33
|
+
export function rememberImages(body, model, totals) {
|
|
34
|
+
totalsByBody.set(body, { model, totals });
|
|
35
|
+
}
|
|
36
|
+
function parseImages(body, model) {
|
|
37
|
+
// Most bodies carry no image: skip the parse.
|
|
38
|
+
if (!body.includes('"image"'))
|
|
39
|
+
return NO_IMAGES;
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(body.toString("utf-8"));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return NO_IMAGES;
|
|
46
|
+
}
|
|
47
|
+
const totals = { imageBytes: 0, imageTokens: 0 };
|
|
48
|
+
const tier = imageTier(model);
|
|
49
|
+
const messages = parsed?.messages;
|
|
50
|
+
if (!Array.isArray(messages))
|
|
51
|
+
return totals;
|
|
52
|
+
for (const message of messages) {
|
|
53
|
+
const content = message?.content;
|
|
54
|
+
if (Array.isArray(content))
|
|
55
|
+
addImages(content, tier, totals);
|
|
56
|
+
}
|
|
57
|
+
return Object.freeze(totals);
|
|
58
|
+
}
|
|
59
|
+
/** Visual tokens of a w x h image in `tier` (polychat visual_tokens). */
|
|
60
|
+
export function visualTokens(width, height, tier = HIGH_RES) {
|
|
61
|
+
if (!(width > 0) || !(height > 0))
|
|
62
|
+
return tier.maxTokens;
|
|
63
|
+
const longest = Math.max(width, height);
|
|
64
|
+
let longEdge = Math.max(1, Math.floor(longest * Math.min(1, tier.maxEdge / longest)));
|
|
65
|
+
while (longEdge > 1) {
|
|
66
|
+
const scale = longEdge / longest;
|
|
67
|
+
const tokens = Math.ceil(Math.max(1, Math.floor(width * scale)) / PATCH_PX) *
|
|
68
|
+
Math.ceil(Math.max(1, Math.floor(height * scale)) / PATCH_PX);
|
|
69
|
+
if (tokens <= tier.maxTokens)
|
|
70
|
+
return tokens;
|
|
71
|
+
longEdge -= Math.max(1, Math.floor(longEdge / 200));
|
|
72
|
+
}
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
/** Claude before 4.7 is standard; 4.7+, other and unknown models are high-res. */
|
|
76
|
+
export function imageTier(model) {
|
|
77
|
+
const match = model ? CLAUDE_VERSION_RE.exec(model) : null;
|
|
78
|
+
if (!match)
|
|
79
|
+
return HIGH_RES;
|
|
80
|
+
const major = Number(match[1]);
|
|
81
|
+
const minor = match[2] ? Number(match[2]) : 0;
|
|
82
|
+
return major > 4 || (major === 4 && minor >= 7) ? HIGH_RES : STANDARD;
|
|
83
|
+
}
|
|
84
|
+
/** Width and height from a PNG, GIF, WebP or JPEG header; undefined when unreadable. */
|
|
85
|
+
export function imageDimensions(base64) {
|
|
86
|
+
let bytes;
|
|
87
|
+
try {
|
|
88
|
+
bytes = Buffer.from(base64.slice(0, HEADER_BASE64_CHARS), "base64");
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
return pngSize(bytes) ?? gifSize(bytes) ?? webpSize(bytes) ?? jpegSize(bytes);
|
|
94
|
+
}
|
|
95
|
+
function addImages(blocks, tier, totals) {
|
|
96
|
+
for (const item of blocks) {
|
|
97
|
+
if (!item || typeof item !== "object")
|
|
98
|
+
continue;
|
|
99
|
+
const block = item;
|
|
100
|
+
const source = block.source;
|
|
101
|
+
if (block.type === "image" && source && typeof source === "object") {
|
|
102
|
+
const data = source.type === "base64" && typeof source.data === "string" ? source.data : "";
|
|
103
|
+
totals.imageBytes += data.length;
|
|
104
|
+
const size = data ? imageDimensions(data) : undefined;
|
|
105
|
+
totals.imageTokens += size ? visualTokens(size[0], size[1], tier) : tier.maxTokens;
|
|
106
|
+
}
|
|
107
|
+
else if (block.type === "tool_result" && Array.isArray(block.content)) {
|
|
108
|
+
addImages(block.content, tier, totals);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function pngSize(b) {
|
|
113
|
+
if (b.length < 24 || b.readUInt32BE(0) !== 0x89504e47 || b.toString("ascii", 12, 16) !== "IHDR") {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
return [b.readUInt32BE(16), b.readUInt32BE(20)];
|
|
117
|
+
}
|
|
118
|
+
function gifSize(b) {
|
|
119
|
+
if (b.length < 10 || b.toString("ascii", 0, 3) !== "GIF")
|
|
120
|
+
return undefined;
|
|
121
|
+
return [b.readUInt16LE(6), b.readUInt16LE(8)];
|
|
122
|
+
}
|
|
123
|
+
function webpSize(b) {
|
|
124
|
+
if (b.length < 30 || b.toString("ascii", 0, 4) !== "RIFF" || b.toString("ascii", 8, 12) !== "WEBP") {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
const chunk = b.toString("ascii", 12, 16);
|
|
128
|
+
if (chunk === "VP8X")
|
|
129
|
+
return [1 + b.readUIntLE(24, 3), 1 + b.readUIntLE(27, 3)];
|
|
130
|
+
if (chunk === "VP8L") {
|
|
131
|
+
const bits = b.readUInt32LE(21);
|
|
132
|
+
return [1 + (bits & 0x3fff), 1 + ((bits >> 14) & 0x3fff)];
|
|
133
|
+
}
|
|
134
|
+
if (chunk === "VP8 ")
|
|
135
|
+
return [b.readUInt16LE(26) & 0x3fff, b.readUInt16LE(28) & 0x3fff];
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
const JPEG_FRAME_MARKERS = new Set([0xc0, 0xc1, 0xc2, 0xc3, 0xc5, 0xc6, 0xc7, 0xc9, 0xca, 0xcb,
|
|
139
|
+
0xcd, 0xce, 0xcf]);
|
|
140
|
+
function jpegSize(b) {
|
|
141
|
+
if (b.length < 4 || b[0] !== 0xff || b[1] !== 0xd8)
|
|
142
|
+
return undefined;
|
|
143
|
+
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);
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=image-tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-tokens.js","sourceRoot":"","sources":["../src/image-tokens.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm install ping: when npm installs ccc (its postinstall), one anonymous
|
|
3
|
+
* "installed" diagnostics event, so polychat can see installs that never run
|
|
4
|
+
* (polychat docs/install_funnel.md).
|
|
5
|
+
*
|
|
6
|
+
* What is sent: the ccc version, Node version, OS, CPU architecture, how it
|
|
7
|
+
* was installed (npm global, local, npx, ...), whether this machine already
|
|
8
|
+
* had an install id, and that install id: the same random id ccc's
|
|
9
|
+
* diagnostics use later (diagnostics.ts), so an install joins its first run.
|
|
10
|
+
* No key, no paths, no env values.
|
|
11
|
+
*
|
|
12
|
+
* Skipped under every diagnostics opt-out (CCC_NO_DIAGNOSTICS, DO_NOT_TRACK,
|
|
13
|
+
* DISABLE_TELEMETRY, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC, "diagnostics":
|
|
14
|
+
* false), in CI, under sudo or as root (sudo drops the user's opt-outs), for
|
|
15
|
+
* `npm install` inside a ccc checkout, and for ccc's own background updates. Never fails or slows the install for long: one POST
|
|
16
|
+
* with a 2 s timeout, every error swallowed, and the process always exits 0.
|
|
17
|
+
*/
|
|
18
|
+
export declare const INSTALL_PING_TIMEOUT_MS = 2000;
|
|
19
|
+
export declare const INSTALL_PING_URL = "https://api.polychat.co/v1/ccc/diagnostics";
|
|
20
|
+
export interface InstallPingOptions {
|
|
21
|
+
env?: NodeJS.ProcessEnv;
|
|
22
|
+
/** The installed package's folder (default: the one this file is in). */
|
|
23
|
+
packageDir?: string;
|
|
24
|
+
/** ccc's config folder (default ~/.claude-code-infinite). */
|
|
25
|
+
configDir?: string;
|
|
26
|
+
platform?: NodeJS.Platform;
|
|
27
|
+
uid?: number;
|
|
28
|
+
fetchImpl?: typeof fetch;
|
|
29
|
+
now?: () => number;
|
|
30
|
+
url?: string;
|
|
31
|
+
}
|
|
32
|
+
export type InstallPingResult = "sent" | "failed" | "opted-out" | "ci" | "dev-install" | "auto-update" | "root";
|
|
33
|
+
/** Send the ping (or say why not). Never throws. */
|
|
34
|
+
export declare function runInstallPing(opts?: InstallPingOptions): Promise<InstallPingResult>;
|
|
35
|
+
/** Why this install sends nothing, or null to send. */
|
|
36
|
+
export declare function skipReason(env: NodeJS.ProcessEnv, packageDir: string, configDir: string, opts?: {
|
|
37
|
+
platform?: NodeJS.Platform;
|
|
38
|
+
uid?: number;
|
|
39
|
+
}): Exclude<InstallPingResult, "sent" | "failed"> | null;
|
|
40
|
+
/** The exact batch the ping sends (polychat's /v1/ccc/diagnostics schema 1). */
|
|
41
|
+
export declare function installBody(env: NodeJS.ProcessEnv, packageDir: string, configDir: string, opts?: InstallPingOptions): Record<string, unknown>;
|
|
42
|
+
/** npm-global, npx, local, or another package manager's global (from npm's own env). */
|
|
43
|
+
export declare function installMethod(env: NodeJS.ProcessEnv, packageDir: string): string;
|
|
44
|
+
export declare function isCi(env: NodeJS.ProcessEnv): boolean;
|
|
45
|
+
/** `npm install` run inside a ccc checkout (its own dependencies), or a git checkout. */
|
|
46
|
+
export declare function isDevInstall(env: NodeJS.ProcessEnv, packageDir: string): boolean;
|
|
47
|
+
/** Running as root or under sudo (POSIX only). */
|
|
48
|
+
export declare function isRoot(env: NodeJS.ProcessEnv, opts?: {
|
|
49
|
+
platform?: NodeJS.Platform;
|
|
50
|
+
uid?: number;
|
|
51
|
+
}): boolean;
|
|
52
|
+
/** postinstall entry: run, then exit 0 whatever happened, at most ~2.5 s later. */
|
|
53
|
+
export declare function main(): void;
|
|
54
|
+
//# sourceMappingURL=install-ping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-ping.d.ts","sourceRoot":"","sources":["../src/install-ping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAqBH,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAC7C,eAAO,MAAM,gBAAgB,+CAA+C,CAAC;AAW7E,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,IAAI,GAAG,aAAa,GACpF,aAAa,GAAG,MAAM,CAAC;AAEzB,oDAAoD;AACpD,wBAAsB,cAAc,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAqB9F;AAED,uDAAuD;AACvD,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAC7D,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GACtD,OAAO,CAAC,iBAAiB,EAAE,MAAM,GAAG,QAAQ,CAAC,GAAG,IAAI,CAStD;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAC3F,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYzB;AAED,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAQhF;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAMpD;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAIhF;AAgBD,kDAAkD;AAClD,wBAAgB,MAAM,CACpB,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9E,OAAO,CAGT;AAgCD,mFAAmF;AACnF,wBAAgB,IAAI,IAAI,IAAI,CAG3B"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm install ping: when npm installs ccc (its postinstall), one anonymous
|
|
3
|
+
* "installed" diagnostics event, so polychat can see installs that never run
|
|
4
|
+
* (polychat docs/install_funnel.md).
|
|
5
|
+
*
|
|
6
|
+
* What is sent: the ccc version, Node version, OS, CPU architecture, how it
|
|
7
|
+
* was installed (npm global, local, npx, ...), whether this machine already
|
|
8
|
+
* had an install id, and that install id: the same random id ccc's
|
|
9
|
+
* diagnostics use later (diagnostics.ts), so an install joins its first run.
|
|
10
|
+
* No key, no paths, no env values.
|
|
11
|
+
*
|
|
12
|
+
* Skipped under every diagnostics opt-out (CCC_NO_DIAGNOSTICS, DO_NOT_TRACK,
|
|
13
|
+
* DISABLE_TELEMETRY, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC, "diagnostics":
|
|
14
|
+
* false), in CI, under sudo or as root (sudo drops the user's opt-outs), for
|
|
15
|
+
* `npm install` inside a ccc checkout, and for ccc's own background updates. Never fails or slows the install for long: one POST
|
|
16
|
+
* with a 2 s timeout, every error swallowed, and the process always exits 0.
|
|
17
|
+
*/
|
|
18
|
+
import { randomUUID } from "node:crypto";
|
|
19
|
+
import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import { join, resolve, sep } from "node:path";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { AUTO_UPDATE_ENV } from "./auto-update.js";
|
|
24
|
+
import { DIAGNOSTICS_PATH, FIRST_RUN_PENDING_FILE, diagnosticsEnabled, readOrCreateInstallId, } from "./diagnostics.js";
|
|
25
|
+
import { DIAGNOSTICS_SCHEMA, sanitizeContext, sanitizeEvent, threePartVersion, } from "./diagnostics-schema.js";
|
|
26
|
+
export const INSTALL_PING_TIMEOUT_MS = 2_000;
|
|
27
|
+
export const INSTALL_PING_URL = `https://api.polychat.co${DIAGNOSTICS_PATH}`;
|
|
28
|
+
const CLIENT_NAME = "cc-infinite";
|
|
29
|
+
/** Set to anything but 0/false on CI services (the `ci-info` list, abridged). */
|
|
30
|
+
const CI_FLAGS = ["CI", "CONTINUOUS_INTEGRATION"];
|
|
31
|
+
/** Present only on a CI service. */
|
|
32
|
+
const CI_MARKERS = [
|
|
33
|
+
"BUILD_NUMBER", "RUN_ID", "GITHUB_ACTIONS", "GITLAB_CI", "BUILDKITE", "CIRCLECI", "TRAVIS",
|
|
34
|
+
"JENKINS_URL", "TF_BUILD", "TEAMCITY_VERSION", "CODEBUILD_BUILD_ID", "BITBUCKET_BUILD_NUMBER",
|
|
35
|
+
"APPVEYOR", "DRONE", "SEMAPHORE", "NETLIFY", "VERCEL", "HEROKU_TEST_RUN_ID", "CI_NAME",
|
|
36
|
+
];
|
|
37
|
+
/** Send the ping (or say why not). Never throws. */
|
|
38
|
+
export async function runInstallPing(opts = {}) {
|
|
39
|
+
try {
|
|
40
|
+
const env = opts.env ?? process.env;
|
|
41
|
+
const packageDir = opts.packageDir ?? fileURLToPath(new URL("..", import.meta.url));
|
|
42
|
+
const configDir = opts.configDir ?? join(homedir(), ".claude-code-infinite");
|
|
43
|
+
const skip = skipReason(env, packageDir, configDir, opts);
|
|
44
|
+
if (skip)
|
|
45
|
+
return skip;
|
|
46
|
+
const body = installBody(env, packageDir, configDir, opts);
|
|
47
|
+
await (opts.fetchImpl ?? fetch)(opts.url ?? INSTALL_PING_URL, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: {
|
|
50
|
+
"content-type": "application/json", "x-client": CLIENT_NAME,
|
|
51
|
+
"x-client-version": packageVersion(packageDir) ?? "unknown",
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify(body),
|
|
54
|
+
signal: AbortSignal.timeout(INSTALL_PING_TIMEOUT_MS),
|
|
55
|
+
});
|
|
56
|
+
return "sent";
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return "failed";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Why this install sends nothing, or null to send. */
|
|
63
|
+
export function skipReason(env, packageDir, configDir, opts = {}) {
|
|
64
|
+
// sudo resets the env and (on Linux) HOME, so the user's opt-outs can't be read: skip.
|
|
65
|
+
// Nothing is written either (a root-owned config folder would break ccc later).
|
|
66
|
+
if (isRoot(env, opts))
|
|
67
|
+
return "root";
|
|
68
|
+
if (!diagnosticsEnabled(env, readConfig(configDir)))
|
|
69
|
+
return "opted-out";
|
|
70
|
+
if (isCi(env))
|
|
71
|
+
return "ci";
|
|
72
|
+
if (env[AUTO_UPDATE_ENV])
|
|
73
|
+
return "auto-update";
|
|
74
|
+
if (isDevInstall(env, packageDir))
|
|
75
|
+
return "dev-install";
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/** The exact batch the ping sends (polychat's /v1/ccc/diagnostics schema 1). */
|
|
79
|
+
export function installBody(env, packageDir, configDir, opts = {}) {
|
|
80
|
+
const platform = opts.platform ?? process.platform;
|
|
81
|
+
const install = installIdForPing(configDir);
|
|
82
|
+
const method = installMethod(env, packageDir);
|
|
83
|
+
const context = sanitizeContext({
|
|
84
|
+
ccc_version: threePartVersion(packageVersion(packageDir) ?? ""),
|
|
85
|
+
node: threePartVersion(process.versions.node), os: platform, arch: process.arch,
|
|
86
|
+
install_method: method,
|
|
87
|
+
});
|
|
88
|
+
const fields = install.created === undefined ? {} : { new_install: install.created };
|
|
89
|
+
const event = sanitizeEvent("installed", fields, (opts.now ?? Date.now)());
|
|
90
|
+
return { schema: DIAGNOSTICS_SCHEMA, install_id: install.id, context, events: [event] };
|
|
91
|
+
}
|
|
92
|
+
/** npm-global, npx, local, or another package manager's global (from npm's own env). */
|
|
93
|
+
export function installMethod(env, packageDir) {
|
|
94
|
+
const p = packageDir.split(sep).join("/").toLowerCase();
|
|
95
|
+
if (p.includes("/_npx/"))
|
|
96
|
+
return "npx";
|
|
97
|
+
const agent = (env.npm_config_user_agent ?? "").split("/")[0];
|
|
98
|
+
if (agent === "pnpm" || agent === "yarn" || agent === "bun")
|
|
99
|
+
return agent;
|
|
100
|
+
if (/\/\.?volta\//.test(p))
|
|
101
|
+
return "volta";
|
|
102
|
+
if (env.npm_config_global === "true" || env.npm_config_location === "global")
|
|
103
|
+
return "npm-global";
|
|
104
|
+
return "local";
|
|
105
|
+
}
|
|
106
|
+
export function isCi(env) {
|
|
107
|
+
if (CI_FLAGS.some((name) => {
|
|
108
|
+
const value = env[name];
|
|
109
|
+
return value !== undefined && !/^(0|false|no|off|)$/i.test(value.trim());
|
|
110
|
+
}))
|
|
111
|
+
return true;
|
|
112
|
+
return CI_MARKERS.some((name) => env[name] !== undefined);
|
|
113
|
+
}
|
|
114
|
+
/** `npm install` run inside a ccc checkout (its own dependencies), or a git checkout. */
|
|
115
|
+
export function isDevInstall(env, packageDir) {
|
|
116
|
+
if (existsSync(join(packageDir, ".git")))
|
|
117
|
+
return true;
|
|
118
|
+
const initCwd = env.INIT_CWD;
|
|
119
|
+
return initCwd !== undefined && samePath(initCwd, packageDir);
|
|
120
|
+
}
|
|
121
|
+
/** The install id (created, with the first-run marker, when missing); random when unwritable. */
|
|
122
|
+
function installIdForPing(configDir) {
|
|
123
|
+
const install = readOrCreateInstallId(configDir);
|
|
124
|
+
if (install === null)
|
|
125
|
+
return { id: randomUUID() };
|
|
126
|
+
if (install.created) {
|
|
127
|
+
try {
|
|
128
|
+
writeFileSync(join(configDir, FIRST_RUN_PENDING_FILE), "\n", { mode: 0o600 });
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// ccc's first launch then just doesn't record first_run.
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return install;
|
|
135
|
+
}
|
|
136
|
+
/** Running as root or under sudo (POSIX only). */
|
|
137
|
+
export function isRoot(env, opts = {}) {
|
|
138
|
+
if ((opts.platform ?? process.platform) === "win32")
|
|
139
|
+
return false;
|
|
140
|
+
return (opts.uid ?? process.getuid?.()) === 0 || env.SUDO_UID !== undefined;
|
|
141
|
+
}
|
|
142
|
+
function readConfig(configDir) {
|
|
143
|
+
try {
|
|
144
|
+
const parsed = JSON.parse(readFileSync(join(configDir, "config.json"), "utf-8"));
|
|
145
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return {};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function packageVersion(packageDir) {
|
|
152
|
+
try {
|
|
153
|
+
const version = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf-8")).version;
|
|
154
|
+
return typeof version === "string" ? version : null;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function samePath(a, b) {
|
|
161
|
+
const real = (p) => {
|
|
162
|
+
try {
|
|
163
|
+
return realpathSync(p);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return resolve(p);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const norm = (p) => (process.platform === "win32" ? real(p).toLowerCase() : real(p));
|
|
170
|
+
return norm(a) === norm(b);
|
|
171
|
+
}
|
|
172
|
+
/** postinstall entry: run, then exit 0 whatever happened, at most ~2.5 s later. */
|
|
173
|
+
export function main() {
|
|
174
|
+
setTimeout(() => process.exit(0), INSTALL_PING_TIMEOUT_MS + 500).unref();
|
|
175
|
+
void runInstallPing().finally(() => process.exit(0));
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=install-ping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-ping.js","sourceRoot":"","sources":["../src/install-ping.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAC;AAC7C,MAAM,CAAC,MAAM,gBAAgB,GAAG,0BAA0B,gBAAgB,EAAE,CAAC;AAC7E,MAAM,WAAW,GAAG,aAAa,CAAC;AAClC,iFAAiF;AACjF,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC;AAClD,oCAAoC;AACpC,MAAM,UAAU,GAAG;IACjB,cAAc,EAAE,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ;IAC1F,aAAa,EAAE,UAAU,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,wBAAwB;IAC7F,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,EAAE,SAAS;CACvF,CAAC;AAkBF,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA2B,EAAE;IAChE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;QACpC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,aAAa,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACpF,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,uBAAuB,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QAC1D,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QAC3D,MAAM,CAAC,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,gBAAgB,EAAE;YAC5D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB,EAAE,UAAU,EAAE,WAAW;gBAC3D,kBAAkB,EAAE,cAAc,CAAC,UAAU,CAAC,IAAI,SAAS;aAC5D;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,uBAAuB,CAAC;SACrD,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,UAAU,CACxB,GAAsB,EAAE,UAAkB,EAAE,SAAiB,EAC7D,OAAqD,EAAE;IAEvD,uFAAuF;IACvF,gFAAgF;IAChF,IAAI,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IACrC,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;QAAE,OAAO,WAAW,CAAC;IACxE,IAAI,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,GAAG,CAAC,eAAe,CAAC;QAAE,OAAO,aAAa,CAAC;IAC/C,IAAI,YAAY,CAAC,GAAG,EAAE,UAAU,CAAC;QAAE,OAAO,aAAa,CAAC;IACxD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,WAAW,CACzB,GAAsB,EAAE,UAAkB,EAAE,SAAiB,EAAE,OAA2B,EAAE;IAE5F,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACnD,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,eAAe,CAAC;QAC9B,WAAW,EAAE,gBAAgB,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QAC/D,IAAI,EAAE,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI;QAC/E,cAAc,EAAE,MAAM;KACvB,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;IACrF,MAAM,KAAK,GAAG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC3E,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;AAC1F,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,GAAsB,EAAE,UAAkB;IACtE,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IACxD,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC1E,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3C,IAAI,GAAG,CAAC,iBAAiB,KAAK,MAAM,IAAI,GAAG,CAAC,mBAAmB,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC;IAClG,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,GAAsB;IACzC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACzB,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,OAAO,KAAK,KAAK,SAAS,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAChB,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC;AAC5D,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,GAAsB,EAAE,UAAkB;IACrE,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC7B,OAAO,OAAO,KAAK,SAAS,IAAI,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAChE,CAAC;AAED,iGAAiG;AACjG,SAAS,gBAAgB,CAAC,SAAiB;IACzC,MAAM,OAAO,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC;IAClD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,sBAAsB,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAChF,CAAC;QAAC,MAAM,CAAC;YACP,yDAAyD;QAC3D,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,MAAM,CACpB,GAAsB,EAAE,OAAqD,EAAE;IAE/E,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAClE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC;AAC9E,CAAC;AAED,SAAS,UAAU,CAAC,SAAiB;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACjF,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,UAAkB;IACxC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5F,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,CAAS;IACpC,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE;QACzB,IAAI,CAAC;YACH,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7F,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,IAAI;IAClB,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,uBAAuB,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC;IACzE,KAAK,cAAc,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC"}
|