claude-code-infinite 1.0.19 → 1.0.21
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 +95 -5
- package/dist/auto-update-runner.d.ts +52 -0
- package/dist/auto-update-runner.d.ts.map +1 -0
- package/dist/auto-update-runner.js +307 -0
- package/dist/auto-update-runner.js.map +1 -0
- package/dist/auto-update-setup.d.ts +38 -0
- package/dist/auto-update-setup.d.ts.map +1 -0
- package/dist/auto-update-setup.js +72 -0
- package/dist/auto-update-setup.js.map +1 -0
- package/dist/auto-update.d.ts +170 -0
- package/dist/auto-update.d.ts.map +1 -0
- package/dist/auto-update.js +401 -0
- package/dist/auto-update.js.map +1 -0
- package/dist/cc-request.d.ts +17 -0
- package/dist/cc-request.d.ts.map +1 -1
- package/dist/cc-request.js +22 -0
- package/dist/cc-request.js.map +1 -1
- package/dist/cli-main.js +35 -7
- package/dist/cli-main.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/count-tokens.d.ts +38 -3
- package/dist/count-tokens.d.ts.map +1 -1
- package/dist/count-tokens.js +88 -19
- package/dist/count-tokens.js.map +1 -1
- package/dist/memtree-finder.d.ts +11 -0
- package/dist/memtree-finder.d.ts.map +1 -1
- package/dist/memtree-finder.js +18 -0
- package/dist/memtree-finder.js.map +1 -1
- package/dist/memtree-mcp-config.d.ts.map +1 -1
- package/dist/memtree-mcp-config.js +4 -2
- package/dist/memtree-mcp-config.js.map +1 -1
- package/dist/memtree-mcp.d.ts +24 -4
- package/dist/memtree-mcp.d.ts.map +1 -1
- package/dist/memtree-mcp.js +47 -14
- package/dist/memtree-mcp.js.map +1 -1
- package/dist/memtree-tools.d.ts +17 -1
- package/dist/memtree-tools.d.ts.map +1 -1
- package/dist/memtree-tools.js +50 -2
- package/dist/memtree-tools.js.map +1 -1
- package/dist/memtree.d.ts +10 -1
- package/dist/memtree.d.ts.map +1 -1
- package/dist/memtree.js +54 -19
- package/dist/memtree.js.map +1 -1
- package/dist/notices.d.ts +6 -1
- package/dist/notices.d.ts.map +1 -1
- package/dist/notices.js +16 -4
- package/dist/notices.js.map +1 -1
- package/dist/proxy.d.ts +17 -1
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +137 -53
- package/dist/proxy.js.map +1 -1
- package/dist/reqlog.d.ts +10 -0
- 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 +60 -7
- package/dist/request-sizing.js.map +1 -1
- package/dist/token-estimate.d.ts +35 -0
- package/dist/token-estimate.d.ts.map +1 -0
- package/dist/token-estimate.js +97 -0
- package/dist/token-estimate.js.map +1 -0
- package/dist/update-check.d.ts +23 -4
- package/dist/update-check.d.ts.map +1 -1
- package/dist/update-check.js +79 -12
- package/dist/update-check.js.map +1 -1
- package/dist/update-lock.d.ts +68 -0
- package/dist/update-lock.d.ts.map +1 -0
- package/dist/update-lock.js +176 -0
- package/dist/update-lock.js.map +1 -0
- package/package.json +1 -1
package/dist/update-check.js
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Startup check for a newer claude-code-infinite release on npm.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The check only detects; installing is auto-update.ts, which acts on this
|
|
5
|
+
* result in the background where that is safe. The result is surfaced
|
|
6
6
|
* inside Claude Code through the SessionStart banner (see notices.ts) — the
|
|
7
7
|
* pre-launch terminal is covered by the TUI within a second, so a line printed
|
|
8
8
|
* there is never read. Bounded by a short timeout and silent on every failure
|
|
9
9
|
* (registry down, offline, malformed JSON, timeout) so startup is never gated
|
|
10
|
-
* on the registry.
|
|
10
|
+
* on the registry. The registry's answer is cached for a day, so most starts
|
|
11
|
+
* make no request at all. `CCC_NO_UPDATE_CHECK=1` (or the older
|
|
12
|
+
* `CCC_SKIP_UPDATE_CHECK=1`), or `"updateCheck": false` in
|
|
13
|
+
* ~/.claude-code-infinite/config.json, disables the check entirely (and
|
|
14
|
+
* with it auto-update).
|
|
11
15
|
*/
|
|
16
|
+
import * as fs from "node:fs";
|
|
17
|
+
import * as path from "node:path";
|
|
12
18
|
export const PACKAGE_NAME = "claude-code-infinite";
|
|
13
19
|
export const NPM_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
20
|
+
export const NO_UPDATE_CHECK_ENV = "CCC_NO_UPDATE_CHECK";
|
|
21
|
+
/** The original opt-out name, still honored. */
|
|
14
22
|
export const SKIP_UPDATE_CHECK_ENV = "CCC_SKIP_UPDATE_CHECK";
|
|
15
|
-
export const UPGRADE_COMMAND = `npm
|
|
16
|
-
const UPDATE_CHECK_TIMEOUT_MS =
|
|
23
|
+
export const UPGRADE_COMMAND = `npm i -g ${PACKAGE_NAME}@latest`;
|
|
24
|
+
export const UPDATE_CHECK_TIMEOUT_MS = 1500;
|
|
25
|
+
export const UPDATE_CHECK_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
26
|
+
export const UPDATE_CHECK_CACHE_FILE = "update-check.json";
|
|
17
27
|
// Dotted numeric core with an optional pre-release/build suffix, as npm
|
|
18
28
|
// publishes it. Anything else (a tag name, an error page) is ignored.
|
|
19
29
|
const VERSION_RE = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9A-Za-z.-]{0,40})?$/;
|
|
@@ -22,12 +32,36 @@ const VERSION_RE = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9A-Za-z.-]{0,40})?$/;
|
|
|
22
32
|
* of `currentVersion`, else null. Never rejects.
|
|
23
33
|
*/
|
|
24
34
|
export async function checkForUpdate(opts) {
|
|
25
|
-
|
|
26
|
-
if (env[SKIP_UPDATE_CHECK_ENV] === "1")
|
|
35
|
+
if (!updateCheckEnabled(opts.env ?? process.env, opts.config ?? {}))
|
|
27
36
|
return null;
|
|
28
37
|
const current = parseVersion(opts.currentVersion);
|
|
29
38
|
if (current === null)
|
|
30
39
|
return null;
|
|
40
|
+
const latestRaw = await latestVersion(opts);
|
|
41
|
+
const latest = latestRaw === null ? null : parseReleaseVersion(latestRaw);
|
|
42
|
+
if (latest === null || compareVersions(latest, current) <= 0)
|
|
43
|
+
return null;
|
|
44
|
+
return { latest: latestRaw, current: opts.currentVersion };
|
|
45
|
+
}
|
|
46
|
+
/** False when the user opted out by env (either name) or config. */
|
|
47
|
+
export function updateCheckEnabled(env, config) {
|
|
48
|
+
if (env[NO_UPDATE_CHECK_ENV] === "1" || env[SKIP_UPDATE_CHECK_ENV] === "1")
|
|
49
|
+
return false;
|
|
50
|
+
return config.updateCheck !== false;
|
|
51
|
+
}
|
|
52
|
+
/** The registry's `latest`, from a fresh cache or a bounded fetch; null on any failure. */
|
|
53
|
+
async function latestVersion(opts) {
|
|
54
|
+
const now = (opts.now ?? Date.now)();
|
|
55
|
+
const cached = opts.cacheFile ? readCache(opts.cacheFile) : null;
|
|
56
|
+
const age = cached ? now - cached.checkedAt : -1;
|
|
57
|
+
if (cached && age >= 0 && age < UPDATE_CHECK_CACHE_TTL_MS)
|
|
58
|
+
return cached.latest;
|
|
59
|
+
const latest = await fetchLatestVersion(opts);
|
|
60
|
+
if (latest !== null && opts.cacheFile)
|
|
61
|
+
writeCache(opts.cacheFile, { checkedAt: now, latest });
|
|
62
|
+
return latest;
|
|
63
|
+
}
|
|
64
|
+
async function fetchLatestVersion(opts) {
|
|
31
65
|
try {
|
|
32
66
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
33
67
|
const res = await fetchImpl(opts.url ?? NPM_LATEST_URL, {
|
|
@@ -37,11 +71,8 @@ export async function checkForUpdate(opts) {
|
|
|
37
71
|
if (!res.ok)
|
|
38
72
|
return null;
|
|
39
73
|
const body = (await res.json());
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
if (latest === null || compareVersions(latest, current) <= 0)
|
|
43
|
-
return null;
|
|
44
|
-
return { latest: latestRaw, current: opts.currentVersion };
|
|
74
|
+
const version = typeof body?.version === "string" ? body.version : "";
|
|
75
|
+
return parseVersion(version) === null ? null : version;
|
|
45
76
|
}
|
|
46
77
|
catch {
|
|
47
78
|
// Registry unreachable, timed out, or answered with something that is
|
|
@@ -49,6 +80,42 @@ export async function checkForUpdate(opts) {
|
|
|
49
80
|
return null;
|
|
50
81
|
}
|
|
51
82
|
}
|
|
83
|
+
function readCache(file) {
|
|
84
|
+
try {
|
|
85
|
+
const data = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
86
|
+
if (typeof data?.checkedAt !== "number" || typeof data.latest !== "string")
|
|
87
|
+
return null;
|
|
88
|
+
if (parseVersion(data.latest) === null)
|
|
89
|
+
return null;
|
|
90
|
+
return { checkedAt: data.checkedAt, latest: data.latest };
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Best effort and atomic: a failed write only means the next start fetches again. */
|
|
97
|
+
function writeCache(file, cache) {
|
|
98
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
99
|
+
try {
|
|
100
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
101
|
+
fs.writeFileSync(tmp, JSON.stringify(cache), { mode: 0o600 });
|
|
102
|
+
fs.renameSync(tmp, file);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
try {
|
|
106
|
+
fs.rmSync(tmp, { force: true });
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Nothing more to clean up.
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** A release version only: prereleases (`1.2.3-beta.1`) are never offered. */
|
|
114
|
+
function parseReleaseVersion(version) {
|
|
115
|
+
if (/^v?\d+\.\d+\.\d+-/.test(version.trim()))
|
|
116
|
+
return null;
|
|
117
|
+
return parseVersion(version);
|
|
118
|
+
}
|
|
52
119
|
/** Parse `1.2.3` (optionally `v`-prefixed / suffixed) into numeric parts. */
|
|
53
120
|
export function parseVersion(version) {
|
|
54
121
|
const match = VERSION_RE.exec(version.trim());
|
package/dist/update-check.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-check.js","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"update-check.js","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auto-update install lock: at most one ccc installs at a time.
|
|
3
|
+
*
|
|
4
|
+
* A directory of entries, one per acquirer, each under its own random name and
|
|
5
|
+
* written whole (temp file, then rename). To acquire, write your entry, then
|
|
6
|
+
* look at everyone else's: if any other entry is live, remove yours and back
|
|
7
|
+
* off. Of two acquirers, whichever looks second sees the first, so at most one
|
|
8
|
+
* proceeds (when both look after both wrote, both back off; the next launch
|
|
9
|
+
* tries again). No process ever deletes or moves a file that a live process
|
|
10
|
+
* owns or may be creating: an entry is removed only by its owner, or by anyone
|
|
11
|
+
* once it is stale, and a stale entry's owner is gone and never writes again.
|
|
12
|
+
*
|
|
13
|
+
* Each entry has one writer at a time. The launching ccc creates it and,
|
|
14
|
+
* right after spawning the runner, records the runner's pid; the runner waits
|
|
15
|
+
* for that handoff before it writes or runs anything (auto-update-runner.ts),
|
|
16
|
+
* and from then on owns the entry, adding npm's process group while one runs.
|
|
17
|
+
* An entry is live while any process it names is alive (the launcher, the
|
|
18
|
+
* runner, or npm's group), however old: a lock is never taken from a live
|
|
19
|
+
* installer by age. A pid reused by an unrelated process can keep it held;
|
|
20
|
+
* callers treat a lock held for over UPDATE_LOCK_STALE_MS as stuck and fall
|
|
21
|
+
* back to the update hint (auto-update.ts). The runner marks its entry
|
|
22
|
+
* `spawning` before starting npm and clears it once npm's group is recorded:
|
|
23
|
+
* an entry left `spawning` by a dead runner may have an unrecorded npm
|
|
24
|
+
* running, so it stays live (fail closed) and, after an hour, the hint shows.
|
|
25
|
+
* Removing update.lock.d by hand clears it.
|
|
26
|
+
*/
|
|
27
|
+
/** Longer than a healthy runner lives (install timeout + kill graces + deadline). */
|
|
28
|
+
export declare const UPDATE_LOCK_STALE_MS: number;
|
|
29
|
+
/** An unreadable entry (never written whole) is garbage only after this. */
|
|
30
|
+
export declare const DEAD_HOLDER_GRACE_MS: number;
|
|
31
|
+
export interface LockEntry {
|
|
32
|
+
token: string;
|
|
33
|
+
startedAt: number;
|
|
34
|
+
/** The launching ccc (or the runner, once it owns the entry). */
|
|
35
|
+
pid?: number;
|
|
36
|
+
/** The runner, recorded by the launcher right after spawning it. */
|
|
37
|
+
runner?: number;
|
|
38
|
+
/** The npm process group the runner started, while it may be alive. */
|
|
39
|
+
group?: number;
|
|
40
|
+
/** Set by the runner just before starting npm, cleared once `group` is recorded. */
|
|
41
|
+
spawning?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Acquire the lock: the owner token, or null when another entry is live (or
|
|
45
|
+
* changed while being judged).
|
|
46
|
+
*
|
|
47
|
+
* Each other entry is read, its named processes checked, then read again: it
|
|
48
|
+
* counts as stale only if every named process was gone and the entry is
|
|
49
|
+
* unchanged. Only named processes ever write an entry (the launcher hands
|
|
50
|
+
* over to the runner, which adds npm's group), and a process found gone
|
|
51
|
+
* before the second read cannot write after it, so an entry unchanged across
|
|
52
|
+
* the check stays dead and is safe to remove. A change (say, a handoff landing
|
|
53
|
+
* mid-check) means it may have a live owner: back off.
|
|
54
|
+
*/
|
|
55
|
+
export declare function acquireUpdateLock(lockDir: string, now: number,
|
|
56
|
+
/** Test-only: runs between the liveness checks and the second read. */
|
|
57
|
+
afterLivenessCheck?: () => void): string | null;
|
|
58
|
+
/** Remove this token's entry (only its owner ever does, or anyone once stale). */
|
|
59
|
+
export declare function releaseUpdateLock(lockDir: string, token: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* The launcher's one write after creating the entry: the runner's pid, so the
|
|
62
|
+
* entry stays live while the runner exists, even before it has run a line.
|
|
63
|
+
*/
|
|
64
|
+
export declare function setLockRunner(lockDir: string, token: string, runnerPid: number): boolean;
|
|
65
|
+
/** When the oldest live entry was taken, or null when the lock is free. */
|
|
66
|
+
export declare function liveLockSince(lockDir: string, now: number): number | null;
|
|
67
|
+
export declare function entryPath(lockDir: string, token: string): string;
|
|
68
|
+
//# sourceMappingURL=update-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-lock.d.ts","sourceRoot":"","sources":["../src/update-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,qFAAqF;AACrF,eAAO,MAAM,oBAAoB,QAAiB,CAAC;AACnD,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,QAAY,CAAC;AAG9C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM;AACX,uEAAuE;AACvE,kBAAkB,CAAC,EAAE,MAAM,IAAI,GAC9B,MAAM,GAAG,IAAI,CAYf;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAGtE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAIxF;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGzE;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auto-update install lock: at most one ccc installs at a time.
|
|
3
|
+
*
|
|
4
|
+
* A directory of entries, one per acquirer, each under its own random name and
|
|
5
|
+
* written whole (temp file, then rename). To acquire, write your entry, then
|
|
6
|
+
* look at everyone else's: if any other entry is live, remove yours and back
|
|
7
|
+
* off. Of two acquirers, whichever looks second sees the first, so at most one
|
|
8
|
+
* proceeds (when both look after both wrote, both back off; the next launch
|
|
9
|
+
* tries again). No process ever deletes or moves a file that a live process
|
|
10
|
+
* owns or may be creating: an entry is removed only by its owner, or by anyone
|
|
11
|
+
* once it is stale, and a stale entry's owner is gone and never writes again.
|
|
12
|
+
*
|
|
13
|
+
* Each entry has one writer at a time. The launching ccc creates it and,
|
|
14
|
+
* right after spawning the runner, records the runner's pid; the runner waits
|
|
15
|
+
* for that handoff before it writes or runs anything (auto-update-runner.ts),
|
|
16
|
+
* and from then on owns the entry, adding npm's process group while one runs.
|
|
17
|
+
* An entry is live while any process it names is alive (the launcher, the
|
|
18
|
+
* runner, or npm's group), however old: a lock is never taken from a live
|
|
19
|
+
* installer by age. A pid reused by an unrelated process can keep it held;
|
|
20
|
+
* callers treat a lock held for over UPDATE_LOCK_STALE_MS as stuck and fall
|
|
21
|
+
* back to the update hint (auto-update.ts). The runner marks its entry
|
|
22
|
+
* `spawning` before starting npm and clears it once npm's group is recorded:
|
|
23
|
+
* an entry left `spawning` by a dead runner may have an unrecorded npm
|
|
24
|
+
* running, so it stays live (fail closed) and, after an hour, the hint shows.
|
|
25
|
+
* Removing update.lock.d by hand clears it.
|
|
26
|
+
*/
|
|
27
|
+
import { randomUUID } from "node:crypto";
|
|
28
|
+
import * as fs from "node:fs";
|
|
29
|
+
import * as path from "node:path";
|
|
30
|
+
/** Longer than a healthy runner lives (install timeout + kill graces + deadline). */
|
|
31
|
+
export const UPDATE_LOCK_STALE_MS = 60 * 60 * 1000;
|
|
32
|
+
/** An unreadable entry (never written whole) is garbage only after this. */
|
|
33
|
+
export const DEAD_HOLDER_GRACE_MS = 30 * 1000;
|
|
34
|
+
const ENTRY_SUFFIX = ".json";
|
|
35
|
+
/**
|
|
36
|
+
* Acquire the lock: the owner token, or null when another entry is live (or
|
|
37
|
+
* changed while being judged).
|
|
38
|
+
*
|
|
39
|
+
* Each other entry is read, its named processes checked, then read again: it
|
|
40
|
+
* counts as stale only if every named process was gone and the entry is
|
|
41
|
+
* unchanged. Only named processes ever write an entry (the launcher hands
|
|
42
|
+
* over to the runner, which adds npm's group), and a process found gone
|
|
43
|
+
* before the second read cannot write after it, so an entry unchanged across
|
|
44
|
+
* the check stays dead and is safe to remove. A change (say, a handoff landing
|
|
45
|
+
* mid-check) means it may have a live owner: back off.
|
|
46
|
+
*/
|
|
47
|
+
export function acquireUpdateLock(lockDir, now,
|
|
48
|
+
/** Test-only: runs between the liveness checks and the second read. */
|
|
49
|
+
afterLivenessCheck) {
|
|
50
|
+
const token = randomUUID();
|
|
51
|
+
if (!writeLockEntry(lockDir, { token, startedAt: now, pid: process.pid }))
|
|
52
|
+
return null;
|
|
53
|
+
const others = otherEntries(lockDir, token);
|
|
54
|
+
const allStale = others.every((entry) => entryIsStale(entry, now));
|
|
55
|
+
afterLivenessCheck?.();
|
|
56
|
+
if (!allStale || others.some((entry) => changedSince(entry))) {
|
|
57
|
+
releaseUpdateLock(lockDir, token);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
for (const entry of others)
|
|
61
|
+
fs.rmSync(entry.file, { force: true });
|
|
62
|
+
return token;
|
|
63
|
+
}
|
|
64
|
+
/** Remove this token's entry (only its owner ever does, or anyone once stale). */
|
|
65
|
+
export function releaseUpdateLock(lockDir, token) {
|
|
66
|
+
if (!/^[0-9a-f-]{36}$/.test(token))
|
|
67
|
+
return;
|
|
68
|
+
fs.rmSync(entryPath(lockDir, token), { force: true });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The launcher's one write after creating the entry: the runner's pid, so the
|
|
72
|
+
* entry stays live while the runner exists, even before it has run a line.
|
|
73
|
+
*/
|
|
74
|
+
export function setLockRunner(lockDir, token, runnerPid) {
|
|
75
|
+
const entry = readEntry(entryPath(lockDir, token))?.data;
|
|
76
|
+
if (entry?.token !== token)
|
|
77
|
+
return false;
|
|
78
|
+
return writeLockEntry(lockDir, { ...entry, runner: runnerPid });
|
|
79
|
+
}
|
|
80
|
+
/** When the oldest live entry was taken, or null when the lock is free. */
|
|
81
|
+
export function liveLockSince(lockDir, now) {
|
|
82
|
+
const live = otherEntries(lockDir, "").filter((e) => e.data && !entryIsStale(e, now));
|
|
83
|
+
return live.length ? Math.min(...live.map((e) => e.data.startedAt)) : null;
|
|
84
|
+
}
|
|
85
|
+
export function entryPath(lockDir, token) {
|
|
86
|
+
return path.join(lockDir, `${token}${ENTRY_SUFFIX}`);
|
|
87
|
+
}
|
|
88
|
+
/** Atomic: readers see a whole entry or none. */
|
|
89
|
+
function writeLockEntry(lockDir, entry) {
|
|
90
|
+
const tmp = path.join(lockDir, `.${entry.token}.${process.pid}.tmp`);
|
|
91
|
+
try {
|
|
92
|
+
fs.mkdirSync(lockDir, { recursive: true, mode: 0o700 });
|
|
93
|
+
fs.writeFileSync(tmp, JSON.stringify(entry), { mode: 0o600 });
|
|
94
|
+
fs.renameSync(tmp, entryPath(lockDir, entry.token));
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
fs.rmSync(tmp, { force: true });
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** True when the entry is no longer exactly what was read (gone counts as unchanged). */
|
|
103
|
+
function changedSince(entry) {
|
|
104
|
+
const again = readEntry(entry.file);
|
|
105
|
+
return again !== null && (again.raw !== entry.raw || again.mtimeMs !== entry.mtimeMs);
|
|
106
|
+
}
|
|
107
|
+
function otherEntries(lockDir, token) {
|
|
108
|
+
let names;
|
|
109
|
+
try {
|
|
110
|
+
names = fs.readdirSync(lockDir);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
return names
|
|
116
|
+
.filter((name) => name.endsWith(ENTRY_SUFFIX) && !name.startsWith(".") && name !== `${token}${ENTRY_SUFFIX}`)
|
|
117
|
+
.map((name) => readEntry(path.join(lockDir, name)))
|
|
118
|
+
.filter((entry) => entry !== null);
|
|
119
|
+
}
|
|
120
|
+
/** null when the file is gone (released meanwhile). */
|
|
121
|
+
function readEntry(file) {
|
|
122
|
+
let mtimeMs;
|
|
123
|
+
let raw;
|
|
124
|
+
try {
|
|
125
|
+
mtimeMs = fs.statSync(file).mtimeMs;
|
|
126
|
+
raw = fs.readFileSync(file, "utf8");
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
return { file, data: parseEntry(raw), mtimeMs, raw };
|
|
132
|
+
}
|
|
133
|
+
function parseEntry(raw) {
|
|
134
|
+
try {
|
|
135
|
+
const data = JSON.parse(raw);
|
|
136
|
+
if (typeof data?.token !== "string" || typeof data.startedAt !== "number")
|
|
137
|
+
return null;
|
|
138
|
+
const int = (v) => (Number.isInteger(v) && v > 0 ? v : undefined);
|
|
139
|
+
return {
|
|
140
|
+
token: data.token,
|
|
141
|
+
startedAt: data.startedAt,
|
|
142
|
+
pid: int(data.pid),
|
|
143
|
+
runner: int(data.runner),
|
|
144
|
+
group: int(data.group),
|
|
145
|
+
...(data.spawning === true ? { spawning: true } : {}),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** Stale only when no process it names is alive (or it was never written whole). */
|
|
153
|
+
function entryIsStale(entry, now) {
|
|
154
|
+
if (entry.data === null)
|
|
155
|
+
return now - entry.mtimeMs > DEAD_HOLDER_GRACE_MS;
|
|
156
|
+
const { pid, runner, group } = entry.data;
|
|
157
|
+
if (pid !== undefined && processAlive(pid))
|
|
158
|
+
return false;
|
|
159
|
+
if (runner !== undefined && processAlive(runner))
|
|
160
|
+
return false;
|
|
161
|
+
if (group !== undefined && processAlive(-group))
|
|
162
|
+
return false;
|
|
163
|
+
// Its runner died while starting npm: npm may be running unrecorded.
|
|
164
|
+
return entry.data.spawning !== true;
|
|
165
|
+
}
|
|
166
|
+
/** Signal 0 to a pid (or, negative, a process group). */
|
|
167
|
+
function processAlive(pid) {
|
|
168
|
+
try {
|
|
169
|
+
process.kill(pid, 0);
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
return err.code === "EPERM";
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=update-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-lock.js","sourceRoot":"","sources":["../src/update-lock.ts"],"names":[],"mappings":"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"}
|