@openagentsinc/pylon 0.1.13 → 0.1.15
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 +17 -9
- package/package.json +1 -1
- package/src/cli.js +14 -4
- package/src/index.js +279 -3
package/README.md
CHANGED
|
@@ -13,8 +13,9 @@ npx @openagentsinc/pylon
|
|
|
13
13
|
bunx @openagentsinc/pylon
|
|
14
14
|
npm install -g @openagentsinc/pylon && pylon
|
|
15
15
|
bun install -g @openagentsinc/pylon && pylon
|
|
16
|
-
npx @openagentsinc/pylon --version 0.1.
|
|
16
|
+
npx @openagentsinc/pylon --version 0.1.15
|
|
17
17
|
npx @openagentsinc/pylon --no-launch
|
|
18
|
+
npx @openagentsinc/pylon --no-updates
|
|
18
19
|
npx @openagentsinc/pylon --download-curated-cache --model gemma-4-e2b --run-diagnostics
|
|
19
20
|
npx @openagentsinc/pylon --verbose
|
|
20
21
|
```
|
|
@@ -25,6 +26,7 @@ The launcher:
|
|
|
25
26
|
`bun install -g` installs with the same `pylon` command
|
|
26
27
|
- checks GitHub for the latest tagged `pylon-v...` release on each default run,
|
|
27
28
|
or resolves a specific tagged `Pylon` version when `--version` is provided
|
|
29
|
+
- only installs releases initiated by `AtlantisPleb` in GitHub Releases
|
|
28
30
|
- resolves the correct `pylon-v<version>-<os>-<arch>.tar.gz` asset for the
|
|
29
31
|
current machine
|
|
30
32
|
- falls back to the exact tagged source checkout and builds `pylon` plus
|
|
@@ -32,8 +34,9 @@ The launcher:
|
|
|
32
34
|
- prompts before installing the Rust toolchain via `rustup` if a source build
|
|
33
35
|
is needed and `cargo` / `rustc` are missing
|
|
34
36
|
- emits best-effort anonymous installer telemetry to `openagents.com` so the
|
|
35
|
-
public stats page can show install starts, completions, source-build
|
|
36
|
-
Rust prompts, and smoke-test
|
|
37
|
+
public stats page can show install starts, completions, source-build
|
|
38
|
+
fallbacks, update checks, restart behavior, Rust prompts, and smoke-test
|
|
39
|
+
outcomes
|
|
37
40
|
- downloads the archive and published SHA-256 checksum
|
|
38
41
|
- verifies the checksum before extracting
|
|
39
42
|
- caches the unpacked binaries under `~/.openagents/pylon/bootstrap/`
|
|
@@ -57,12 +60,17 @@ The launcher:
|
|
|
57
60
|
- starts the installed `pylon-tui` by default after the smoke path; that TUI
|
|
58
61
|
starts and supervises the earning worker
|
|
59
62
|
unless `--no-launch` is set
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
- while the TUI is running on the default release track, checks GitHub Releases
|
|
64
|
+
every 30 seconds and restarts the TUI from a newer trusted cached release
|
|
65
|
+
without replacing the global npm/bun command
|
|
66
|
+
- use `--no-updates` to keep the current installed release running without
|
|
67
|
+
background GitHub release checks; `--version` remains a pinned release run
|
|
68
|
+
- for hosted homework/training work, use launcher `0.1.15` or newer so the
|
|
69
|
+
cached standalone binary auto-updates while the dashboard is open. The
|
|
70
|
+
`pylon-v0.1.15` standalone binary keeps the long hosted homework ID hashing
|
|
71
|
+
from `0.1.14` and also refuses to seal terminal training windows until the
|
|
72
|
+
worker contribution artifact bundle has uploaded and verified for validator
|
|
73
|
+
replay.
|
|
66
74
|
- does not try to install or register a local runtime automatically; the
|
|
67
75
|
bootstrap stays honest about the separate local Gemma runtime
|
|
68
76
|
prerequisite instead of mutating the host behind the user's back
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
bootstrapInstalledPylon,
|
|
8
8
|
ensureReleaseInstall,
|
|
9
9
|
launchInstalledPylon,
|
|
10
|
+
launchInstalledPylonWithUpdates,
|
|
10
11
|
resolveBootstrapOutcome,
|
|
11
12
|
resolvePlatformTarget,
|
|
12
13
|
renderBootstrapSummary,
|
|
@@ -85,9 +86,10 @@ Description:
|
|
|
85
86
|
and build it locally instead. Cache the binaries, run the first-run smoke
|
|
86
87
|
path, and then start the Pylon terminal UI by default. The terminal UI manages
|
|
87
88
|
the earning worker and keeps live status visible. The launcher checks GitHub
|
|
88
|
-
for newer tagged pylon-v... releases on
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
for newer tagged pylon-v... releases on each default run and every 30 seconds
|
|
90
|
+
while the dashboard is open. Only releases initiated by AtlantisPleb are
|
|
91
|
+
accepted. New standalone binaries are cached under the local bootstrap root;
|
|
92
|
+
the global npm or bun pylon command is not replaced.
|
|
91
93
|
|
|
92
94
|
Options:
|
|
93
95
|
--version <x.y.z> Resolve a specific Pylon release.
|
|
@@ -107,6 +109,8 @@ Options:
|
|
|
107
109
|
--skip-model-download Keep the curated GGUF cache skipped.
|
|
108
110
|
--skip-diagnostics Keep optional pylon gemma diagnose skipped.
|
|
109
111
|
--no-launch Do not start pylon-tui after bootstrap.
|
|
112
|
+
--no-updates Disable 30-second GitHub release polling
|
|
113
|
+
and dashboard restart while pylon runs.
|
|
110
114
|
--verbose Print extra network and recovery detail.
|
|
111
115
|
--debug-network Alias for --verbose.
|
|
112
116
|
--json Emit a machine-readable JSON summary.
|
|
@@ -132,6 +136,7 @@ export function parseArgs(argv) {
|
|
|
132
136
|
skipModelDownload: true,
|
|
133
137
|
skipDiagnostics: true,
|
|
134
138
|
noLaunch: false,
|
|
139
|
+
noUpdates: false,
|
|
135
140
|
verbose: false,
|
|
136
141
|
json: false,
|
|
137
142
|
help: false,
|
|
@@ -197,6 +202,9 @@ export function parseArgs(argv) {
|
|
|
197
202
|
case "--no-launch":
|
|
198
203
|
options.noLaunch = true;
|
|
199
204
|
break;
|
|
205
|
+
case "--no-updates":
|
|
206
|
+
options.noUpdates = true;
|
|
207
|
+
break;
|
|
200
208
|
case "--verbose":
|
|
201
209
|
case "--debug-network":
|
|
202
210
|
options.verbose = true;
|
|
@@ -232,7 +240,7 @@ export async function main(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
232
240
|
const {
|
|
233
241
|
ensureReleaseInstallImpl = ensureReleaseInstall,
|
|
234
242
|
bootstrapInstalledPylonImpl = bootstrapInstalledPylon,
|
|
235
|
-
launchInstalledPylonImpl =
|
|
243
|
+
launchInstalledPylonImpl = launchInstalledPylonWithUpdates,
|
|
236
244
|
createTelemetryClientImpl = createTelemetryClient,
|
|
237
245
|
} = dependencies;
|
|
238
246
|
const options = parseArgs(argv);
|
|
@@ -318,10 +326,12 @@ export async function main(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
318
326
|
...options,
|
|
319
327
|
...install,
|
|
320
328
|
version: install.version,
|
|
329
|
+
pinnedVersion: Boolean(options.version),
|
|
321
330
|
},
|
|
322
331
|
{
|
|
323
332
|
...dependencies,
|
|
324
333
|
onStatus: reporter?.status,
|
|
334
|
+
telemetryClient,
|
|
325
335
|
},
|
|
326
336
|
);
|
|
327
337
|
} else {
|
package/src/index.js
CHANGED
|
@@ -20,6 +20,8 @@ export const DEFAULT_MODEL_ID = "gemma-4-e4b";
|
|
|
20
20
|
export const DEFAULT_DIAGNOSTIC_REPEATS = 3;
|
|
21
21
|
export const DEFAULT_DIAGNOSTIC_MAX_OUTPUT_TOKENS = 96;
|
|
22
22
|
export const DEFAULT_FETCH_TIMEOUT_MS = 15_000;
|
|
23
|
+
export const DEFAULT_UPDATE_CHECK_INTERVAL_MS = 30_000;
|
|
24
|
+
export const DEFAULT_TRUSTED_RELEASE_AUTHOR = "AtlantisPleb";
|
|
23
25
|
const PYLON_RELEASE_TAG_PREFIX = "pylon-v";
|
|
24
26
|
const RELEASE_ASSET_INSTALL_METHOD = "release_asset";
|
|
25
27
|
const SOURCE_BUILD_INSTALL_METHOD = "source_build";
|
|
@@ -116,6 +118,36 @@ function comparePylonReleaseTags(leftTagName, rightTagName) {
|
|
|
116
118
|
return left.normalized.localeCompare(right.normalized);
|
|
117
119
|
}
|
|
118
120
|
|
|
121
|
+
function trustedReleaseAuthor(release, trustedAuthor = DEFAULT_TRUSTED_RELEASE_AUTHOR) {
|
|
122
|
+
if (!trustedAuthor) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return release?.author?.login === trustedAuthor;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function assertTrustedReleaseAuthor(
|
|
129
|
+
release,
|
|
130
|
+
trustedAuthor = DEFAULT_TRUSTED_RELEASE_AUTHOR,
|
|
131
|
+
) {
|
|
132
|
+
if (trustedReleaseAuthor(release, trustedAuthor)) {
|
|
133
|
+
return release;
|
|
134
|
+
}
|
|
135
|
+
const tagName = release?.tag_name ?? "unknown";
|
|
136
|
+
const author = release?.author?.login ?? "unknown";
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Refusing ${tagName}: GitHub release author ${author} is not trusted. Expected ${trustedAuthor}.`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function isNewerPylonVersion(candidateVersion, currentVersion) {
|
|
143
|
+
return (
|
|
144
|
+
comparePylonReleaseTags(
|
|
145
|
+
`${PYLON_RELEASE_TAG_PREFIX}${normalizeVersion(candidateVersion)}`,
|
|
146
|
+
`${PYLON_RELEASE_TAG_PREFIX}${normalizeVersion(currentVersion)}`,
|
|
147
|
+
) > 0
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
119
151
|
function createBootstrapError(message, context = {}) {
|
|
120
152
|
const error = new Error(message);
|
|
121
153
|
Object.assign(error, context);
|
|
@@ -619,14 +651,19 @@ export function selectLatestPylonRelease(releases, target = null) {
|
|
|
619
651
|
}
|
|
620
652
|
|
|
621
653
|
const candidates = releases
|
|
622
|
-
.filter(
|
|
654
|
+
.filter(
|
|
655
|
+
(candidate) =>
|
|
656
|
+
!candidate?.draft &&
|
|
657
|
+
isPylonReleaseTag(candidate?.tag_name) &&
|
|
658
|
+
trustedReleaseAuthor(candidate),
|
|
659
|
+
)
|
|
623
660
|
.sort((left, right) => comparePylonReleaseTags(right.tag_name, left.tag_name));
|
|
624
661
|
const release =
|
|
625
662
|
candidates.find((candidate) => releaseHasTargetAssets(candidate, target)) ??
|
|
626
663
|
candidates[0];
|
|
627
664
|
if (!release) {
|
|
628
665
|
throw new Error(
|
|
629
|
-
`GitHub release lookup did not find any published ${PYLON_RELEASE_TAG_PREFIX} releases.`,
|
|
666
|
+
`GitHub release lookup did not find any published ${PYLON_RELEASE_TAG_PREFIX} releases initiated by ${DEFAULT_TRUSTED_RELEASE_AUTHOR}.`,
|
|
630
667
|
);
|
|
631
668
|
}
|
|
632
669
|
|
|
@@ -658,7 +695,10 @@ export async function fetchReleaseMetadata({
|
|
|
658
695
|
? "GitHub tagged release lookup"
|
|
659
696
|
: "GitHub release list lookup",
|
|
660
697
|
});
|
|
661
|
-
|
|
698
|
+
const release = normalizedVersion
|
|
699
|
+
? payload
|
|
700
|
+
: selectLatestPylonRelease(payload, target);
|
|
701
|
+
return assertTrustedReleaseAuthor(release);
|
|
662
702
|
}
|
|
663
703
|
|
|
664
704
|
export function selectReleaseAssets(release, target) {
|
|
@@ -1367,6 +1407,45 @@ export async function runProcess(
|
|
|
1367
1407
|
});
|
|
1368
1408
|
}
|
|
1369
1409
|
|
|
1410
|
+
function delay(ms) {
|
|
1411
|
+
return new Promise((resolve) => {
|
|
1412
|
+
setTimeout(resolve, ms);
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
function waitForChildExit(child) {
|
|
1417
|
+
return new Promise((resolve, reject) => {
|
|
1418
|
+
child.once?.("error", reject);
|
|
1419
|
+
child.once?.("close", (code, signal) => {
|
|
1420
|
+
resolve({ code, signal });
|
|
1421
|
+
});
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
async function stopChild(child, timeoutMs = 5_000) {
|
|
1426
|
+
if (!child || child.killed || child.exitCode != null) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
const exited = waitForChildExit(child).catch(() => null);
|
|
1431
|
+
child.kill?.("SIGTERM");
|
|
1432
|
+
const result = await Promise.race([
|
|
1433
|
+
exited,
|
|
1434
|
+
delay(timeoutMs).then(() => "timeout"),
|
|
1435
|
+
]);
|
|
1436
|
+
if (result === "timeout" && child.exitCode == null) {
|
|
1437
|
+
child.kill?.("SIGKILL");
|
|
1438
|
+
await exited;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
function spawnPylonTui(pylonTuiPath, options, spawnProcessImpl) {
|
|
1443
|
+
return spawnProcessImpl(pylonTuiPath, [], {
|
|
1444
|
+
env: buildPylonEnv(options),
|
|
1445
|
+
stdio: "inherit",
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1370
1449
|
async function extractArchive(archivePath, destinationDir, runProcessImpl) {
|
|
1371
1450
|
await fs.mkdir(destinationDir, { recursive: true });
|
|
1372
1451
|
await runProcessImpl("tar", ["-xzf", archivePath, "-C", destinationDir]);
|
|
@@ -1937,6 +2016,203 @@ export async function launchInstalledPylon(
|
|
|
1937
2016
|
|
|
1938
2017
|
export const launchInstalledPylonTui = launchInstalledPylon;
|
|
1939
2018
|
|
|
2019
|
+
export async function launchInstalledPylonWithUpdates(
|
|
2020
|
+
options,
|
|
2021
|
+
{
|
|
2022
|
+
ensureReleaseInstallImpl = ensureReleaseInstall,
|
|
2023
|
+
spawnProcessImpl = spawn,
|
|
2024
|
+
updateCheckIntervalMs = DEFAULT_UPDATE_CHECK_INTERVAL_MS,
|
|
2025
|
+
onStatus = null,
|
|
2026
|
+
telemetryClient = null,
|
|
2027
|
+
...dependencies
|
|
2028
|
+
} = {},
|
|
2029
|
+
) {
|
|
2030
|
+
const currentReleaseTag =
|
|
2031
|
+
options.tagName ?? `pylon-v${normalizeVersion(options.version)}`;
|
|
2032
|
+
const updateTelemetryBase = {
|
|
2033
|
+
current_release_tag: currentReleaseTag,
|
|
2034
|
+
current_version: normalizeVersion(options.version),
|
|
2035
|
+
os: options.target?.os ?? null,
|
|
2036
|
+
arch: options.target?.arch ?? null,
|
|
2037
|
+
platform_key:
|
|
2038
|
+
options.target?.os && options.target?.arch
|
|
2039
|
+
? `${options.target.os}-${options.target.arch}`
|
|
2040
|
+
: null,
|
|
2041
|
+
install_source: installSourceForTelemetry(
|
|
2042
|
+
options.installMethod,
|
|
2043
|
+
Boolean(options.cached),
|
|
2044
|
+
),
|
|
2045
|
+
update_check_interval_ms: updateCheckIntervalMs,
|
|
2046
|
+
};
|
|
2047
|
+
|
|
2048
|
+
if (options.noUpdates || options.pinnedVersion) {
|
|
2049
|
+
emitTelemetry(
|
|
2050
|
+
telemetryClient,
|
|
2051
|
+
options.noUpdates
|
|
2052
|
+
? "installer_update_disabled"
|
|
2053
|
+
: "installer_update_pinned_run",
|
|
2054
|
+
{
|
|
2055
|
+
...updateTelemetryBase,
|
|
2056
|
+
update_mode: options.noUpdates ? "no_updates" : "pinned_version",
|
|
2057
|
+
update_reason: options.noUpdates ? "--no-updates" : "--version",
|
|
2058
|
+
},
|
|
2059
|
+
);
|
|
2060
|
+
await telemetryClient?.flush?.();
|
|
2061
|
+
return launchInstalledPylon(options, { ...dependencies, onStatus });
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
let current = {
|
|
2065
|
+
...options,
|
|
2066
|
+
version: normalizeVersion(options.version),
|
|
2067
|
+
};
|
|
2068
|
+
let lastUpdateError = null;
|
|
2069
|
+
let pendingRestartTelemetry = null;
|
|
2070
|
+
|
|
2071
|
+
while (true) {
|
|
2072
|
+
const pylonTuiPath = path.resolve(current.pylonTuiPath);
|
|
2073
|
+
emitStatus(
|
|
2074
|
+
onStatus,
|
|
2075
|
+
"Starting Pylon terminal UI",
|
|
2076
|
+
`${path.basename(pylonTuiPath)} manages the earning worker`,
|
|
2077
|
+
);
|
|
2078
|
+
if (pendingRestartTelemetry) {
|
|
2079
|
+
emitTelemetry(telemetryClient, "installer_update_restart_succeeded", {
|
|
2080
|
+
...pendingRestartTelemetry,
|
|
2081
|
+
restarted_release_tag: current.tagName,
|
|
2082
|
+
restarted_version: current.version,
|
|
2083
|
+
});
|
|
2084
|
+
pendingRestartTelemetry = null;
|
|
2085
|
+
}
|
|
2086
|
+
const child = spawnPylonTui(pylonTuiPath, current, spawnProcessImpl);
|
|
2087
|
+
const childExit = waitForChildExit(child);
|
|
2088
|
+
let restartForUpdate = false;
|
|
2089
|
+
|
|
2090
|
+
while (!restartForUpdate) {
|
|
2091
|
+
const result = await Promise.race([
|
|
2092
|
+
childExit.then((exit) => ({ type: "exit", exit })),
|
|
2093
|
+
delay(updateCheckIntervalMs).then(() => ({ type: "tick" })),
|
|
2094
|
+
]);
|
|
2095
|
+
|
|
2096
|
+
if (result.type === "exit") {
|
|
2097
|
+
const { code, signal } = result.exit;
|
|
2098
|
+
if (code === 0 || signal === "SIGTERM" || signal === "SIGINT") {
|
|
2099
|
+
return result.exit;
|
|
2100
|
+
}
|
|
2101
|
+
throw new Error(
|
|
2102
|
+
`${path.basename(pylonTuiPath)} exited with code ${
|
|
2103
|
+
code ?? "null"
|
|
2104
|
+
}${signal ? ` signal ${signal}` : ""}`,
|
|
2105
|
+
);
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
emitTelemetry(telemetryClient, "installer_update_check_started", {
|
|
2109
|
+
...updateTelemetryBase,
|
|
2110
|
+
observed_release_tag: current.tagName,
|
|
2111
|
+
observed_version: current.version,
|
|
2112
|
+
});
|
|
2113
|
+
|
|
2114
|
+
try {
|
|
2115
|
+
const install = await ensureReleaseInstallImpl(
|
|
2116
|
+
{
|
|
2117
|
+
...options,
|
|
2118
|
+
version: null,
|
|
2119
|
+
},
|
|
2120
|
+
{
|
|
2121
|
+
...dependencies,
|
|
2122
|
+
onStatus: null,
|
|
2123
|
+
},
|
|
2124
|
+
);
|
|
2125
|
+
lastUpdateError = null;
|
|
2126
|
+
if (!isNewerPylonVersion(install.version, current.version)) {
|
|
2127
|
+
continue;
|
|
2128
|
+
}
|
|
2129
|
+
emitTelemetry(telemetryClient, "installer_update_available", {
|
|
2130
|
+
...updateTelemetryBase,
|
|
2131
|
+
observed_release_tag: current.tagName,
|
|
2132
|
+
observed_version: current.version,
|
|
2133
|
+
available_release_tag: install.tagName,
|
|
2134
|
+
available_version: install.version,
|
|
2135
|
+
available_install_source: installSourceForTelemetry(
|
|
2136
|
+
install.installMethod,
|
|
2137
|
+
Boolean(install.cached),
|
|
2138
|
+
),
|
|
2139
|
+
});
|
|
2140
|
+
if (
|
|
2141
|
+
install.installMethod === RELEASE_ASSET_INSTALL_METHOD &&
|
|
2142
|
+
!install.cached
|
|
2143
|
+
) {
|
|
2144
|
+
emitTelemetry(telemetryClient, "installer_update_downloaded", {
|
|
2145
|
+
...updateTelemetryBase,
|
|
2146
|
+
observed_release_tag: current.tagName,
|
|
2147
|
+
observed_version: current.version,
|
|
2148
|
+
available_release_tag: install.tagName,
|
|
2149
|
+
available_version: install.version,
|
|
2150
|
+
});
|
|
2151
|
+
emitTelemetry(telemetryClient, "installer_update_verified", {
|
|
2152
|
+
...updateTelemetryBase,
|
|
2153
|
+
observed_release_tag: current.tagName,
|
|
2154
|
+
observed_version: current.version,
|
|
2155
|
+
available_release_tag: install.tagName,
|
|
2156
|
+
available_version: install.version,
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
emitTelemetry(telemetryClient, "installer_update_applied", {
|
|
2160
|
+
...updateTelemetryBase,
|
|
2161
|
+
previous_release_tag: current.tagName,
|
|
2162
|
+
previous_version: current.version,
|
|
2163
|
+
applied_release_tag: install.tagName,
|
|
2164
|
+
applied_version: install.version,
|
|
2165
|
+
applied_install_source: installSourceForTelemetry(
|
|
2166
|
+
install.installMethod,
|
|
2167
|
+
Boolean(install.cached),
|
|
2168
|
+
),
|
|
2169
|
+
});
|
|
2170
|
+
emitStatus(
|
|
2171
|
+
onStatus,
|
|
2172
|
+
"Installed newer Pylon release",
|
|
2173
|
+
`${install.tagName}; restarting dashboard`,
|
|
2174
|
+
);
|
|
2175
|
+
emitTelemetry(telemetryClient, "installer_update_restart_attempted", {
|
|
2176
|
+
...updateTelemetryBase,
|
|
2177
|
+
previous_release_tag: current.tagName,
|
|
2178
|
+
previous_version: current.version,
|
|
2179
|
+
restart_target_release_tag: install.tagName,
|
|
2180
|
+
restart_target_version: install.version,
|
|
2181
|
+
});
|
|
2182
|
+
await stopChild(child);
|
|
2183
|
+
pendingRestartTelemetry = {
|
|
2184
|
+
previous_release_tag: current.tagName,
|
|
2185
|
+
previous_version: current.version,
|
|
2186
|
+
applied_release_tag: install.tagName,
|
|
2187
|
+
applied_version: install.version,
|
|
2188
|
+
applied_install_source: installSourceForTelemetry(
|
|
2189
|
+
install.installMethod,
|
|
2190
|
+
Boolean(install.cached),
|
|
2191
|
+
),
|
|
2192
|
+
};
|
|
2193
|
+
current = {
|
|
2194
|
+
...options,
|
|
2195
|
+
...install,
|
|
2196
|
+
version: install.version,
|
|
2197
|
+
};
|
|
2198
|
+
restartForUpdate = true;
|
|
2199
|
+
} catch (error) {
|
|
2200
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2201
|
+
emitTelemetry(telemetryClient, "installer_update_failed", {
|
|
2202
|
+
...updateTelemetryBase,
|
|
2203
|
+
observed_release_tag: current.tagName,
|
|
2204
|
+
observed_version: current.version,
|
|
2205
|
+
...telemetryFailureContext(error, "update_check"),
|
|
2206
|
+
});
|
|
2207
|
+
if (message !== lastUpdateError) {
|
|
2208
|
+
emitStatus(onStatus, "Pylon update check failed", message);
|
|
2209
|
+
lastUpdateError = message;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
|
|
1940
2216
|
export function resolveBootstrapOutcome(summary) {
|
|
1941
2217
|
const runtimeState =
|
|
1942
2218
|
summary.status?.snapshot?.runtime?.authoritative_status ?? "unknown";
|