coolhand-cli 0.11.1 → 0.13.0
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 +13 -1
- package/dist/api/client-file-client.d.ts +19 -0
- package/dist/api/client-file-client.d.ts.map +1 -0
- package/dist/api/client-file-client.js +47 -0
- package/dist/api/client-file-client.js.map +1 -0
- package/dist/api/help-center-client.d.ts +26 -0
- package/dist/api/help-center-client.d.ts.map +1 -0
- package/dist/api/help-center-client.js +90 -0
- package/dist/api/help-center-client.js.map +1 -0
- package/dist/api/last-sync.d.ts.map +1 -1
- package/dist/api/last-sync.js +2 -1
- package/dist/api/last-sync.js.map +1 -1
- package/dist/api/workload-client.d.ts +16 -0
- package/dist/api/workload-client.d.ts.map +1 -0
- package/dist/api/workload-client.js +28 -0
- package/dist/api/workload-client.js.map +1 -0
- package/dist/auth/callback-server.d.ts.map +1 -1
- package/dist/auth/callback-server.js +13 -1
- package/dist/auth/callback-server.js.map +1 -1
- package/dist/auto-update/index.d.ts +61 -0
- package/dist/auto-update/index.d.ts.map +1 -0
- package/dist/auto-update/index.js +355 -0
- package/dist/auto-update/index.js.map +1 -0
- package/dist/auto-update/select.d.ts +28 -0
- package/dist/auto-update/select.d.ts.map +1 -0
- package/dist/auto-update/select.js +45 -0
- package/dist/auto-update/select.js.map +1 -0
- package/dist/auto-update/state.d.ts +25 -0
- package/dist/auto-update/state.d.ts.map +1 -0
- package/dist/auto-update/state.js +81 -0
- package/dist/auto-update/state.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +525 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/capture-turn.d.ts +18 -0
- package/dist/commands/capture-turn.d.ts.map +1 -0
- package/dist/commands/capture-turn.js +152 -0
- package/dist/commands/capture-turn.js.map +1 -0
- package/dist/commands/download-client-file.d.ts +9 -0
- package/dist/commands/download-client-file.d.ts.map +1 -0
- package/dist/commands/download-client-file.js +71 -0
- package/dist/commands/download-client-file.js.map +1 -0
- package/dist/commands/fetch-log.d.ts.map +1 -1
- package/dist/commands/fetch-log.js +9 -0
- package/dist/commands/fetch-log.js.map +1 -1
- package/dist/commands/flush-pending.d.ts.map +1 -1
- package/dist/commands/flush-pending.js +1 -30
- package/dist/commands/flush-pending.js.map +1 -1
- package/dist/commands/get-help-article.d.ts +3 -0
- package/dist/commands/get-help-article.d.ts.map +1 -0
- package/dist/commands/get-help-article.js +49 -0
- package/dist/commands/get-help-article.js.map +1 -0
- package/dist/commands/get-template.d.ts.map +1 -1
- package/dist/commands/get-template.js +18 -1
- package/dist/commands/get-template.js.map +1 -1
- package/dist/commands/link-feedback.d.ts +5 -0
- package/dist/commands/link-feedback.d.ts.map +1 -0
- package/dist/commands/link-feedback.js +89 -0
- package/dist/commands/link-feedback.js.map +1 -0
- package/dist/commands/list-client-files.d.ts +3 -0
- package/dist/commands/list-client-files.d.ts.map +1 -0
- package/dist/commands/list-client-files.js +55 -0
- package/dist/commands/list-client-files.js.map +1 -0
- package/dist/commands/list-help-categories.d.ts +3 -0
- package/dist/commands/list-help-categories.d.ts.map +1 -0
- package/dist/commands/list-help-categories.js +47 -0
- package/dist/commands/list-help-categories.js.map +1 -0
- package/dist/commands/search-help-articles.d.ts +3 -0
- package/dist/commands/search-help-articles.d.ts.map +1 -0
- package/dist/commands/search-help-articles.js +73 -0
- package/dist/commands/search-help-articles.js.map +1 -0
- package/dist/commands/search-logs.d.ts.map +1 -1
- package/dist/commands/search-logs.js +26 -0
- package/dist/commands/search-logs.js.map +1 -1
- package/dist/commands/search-templates.d.ts.map +1 -1
- package/dist/commands/search-templates.js +12 -0
- package/dist/commands/search-templates.js.map +1 -1
- package/dist/commands/search-workloads.d.ts +3 -0
- package/dist/commands/search-workloads.d.ts.map +1 -0
- package/dist/commands/search-workloads.js +79 -0
- package/dist/commands/search-workloads.js.map +1 -0
- package/dist/commands/update-client-file.d.ts +3 -0
- package/dist/commands/update-client-file.d.ts.map +1 -0
- package/dist/commands/update-client-file.js +39 -0
- package/dist/commands/update-client-file.js.map +1 -0
- package/dist/commands/update.d.ts +9 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +63 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -7
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/format-cost.d.ts +9 -0
- package/dist/format-cost.d.ts.map +1 -0
- package/dist/format-cost.js +22 -0
- package/dist/format-cost.js.map +1 -0
- package/dist/hooks/adapters/claude.d.ts +12 -0
- package/dist/hooks/adapters/claude.d.ts.map +1 -0
- package/dist/hooks/adapters/claude.js +98 -0
- package/dist/hooks/adapters/claude.js.map +1 -0
- package/dist/hooks/adapters/copilot.d.ts +15 -0
- package/dist/hooks/adapters/copilot.d.ts.map +1 -0
- package/dist/hooks/adapters/copilot.js +96 -0
- package/dist/hooks/adapters/copilot.js.map +1 -0
- package/dist/hooks/adapters/index.d.ts +5 -0
- package/dist/hooks/adapters/index.d.ts.map +1 -0
- package/dist/hooks/adapters/index.js +8 -0
- package/dist/hooks/adapters/index.js.map +1 -0
- package/dist/hooks/adapters/shared.d.ts +25 -0
- package/dist/hooks/adapters/shared.d.ts.map +1 -0
- package/dist/hooks/adapters/shared.js +37 -0
- package/dist/hooks/adapters/shared.js.map +1 -0
- package/dist/hooks/disabled.d.ts +3 -0
- package/dist/hooks/disabled.d.ts.map +1 -0
- package/dist/hooks/disabled.js +5 -0
- package/dist/hooks/disabled.js.map +1 -0
- package/dist/hooks/hook-log.d.ts +7 -0
- package/dist/hooks/hook-log.d.ts.map +1 -0
- package/dist/hooks/hook-log.js +34 -0
- package/dist/hooks/hook-log.js.map +1 -0
- package/dist/hooks/identity.d.ts +21 -0
- package/dist/hooks/identity.d.ts.map +1 -0
- package/dist/hooks/identity.js +121 -0
- package/dist/hooks/identity.js.map +1 -0
- package/dist/hooks/log-turn.d.ts +23 -0
- package/dist/hooks/log-turn.d.ts.map +1 -0
- package/dist/hooks/log-turn.js +48 -0
- package/dist/hooks/log-turn.js.map +1 -0
- package/dist/hooks/send-turn.d.ts +32 -0
- package/dist/hooks/send-turn.d.ts.map +1 -0
- package/dist/hooks/send-turn.js +305 -0
- package/dist/hooks/send-turn.js.map +1 -0
- package/dist/hooks/silence.d.ts +7 -0
- package/dist/hooks/silence.d.ts.map +1 -0
- package/dist/hooks/silence.js +22 -0
- package/dist/hooks/silence.js.map +1 -0
- package/dist/hooks/stdin.d.ts +11 -0
- package/dist/hooks/stdin.d.ts.map +1 -0
- package/dist/hooks/stdin.js +51 -0
- package/dist/hooks/stdin.js.map +1 -0
- package/dist/hooks/turn-state.d.ts +40 -0
- package/dist/hooks/turn-state.d.ts.map +1 -0
- package/dist/hooks/turn-state.js +97 -0
- package/dist/hooks/turn-state.js.map +1 -0
- package/dist/hooks/types.d.ts +74 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +3 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +12 -1
- package/dist/logger.js.map +1 -1
- package/dist/pending-store.d.ts +22 -2
- package/dist/pending-store.d.ts.map +1 -1
- package/dist/pending-store.js +25 -3
- package/dist/pending-store.js.map +1 -1
- package/dist/pending-upload.d.ts +12 -0
- package/dist/pending-upload.d.ts.map +1 -0
- package/dist/pending-upload.js +51 -0
- package/dist/pending-upload.js.map +1 -0
- package/dist/sessions/blocks.d.ts +51 -0
- package/dist/sessions/blocks.d.ts.map +1 -0
- package/dist/sessions/blocks.js +193 -0
- package/dist/sessions/blocks.js.map +1 -0
- package/dist/sessions/claude-scanner.d.ts +60 -1
- package/dist/sessions/claude-scanner.d.ts.map +1 -1
- package/dist/sessions/claude-scanner.js +277 -1
- package/dist/sessions/claude-scanner.js.map +1 -1
- package/dist/sessions/copilot-scanner.d.ts +27 -0
- package/dist/sessions/copilot-scanner.d.ts.map +1 -0
- package/dist/sessions/copilot-scanner.js +171 -0
- package/dist/sessions/copilot-scanner.js.map +1 -0
- package/dist/sessions/redact-secrets.d.ts.map +1 -1
- package/dist/sessions/redact-secrets.js +49 -10
- package/dist/sessions/redact-secrets.js.map +1 -1
- package/dist/sessions/turn-envelope.d.ts +62 -0
- package/dist/sessions/turn-envelope.d.ts.map +1 -0
- package/dist/sessions/turn-envelope.js +182 -0
- package/dist/sessions/turn-envelope.js.map +1 -0
- package/dist/sessions/usage.d.ts +23 -0
- package/dist/sessions/usage.d.ts.map +1 -0
- package/dist/sessions/usage.js +56 -0
- package/dist/sessions/usage.js.map +1 -0
- package/dist/skills/skill-scanner.d.ts.map +1 -1
- package/dist/skills/skill-scanner.js +3 -1
- package/dist/skills/skill-scanner.js.map +1 -1
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/package.json +7 -4
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { readFileSync, realpathSync } from 'fs';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { isAllowedBaseUrlScheme } from '../base-url.js';
|
|
6
|
+
import { configDir, loadConfig } from '../config.js';
|
|
7
|
+
import { ExitCode } from '../errors.js';
|
|
8
|
+
import { logger } from '../logger.js';
|
|
9
|
+
import { PACKAGE_NAME, PACKAGE_VERSION } from '../version.js';
|
|
10
|
+
import { resolveWrapSpawn } from '../win-spawn.js';
|
|
11
|
+
import { BAKE_PERIOD_MS, compareSemver, parseStable, selectUpdate } from './select.js';
|
|
12
|
+
import { acquireLock, loadState, saveState } from './state.js';
|
|
13
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
|
14
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
15
|
+
const FETCH_TIMEOUT_MS = 10000;
|
|
16
|
+
const NPM_CONFIG_TIMEOUT_MS = 10000;
|
|
17
|
+
const INSTALL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
18
|
+
/** A failed install blocks retrying the same version this long; transient causes (no `npm` on PATH, offline) shouldn't stick forever. */
|
|
19
|
+
const FAILED_RETRY_MS = 7 * 24 * 60 * 60 * 1000;
|
|
20
|
+
/**
|
|
21
|
+
* Working directory for every spawn. npm reads a project-level `.npmrc` from the cwd, even for `-g`, so a
|
|
22
|
+
* cloned repo could point the silent install at its own registry; the home directory only has the user's own config.
|
|
23
|
+
*/
|
|
24
|
+
function neutralCwd() {
|
|
25
|
+
return os.homedir();
|
|
26
|
+
}
|
|
27
|
+
/** Commands that must never trigger (or announce) an update: the updater itself, the other background job, and passive output. */
|
|
28
|
+
const NO_AUTO_UPDATE = new Set(['__auto-update', '__flush-pending', 'capture-turn', 'update', 'help', 'version']);
|
|
29
|
+
/** Package-manager layouts that own their own global installs; `npm install -g` would create a second copy instead of upgrading this one. */
|
|
30
|
+
const FOREIGN_MANAGERS = ['_npx', '.volta', '.pnpm', 'pnpm', '.yarn', '.bun'];
|
|
31
|
+
/**
|
|
32
|
+
* True when `scriptPath` looks like a global `npm install -g` of this package:
|
|
33
|
+
* `<prefix>/lib/node_modules/<pkg>/…` (macOS/Linux, nvm, Homebrew) or `<prefix>/node_modules/<pkg>/…`
|
|
34
|
+
* under an `npm` directory (Windows). A project-local install (`<project>/node_modules`), `npx` cache,
|
|
35
|
+
* `npm link` checkout, or another package manager's global dir does not match, so we never touch it.
|
|
36
|
+
*/
|
|
37
|
+
export function isGlobalNpmInstall(scriptPath) {
|
|
38
|
+
const parts = scriptPath.split(/[\\/]+/);
|
|
39
|
+
const at = parts.lastIndexOf(PACKAGE_NAME);
|
|
40
|
+
if (at < 2 || parts[at - 1] !== 'node_modules') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (parts.some((p) => FOREIGN_MANAGERS.includes(p.toLowerCase()))) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const owner = (parts[at - 2] ?? '').toLowerCase();
|
|
47
|
+
return owner === 'lib' || owner === 'npm';
|
|
48
|
+
}
|
|
49
|
+
function runningScript() {
|
|
50
|
+
const argv1 = process.argv[1];
|
|
51
|
+
if (!argv1) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return realpathSync(argv1);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return argv1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function canSelfInstall() {
|
|
62
|
+
const script = runningScript();
|
|
63
|
+
return script !== undefined && isGlobalNpmInstall(script);
|
|
64
|
+
}
|
|
65
|
+
/** Set means non-empty and not an explicit "0"/"false", so `CI=false` in a dev shell doesn't disable updates. */
|
|
66
|
+
function envFlag(name) {
|
|
67
|
+
const v = (process.env[name] ?? '').trim().toLowerCase();
|
|
68
|
+
return v !== '' && v !== '0' && v !== 'false';
|
|
69
|
+
}
|
|
70
|
+
function optedOutByEnv() {
|
|
71
|
+
return envFlag('COOLHAND_NO_AUTO_UPDATE') || envFlag('CI');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The validated `COOLHAND_UPDATE_REGISTRY` override, undefined when unset. A value that is set but rejected
|
|
75
|
+
* throws rather than falling back to the public registry, which someone using a private mirror may be avoiding.
|
|
76
|
+
*/
|
|
77
|
+
function registryOverride() {
|
|
78
|
+
const override = process.env.COOLHAND_UPDATE_REGISTRY;
|
|
79
|
+
if (!override) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const url = new URL(override);
|
|
84
|
+
// Credentials would end up in `npm`'s argv (visible in the process list); auth belongs in .npmrc.
|
|
85
|
+
if (isAllowedBaseUrlScheme(url) && !url.username && !url.password && !url.search && !url.hash) {
|
|
86
|
+
return url.href.replace(/\/+$/, '');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// fall through to the error below
|
|
91
|
+
}
|
|
92
|
+
throw new Error('COOLHAND_UPDATE_REGISTRY must be an https:// URL (http:// only for localhost) without credentials, query, or fragment');
|
|
93
|
+
}
|
|
94
|
+
function registryUrl() {
|
|
95
|
+
return registryOverride() ?? DEFAULT_REGISTRY;
|
|
96
|
+
}
|
|
97
|
+
export async function fetchPackument(fetchFn = fetch) {
|
|
98
|
+
const res = await fetchFn(`${registryUrl()}/${PACKAGE_NAME}`, {
|
|
99
|
+
headers: { accept: 'application/json' },
|
|
100
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
101
|
+
});
|
|
102
|
+
if (!res.ok) {
|
|
103
|
+
throw new Error(`registry responded ${res.status}`);
|
|
104
|
+
}
|
|
105
|
+
return (await res.json());
|
|
106
|
+
}
|
|
107
|
+
/** Newest version that has cleared the bake period and is newer than the running one, or null. */
|
|
108
|
+
export async function findEligibleUpdate(now = Date.now(), fetchFn) {
|
|
109
|
+
return selectUpdate(await fetchPackument(fetchFn), { current: PACKAGE_VERSION, now });
|
|
110
|
+
}
|
|
111
|
+
/** Value of `npm config get <key>`, or undefined when unset or npm doesn't know the key. */
|
|
112
|
+
function npmConfigValue(key, spawnFn) {
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
const { cmd, spawnArgs, windowsVerbatimArguments } = resolveWrapSpawn('npm', ['config', 'get', key]);
|
|
115
|
+
try {
|
|
116
|
+
const child = spawnFn(cmd, spawnArgs, {
|
|
117
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
118
|
+
cwd: neutralCwd(),
|
|
119
|
+
windowsHide: true,
|
|
120
|
+
timeout: NPM_CONFIG_TIMEOUT_MS,
|
|
121
|
+
...(windowsVerbatimArguments ? { windowsVerbatimArguments } : {}),
|
|
122
|
+
});
|
|
123
|
+
let out = '';
|
|
124
|
+
child.stdout?.on('data', (chunk) => {
|
|
125
|
+
out += chunk.toString();
|
|
126
|
+
});
|
|
127
|
+
child.on('error', () => resolve(undefined));
|
|
128
|
+
child.on('close', () => {
|
|
129
|
+
const v = out.trim();
|
|
130
|
+
resolve(v === '' || v === 'undefined' || v === 'null' || v === '0' ? undefined : v);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
resolve(undefined);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async function npmConfigHasBake(spawnFn) {
|
|
139
|
+
const values = await Promise.all([npmConfigValue('min-release-age', spawnFn), npmConfigValue('before', spawnFn)]);
|
|
140
|
+
return values.some((v) => v !== undefined);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* `npm install -g <pkg>@<exact version>`; resolves true on a zero exit. Pinned so the bake check is what
|
|
144
|
+
* gets installed, and `--before` holds every transitive dependency to the same 48-hour bake. If the user's
|
|
145
|
+
* npm config already sets `min-release-age` or `before`, ours is left off: npm rejects combining them, and
|
|
146
|
+
* their own bake then covers the dependencies.
|
|
147
|
+
*/
|
|
148
|
+
export async function installVersion(version, stdio = 'ignore', deps = {}) {
|
|
149
|
+
const spawnFn = deps.spawnFn ?? spawn;
|
|
150
|
+
// When the version was chosen from an overridden registry, install from that same one so the
|
|
151
|
+
// bake check and the installed bits agree; otherwise defer to the user's own npm config.
|
|
152
|
+
// Resolved first so an invalid override fails before any child process starts.
|
|
153
|
+
const registry = registryOverride();
|
|
154
|
+
const userBake = await (deps.userHasBake ?? (() => npmConfigHasBake(spawnFn)))();
|
|
155
|
+
const { cmd, spawnArgs, windowsVerbatimArguments } = resolveWrapSpawn('npm', [
|
|
156
|
+
'install',
|
|
157
|
+
'-g',
|
|
158
|
+
...(registry ? ['--registry', registry] : []),
|
|
159
|
+
...(userBake ? [] : [`--before=${new Date(Date.now() - BAKE_PERIOD_MS).toISOString()}`]),
|
|
160
|
+
`${PACKAGE_NAME}@${version}`,
|
|
161
|
+
]);
|
|
162
|
+
return new Promise((resolve) => {
|
|
163
|
+
try {
|
|
164
|
+
const child = spawnFn(cmd, spawnArgs, {
|
|
165
|
+
stdio,
|
|
166
|
+
cwd: neutralCwd(),
|
|
167
|
+
windowsHide: true,
|
|
168
|
+
timeout: INSTALL_TIMEOUT_MS,
|
|
169
|
+
...(windowsVerbatimArguments ? { windowsVerbatimArguments } : {}),
|
|
170
|
+
});
|
|
171
|
+
child.on('error', () => resolve(false));
|
|
172
|
+
child.on('close', (code) => resolve(code === 0));
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
resolve(false);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
export function manualInstallHint(version) {
|
|
180
|
+
return `npm install -g ${PACKAGE_NAME}@${version}`;
|
|
181
|
+
}
|
|
182
|
+
/** How to update by hand; softened when this copy isn't a global npm install, where that command would add a second copy. */
|
|
183
|
+
export function updateHint(version) {
|
|
184
|
+
return canSelfInstall()
|
|
185
|
+
? `Run \`${manualInstallHint(version)}\` to update.`
|
|
186
|
+
: `Update with the package manager you installed it with (for npm: \`${manualInstallHint(version)}\`).`;
|
|
187
|
+
}
|
|
188
|
+
/** Version in the package.json of the install the running script belongs to, or undefined if it can't be read. */
|
|
189
|
+
export function installedVersionOnDisk() {
|
|
190
|
+
const script = runningScript();
|
|
191
|
+
if (!script) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
for (let dir = path.dirname(script); dir !== path.dirname(dir); dir = path.dirname(dir)) {
|
|
195
|
+
try {
|
|
196
|
+
const pkg = JSON.parse(readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
197
|
+
if (pkg.name === PACKAGE_NAME) {
|
|
198
|
+
return pkg.version;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// keep walking up
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Hidden `__auto-update` command, run detached by `maybeAutoUpdate`. Checks the registry and, for a
|
|
209
|
+
* global npm install, installs the eligible version; otherwise records it so the next launch can
|
|
210
|
+
* mention it. Network and install failures are swallowed. A version whose install failed is not
|
|
211
|
+
* retried (a newer one is, and so is that version after a week), so a root-owned prefix doesn't re-run `npm install -g` every day.
|
|
212
|
+
*/
|
|
213
|
+
export async function run(deps = {}) {
|
|
214
|
+
// Two launches past the daily interval can both spawn this job; only one may touch the global install.
|
|
215
|
+
const release = await acquireLock();
|
|
216
|
+
if (!release) {
|
|
217
|
+
return ExitCode.OK;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
return await runLocked(deps);
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
// On Windows an antivirus/indexer can briefly hold the lock file; a stale lock is taken over later anyway.
|
|
224
|
+
await release().catch(() => undefined);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async function runLocked(deps) {
|
|
228
|
+
const checkedAt = new Date().toISOString();
|
|
229
|
+
// Fields this job owns; merged into a freshly re-read state at the end, because the install can take
|
|
230
|
+
// minutes and foreground launches write `notified`/`last_check` in the meantime.
|
|
231
|
+
const update = {};
|
|
232
|
+
let found = false;
|
|
233
|
+
try {
|
|
234
|
+
const priorFailed = (await loadState()).failed;
|
|
235
|
+
const target = selectUpdate(await fetchPackument(deps.fetchFn), { current: PACKAGE_VERSION, now: Date.now() });
|
|
236
|
+
found = true;
|
|
237
|
+
if (target) {
|
|
238
|
+
update.available = target;
|
|
239
|
+
const failedAt = priorFailed ? Date.parse(priorFailed.at) : NaN;
|
|
240
|
+
if (priorFailed?.version === target && Date.now() - failedAt < FAILED_RETRY_MS) {
|
|
241
|
+
update.failed = priorFailed;
|
|
242
|
+
}
|
|
243
|
+
else if (deps.selfInstall ?? canSelfInstall()) {
|
|
244
|
+
const ok = await (deps.install ?? installVersion)(target);
|
|
245
|
+
// Only trust the install if the files for the running copy now say so; npm may have
|
|
246
|
+
// targeted a different global prefix than the one holding this script.
|
|
247
|
+
if (ok && (deps.versionOnDisk ?? installedVersionOnDisk)() === target) {
|
|
248
|
+
update.installed = target;
|
|
249
|
+
delete update.available;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
update.failed = { version: target, at: new Date().toISOString() };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// Offline or registry hiccup.
|
|
259
|
+
}
|
|
260
|
+
const state = await loadState();
|
|
261
|
+
state.last_check = checkedAt;
|
|
262
|
+
if (found) {
|
|
263
|
+
delete state.available;
|
|
264
|
+
delete state.failed;
|
|
265
|
+
Object.assign(state, update);
|
|
266
|
+
}
|
|
267
|
+
await saveState(state);
|
|
268
|
+
return ExitCode.OK;
|
|
269
|
+
}
|
|
270
|
+
function spawnBackgroundUpdate(deps = {}) {
|
|
271
|
+
const spawnFn = deps.spawnFn ?? spawn;
|
|
272
|
+
const cliPath = process.argv[1];
|
|
273
|
+
if (!cliPath) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
const child = spawnFn(process.execPath, [cliPath, '__auto-update'], {
|
|
278
|
+
detached: true,
|
|
279
|
+
stdio: 'ignore',
|
|
280
|
+
cwd: neutralCwd(),
|
|
281
|
+
// The child runs from another cwd, so hand it the absolute config dir a relative override would break.
|
|
282
|
+
env: { ...process.env, COOLHAND_CONFIG_DIR: path.resolve(configDir()) },
|
|
283
|
+
windowsHide: true,
|
|
284
|
+
});
|
|
285
|
+
child.on('error', () => {
|
|
286
|
+
// Best-effort; the next launch past the interval tries again.
|
|
287
|
+
});
|
|
288
|
+
child.unref();
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// Same: never let the updater break the command the user asked for.
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Called at the start of every command. Costs one small file read: it announces a finished or
|
|
296
|
+
* pending update (once, on stderr) and, at most daily, launches the detached background check.
|
|
297
|
+
* Quiet under `--json`, in CI, and when opted out (`COOLHAND_NO_AUTO_UPDATE=1` or `auto_update: false`
|
|
298
|
+
* in config.json). Best-effort — never throws.
|
|
299
|
+
*/
|
|
300
|
+
export async function maybeAutoUpdate(opts, deps = {}) {
|
|
301
|
+
try {
|
|
302
|
+
const passive = opts.command === undefined || NO_AUTO_UPDATE.has(opts.command) || opts.command.startsWith('-');
|
|
303
|
+
if (passive || optedOutByEnv()) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
// `run` is a public export: a host program embedding it must not have its own script re-executed.
|
|
307
|
+
if (!(deps.ownScript ?? installedVersionOnDisk() !== undefined)) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if ((await loadConfig()).auto_update === false) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const now = deps.now ?? Date.now();
|
|
314
|
+
const state = await loadState();
|
|
315
|
+
let dirty = false;
|
|
316
|
+
const current = parseStable(PACKAGE_VERSION);
|
|
317
|
+
const installed = state.installed ? parseStable(state.installed) : null;
|
|
318
|
+
if (state.installed && current && installed && compareSemver(current, installed) >= 0) {
|
|
319
|
+
// Under --json the announcement is deferred (kept) rather than lost, unless it's already stale.
|
|
320
|
+
if (!opts.json && state.installed === PACKAGE_VERSION) {
|
|
321
|
+
logger.info(`${PACKAGE_NAME} updated to ${state.installed}.`);
|
|
322
|
+
}
|
|
323
|
+
if (!opts.json || state.installed !== PACKAGE_VERSION) {
|
|
324
|
+
delete state.installed;
|
|
325
|
+
dirty = true;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const available = state.available ? parseStable(state.available) : null;
|
|
329
|
+
if (state.available && current && available && compareSemver(available, current) > 0) {
|
|
330
|
+
if (!opts.json && state.notified !== state.available) {
|
|
331
|
+
logger.info(`${PACKAGE_NAME} ${state.available} is available. ${updateHint(state.available)}`);
|
|
332
|
+
state.notified = state.available;
|
|
333
|
+
dirty = true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const last = state.last_check ? Date.parse(state.last_check) : NaN;
|
|
337
|
+
// A last_check in the future (clock change, hand-edited file) must not suppress checks indefinitely.
|
|
338
|
+
const due = Number.isNaN(last) || last > now || now - last >= CHECK_INTERVAL_MS;
|
|
339
|
+
if (due) {
|
|
340
|
+
// Stamp before spawning so concurrent launches don't each start a check.
|
|
341
|
+
state.last_check = new Date(now).toISOString();
|
|
342
|
+
dirty = true;
|
|
343
|
+
}
|
|
344
|
+
if (dirty) {
|
|
345
|
+
await saveState(state);
|
|
346
|
+
}
|
|
347
|
+
if (due) {
|
|
348
|
+
spawnBackgroundUpdate(deps);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
// Never let the updater break the command the user asked for.
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auto-update/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** A release must have been on the registry at least this long before we install it. */
|
|
2
|
+
export declare const BAKE_PERIOD_MS: number;
|
|
3
|
+
/** The subset of the npm registry packument (`GET /<package>`) that selection needs. */
|
|
4
|
+
export interface Packument {
|
|
5
|
+
'dist-tags'?: Record<string, string>;
|
|
6
|
+
time?: Record<string, string>;
|
|
7
|
+
versions?: Record<string, {
|
|
8
|
+
deprecated?: string;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
export type Semver = readonly [number, number, number];
|
|
12
|
+
/** Parses `MAJOR.MINOR.PATCH`; returns null for prereleases, build metadata, or anything else. */
|
|
13
|
+
export declare function parseStable(version: string): Semver | null;
|
|
14
|
+
export declare function compareSemver(a: Semver, b: Semver): number;
|
|
15
|
+
export interface SelectOptions {
|
|
16
|
+
current: string;
|
|
17
|
+
now: number;
|
|
18
|
+
bakeMs?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Pick the version to upgrade to: the highest stable release that is newer than `current`,
|
|
22
|
+
* has been published for at least the bake period, is not deprecated, and is not above the
|
|
23
|
+
* registry's `latest` dist-tag. Capping at `latest` means rolling that tag back is enough to
|
|
24
|
+
* stop a bad release from spreading; a version that was unpublished is no longer in `versions`.
|
|
25
|
+
* Returns null when nothing qualifies (including when `current` is itself ahead of `latest`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function selectUpdate(packument: Packument, opts: SelectOptions): string | null;
|
|
28
|
+
//# sourceMappingURL=select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/auto-update/select.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,eAAO,MAAM,cAAc,QAAsB,CAAC;AAElD,wFAAwF;AACxF,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED,MAAM,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAIvD,kGAAkG;AAClG,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG1D;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CA0BrF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** A release must have been on the registry at least this long before we install it. */
|
|
2
|
+
export const BAKE_PERIOD_MS = 48 * 60 * 60 * 1000;
|
|
3
|
+
const STABLE_SEMVER = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
4
|
+
/** Parses `MAJOR.MINOR.PATCH`; returns null for prereleases, build metadata, or anything else. */
|
|
5
|
+
export function parseStable(version) {
|
|
6
|
+
const m = STABLE_SEMVER.exec(version);
|
|
7
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
8
|
+
}
|
|
9
|
+
export function compareSemver(a, b) {
|
|
10
|
+
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Pick the version to upgrade to: the highest stable release that is newer than `current`,
|
|
14
|
+
* has been published for at least the bake period, is not deprecated, and is not above the
|
|
15
|
+
* registry's `latest` dist-tag. Capping at `latest` means rolling that tag back is enough to
|
|
16
|
+
* stop a bad release from spreading; a version that was unpublished is no longer in `versions`.
|
|
17
|
+
* Returns null when nothing qualifies (including when `current` is itself ahead of `latest`).
|
|
18
|
+
*/
|
|
19
|
+
export function selectUpdate(packument, opts) {
|
|
20
|
+
const current = parseStable(opts.current);
|
|
21
|
+
const latest = parseStable(packument['dist-tags']?.latest ?? '');
|
|
22
|
+
if (!current || !latest) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const bakeMs = opts.bakeMs ?? BAKE_PERIOD_MS;
|
|
26
|
+
let best = null;
|
|
27
|
+
for (const [version, meta] of Object.entries(packument.versions ?? {})) {
|
|
28
|
+
const semver = parseStable(version);
|
|
29
|
+
if (!semver || meta?.deprecated) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (compareSemver(semver, current) <= 0 || compareSemver(semver, latest) > 0) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const published = Date.parse(packument.time?.[version] ?? '');
|
|
36
|
+
if (Number.isNaN(published) || opts.now - published < bakeMs) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (!best || compareSemver(semver, best.semver) > 0) {
|
|
40
|
+
best = { version, semver };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return best?.version ?? null;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/auto-update/select.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAWlD,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAE9C,kGAAkG;AAClG,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,CAAS,EAAE,CAAS;IAChD,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC;AAQD;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,SAAoB,EAAE,IAAmB;IACpE,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;IACjE,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,cAAc,CAAC;IAE7C,IAAI,IAAI,GAA+C,IAAI,CAAC;IAC5D,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;QACvE,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,UAAU,EAAE,CAAC;YAChC,SAAS;QACX,CAAC;QACD,IAAI,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7E,SAAS;QACX,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9D,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,GAAG,GAAG,SAAS,GAAG,MAAM,EAAE,CAAC;YAC7D,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACpD,IAAI,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Bookkeeping for the background updater; lives next to config.json. */
|
|
2
|
+
export interface UpdateState {
|
|
3
|
+
/** ISO time of the last registry check (success or failure) — throttles checks. */
|
|
4
|
+
last_check?: string;
|
|
5
|
+
/** Newest eligible version found that we did not install (not a global npm install, or the install failed). */
|
|
6
|
+
available?: string;
|
|
7
|
+
/** Version `available` was last announced to the user, so the notice prints once. */
|
|
8
|
+
notified?: string;
|
|
9
|
+
/** Version a background `npm install -g` last completed for; announced after the next launch picks it up. */
|
|
10
|
+
installed?: string;
|
|
11
|
+
/** Last version we tried and failed to install, with when — that version is not retried; a newer one is. */
|
|
12
|
+
failed?: {
|
|
13
|
+
version: string;
|
|
14
|
+
at: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function statePath(): string;
|
|
18
|
+
export declare function loadState(): Promise<UpdateState>;
|
|
19
|
+
export declare function saveState(state: UpdateState): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Exclusive lock so only one background job (and so one `npm install -g`) runs at a time. Returns a release
|
|
22
|
+
* function, or null when another job holds it. A lock left by a crashed job is taken over once stale.
|
|
23
|
+
*/
|
|
24
|
+
export declare function acquireLock(): Promise<(() => Promise<void>) | null>;
|
|
25
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/auto-update/state.ts"],"names":[],"mappings":"AAOA,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+GAA+G;IAC/G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6GAA6G;IAC7G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4GAA4G;IAC5G,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,CA0BtD;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAIjE;AAMD;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CA4BzE"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { atomicWriteFile, configDir } from '../config.js';
|
|
4
|
+
import { parseStable } from './select.js';
|
|
5
|
+
const STATE_FILENAME = 'update-state.json';
|
|
6
|
+
export function statePath() {
|
|
7
|
+
return path.join(configDir(), STATE_FILENAME);
|
|
8
|
+
}
|
|
9
|
+
export async function loadState() {
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(await fs.readFile(statePath(), 'utf8'));
|
|
12
|
+
const raw = typeof parsed === 'object' && parsed !== null ? parsed : {};
|
|
13
|
+
const state = {};
|
|
14
|
+
for (const key of ['last_check', 'notified']) {
|
|
15
|
+
if (typeof raw[key] === 'string') {
|
|
16
|
+
state[key] = raw[key];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// Versions we act on must be plain stable semver; anything else is dropped.
|
|
20
|
+
for (const key of ['available', 'installed']) {
|
|
21
|
+
const v = raw[key];
|
|
22
|
+
if (typeof v === 'string' && parseStable(v)) {
|
|
23
|
+
state[key] = v;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const failed = raw.failed;
|
|
27
|
+
if (typeof failed?.version === 'string' && typeof failed.at === 'string') {
|
|
28
|
+
state.failed = { version: failed.version, at: failed.at };
|
|
29
|
+
}
|
|
30
|
+
return state;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Missing or corrupt: start fresh. The state only throttles; losing it costs one extra check.
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function saveState(state) {
|
|
38
|
+
const file = statePath();
|
|
39
|
+
await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
40
|
+
await atomicWriteFile(file, `${JSON.stringify(state, null, 2)}\n`);
|
|
41
|
+
}
|
|
42
|
+
const LOCK_FILENAME = 'update.lock';
|
|
43
|
+
/** Longer than the install timeout, so a live install's lock is never taken over. */
|
|
44
|
+
const LOCK_STALE_MS = 10 * 60 * 1000;
|
|
45
|
+
/**
|
|
46
|
+
* Exclusive lock so only one background job (and so one `npm install -g`) runs at a time. Returns a release
|
|
47
|
+
* function, or null when another job holds it. A lock left by a crashed job is taken over once stale.
|
|
48
|
+
*/
|
|
49
|
+
export async function acquireLock() {
|
|
50
|
+
const file = path.join(configDir(), LOCK_FILENAME);
|
|
51
|
+
try {
|
|
52
|
+
await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
53
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
54
|
+
try {
|
|
55
|
+
await (await fs.open(file, 'wx', 0o600)).close();
|
|
56
|
+
return () => fs.rm(file, { force: true });
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (err.code !== 'EEXIST') {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
let mtimeMs;
|
|
63
|
+
try {
|
|
64
|
+
mtimeMs = (await fs.stat(file)).mtimeMs;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
continue; // released between open and stat: try to take it
|
|
68
|
+
}
|
|
69
|
+
if (Date.now() - mtimeMs < LOCK_STALE_MS) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
await fs.rm(file, { force: true });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Can't lock (read-only dir, race with release): skip this run rather than risk a double install.
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/auto-update/state.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAsFA,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC;CACpD;AA4hBD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAgE7F;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAiDpD;AAsnCD,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAiMzD"}
|