yaver-cli 1.99.459 → 1.99.460
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 +1 -1
- package/src/agent-runtime.js +75 -1
- package/src/postinstall.js +2 -37
package/package.json
CHANGED
package/src/agent-runtime.js
CHANGED
|
@@ -32,7 +32,17 @@ const EXECUTABLE_MAGICS = [
|
|
|
32
32
|
Buffer.from([0x4d, 0x5a]), // PE (Windows) — MZ
|
|
33
33
|
];
|
|
34
34
|
|
|
35
|
-
async function ensureAgentBinary({
|
|
35
|
+
async function ensureAgentBinary({
|
|
36
|
+
quiet = false,
|
|
37
|
+
resolveBinary = resolveAgentBinary,
|
|
38
|
+
refreshLink = refreshCurrentSymlink,
|
|
39
|
+
} = {}) {
|
|
40
|
+
const binaryPath = await resolveBinary({ quiet });
|
|
41
|
+
refreshLink(binaryPath, { quiet });
|
|
42
|
+
return binaryPath;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function resolveAgentBinary({ quiet = false } = {}) {
|
|
36
46
|
// Try cache first — when the user already has any version of yaver
|
|
37
47
|
// installed locally, we shouldn't fail just because GitHub's API
|
|
38
48
|
// rate-limited the unauthenticated /releases call. Falling back to
|
|
@@ -96,6 +106,69 @@ async function ensureAgentBinary({ quiet = false } = {}) {
|
|
|
96
106
|
}
|
|
97
107
|
}
|
|
98
108
|
|
|
109
|
+
// Keep the supervisor's stable path aligned with the binary the wrapper has
|
|
110
|
+
// actually selected. Postinstall also does this, but agent releases can be
|
|
111
|
+
// discovered and downloaded lazily on any later CLI invocation. Before this
|
|
112
|
+
// guard, that path ran the new binary for the command while launchd/systemd
|
|
113
|
+
// continued restarting `current`, which could still name an older version
|
|
114
|
+
// (observed 2026-09-08: wrapper 1.99.459, live launchd agent 1.99.452).
|
|
115
|
+
//
|
|
116
|
+
// Use an atomic rename so a concurrent supervisor restart never observes a
|
|
117
|
+
// missing `current` entry. The function is deliberately best-effort: resolving
|
|
118
|
+
// a usable agent must not fail because a local symlink cannot be repaired.
|
|
119
|
+
function refreshCurrentSymlink(binaryPath, {
|
|
120
|
+
quiet = false,
|
|
121
|
+
platform = process.platform,
|
|
122
|
+
cacheRoot = CACHE_ROOT,
|
|
123
|
+
fsImpl = fs,
|
|
124
|
+
} = {}) {
|
|
125
|
+
let temporaryLink = '';
|
|
126
|
+
try {
|
|
127
|
+
if (platform === 'win32' || !binaryPath) return 'skipped';
|
|
128
|
+
|
|
129
|
+
const resolvedCacheRoot = path.resolve(cacheRoot);
|
|
130
|
+
const versionDir = path.resolve(path.dirname(path.dirname(binaryPath)));
|
|
131
|
+
const relativeVersionDir = path.relative(resolvedCacheRoot, versionDir);
|
|
132
|
+
if (
|
|
133
|
+
!relativeVersionDir ||
|
|
134
|
+
relativeVersionDir.startsWith(`..${path.sep}`) ||
|
|
135
|
+
path.isAbsolute(relativeVersionDir) ||
|
|
136
|
+
!semver.valid(path.basename(versionDir)) ||
|
|
137
|
+
!fsImpl.existsSync(versionDir)
|
|
138
|
+
) {
|
|
139
|
+
return 'skipped';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const currentLink = path.join(resolvedCacheRoot, 'current');
|
|
143
|
+
try {
|
|
144
|
+
const existingTarget = fsImpl.readlinkSync(currentLink);
|
|
145
|
+
const existingResolved = path.resolve(path.dirname(currentLink), existingTarget);
|
|
146
|
+
if (existingResolved === versionDir) return 'already-current';
|
|
147
|
+
} catch (_) {}
|
|
148
|
+
|
|
149
|
+
temporaryLink = path.join(
|
|
150
|
+
resolvedCacheRoot,
|
|
151
|
+
`.current-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
152
|
+
);
|
|
153
|
+
fsImpl.symlinkSync(versionDir, temporaryLink);
|
|
154
|
+
fsImpl.renameSync(temporaryLink, currentLink);
|
|
155
|
+
temporaryLink = '';
|
|
156
|
+
if (!quiet) {
|
|
157
|
+
console.error(`[yaver] Repointed ~/.yaver/bin/current -> ${path.basename(versionDir)}`);
|
|
158
|
+
}
|
|
159
|
+
return 'repointed';
|
|
160
|
+
} catch (err) {
|
|
161
|
+
if (!quiet) {
|
|
162
|
+
console.error(`[yaver] Could not refresh the current agent link: ${err.message}`);
|
|
163
|
+
}
|
|
164
|
+
return 'failed';
|
|
165
|
+
} finally {
|
|
166
|
+
if (temporaryLink) {
|
|
167
|
+
try { fsImpl.unlinkSync(temporaryLink); } catch (_) {}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
99
172
|
/** A cached agent binary is only usable if it actually RUNS.
|
|
100
173
|
*
|
|
101
174
|
* `fs.existsSync` answers "is there a file here", which is a proxy for the
|
|
@@ -730,6 +803,7 @@ async function extractTarball(archivePath, destDir) {
|
|
|
730
803
|
module.exports = {
|
|
731
804
|
ensureAgentBinary,
|
|
732
805
|
ensureValidMacSignature,
|
|
806
|
+
refreshCurrentSymlink,
|
|
733
807
|
resolveAgentInfo,
|
|
734
808
|
runAgentCommand,
|
|
735
809
|
};
|
package/src/postinstall.js
CHANGED
|
@@ -400,39 +400,6 @@ function ensureZshenvRescue() {
|
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
-
// Update ~/.yaver/bin/current → versioned dir for the binary just
|
|
404
|
-
// installed. Without this, the symlink can lag behind npm — every time
|
|
405
|
-
// I bumped CLI on the user's Mac mini, `~/.yaver/bin/current` kept
|
|
406
|
-
// pointing at an older version, so `~/.yaver/bin/current/<arch>/yaver`
|
|
407
|
-
// silently ran the wrong binary even though `npm install -g yaver-cli@
|
|
408
|
-
// <new>` had succeeded. The auto-update watchdogs (heartbeat_watcher
|
|
409
|
-
// systemctl/launchctl kickstart paths) trust `current` too — so a
|
|
410
|
-
// stale symlink keeps the old binary running across a "restart"
|
|
411
|
-
// without anyone noticing. This belongs in postinstall, runs every
|
|
412
|
-
// global install, never throws.
|
|
413
|
-
function refreshCurrentSymlink(binaryPath) {
|
|
414
|
-
try {
|
|
415
|
-
if (process.platform === "win32") return;
|
|
416
|
-
if (!binaryPath) return;
|
|
417
|
-
// binaryPath is like ~/.yaver/bin/<version>/<cacheKey>/yaver — walk
|
|
418
|
-
// up two levels to the version dir, point `current` at it.
|
|
419
|
-
const versionDir = path.dirname(path.dirname(binaryPath));
|
|
420
|
-
if (!versionDir.startsWith(path.join(os.homedir(), ".yaver", "bin") + path.sep)) {
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
if (!fs.existsSync(versionDir)) return;
|
|
424
|
-
const symlink = path.join(os.homedir(), ".yaver", "bin", "current");
|
|
425
|
-
let already = "";
|
|
426
|
-
try { already = fs.readlinkSync(symlink); } catch (_) {}
|
|
427
|
-
if (already === versionDir) return;
|
|
428
|
-
try { fs.unlinkSync(symlink); } catch (_) {}
|
|
429
|
-
fs.symlinkSync(versionDir, symlink);
|
|
430
|
-
log(`Repointed ~/.yaver/bin/current → ${path.basename(versionDir)}`);
|
|
431
|
-
} catch (err) {
|
|
432
|
-
log(`Skipping current symlink refresh: ${err.message}`);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
403
|
// Restart whichever service supervisor the user has registered. Without
|
|
437
404
|
// this, a fresh `npm install -g yaver-cli@latest` updates the binary on
|
|
438
405
|
// disk but leaves the still-running agent process on the OLD binary
|
|
@@ -497,10 +464,8 @@ async function main() {
|
|
|
497
464
|
log(`Skipping agent prefetch: ${error.message}`);
|
|
498
465
|
return;
|
|
499
466
|
}
|
|
500
|
-
//
|
|
501
|
-
// service so
|
|
502
|
-
// takes effect without a manual `yaver restart`.
|
|
503
|
-
refreshCurrentSymlink(installedBinary);
|
|
467
|
+
// ensureAgentBinary has atomically reconciled the canonical `current`
|
|
468
|
+
// symlink. Bounce any registered service so the selected binary is live.
|
|
504
469
|
bounceRunningAgent();
|
|
505
470
|
|
|
506
471
|
if (isIotEdgeInstall()) {
|