claude-code-infinite 1.0.25 → 1.0.27
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 +72 -20
- package/dist/auto-update-runner.d.ts +13 -0
- package/dist/auto-update-runner.d.ts.map +1 -1
- package/dist/auto-update-runner.js +51 -7
- package/dist/auto-update-runner.js.map +1 -1
- package/dist/auto-update-setup.d.ts +19 -3
- package/dist/auto-update-setup.d.ts.map +1 -1
- package/dist/auto-update-setup.js +42 -16
- package/dist/auto-update-setup.js.map +1 -1
- package/dist/auto-update.d.ts +59 -6
- package/dist/auto-update.d.ts.map +1 -1
- package/dist/auto-update.js +207 -40
- package/dist/auto-update.js.map +1 -1
- package/dist/claude-auth.d.ts +55 -0
- package/dist/claude-auth.d.ts.map +1 -0
- package/dist/claude-auth.js +193 -0
- package/dist/claude-auth.js.map +1 -0
- package/dist/claude-env.d.ts +11 -4
- package/dist/claude-env.d.ts.map +1 -1
- package/dist/claude-env.js +11 -4
- package/dist/claude-env.js.map +1 -1
- package/dist/claude-locate.d.ts +80 -0
- package/dist/claude-locate.d.ts.map +1 -0
- package/dist/claude-locate.js +303 -0
- package/dist/claude-locate.js.map +1 -0
- package/dist/claude-plan-warning.d.ts +55 -0
- package/dist/claude-plan-warning.d.ts.map +1 -0
- package/dist/claude-plan-warning.js +95 -0
- package/dist/claude-plan-warning.js.map +1 -0
- package/dist/cli-lifecycle.d.ts +5 -0
- package/dist/cli-lifecycle.d.ts.map +1 -1
- package/dist/cli-lifecycle.js +13 -0
- package/dist/cli-lifecycle.js.map +1 -1
- package/dist/cli-main.js +124 -18
- package/dist/cli-main.js.map +1 -1
- package/dist/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/dist/diagnostics-schema.d.ts +11 -1
- package/dist/diagnostics-schema.d.ts.map +1 -1
- package/dist/diagnostics-schema.js +38 -2
- package/dist/diagnostics-schema.js.map +1 -1
- package/dist/diagnostics.d.ts +10 -1
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +26 -1
- package/dist/diagnostics.js.map +1 -1
- package/dist/memtree-mcp.d.ts.map +1 -1
- package/dist/memtree-mcp.js +8 -5
- package/dist/memtree-mcp.js.map +1 -1
- package/dist/notices.d.ts.map +1 -1
- package/dist/notices.js +3 -2
- package/dist/notices.js.map +1 -1
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +7 -0
- package/dist/proxy.js.map +1 -1
- package/dist/reqlog.d.ts +7 -0
- package/dist/reqlog.d.ts.map +1 -1
- package/dist/reqlog.js.map +1 -1
- package/dist/resume-repair.d.ts +2 -0
- package/dist/resume-repair.d.ts.map +1 -1
- package/dist/resume-repair.js +7 -5
- package/dist/resume-repair.js.map +1 -1
- package/dist/update-check.d.ts +2 -0
- package/dist/update-check.d.ts.map +1 -1
- package/dist/update-check.js.map +1 -1
- package/dist/update-lock.js +3 -1
- package/dist/update-lock.js.map +1 -1
- package/dist/update-wait.d.ts +25 -0
- package/dist/update-wait.d.ts.map +1 -0
- package/dist/update-wait.js +63 -0
- package/dist/update-wait.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
# Claude Code Infinite
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="#long-session-benchmark"><img width="640" alt="Long-session benchmark, Claude Opus 5.5, graded blind: MemTree 69% at 39M tokens per run, full context 69% at 113M, /compact 49% at 17M" src="https://raw.githubusercontent.com/crizCraig/claude-code-infinite/main/docs/images/benchmark-chart.png"></a>
|
|
9
|
+
</p>
|
|
10
|
+
<p align="center"><sub>Long-session benchmark on a 775k-token Claude Code session (Claude Opus 5.5, graded blind):
|
|
11
|
+
MemTree matched full context at 69% with a third of the tokens; <code>/compact</code> scored 49%.
|
|
12
|
+
<a href="#long-session-benchmark">Full results</a></sub></p>
|
|
13
|
+
|
|
14
|
+
Infinite context for Claude Code: memory for long-running agents. Claude Code Infinite indexes your Claude Code sessions with
|
|
8
15
|
[MemTree](https://memtree.dev) so their history can be easily searched, navigated, and understood,
|
|
9
16
|
by you and by Claude, within a session and across all of them.
|
|
10
17
|
|
|
@@ -30,14 +37,8 @@ work: your priorities, the nuances of your projects, your company and your envir
|
|
|
30
37
|
orchestrate dozens of subagents on your behalf.
|
|
31
38
|
|
|
32
39
|
> [!IMPORTANT]
|
|
33
|
-
>
|
|
34
|
-
|
|
35
|
-
<p align="center">
|
|
36
|
-
<picture>
|
|
37
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/crizCraig/claude-code-infinite/main/docs/images/benchmark-dark.svg">
|
|
38
|
-
<img width="600" alt="Long-session benchmark, judged score out of 100: Claude Code Infinite 69, full context 69, /compact 49" src="https://raw.githubusercontent.com/crizCraig/claude-code-infinite/main/docs/images/benchmark-light.svg">
|
|
39
|
-
</picture>
|
|
40
|
-
</p>
|
|
40
|
+
> Install [Claude Code in your terminal](#setup) and sign in with Claude Pro or Max
|
|
41
|
+
> before running `ccc`. The Claude desktop app isn't enough.
|
|
41
42
|
|
|
42
43
|
## Search across sessions
|
|
43
44
|
|
|
@@ -82,19 +83,32 @@ tokens; `/compact`'s exclude the one-time `/compact` step. This is one task fami
|
|
|
82
83
|
|
|
83
84
|
## Requirements
|
|
84
85
|
|
|
86
|
+
* **[Claude Code in your terminal](https://code.claude.com/docs/en/quickstart), signed in
|
|
87
|
+
with Claude Pro or Max.** The Claude desktop app isn't enough: `ccc` needs the terminal
|
|
88
|
+
version.
|
|
85
89
|
* [node.js 20.3 or newer](https://nodejs.org/en/download/)
|
|
86
|
-
* [Claude Code (the terminal version)](https://code.claude.com/docs/en/quickstart)
|
|
87
|
-
* **A Claude Pro or Max subscription**
|
|
88
90
|
|
|
89
|
-
<sub>Also works with an Anthropic API key, but that's far costlier than Pro or Max.</sub>
|
|
91
|
+
<sub>Also works with an Anthropic API key that has credit, but that's far costlier than Pro or Max.</sub>
|
|
90
92
|
|
|
91
93
|
## Setup
|
|
92
94
|
|
|
93
|
-
1. Install
|
|
95
|
+
1. **Install Claude Code first** and sign in with Claude Pro or Max. The Claude desktop app
|
|
96
|
+
isn't enough: `ccc` needs the terminal version.
|
|
97
|
+
```powershell
|
|
98
|
+
# Windows PowerShell
|
|
99
|
+
irm https://claude.ai/install.ps1 | iex
|
|
100
|
+
```
|
|
101
|
+
```bash
|
|
102
|
+
# macOS / Linux
|
|
103
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
104
|
+
```
|
|
105
|
+
Open a new terminal and run `claude` once to sign in
|
|
106
|
+
([help](#claude-code-isnt-installed)).
|
|
107
|
+
2. Install Claude Code Infinite (Node ≥ 20.3; no git needed)
|
|
94
108
|
```bash
|
|
95
109
|
npm install -g claude-code-infinite
|
|
96
110
|
```
|
|
97
|
-
|
|
111
|
+
3. Run it from your project folder
|
|
98
112
|
```bash
|
|
99
113
|
ccc
|
|
100
114
|
```
|
|
@@ -136,7 +150,7 @@ Environment variables, set when launching `ccc`:
|
|
|
136
150
|
| `CCC_COMPACT_TARGET=500k` | Compaction target for every session (useful for headless `-p` runs); `off` starts every session with compaction off. |
|
|
137
151
|
| `CCC_MEMTREE_MCP=0` | Turns off the `memtree` MCP tools ([details](#memory-tools)), in interactive and headless (`-p`) runs. |
|
|
138
152
|
| `CCC_MEMTREE_LINK` | Where the MemTree link appears: `turn` (default, end of a turn with a new page), `message` (under every reply), `stop` (end of every turn), `success` (only after an optimized turn), or `off`. |
|
|
139
|
-
| `CCC_AUTO_COMPACT=1` | Restores Claude Code's own auto-compaction
|
|
153
|
+
| `CCC_AUTO_COMPACT=1` | Restores Claude Code's own auto-compaction and idle compaction settings ([details](#claude-code-auto-compaction)). |
|
|
140
154
|
| `CCC_NO_UPDATE_CHECK=1` | Turns off the update check, and with it automatic updates ([details](#upgrading)). |
|
|
141
155
|
| `CCC_DISABLE_AUTOUPDATE=1` | Keeps the update notice but never installs ([details](#automatic-updates)). |
|
|
142
156
|
| `CCC_NO_DIAGNOSTICS=1` | Turns off diagnostics ([details](#diagnostics)). So do `DO_NOT_TRACK=1`, Claude Code's `DISABLE_TELEMETRY=1` or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`, and `"diagnostics": false` in `~/.claude-code-infinite/config.json`. |
|
|
@@ -148,6 +162,12 @@ kitty, VS Code, JetBrains, Windows Terminal); elsewhere you get the plain URL.
|
|
|
148
162
|
|
|
149
163
|
`ccc` disables Claude Code's automatic conversation compaction for the Claude process it launches. MemTree manages the context sent to the model, so letting Claude Code independently summarize the full local transcript can compact a conversation MemTree already reduced. Manual `/compact` remains available.
|
|
150
164
|
|
|
165
|
+
That includes idle compaction (Claude Code 2.1.286 and later), which summarizes a
|
|
166
|
+
long conversation while you're away, just before the prompt cache expires
|
|
167
|
+
("Compacted while idle, before the prompt cache expired"). The
|
|
168
|
+
`DISABLE_AUTO_COMPACT=1` that `ccc` sets turns it off too, so you don't need
|
|
169
|
+
`"idleCompaction": false` in your settings.
|
|
170
|
+
|
|
151
171
|
Claude Code 2.1.219 also recommends “Resume from summary” for any old session
|
|
152
172
|
above a fixed 100k-token threshold, even when the active model has a 1M window
|
|
153
173
|
and auto-compaction is disabled. `ccc` suppresses that resume-time
|
|
@@ -168,7 +188,8 @@ On `ccc --resume <id>`, `ccc` backs the transcript up to
|
|
|
168
188
|
recorded usage to half the window, so the next message is sent and MemTree
|
|
169
189
|
compresses it.
|
|
170
190
|
|
|
171
|
-
To restore Claude Code's native auto-compaction
|
|
191
|
+
To restore Claude Code's native auto-compaction and idle compaction settings for one
|
|
192
|
+
invocation, use:
|
|
172
193
|
|
|
173
194
|
```bash
|
|
174
195
|
CCC_AUTO_COMPACT=1 ccc
|
|
@@ -202,6 +223,10 @@ Claude Code ──▶ localhost proxy (ccc)
|
|
|
202
223
|
|
|
203
224
|
- versions (ccc, Claude Code, Node), how ccc was installed (npm, npx, linked), OS and CPU
|
|
204
225
|
architecture, terminal type and locale;
|
|
226
|
+
- how Claude Code is signed in, as a type only (Claude.ai subscription, API key, Bedrock,
|
|
227
|
+
Vertex, Foundry), the plan's tier name when Claude Code records one (Pro, Max, Team), the main
|
|
228
|
+
model's context window ccc uses (200k or 1M), and where Claude Code was found (on PATH,
|
|
229
|
+
its installer's folder, npm, winget);
|
|
205
230
|
- lifecycle events (first run, sign-in prompt shown, key accepted or rejected, session start,
|
|
206
231
|
the MemTree-paused notice shown, update hint or background update result);
|
|
207
232
|
- errors as codes, status numbers, sizes and timings (an Anthropic 401, a MemTree 402, a
|
|
@@ -371,6 +396,24 @@ publish.yml still tests that tag but skips publishing, since the version is alre
|
|
|
371
396
|
|
|
372
397
|
`ccc` never touches your Anthropic credentials — Claude Code manages its own login exactly as it does without `ccc`. If you see auth errors, fix them the vanilla way: run `/login` inside Claude Code (or `claude` directly) and re-authenticate.
|
|
373
398
|
|
|
399
|
+
### "Claude Code isn't installed"
|
|
400
|
+
|
|
401
|
+
`ccc` runs Claude Code, so it has to be installed and signed in first. `ccc` finds it on
|
|
402
|
+
PATH and, failing that, where its installers put it (`%USERPROFILE%\.local\bin`,
|
|
403
|
+
`%APPDATA%\npm`, winget's folder and your saved user PATH on Windows; `~/.local/bin`,
|
|
404
|
+
`~/.claude/local` and Homebrew elsewhere), so a terminal opened before the install works
|
|
405
|
+
too. If it is not there, install it with the official command (Windows PowerShell:
|
|
406
|
+
`irm https://claude.ai/install.ps1 | iex`; macOS/Linux:
|
|
407
|
+
`curl -fsSL https://claude.ai/install.sh | bash`), open a new terminal, run `claude` once
|
|
408
|
+
to sign in, then run
|
|
409
|
+
`ccc` again, from your project folder.
|
|
410
|
+
|
|
411
|
+
### "This API key has no credit"
|
|
412
|
+
|
|
413
|
+
`ccc` isn't free Claude: it runs your own Claude Code with your own Anthropic login. When
|
|
414
|
+
Anthropic refuses an API key for having no credit, `ccc` says so as it exits. Sign in with
|
|
415
|
+
a Claude Pro or Max plan (`claude`, then `/login`) or add credit to the key.
|
|
416
|
+
|
|
374
417
|
### MemTree degraded / passthrough mode
|
|
375
418
|
|
|
376
419
|
If you see an inline "⚠ MemTree degraded — this turn ran uncompressed" notice, the compression API is unreachable or your MemTree key is invalid/expired. Your session keeps working uncompressed. Check your key at [polychat.co](https://polychat.co/auth?memtree=true), or delete it from `~/.claude-code-infinite/config.json` and re-run `ccc` to re-enter it.
|
|
@@ -423,15 +466,24 @@ at once; after an hour `ccc` shows the update line instead, and deleting
|
|
|
423
466
|
|
|
424
467
|
**When it doesn't.** `ccc` updates itself only when it is a plain global npm install
|
|
425
468
|
(`npm i -g`) whose files are in `npm root -g` and writable by you. It never uses `sudo`.
|
|
426
|
-
In every other case it only shows the update line:
|
|
469
|
+
In every other case it only shows the update line, with the command that fits the install:
|
|
427
470
|
|
|
428
|
-
- `npm link`ed or git checkouts
|
|
471
|
+
- `npm link`ed or git checkouts: a development copy is never touched, and the line says to
|
|
472
|
+
`git pull && npm run build` in a git checkout, or to update and rebuild the linked folder
|
|
473
|
+
otherwise (`npm i -g` would replace the link with the published package),
|
|
429
474
|
- `npx` and project-local installs,
|
|
430
|
-
- pnpm, yarn, bun and Volta global installs (
|
|
475
|
+
- pnpm, yarn, bun and Volta global installs (the line gives your tool's own command),
|
|
431
476
|
- a global prefix that needs `sudo` to write, or `ccc` running as root,
|
|
432
|
-
- Windows,
|
|
433
477
|
- print (`-p`) and non-TTY runs, which never install anything.
|
|
434
478
|
|
|
479
|
+
**Windows.** The install starts as `ccc` exits rather than during the session: the
|
|
480
|
+
background installer waits for that `ccc` to be gone before npm runs, so npm does not
|
|
481
|
+
rewrite files under the session that started it. The banner says so (`• ccc X is
|
|
482
|
+
available … · installs when you exit ccc`). A `ccc` started while it installs says
|
|
483
|
+
"Finishing a ccc update…" and waits for it (usually seconds, at most two minutes), then runs the new
|
|
484
|
+
version. Closing the terminal window usually counts as exiting too. By hand:
|
|
485
|
+
`npm i -g claude-code-infinite@latest`.
|
|
486
|
+
|
|
435
487
|
`ccc --debug` prints which install method was detected and what it decided.
|
|
436
488
|
|
|
437
489
|
**Sessions running during an update.** npm replaces `ccc`'s files while other `ccc`
|
|
@@ -19,6 +19,13 @@
|
|
|
19
19
|
* --no-fund --no-audit` (argv, never a shell); confirm the package.json now
|
|
20
20
|
* says that version. Any failure is recorded with a short reason.
|
|
21
21
|
*
|
|
22
|
+
* Windows (waitForPid): the runner first waits for the ccc that started it to
|
|
23
|
+
* exit, so npm does not rewrite files under that session; npm
|
|
24
|
+
* runs as `node npm-cli.js` (npmArgs), and its "group" is npm's own pid,
|
|
25
|
+
* stopped with `taskkill /T /F` (its whole process tree) instead of signals
|
|
26
|
+
* to a POSIX process group. Unlike a POSIX group, a descendant that outlives
|
|
27
|
+
* npm is not tracked there (ccc and its dependency have no install scripts).
|
|
28
|
+
*
|
|
22
29
|
* Bounded: each npm command runs in its own process group, recorded in the
|
|
23
30
|
* lock entry. When it ends (or times out) the whole group is reaped: SIGTERM,
|
|
24
31
|
* then SIGKILL, until no member is left, so a descendant that outlives npm
|
|
@@ -31,6 +38,12 @@
|
|
|
31
38
|
/** What ccc passes the runner, as JSON in argv[1]. */
|
|
32
39
|
export interface RunnerConfig {
|
|
33
40
|
npm: string;
|
|
41
|
+
/** Arguments before npm's own: Windows runs `node npm-cli.js`. */
|
|
42
|
+
npmArgs?: string[];
|
|
43
|
+
/** Windows: wait for this pid (the launching ccc) to exit before installing. */
|
|
44
|
+
waitForPid?: number;
|
|
45
|
+
/** Test-only: how long to wait for waitForPid (default 60 s). */
|
|
46
|
+
waitForPidMs?: number;
|
|
34
47
|
packageName: string;
|
|
35
48
|
version: string;
|
|
36
49
|
packageDir: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-update-runner.d.ts","sourceRoot":"","sources":["../src/auto-update-runner.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auto-update-runner.d.ts","sourceRoot":"","sources":["../src/auto-update-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,aAAa,QAwTzB,CAAC"}
|
|
@@ -19,6 +19,13 @@
|
|
|
19
19
|
* --no-fund --no-audit` (argv, never a shell); confirm the package.json now
|
|
20
20
|
* says that version. Any failure is recorded with a short reason.
|
|
21
21
|
*
|
|
22
|
+
* Windows (waitForPid): the runner first waits for the ccc that started it to
|
|
23
|
+
* exit, so npm does not rewrite files under that session; npm
|
|
24
|
+
* runs as `node npm-cli.js` (npmArgs), and its "group" is npm's own pid,
|
|
25
|
+
* stopped with `taskkill /T /F` (its whole process tree) instead of signals
|
|
26
|
+
* to a POSIX process group. Unlike a POSIX group, a descendant that outlives
|
|
27
|
+
* npm is not tracked there (ccc and its dependency have no install scripts).
|
|
28
|
+
*
|
|
22
29
|
* Bounded: each npm command runs in its own process group, recorded in the
|
|
23
30
|
* lock entry. When it ends (or times out) the whole group is reaped: SIGTERM,
|
|
24
31
|
* then SIGKILL, until no member is left, so a descendant that outlives npm
|
|
@@ -36,7 +43,9 @@ const path = require("node:path");
|
|
|
36
43
|
|
|
37
44
|
const VERSION_RE = /^(0|[1-9]\d{0,8})\.(0|[1-9]\d{0,8})\.(0|[1-9]\d{0,8})$/;
|
|
38
45
|
const ROOT_TIMEOUT_MS = 60000;
|
|
46
|
+
const IS_WINDOWS = process.platform === "win32";
|
|
39
47
|
const cfg = JSON.parse(process.argv[1]);
|
|
48
|
+
const WAIT_FOR_PID_MS = cfg.waitForPidMs || 60000;
|
|
40
49
|
const KILL_GRACE_MS = cfg.killGraceMs || 10000;
|
|
41
50
|
const PIPE_GRACE_MS = cfg.pipeGraceMs || 5000;
|
|
42
51
|
const ENTRY = path.join(cfg.lockDir, cfg.token + ".json");
|
|
@@ -49,7 +58,8 @@ let stopping = null;
|
|
|
49
58
|
|
|
50
59
|
// Hard deadline: whatever npm does, this process kills its group, records a
|
|
51
60
|
// result and exits, so a live holder never outlasts the lock's stale age.
|
|
52
|
-
const DEADLINE_MS = ROOT_TIMEOUT_MS + cfg.timeoutMs + 2 * (2 * KILL_GRACE_MS + 2 * PIPE_GRACE_MS) +
|
|
61
|
+
const DEADLINE_MS = ROOT_TIMEOUT_MS + cfg.timeoutMs + 2 * (2 * KILL_GRACE_MS + 2 * PIPE_GRACE_MS) +
|
|
62
|
+
(cfg.waitForPid ? WAIT_FOR_PID_MS : 0) + 30000;
|
|
53
63
|
setTimeout(() => abort("runner deadline passed"), DEADLINE_MS);
|
|
54
64
|
for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) {
|
|
55
65
|
process.on(signal, () => abort("runner stopped by " + signal));
|
|
@@ -65,19 +75,51 @@ function sleep(ms) {
|
|
|
65
75
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
66
76
|
}
|
|
67
77
|
|
|
68
|
-
function
|
|
78
|
+
function pidAlive(pid) {
|
|
69
79
|
try {
|
|
70
|
-
process.kill(
|
|
80
|
+
process.kill(pid, 0);
|
|
71
81
|
return true;
|
|
72
82
|
} catch (err) {
|
|
73
83
|
return err.code === "EPERM";
|
|
74
84
|
}
|
|
75
85
|
}
|
|
76
86
|
|
|
87
|
+
// Windows has no process groups: the "group" is npm's pid and its tree.
|
|
88
|
+
function groupAlive(pgid) {
|
|
89
|
+
return pidAlive(IS_WINDOWS ? pgid : -pgid);
|
|
90
|
+
}
|
|
91
|
+
|
|
77
92
|
function signalGroup(pgid, signal) {
|
|
93
|
+
if (IS_WINDOWS) {
|
|
94
|
+
try {
|
|
95
|
+
const taskkill = path.join(process.env.SystemRoot || "C:\\Windows", "System32", "taskkill.exe");
|
|
96
|
+
require("node:child_process").spawnSync(taskkill, ["/PID", String(pgid), "/T", "/F"], {
|
|
97
|
+
stdio: "ignore", windowsHide: true, timeout: 10000,
|
|
98
|
+
});
|
|
99
|
+
} catch {}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
78
102
|
try { process.kill(-pgid, signal); } catch {}
|
|
79
103
|
}
|
|
80
104
|
|
|
105
|
+
async function awaitLauncherExit() {
|
|
106
|
+
if (!cfg.waitForPid) return true;
|
|
107
|
+
const until = Date.now() + WAIT_FOR_PID_MS;
|
|
108
|
+
while (pidAlive(cfg.waitForPid)) {
|
|
109
|
+
if (Date.now() >= until || stopping) return false;
|
|
110
|
+
await sleep(100);
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function npmArgs(args) {
|
|
116
|
+
return (cfg.npmArgs || []).concat(args);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function samePath(a, b) {
|
|
120
|
+
return a !== null && b !== null && (IS_WINDOWS ? a.toLowerCase() === b.toLowerCase() : a === b);
|
|
121
|
+
}
|
|
122
|
+
|
|
81
123
|
async function goneWithin(pgid, ms) {
|
|
82
124
|
const until = Date.now() + ms;
|
|
83
125
|
while (groupAlive(pgid)) {
|
|
@@ -168,6 +210,7 @@ function run(command, args, timeoutMs, capture) {
|
|
|
168
210
|
env: process.env,
|
|
169
211
|
stdio: ["ignore", "pipe", "pipe"],
|
|
170
212
|
detached: true,
|
|
213
|
+
windowsHide: true,
|
|
171
214
|
});
|
|
172
215
|
} catch (err) {
|
|
173
216
|
recordGroup(null, false);
|
|
@@ -225,20 +268,21 @@ function describe(result, what) {
|
|
|
225
268
|
|
|
226
269
|
async function install() {
|
|
227
270
|
if (!VERSION_RE.test(cfg.version)) throw new Error("refusing version " + JSON.stringify(cfg.version));
|
|
228
|
-
|
|
271
|
+
if (!(await awaitLauncherExit())) throw new Error(stopping || "ccc did not exit");
|
|
272
|
+
const rootRun = await runReaped(cfg.npm, npmArgs(["root", "--global"]), ROOT_TIMEOUT_MS, true);
|
|
229
273
|
const rootFailure = describe(rootRun, "npm root -g");
|
|
230
274
|
if (rootFailure) throw new Error(rootFailure);
|
|
231
275
|
const npmRoot = realpath(rootRun.out.trim());
|
|
232
|
-
if (npmRoot === null || npmRoot
|
|
276
|
+
if (npmRoot === null || !samePath(npmRoot, realpath(cfg.npmRoot))) {
|
|
233
277
|
throw new Error("npm root -g is " + rootRun.out.trim() + ", not " + cfg.npmRoot + ": wrong prefix");
|
|
234
278
|
}
|
|
235
279
|
const entry = path.join(npmRoot, cfg.packageName);
|
|
236
280
|
if (fs.lstatSync(entry).isSymbolicLink()) throw new Error(entry + " is a symlink (npm link)");
|
|
237
|
-
if (realpath(entry)
|
|
281
|
+
if (!samePath(realpath(entry), realpath(cfg.packageDir))) throw new Error(entry + " is not this ccc");
|
|
238
282
|
const spec = cfg.packageName + "@" + cfg.version;
|
|
239
283
|
const args = ["install", "--global", "--prefix=" + cfg.prefix, spec, "--no-fund", "--no-audit"];
|
|
240
284
|
note("npm " + args.join(" "));
|
|
241
|
-
const failure = describe(await runReaped(cfg.npm, args, cfg.timeoutMs, false), "npm install");
|
|
285
|
+
const failure = describe(await runReaped(cfg.npm, npmArgs(args), cfg.timeoutMs, false), "npm install");
|
|
242
286
|
if (failure) throw new Error(failure);
|
|
243
287
|
const pkg = JSON.parse(fs.readFileSync(path.join(cfg.packageDir, "package.json"), "utf8"));
|
|
244
288
|
if (pkg.version !== cfg.version) throw new Error("installed version is " + pkg.version);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-update-runner.js","sourceRoot":"","sources":["../src/auto-update-runner.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auto-update-runner.js","sourceRoot":"","sources":["../src/auto-update-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AA6BH,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwTtC,CAAC"}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Launch-time glue for auto-update.ts: decide what this interactive launch
|
|
3
3
|
* does about an available update, what the SessionStart banner says now, and
|
|
4
4
|
* when the background install starts (after Claude Code is up, never on the
|
|
5
|
-
* startup path).
|
|
5
|
+
* startup path; on Windows only as ccc exits, see auto-update.ts).
|
|
6
6
|
*/
|
|
7
7
|
import { AutoUpdateNotices, type AutoUpdateConfig, type AutoUpdateDecision, type InstallMethod, type SpawnLike, type StartInstallResult } from "./auto-update.js";
|
|
8
|
-
import type
|
|
8
|
+
import { type UpdateAvailable } from "./update-check.js";
|
|
9
9
|
export interface AutoUpdateSetupOptions {
|
|
10
10
|
update: UpdateAvailable | null;
|
|
11
11
|
currentVersion: string;
|
|
@@ -18,6 +18,12 @@ export interface AutoUpdateSetupOptions {
|
|
|
18
18
|
spawnImpl?: SpawnLike;
|
|
19
19
|
platform?: NodeJS.Platform;
|
|
20
20
|
uid?: number;
|
|
21
|
+
/** process.argv[1], to see an `npm link` through the bin (detectInstallMethod). */
|
|
22
|
+
scriptPath?: string;
|
|
23
|
+
/** This process (Windows: the runner waits for it to exit). */
|
|
24
|
+
pid?: number;
|
|
25
|
+
/** Test-only: the Node (and so npm) the runner uses. */
|
|
26
|
+
execPath?: string;
|
|
21
27
|
}
|
|
22
28
|
export interface AutoUpdateSetup {
|
|
23
29
|
decision: AutoUpdateDecision;
|
|
@@ -29,10 +35,20 @@ export interface AutoUpdateSetup {
|
|
|
29
35
|
bannerInstalled: string | null;
|
|
30
36
|
/** The SessionStart line for later hooks in this process (proxy option). */
|
|
31
37
|
notices: AutoUpdateNotices;
|
|
32
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Start the planned install after `delayMs`; a no-op unless planned, and on
|
|
40
|
+
* Windows, where it starts in installOnExit instead.
|
|
41
|
+
*/
|
|
33
42
|
scheduleInstall(delayMs?: number, onResult?: (result: StartInstallResult) => void): void;
|
|
43
|
+
/**
|
|
44
|
+
* Windows: start the planned install now, as ccc exits; the runner waits for
|
|
45
|
+
* this process to be gone. A no-op elsewhere or unless planned.
|
|
46
|
+
*/
|
|
47
|
+
installOnExit(onResult?: (result: StartInstallResult) => void): void;
|
|
34
48
|
/** One line naming the install method and the decision, for --debug. */
|
|
35
49
|
debugLine: string;
|
|
36
50
|
}
|
|
51
|
+
/** The banner's ending for a Windows update that installs as ccc exits. */
|
|
52
|
+
export declare const WINDOWS_ON_EXIT_TEXT = "installs when you exit ccc";
|
|
37
53
|
export declare function setupAutoUpdate(opts: AutoUpdateSetupOptions): AutoUpdateSetup;
|
|
38
54
|
//# sourceMappingURL=auto-update-setup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-update-setup.d.ts","sourceRoot":"","sources":["../src/auto-update-setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAEL,iBAAiB,
|
|
1
|
+
{"version":3,"file":"auto-update-setup.d.ts","sourceRoot":"","sources":["../src/auto-update-setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAEL,iBAAiB,EAUjB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE1E,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,uDAAuD;IACvD,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,uEAAuE;IACvE,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,+EAA+E;IAC/E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,4EAA4E;IAC5E,OAAO,EAAE,iBAAiB,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,GAAG,IAAI,CAAC;IACzF;;;OAGG;IACH,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,GAAG,IAAI,CAAC;IACrE,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AAEjE,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,eAAe,CA8D7E"}
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
* Launch-time glue for auto-update.ts: decide what this interactive launch
|
|
3
3
|
* does about an available update, what the SessionStart banner says now, and
|
|
4
4
|
* when the background install starts (after Claude Code is up, never on the
|
|
5
|
-
* startup path).
|
|
5
|
+
* startup path; on Windows only as ccc exits, see auto-update.ts).
|
|
6
6
|
*/
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
|
-
import { AUTO_UPDATE_DELAY_MS, AutoUpdateNotices, PACKAGE_DIR, detectInstallMethod, liveLockSince, planAutoUpdate, readInstalledVersion, readUpdateState, startAutoUpdateInstall, updateFiles, } from "./auto-update.js";
|
|
8
|
+
import { AUTO_UPDATE_DELAY_MS, AutoUpdateNotices, PACKAGE_DIR, detectInstallMethod, manualUpdateCommand, liveLockSince, planAutoUpdate, readInstalledVersion, readUpdateState, startAutoUpdateInstall, updateFiles, } from "./auto-update.js";
|
|
9
9
|
import { updateInstalledNoticeText, updateNoticeText } from "./notices.js";
|
|
10
|
+
import { UPGRADE_COMMAND } from "./update-check.js";
|
|
11
|
+
/** The banner's ending for a Windows update that installs as ccc exits. */
|
|
12
|
+
export const WINDOWS_ON_EXIT_TEXT = "installs when you exit ccc";
|
|
10
13
|
export function setupAutoUpdate(opts) {
|
|
11
14
|
const env = opts.env ?? process.env;
|
|
12
15
|
const packageDir = opts.packageDir ?? PACKAGE_DIR;
|
|
@@ -24,34 +27,53 @@ export function setupAutoUpdate(opts) {
|
|
|
24
27
|
installedVersion: readInstalledVersion(method.packageDir),
|
|
25
28
|
lockHeldSince: liveLockSince(files.lockDir, now()),
|
|
26
29
|
})
|
|
27
|
-
: { action: "skip", reason: "no update available" };
|
|
30
|
+
: { action: "skip", reason: "no update available", code: "no-update" };
|
|
31
|
+
const platform = opts.platform ?? process.platform;
|
|
32
|
+
const windows = platform === "win32";
|
|
33
|
+
const command = method ? manualUpdateCommand(method.kind) : undefined;
|
|
34
|
+
const withCommand = (update) => command && command !== UPGRADE_COMMAND ? { ...update, command } : update;
|
|
35
|
+
// Windows installs as ccc exits: say so now, since nothing installs during the session.
|
|
36
|
+
const onExitBanner = windows && decision.action === "install" && opts.update
|
|
37
|
+
? { ...opts.update, command: WINDOWS_ON_EXIT_TEXT }
|
|
38
|
+
: null;
|
|
39
|
+
let started = false;
|
|
40
|
+
const start = (onResult) => {
|
|
41
|
+
if (decision.action !== "install" || started)
|
|
42
|
+
return;
|
|
43
|
+
started = true;
|
|
44
|
+
const spawnImpl = opts.spawnImpl ?? spawn;
|
|
45
|
+
const result = startAutoUpdateInstall({
|
|
46
|
+
decision, files, spawnImpl, env, now, platform, execPath: opts.execPath,
|
|
47
|
+
...(windows ? { waitForPid: opts.pid ?? process.pid } : {}),
|
|
48
|
+
});
|
|
49
|
+
onResult?.(result);
|
|
50
|
+
};
|
|
28
51
|
const notices = new AutoUpdateNotices({
|
|
29
52
|
currentVersion: opts.currentVersion,
|
|
30
53
|
packageDir: method?.packageDir ?? packageDir,
|
|
31
54
|
stateFile: files.stateFile,
|
|
32
55
|
startedAt: now(),
|
|
33
56
|
installedText: (version) => updateInstalledNoticeText(version, opts.color),
|
|
34
|
-
hintText: (update) => updateNoticeText(update, opts.color),
|
|
57
|
+
hintText: (update) => updateNoticeText(withCommand(update), opts.color),
|
|
35
58
|
});
|
|
36
59
|
if (decision.action === "installed")
|
|
37
60
|
notices.markShown();
|
|
38
61
|
return {
|
|
39
62
|
decision,
|
|
40
63
|
method,
|
|
41
|
-
bannerUpdate: decision.action === "skip" ? opts.update :
|
|
64
|
+
bannerUpdate: decision.action === "skip" && opts.update ? withCommand(opts.update) : onExitBanner,
|
|
42
65
|
bannerInstalled: decision.action === "installed" ? decision.version : null,
|
|
43
66
|
notices,
|
|
44
67
|
scheduleInstall(delayMs = AUTO_UPDATE_DELAY_MS, onResult) {
|
|
45
|
-
if (decision.action !== "install")
|
|
68
|
+
if (decision.action !== "install" || windows)
|
|
46
69
|
return;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
timer.unref();
|
|
70
|
+
setTimeout(() => start(onResult), delayMs).unref();
|
|
71
|
+
},
|
|
72
|
+
installOnExit(onResult) {
|
|
73
|
+
if (windows)
|
|
74
|
+
start(onResult);
|
|
53
75
|
},
|
|
54
|
-
debugLine: debugLine(method, decision),
|
|
76
|
+
debugLine: debugLine(method, decision, windows),
|
|
55
77
|
};
|
|
56
78
|
}
|
|
57
79
|
function detectMethod(opts, env, packageDir) {
|
|
@@ -60,13 +82,17 @@ function detectMethod(opts, env, packageDir) {
|
|
|
60
82
|
env,
|
|
61
83
|
platform: opts.platform,
|
|
62
84
|
uid: opts.uid ?? process.getuid?.(),
|
|
85
|
+
scriptPath: opts.scriptPath,
|
|
63
86
|
});
|
|
64
87
|
}
|
|
65
|
-
function debugLine(method, decision) {
|
|
88
|
+
function debugLine(method, decision, windows) {
|
|
66
89
|
const how = method ? `install method ${method.kind} (${method.detail})` : "no update to install";
|
|
67
|
-
const what = decision.action === "install"
|
|
90
|
+
const what = decision.action === "install"
|
|
91
|
+
? (windows ? `installs ${decision.version} when ccc exits`
|
|
92
|
+
: `installing ${decision.version} in the background`)
|
|
68
93
|
: decision.action === "installed" ? `${decision.version} already installed, restart to use it`
|
|
69
|
-
:
|
|
94
|
+
: decision.code === "linked-install" ? "skipped: linked install"
|
|
95
|
+
: `not installing: ${decision.reason}`;
|
|
70
96
|
return `Auto-update: ${how}; ${what}`;
|
|
71
97
|
}
|
|
72
98
|
//# sourceMappingURL=auto-update-setup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-update-setup.js","sourceRoot":"","sources":["../src/auto-update-setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,WAAW,GAMZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"auto-update-setup.js","sourceRoot":"","sources":["../src/auto-update-setup.ts"],"names":[],"mappings":"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"}
|
package/dist/auto-update.d.ts
CHANGED
|
@@ -4,12 +4,22 @@
|
|
|
4
4
|
* Code does ("✔ Update installed · Restart to update").
|
|
5
5
|
*
|
|
6
6
|
* Only when self-updating is safe: a plain global npm install whose package
|
|
7
|
-
* directory is physically inside `<prefix>/lib/node_modules`
|
|
8
|
-
* this user (no sudo, ever).
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* directory is physically inside `<prefix>/lib/node_modules` (on Windows
|
|
8
|
+
* `<prefix>\node_modules`) and writable by this user (no sudo, ever).
|
|
9
|
+
* npm-linked and git checkouts (detected through the bin link too, so an
|
|
10
|
+
* `npm link` is never replaced), npx and local installs, pnpm/yarn/bun/volta
|
|
11
|
+
* globals and root/sudo runs get the hint line only, with the command that
|
|
12
|
+
* fits how they were installed. The install runs in a detached runner
|
|
13
|
+
* (auto-update-runner.ts) that loads no file from the package, so it survives
|
|
14
|
+
* this ccc exiting and npm replacing the package under it.
|
|
15
|
+
*
|
|
16
|
+
* Windows: the runner starts only as ccc exits (also when the window is
|
|
17
|
+
* closed) and waits for this process to be gone before npm runs, so npm does
|
|
18
|
+
* not rewrite files under the session that started it (other running
|
|
19
|
+
* sessions are as on POSIX: they already loaded their code; the shim npm
|
|
20
|
+
* rewrites is identical between versions). npm is run as `node npm-cli.js` (no
|
|
21
|
+
* shell: Node refuses to spawn a .cmd without one), and the runner kills
|
|
22
|
+
* npm's process tree with taskkill instead of a POSIX process group.
|
|
13
23
|
*
|
|
14
24
|
* One install at a time across sessions (a lock file with a stale timeout).
|
|
15
25
|
* Every attempt is recorded in update-state.json: a failure backs off for a
|
|
@@ -60,12 +70,17 @@ export interface UpdateAttempt {
|
|
|
60
70
|
at: number;
|
|
61
71
|
packageDir: string;
|
|
62
72
|
error?: string;
|
|
73
|
+
/** A launch has sent this result to diagnostics (takeUnreportedOutcome). */
|
|
74
|
+
reported?: boolean;
|
|
63
75
|
}
|
|
64
76
|
export interface UpdateState {
|
|
65
77
|
/** Install attempts per version (newest few kept). */
|
|
66
78
|
attempts: Record<string, number>;
|
|
67
79
|
last?: UpdateAttempt;
|
|
68
80
|
}
|
|
81
|
+
/** Why a launch with an update available did not install it, for diagnostics. */
|
|
82
|
+
export declare const SKIP_CODES: readonly ["disabled", "not-release", "linked-install", "install-method", "backoff", "max-attempts", "lock-held"];
|
|
83
|
+
export type SkipCode = (typeof SKIP_CODES)[number] | "no-update";
|
|
69
84
|
export type AutoUpdateDecision = {
|
|
70
85
|
action: "install";
|
|
71
86
|
version: string;
|
|
@@ -76,9 +91,21 @@ export type AutoUpdateDecision = {
|
|
|
76
91
|
} | {
|
|
77
92
|
action: "skip";
|
|
78
93
|
reason: string;
|
|
94
|
+
code: SkipCode;
|
|
79
95
|
};
|
|
80
96
|
export interface DetectInstallOptions {
|
|
81
97
|
packageDir: string;
|
|
98
|
+
/**
|
|
99
|
+
* The script as started (process.argv[1]), before symlinks are resolved:
|
|
100
|
+
* through it an `npm link` shows even though Node reports the checkout as
|
|
101
|
+
* the package directory.
|
|
102
|
+
*/
|
|
103
|
+
scriptPath?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Windows: test writability by creating a file (default true). Diagnostics
|
|
106
|
+
* pass false, so a launch with no update writes nothing into the package.
|
|
107
|
+
*/
|
|
108
|
+
probeWrites?: boolean;
|
|
82
109
|
platform?: NodeJS.Platform;
|
|
83
110
|
/** process.getuid(); undefined where there is none. */
|
|
84
111
|
uid?: number;
|
|
@@ -105,6 +132,7 @@ export type SpawnLike = (command: string, args: string[], options: {
|
|
|
105
132
|
detached: boolean;
|
|
106
133
|
stdio: "ignore";
|
|
107
134
|
env: NodeJS.ProcessEnv;
|
|
135
|
+
windowsHide: boolean;
|
|
108
136
|
}) => {
|
|
109
137
|
pid?: number;
|
|
110
138
|
unref(): void;
|
|
@@ -119,6 +147,11 @@ export interface StartInstallOptions {
|
|
|
119
147
|
execPath?: string;
|
|
120
148
|
/** The npm to run; default the one beside this Node, else `npm` on PATH. */
|
|
121
149
|
npmPath?: string;
|
|
150
|
+
/** Arguments before npm's own (Windows: npm-cli.js, run by this Node). */
|
|
151
|
+
npmArgs?: string[];
|
|
152
|
+
platform?: NodeJS.Platform;
|
|
153
|
+
/** Windows: the ccc whose exit the runner waits for before installing. */
|
|
154
|
+
waitForPid?: number;
|
|
122
155
|
}
|
|
123
156
|
export type StartInstallResult = "started" | "locked" | "not-planned" | "spawn-failed"
|
|
124
157
|
/** Rechecked under the lock: backed off, tried enough, or already installed. */
|
|
@@ -132,6 +165,14 @@ export declare function strictReleaseVersion(version: unknown): string | null;
|
|
|
132
165
|
* runner re-checks the root against `npm root -g` before installing.
|
|
133
166
|
*/
|
|
134
167
|
export declare function detectInstallMethod(opts: DetectInstallOptions): InstallMethod;
|
|
168
|
+
/** True for the installs whose files are the user's own checkout. */
|
|
169
|
+
export declare function isLinkedInstall(kind: InstallMethodKind): boolean;
|
|
170
|
+
/**
|
|
171
|
+
* How to update this install by hand: never `npm i -g` over a linked checkout
|
|
172
|
+
* (that replaces the link with the published package), and each package
|
|
173
|
+
* manager's own command for its globals.
|
|
174
|
+
*/
|
|
175
|
+
export declare function manualUpdateCommand(kind: InstallMethodKind): string;
|
|
135
176
|
/** Decide what this launch does about an available update. */
|
|
136
177
|
export declare function planAutoUpdate(opts: PlanOptions): AutoUpdateDecision;
|
|
137
178
|
/**
|
|
@@ -161,10 +202,22 @@ export declare class AutoUpdateNotices {
|
|
|
161
202
|
sessionStartLine(): string | undefined;
|
|
162
203
|
private pendingLine;
|
|
163
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* The result of the last background install, once: "ok" or "failed" the
|
|
207
|
+
* first time a launch sees it finished, then null (marked reported in the
|
|
208
|
+
* state file). For diagnostics, which the detached runner cannot send.
|
|
209
|
+
*/
|
|
210
|
+
export declare function takeUnreportedOutcome(stateFile: string, lockDir?: string, now?: number): "ok" | "failed" | null;
|
|
164
211
|
/** The version in `<packageDir>/package.json`, or null. */
|
|
165
212
|
export declare function readInstalledVersion(packageDir: string): string | null;
|
|
166
213
|
export declare function updateFiles(configDir: string): UpdateFiles;
|
|
167
214
|
export declare function readUpdateState(file: string): UpdateState;
|
|
168
215
|
/** Best effort and atomic: a failed write only loses bookkeeping. */
|
|
169
216
|
export declare function writeUpdateState(file: string, state: UpdateState): void;
|
|
217
|
+
/**
|
|
218
|
+
* The `node_modules/claude-code-infinite` entry the script was started
|
|
219
|
+
* through, when that entry is a symlink or junction (an `npm link`). The bin
|
|
220
|
+
* itself may be a symlink (POSIX `<prefix>/bin/ccc`): one hop is followed.
|
|
221
|
+
*/
|
|
222
|
+
export declare function linkedPackageEntry(scriptPath: string): string | null;
|
|
170
223
|
//# sourceMappingURL=auto-update.d.ts.map
|