blun-king-cli 9.1.392 → 9.1.394

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.392",
3
+ "version": "9.1.394",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -1,4 +1,15 @@
1
1
  {
2
- "version": "9.1.69",
3
- "removals": []
2
+ "version": "9.1.393",
3
+ "removals": [
4
+ {
5
+ "path": "package/bin/running-update.cjs",
6
+ "reason": "Background updater removed by owner decision; the visible manual startup picker remains.",
7
+ "decisionRef": "Telegram DM 5597, 5603, 5610, 5614 and 5619, 2026-08-28"
8
+ },
9
+ {
10
+ "path": "package/bin/running-update-preference.cjs",
11
+ "reason": "Background updater preference removed with the background updater; manual startup choices remain.",
12
+ "decisionRef": "Telegram DM 5597, 5603, 5610, 5614 and 5619, 2026-08-28"
13
+ }
14
+ ]
4
15
  }
@@ -1,68 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const { randomUUID } = require('node:crypto');
6
-
7
- const { ensurePrivateDirectory, securePrivateFile, writePrivateFile } = require('./private-paths');
8
-
9
- const RUNNING_UPDATE_PREFERENCE_FILE = 'running-update.json';
10
- const RUNNING_UPDATE_MODES = Object.freeze({
11
- MANUAL: 'manual',
12
- RESUME: 'resume',
13
- NEW: 'new',
14
- OFF: 'off',
15
- });
16
- const DEFAULT_RUNNING_UPDATE_MODE = RUNNING_UPDATE_MODES.RESUME;
17
- const VALID_MODES = new Set(Object.values(RUNNING_UPDATE_MODES));
18
-
19
- function preferencePath(profileHome) {
20
- return path.join(path.resolve(profileHome), RUNNING_UPDATE_PREFERENCE_FILE);
21
- }
22
-
23
- function normalizeRunningUpdateMode(value) {
24
- return typeof value === 'string' && VALID_MODES.has(value)
25
- ? value
26
- : DEFAULT_RUNNING_UPDATE_MODE;
27
- }
28
-
29
- function readRunningUpdateMode(profileHome) {
30
- try {
31
- const filePath = preferencePath(profileHome);
32
- const stat = fs.lstatSync(filePath);
33
- if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 16 * 1024) {
34
- return DEFAULT_RUNNING_UPDATE_MODE;
35
- }
36
- const record = JSON.parse(fs.readFileSync(filePath, 'utf8'));
37
- return normalizeRunningUpdateMode(record?.mode);
38
- } catch {
39
- return DEFAULT_RUNNING_UPDATE_MODE;
40
- }
41
- }
42
-
43
- function writeRunningUpdateMode(profileHome, mode) {
44
- if (!VALID_MODES.has(mode)) throw new Error('RUNNING_UPDATE_MODE_INVALID');
45
- const home = path.resolve(profileHome);
46
- const filePath = preferencePath(home);
47
- const temporaryPath = path.join(home, `.${RUNNING_UPDATE_PREFERENCE_FILE}.${process.pid}.${randomUUID()}.tmp`);
48
- ensurePrivateDirectory(home);
49
- try {
50
- writePrivateFile(temporaryPath, `${JSON.stringify({ mode })}\n`);
51
- fs.renameSync(temporaryPath, filePath);
52
- securePrivateFile(filePath);
53
- } catch (error) {
54
- fs.rmSync(temporaryPath, { force: true });
55
- throw error;
56
- }
57
- return mode;
58
- }
59
-
60
- module.exports = {
61
- DEFAULT_RUNNING_UPDATE_MODE,
62
- RUNNING_UPDATE_MODES,
63
- RUNNING_UPDATE_PREFERENCE_FILE,
64
- normalizeRunningUpdateMode,
65
- preferencePath,
66
- readRunningUpdateMode,
67
- writeRunningUpdateMode,
68
- };
@@ -1,588 +0,0 @@
1
- 'use strict';
2
-
3
- const { createHash, randomUUID } = require('node:crypto');
4
- const fs = require('node:fs');
5
- const https = require('node:https');
6
- const path = require('node:path');
7
- const { spawn } = require('node:child_process');
8
-
9
- const { compareSemver, requestTrustedJson } = require('./update-notice');
10
- const { tryAcquireUpdateLease } = require('./update-lease');
11
- const { ensurePrivateDirectory, securePrivateFile, writePrivateFile } = require('./private-paths');
12
-
13
- const PACKAGE_NAME = 'blun-king-cli';
14
- const MANIFEST_URL = 'https://chat.blun.ai/blun-code-version.json';
15
- const REGISTRY_URL = 'https://registry.npmjs.org/blun-king-cli';
16
- const ACTIVE_RUNTIME_FILE = 'active-runtime.json';
17
- const PENDING_RUNTIME_FILE = 'pending-runtime.json';
18
- const RUNNING_UPDATE_HANDOFF_EXIT_CODE = 76;
19
- const RUNNING_UPDATE_HANDOFF_ACK_MESSAGE = 'blun-running-update-handoff-ack';
20
- const RUNNING_UPDATE_PREPARED_MESSAGE = 'blun-running-update-prepared';
21
- const RUNNING_UPDATE_HANDOFF_MESSAGE = 'blun-running-update-handoff';
22
- const RUNNING_UPDATE_MODE_MESSAGE = 'blun-running-update-mode';
23
- const RUNTIME_READY_MESSAGE = 'blun-runtime-session-ready';
24
- const RUNTIME_EXIT_INTENT_MESSAGE = 'blun-runtime-exit-intent';
25
- const MAX_TARBALL_BYTES = 128 * 1024 * 1024;
26
- const AUTO_UPDATE_SETTLE_MS = 0;
27
- const DEFAULT_RETAINED_RELEASE_VERSIONS = 3;
28
-
29
- function ownData(value, key) {
30
- if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;
31
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
32
- return descriptor && Object.hasOwn(descriptor, 'value') ? descriptor.value : undefined;
33
- }
34
-
35
- function validIntegrity(value) {
36
- if (typeof value !== 'string') return false;
37
- const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/u.exec(value);
38
- if (!match) return false;
39
- try {
40
- return Buffer.from(match[1], 'base64').length === 64;
41
- } catch {
42
- return false;
43
- }
44
- }
45
-
46
- function selectRunningUpdateTarget(currentVersion, manifest, options = {}) {
47
- const name = ownData(manifest, 'name');
48
- const version = ownData(manifest, 'latest');
49
- const integrity = ownData(manifest, 'integrity');
50
- const releasedAt = ownData(manifest, 'releasedAt');
51
- const releasedAtMs = Date.parse(releasedAt);
52
- const now = options.now || Date.now;
53
- if (name !== PACKAGE_NAME
54
- || typeof version !== 'string'
55
- || compareSemver(currentVersion, version) !== -1
56
- || !validIntegrity(integrity)
57
- || typeof releasedAt !== 'string'
58
- || !Number.isFinite(releasedAtMs)
59
- || now() - releasedAtMs < AUTO_UPDATE_SETTLE_MS) {
60
- return null;
61
- }
62
- return Object.freeze({ version, integrity });
63
- }
64
-
65
- function releaseVersionEntries(sharedHome) {
66
- const root = releasesRoot(sharedHome);
67
- let entries;
68
- try {
69
- entries = fs.readdirSync(root, { withFileTypes: true });
70
- } catch {
71
- return [];
72
- }
73
- const releases = [];
74
- for (const entry of entries) {
75
- if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
76
- const version = entry.name;
77
- try {
78
- if (compareSemver('0.0.0', version) !== -1) continue;
79
- } catch {
80
- continue;
81
- }
82
- const versionRoot = path.join(root, version);
83
- const digestEntries = fs.readdirSync(versionRoot, { withFileTypes: true });
84
- const packageRoots = [];
85
- let valid = digestEntries.length > 0;
86
- for (const digestEntry of digestEntries) {
87
- if (!digestEntry.isDirectory() || digestEntry.isSymbolicLink()) {
88
- valid = false;
89
- break;
90
- }
91
- const packageRoot = path.join(versionRoot, digestEntry.name, 'node_modules', PACKAGE_NAME);
92
- if (!verifyRuntimePackage(packageRoot, version, { allowedRoot: versionRoot })) {
93
- valid = false;
94
- break;
95
- }
96
- packageRoots.push(path.resolve(packageRoot));
97
- }
98
- if (valid && packageRoots.length > 0) releases.push({ packageRoots, version, versionRoot });
99
- }
100
- releases.sort((left, right) => compareSemver(right.version, left.version));
101
- return releases;
102
- }
103
-
104
- async function pruneRunningUpdateReleases(sharedHome, options = {}) {
105
- const keepVersions = Number.isInteger(options.keepVersions) && options.keepVersions >= 1
106
- ? options.keepVersions
107
- : DEFAULT_RETAINED_RELEASE_VERSIONS;
108
- const activePackageRoot = typeof options.activePackageRoot === 'string'
109
- ? path.resolve(options.activePackageRoot)
110
- : readActiveRuntime(sharedHome)?.packageRoot;
111
- const acquire = options.tryAcquireLease
112
- || (({ packageRoot }) => tryAcquireUpdateLease({ packageRoot, role: 'install' }));
113
- const releases = releaseVersionEntries(sharedHome);
114
- const kept = new Set(releases.slice(0, keepVersions).map((release) => release.version));
115
- for (const release of releases) {
116
- if (release.packageRoots.includes(activePackageRoot)) kept.add(release.version);
117
- }
118
- const removedVersions = [];
119
- const busyVersions = [];
120
- for (const release of releases) {
121
- if (kept.has(release.version)) continue;
122
- const leases = [];
123
- let blocked = false;
124
- try {
125
- for (const packageRoot of release.packageRoots) {
126
- const result = await acquire({ packageRoot });
127
- if (!result?.acquired || !result.lease) {
128
- blocked = true;
129
- break;
130
- }
131
- leases.push(result.lease);
132
- }
133
- if (blocked) {
134
- busyVersions.push(release.version);
135
- continue;
136
- }
137
- for (const lease of leases) lease.assertOwned();
138
- fs.rmSync(release.versionRoot, { recursive: true, force: true });
139
- removedVersions.push(release.version);
140
- } finally {
141
- await Promise.allSettled(leases.map((lease) => lease.release()));
142
- }
143
- }
144
- return Object.freeze({
145
- busyVersions: Object.freeze(busyVersions),
146
- removedVersions: Object.freeze(removedVersions),
147
- });
148
- }
149
-
150
- function isSafeRuntimeBoundary(state) {
151
- return state?.isShuttingDown === false
152
- && state.streamingPhase === 'idle'
153
- && state.isCompacting === false
154
- && (state.queuedMessages === 0 || state.queuedMessagesDurable === true)
155
- && state.activeToolCalls === 0
156
- && state.shellCommands === 0
157
- && state.queueCommandRunning === false;
158
- }
159
-
160
- function resumeArgsForHandoff(args, sessionId) {
161
- const filtered = [];
162
- for (let index = 0; index < args.length; index += 1) {
163
- const arg = args[index];
164
- if (arg === '-c' || arg === '--continue') continue;
165
- if (arg === '-r' || arg === '--resume' || arg === '--session') {
166
- index += 1;
167
- continue;
168
- }
169
- if (/^(?:--resume|--session)=/u.test(arg)) continue;
170
- filtered.push(arg);
171
- }
172
- return [...filtered, '--resume', sessionId];
173
- }
174
-
175
- function handoffArgsForMode(args, mode, sessionId) {
176
- const withoutSession = resumeArgsForHandoff(args, sessionId).slice(0, -2);
177
- return mode === 'new' ? withoutSession : [...withoutSession, '--resume', sessionId];
178
- }
179
-
180
- async function handoffRuntime(options) {
181
- const handoffArgs = handoffArgsForMode(options.args, options.mode || 'resume', options.sessionId);
182
- if (!await options.probeTarget(options.target)) {
183
- return { kind: 'probe-failed', runtime: options.previous };
184
- }
185
- await options.stopOld();
186
- const targetCore = await options.startCore({
187
- packageRoot: options.target.packageRoot,
188
- args: handoffArgs,
189
- });
190
- if (targetCore.ready === true) {
191
- await options.activateTarget(options.target);
192
- return { kind: 'activated', runtime: options.target, core: targetCore };
193
- }
194
- const fallbackCore = await options.startCore({
195
- packageRoot: options.previous.packageRoot,
196
- args: handoffArgs,
197
- });
198
- return { kind: 'rolled-back', runtime: options.previous, core: fallbackCore };
199
- }
200
-
201
- function releasesRoot(sharedHome) {
202
- return path.join(path.resolve(sharedHome), 'updates', 'releases');
203
- }
204
-
205
- function activeRuntimePath(sharedHome) {
206
- return path.join(path.resolve(sharedHome), 'updates', ACTIVE_RUNTIME_FILE);
207
- }
208
-
209
- function pendingRuntimePath(sharedHome) {
210
- return path.join(path.resolve(sharedHome), 'updates', PENDING_RUNTIME_FILE);
211
- }
212
-
213
- function pathWithin(root, candidate) {
214
- const relative = path.relative(root, candidate);
215
- return relative === '' || (relative !== '..'
216
- && !relative.startsWith(`..${path.sep}`)
217
- && !path.isAbsolute(relative));
218
- }
219
-
220
- function verifyRuntimePackage(packageRoot, version, options = {}) {
221
- try {
222
- const root = path.resolve(packageRoot);
223
- const allowedRoot = path.resolve(options.allowedRoot || releasesRoot(options.sharedHome));
224
- if (!pathWithin(allowedRoot, root)) return false;
225
- const manifestPath = path.join(root, 'package.json');
226
- const bundlePath = path.join(root, 'blun.mjs');
227
- const bootstrapPath = path.join(root, 'bin', 'core-bootstrap.js');
228
- for (const filePath of [manifestPath, bundlePath, bootstrapPath]) {
229
- const stat = fs.lstatSync(filePath);
230
- if (!stat.isFile() || stat.isSymbolicLink()) return false;
231
- }
232
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
233
- return ownData(manifest, 'name') === PACKAGE_NAME && ownData(manifest, 'version') === version;
234
- } catch {
235
- return false;
236
- }
237
- }
238
-
239
- function readRuntimeRecord(sharedHome, filePath) {
240
- try {
241
- const stat = fs.lstatSync(filePath);
242
- if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 16 * 1024) return null;
243
- const record = JSON.parse(fs.readFileSync(filePath, 'utf8'));
244
- const version = ownData(record, 'version');
245
- const packageRoot = ownData(record, 'packageRoot');
246
- const integrity = ownData(record, 'integrity');
247
- if (typeof version !== 'string'
248
- || typeof packageRoot !== 'string'
249
- || !path.isAbsolute(packageRoot)
250
- || !validIntegrity(integrity)
251
- || !verifyRuntimePackage(packageRoot, version, { sharedHome })) {
252
- return null;
253
- }
254
- const mode = ownData(record, 'mode');
255
- const sessionId = ownData(record, 'sessionId');
256
- const cwd = ownData(record, 'cwd');
257
- return Object.freeze({
258
- version,
259
- packageRoot: path.resolve(packageRoot),
260
- integrity,
261
- ...(mode === 'resume' || mode === 'new' ? { mode } : {}),
262
- ...(typeof sessionId === 'string' && sessionId.length > 0 ? { sessionId } : {}),
263
- ...(typeof cwd === 'string' && path.isAbsolute(cwd) ? { cwd: path.resolve(cwd) } : {}),
264
- });
265
- } catch {
266
- return null;
267
- }
268
- }
269
-
270
- function readActiveRuntime(sharedHome) {
271
- return readRuntimeRecord(sharedHome, activeRuntimePath(sharedHome));
272
- }
273
-
274
- function readPendingRuntime(sharedHome) {
275
- return readRuntimeRecord(sharedHome, pendingRuntimePath(sharedHome));
276
- }
277
-
278
- function writeRuntimeRecord(sharedHome, fileName, target, extra = {}) {
279
- if (!verifyRuntimePackage(target.packageRoot, target.version, { sharedHome })
280
- || !validIntegrity(target.integrity)) {
281
- throw new Error('RUNNING_UPDATE_TARGET_INVALID');
282
- }
283
- const updateRoot = path.join(path.resolve(sharedHome), 'updates');
284
- ensurePrivateDirectory(path.resolve(sharedHome));
285
- ensurePrivateDirectory(updateRoot);
286
- const filePath = path.join(updateRoot, fileName);
287
- const temporaryPath = path.join(updateRoot, `.${fileName}.${process.pid}.${randomUUID()}.tmp`);
288
- try {
289
- writePrivateFile(temporaryPath, `${JSON.stringify({
290
- version: target.version,
291
- packageRoot: path.resolve(target.packageRoot),
292
- integrity: target.integrity,
293
- ...extra,
294
- })}\n`);
295
- fs.renameSync(temporaryPath, filePath);
296
- securePrivateFile(filePath);
297
- } catch (error) {
298
- fs.rmSync(temporaryPath, { force: true });
299
- throw error;
300
- }
301
- }
302
-
303
- function activateRuntime(sharedHome, target) {
304
- writeRuntimeRecord(sharedHome, ACTIVE_RUNTIME_FILE, target, {
305
- activatedAt: new Date().toISOString(),
306
- });
307
- }
308
-
309
- function stageRuntime(sharedHome, target, options = {}) {
310
- const mode = options.mode === 'resume' || options.mode === 'new' ? options.mode : undefined;
311
- const sessionId = typeof options.sessionId === 'string' && options.sessionId.length > 0
312
- ? options.sessionId
313
- : undefined;
314
- const cwd = typeof options.cwd === 'string' && path.isAbsolute(options.cwd)
315
- ? path.resolve(options.cwd)
316
- : undefined;
317
- writeRuntimeRecord(sharedHome, PENDING_RUNTIME_FILE, target, {
318
- preparedAt: new Date((options.now || Date.now)()).toISOString(),
319
- ...(mode === undefined ? {} : { mode }),
320
- ...(sessionId === undefined ? {} : { sessionId }),
321
- ...(cwd === undefined ? {} : { cwd }),
322
- });
323
- }
324
-
325
- function clearRuntimeRecord(sharedHome, expected, readRecord, filePath) {
326
- const runtime = readRecord(sharedHome);
327
- if (runtime === null) return false;
328
- if (expected !== undefined
329
- && (runtime.version !== expected.version
330
- || runtime.packageRoot !== path.resolve(expected.packageRoot))) {
331
- return false;
332
- }
333
- fs.rmSync(filePath(sharedHome), { force: true });
334
- return true;
335
- }
336
-
337
- function clearActiveRuntime(sharedHome, expected) {
338
- return clearRuntimeRecord(sharedHome, expected, readActiveRuntime, activeRuntimePath);
339
- }
340
-
341
- function clearPendingRuntime(sharedHome, expected) {
342
- return clearRuntimeRecord(sharedHome, expected, readPendingRuntime, pendingRuntimePath);
343
- }
344
-
345
- function restoreActiveRuntime(sharedHome, runtime) {
346
- if (runtime === null || runtime === undefined) {
347
- fs.rmSync(activeRuntimePath(sharedHome), { force: true });
348
- return;
349
- }
350
- activateRuntime(sharedHome, runtime);
351
- }
352
-
353
- function recordRunningUpdateEvent(sharedHome, event, options = {}) {
354
- if (typeof sharedHome !== 'string' || !path.isAbsolute(sharedHome)) return false;
355
- const updateRoot = path.join(path.resolve(sharedHome), 'updates');
356
- ensurePrivateDirectory(path.resolve(sharedHome));
357
- ensurePrivateDirectory(updateRoot);
358
- const filePath = path.join(updateRoot, 'handoff.jsonl');
359
- fs.appendFileSync(filePath, `${JSON.stringify({
360
- at: new Date((options.now || Date.now)()).toISOString(),
361
- ...event,
362
- })}\n`, { encoding: 'utf8', mode: 0o600 });
363
- securePrivateFile(filePath);
364
- return true;
365
- }
366
-
367
- function registryRelease(registry, version) {
368
- const versions = ownData(registry, 'versions');
369
- const entry = ownData(versions, version);
370
- const dist = ownData(entry, 'dist');
371
- const integrity = ownData(dist, 'integrity');
372
- const tarball = ownData(dist, 'tarball');
373
- if (!validIntegrity(integrity) || typeof tarball !== 'string') return null;
374
- let url;
375
- try {
376
- url = new URL(tarball);
377
- } catch {
378
- return null;
379
- }
380
- if (url.protocol !== 'https:'
381
- || url.hostname !== 'registry.npmjs.org'
382
- || url.username !== ''
383
- || url.password !== '') {
384
- return null;
385
- }
386
- return { integrity, tarball: url.href };
387
- }
388
-
389
- function downloadTarball(url, destination, options = {}) {
390
- const httpsImpl = options.httpsImpl || https;
391
- return new Promise((resolve, reject) => {
392
- const request = httpsImpl.get(url, {
393
- headers: { 'user-agent': 'blun-king-running-update' },
394
- timeout: options.timeoutMs || 30_000,
395
- }, (response) => {
396
- if (response.statusCode !== 200) {
397
- response.resume();
398
- reject(new Error(`RUNNING_UPDATE_DOWNLOAD_${String(response.statusCode)}`));
399
- return;
400
- }
401
- let bytes = 0;
402
- const handle = fs.openSync(destination, 'wx', 0o600);
403
- let settled = false;
404
- const fail = (error) => {
405
- if (settled) return;
406
- settled = true;
407
- try { fs.closeSync(handle); } catch {}
408
- fs.rmSync(destination, { force: true });
409
- reject(error);
410
- };
411
- response.on('data', (chunk) => {
412
- bytes += chunk.length;
413
- if (bytes > MAX_TARBALL_BYTES) {
414
- response.destroy(new Error('RUNNING_UPDATE_TARBALL_TOO_LARGE'));
415
- return;
416
- }
417
- try {
418
- fs.writeSync(handle, chunk);
419
- } catch (error) {
420
- response.destroy(error);
421
- }
422
- });
423
- response.once('error', fail);
424
- response.once('end', () => {
425
- if (settled) return;
426
- settled = true;
427
- fs.fsyncSync(handle);
428
- fs.closeSync(handle);
429
- resolve();
430
- });
431
- });
432
- request.once('timeout', () => request.destroy(new Error('RUNNING_UPDATE_DOWNLOAD_TIMEOUT')));
433
- request.once('error', reject);
434
- });
435
- }
436
-
437
- function resolveNpmCliPath(execPath = process.execPath) {
438
- const executableDirectory = path.dirname(path.resolve(execPath));
439
- const candidates = [
440
- path.join(executableDirectory, 'node_modules', 'npm', 'bin', 'npm-cli.js'),
441
- path.resolve(executableDirectory, '..', 'lib', 'node_modules', 'npm', 'bin', 'npm-cli.js'),
442
- path.resolve(executableDirectory, '..', 'node_modules', 'npm', 'bin', 'npm-cli.js'),
443
- path.resolve(__dirname, '..', '..', 'npm', 'bin', 'npm-cli.js'),
444
- ];
445
- for (const candidate of candidates) {
446
- try {
447
- const resolved = fs.realpathSync(candidate);
448
- if (fs.statSync(resolved).isFile() && path.basename(resolved) === 'npm-cli.js') return resolved;
449
- } catch {}
450
- }
451
- throw new Error('TRUSTED_NPM_CLI_NOT_FOUND');
452
- }
453
-
454
- function runProcess(command, args, options = {}) {
455
- return new Promise((resolve, reject) => {
456
- const child = (options.spawnImpl || spawn)(command, args, {
457
- cwd: options.cwd,
458
- env: options.env,
459
- stdio: options.stdio || 'ignore',
460
- windowsHide: true,
461
- });
462
- child.once('error', reject);
463
- child.once('exit', (code, signal) => {
464
- if (code === 0) resolve();
465
- else reject(new Error(`RUNNING_UPDATE_PROCESS_FAILED:${code ?? signal ?? 'unknown'}`));
466
- });
467
- });
468
- }
469
-
470
- async function defaultInstallTarball(tarballPath, installPrefix, options = {}) {
471
- const npmCliPath = resolveNpmCliPath(options.execPath);
472
- const env = {
473
- ...process.env,
474
- TEMP: installPrefix,
475
- TMP: installPrefix,
476
- TMPDIR: installPrefix,
477
- npm_config_registry: 'https://registry.npmjs.org/',
478
- npm_config_strict_ssl: 'true',
479
- };
480
- await runProcess(options.execPath || process.execPath, [
481
- npmCliPath,
482
- 'install',
483
- `--prefix=${installPrefix}`,
484
- '--ignore-scripts=false',
485
- '--package-lock=false',
486
- '--audit=false',
487
- '--fund=false',
488
- tarballPath,
489
- ], { cwd: installPrefix, env, spawnImpl: options.spawnImpl });
490
- }
491
-
492
- async function defaultProbeRuntime(packageRoot, options = {}) {
493
- try {
494
- await runProcess(options.execPath || process.execPath, ['--check', path.join(packageRoot, 'blun.mjs')], options);
495
- await runProcess(options.execPath || process.execPath, ['--check', path.join(packageRoot, 'bin', 'core-bootstrap.js')], options);
496
- return true;
497
- } catch {
498
- return false;
499
- }
500
- }
501
-
502
- async function prepareRunningUpdate(options) {
503
- if (String(options.env?.BLUN_NO_AUTO_UPDATE || '') === '1') return null;
504
- const manifest = await (options.loadManifest
505
- ? options.loadManifest()
506
- : requestTrustedJson(MANIFEST_URL));
507
- const target = selectRunningUpdateTarget(options.currentVersion, manifest, { now: options.now });
508
- if (target === null) return null;
509
- const registry = await (options.loadRegistry
510
- ? options.loadRegistry()
511
- : requestTrustedJson(REGISTRY_URL));
512
- const release = registryRelease(registry, target.version);
513
- if (release === null || release.integrity !== target.integrity) {
514
- throw new Error('RUNNING_UPDATE_INTEGRITY_SOURCE_MISMATCH');
515
- }
516
- const digest = createHash('sha256').update(target.integrity).digest('hex').slice(0, 16);
517
- const releaseDirectory = path.join(releasesRoot(options.sharedHome), target.version, digest);
518
- const packageRoot = path.join(releaseDirectory, 'node_modules', PACKAGE_NAME);
519
- if (verifyRuntimePackage(packageRoot, target.version, { sharedHome: options.sharedHome })) {
520
- return Object.freeze({ ...target, packageRoot });
521
- }
522
-
523
- const updateRoot = path.join(path.resolve(options.sharedHome), 'updates');
524
- const stagingRoot = path.join(updateRoot, 'staging');
525
- ensurePrivateDirectory(path.resolve(options.sharedHome));
526
- ensurePrivateDirectory(updateRoot);
527
- ensurePrivateDirectory(stagingRoot);
528
- const temporaryRoot = fs.mkdtempSync(path.join(stagingRoot, 'runtime-'));
529
- ensurePrivateDirectory(temporaryRoot);
530
- const tarballPath = path.join(temporaryRoot, `${PACKAGE_NAME}.tgz`);
531
- const installPrefix = path.join(temporaryRoot, 'install');
532
- ensurePrivateDirectory(installPrefix);
533
- try {
534
- await (options.downloadTarball || downloadTarball)(release.tarball, tarballPath);
535
- const actualIntegrity = `sha512-${createHash('sha512').update(fs.readFileSync(tarballPath)).digest('base64')}`;
536
- if (actualIntegrity !== target.integrity) throw new Error('RUNNING_UPDATE_TARBALL_INTEGRITY_FAILED');
537
- await (options.installTarball || defaultInstallTarball)(tarballPath, installPrefix, options);
538
- const installedRoot = path.join(installPrefix, 'node_modules', PACKAGE_NAME);
539
- if (!verifyRuntimePackage(installedRoot, target.version, { allowedRoot: installPrefix })) {
540
- throw new Error('RUNNING_UPDATE_PACKAGE_INVALID');
541
- }
542
- if (!await (options.probeRuntime || defaultProbeRuntime)(installedRoot, options)) {
543
- throw new Error('RUNNING_UPDATE_PROBE_FAILED');
544
- }
545
- ensurePrivateDirectory(releasesRoot(options.sharedHome));
546
- ensurePrivateDirectory(path.dirname(releaseDirectory));
547
- if (fs.existsSync(releaseDirectory)) fs.rmSync(releaseDirectory, { recursive: true, force: true });
548
- fs.renameSync(installPrefix, releaseDirectory);
549
- if (!verifyRuntimePackage(packageRoot, target.version, { sharedHome: options.sharedHome })) {
550
- throw new Error('RUNNING_UPDATE_ACTIVATION_PACKAGE_INVALID');
551
- }
552
- return Object.freeze({ ...target, packageRoot });
553
- } finally {
554
- fs.rmSync(temporaryRoot, { recursive: true, force: true });
555
- }
556
- }
557
-
558
- module.exports = {
559
- ACTIVE_RUNTIME_FILE,
560
- PENDING_RUNTIME_FILE,
561
- AUTO_UPDATE_SETTLE_MS,
562
- RUNNING_UPDATE_HANDOFF_EXIT_CODE,
563
- RUNNING_UPDATE_HANDOFF_ACK_MESSAGE,
564
- RUNNING_UPDATE_HANDOFF_MESSAGE,
565
- RUNNING_UPDATE_MODE_MESSAGE,
566
- RUNNING_UPDATE_PREPARED_MESSAGE,
567
- RUNTIME_EXIT_INTENT_MESSAGE,
568
- RUNTIME_READY_MESSAGE,
569
- activateRuntime,
570
- activeRuntimePath,
571
- clearActiveRuntime,
572
- clearPendingRuntime,
573
- defaultProbeRuntime,
574
- handoffArgsForMode,
575
- handoffRuntime,
576
- isSafeRuntimeBoundary,
577
- prepareRunningUpdate,
578
- pruneRunningUpdateReleases,
579
- recordRunningUpdateEvent,
580
- readActiveRuntime,
581
- readPendingRuntime,
582
- restoreActiveRuntime,
583
- resumeArgsForHandoff,
584
- selectRunningUpdateTarget,
585
- stageRuntime,
586
- pendingRuntimePath,
587
- verifyRuntimePackage,
588
- };